From 257cf986c5900289f7d21545c08b25fa5a5ef1da Mon Sep 17 00:00:00 2001 From: CitadelStationBot Date: Thu, 18 May 2017 00:37:20 -0500 Subject: [PATCH] Repaths C4 (#808) * Repaths C4 * Delete tgstation.2.1.3.dmm.rej * Add files via upload * update CitadelStation-1.2.1.dmm --- SQL/tgstation_schema.sql | 4 +- _maps/RandomZLevels/research.dmm | 4 +- _maps/RandomZLevels/snowdin.dmm | 2 +- _maps/RandomZLevels/spacebattle.dmm | 4 +- _maps/map_files/Cerestation/cerestation.dmm | 10 +- .../CitadelStation/CitadelStation-1.2.1.dmm | 2 +- _maps/map_files/TgStation/tgstation.2.1.3.dmm | 25 +- _maps/map_files/generic/Centcomm.dmm | 4 +- code/__DEFINES/jobs.dm | 2 +- code/__HELPERS/_lists.dm | 2 +- code/__HELPERS/game.dm | 1206 +++--- code/_globalvars/logging.dm | 66 +- code/_onclick/click.dm | 160 +- code/_onclick/hud/_defines.dm | 324 +- code/_onclick/hud/ghost.dm | 8 +- code/_onclick/hud/hud.dm | 4 +- code/_onclick/hud/human.dm | 24 +- code/datums/holocall.dm | 320 +- code/datums/mind.dm | 3284 ++++++++-------- code/datums/wires/explosive.dm | 6 +- code/game/gamemodes/antag_spawner.dm | 4 +- code/game/gamemodes/nuclear/pinpointer.dm | 12 +- code/game/gamemodes/wizard/spellbook.dm | 1832 ++++----- code/game/machinery/cloning.dm | 998 ++--- code/game/machinery/computer/cloning.dm | 16 +- code/game/machinery/computer/robot.dm | 6 +- .../machinery/computer/telecrystalconsoles.dm | 48 +- code/game/machinery/recycler.dm | 2 +- code/game/machinery/suit_storage_unit.dm | 18 +- code/game/mecha/equipment/weapons/weapons.dm | 6 +- code/game/objects/effects/landmarks.dm | 2 +- code/game/objects/effects/portals.dm | 2 +- code/game/objects/items/devices/PDA/cart.dm | 2 +- code/game/objects/items/devices/gps.dm | 160 +- code/game/objects/items/devices/powersink.dm | 4 +- .../objects/items/devices/transfer_valve.dm | 8 +- .../objects/items/robot/robot_upgrades.dm | 4 +- code/game/objects/items/weapons/explosives.dm | 115 - .../objects/items/weapons/grenades/plastic.dm | 109 + .../items/weapons/grenades/spawnergrenade.dm | 2 +- .../items/weapons/implants/implantchair.dm | 8 +- .../objects/items/weapons/melee/energy.dm | 16 +- .../objects/items/weapons/storage/belt.dm | 2 +- .../objects/items/weapons/vending_items.dm | 10 +- code/game/objects/items/weapons/weaponry.dm | 22 +- code/game/objects/structures/bedsheet_bin.dm | 10 +- .../crates_lockers/closets/secure/security.dm | 2 +- code/game/objects/structures/displaycase.dm | 722 ++-- code/game/objects/structures/mineral_doors.dm | 6 +- code/modules/admin/DB_ban/functions.dm | 10 +- code/modules/admin/admin.dm | 1642 ++++---- code/modules/admin/admin_investigate.dm | 40 +- code/modules/assembly/timer.dm | 4 +- .../atmospherics/machinery/other/meter.dm | 8 +- .../atmospherics/machinery/portable/pump.dm | 6 +- code/modules/awaymissions/gateway.dm | 2 +- .../awaymissions/mission_code/Academy.dm | 20 +- code/modules/client/preferences.dm | 3286 ++++++++--------- code/modules/clothing/clothing.dm | 6 +- code/modules/clothing/head/collectable.dm | 2 +- code/modules/clothing/head/jobs.dm | 2 +- code/modules/clothing/suits/jobs.dm | 2 +- code/modules/detectivework/scanner.dm | 8 +- code/modules/jobs/access.dm | 2 +- code/modules/jobs/jobs.dm | 2 +- code/modules/language/language.dm.rej | 26 +- code/modules/library/lib_readme.dm | 8 +- code/modules/mapping/ruins.dm | 6 +- code/modules/mob/dead/observer/say.dm | 50 +- .../carbon/alien/special/alien_embryo.dm | 268 +- code/modules/mob/living/carbon/human/emote.dm | 6 +- .../mob/living/carbon/human/human_defines.dm | 2 +- .../mob/living/carbon/human/inventory.dm | 6 +- .../carbon/human/species_types/flypeople.dm | 2 +- .../carbon/human/species_types/golems.dm | 1326 +++---- .../human/species_types/lizardpeople.dm | 6 +- .../carbon/human/species_types/zombies.dm | 14 +- code/modules/mob/living/death.dm | 2 +- code/modules/mob/living/silicon/silicon.dm | 2 +- .../mob/living/simple_animal/bot/bot.dm | 6 +- .../mob/living/simple_animal/bot/secbot.dm | 2 +- .../mob/living/simple_animal/friendly/cat.dm | 2 +- .../simple_animal/friendly/farm_animals.dm | 38 +- .../living/simple_animal/hostile/hivebot.dm | 2 +- .../modules/mob/living/simple_animal/shade.dm | 4 +- code/modules/paperwork/clipboard.dm | 16 +- code/modules/power/singularity/emitter.dm | 986 ++--- .../particle_accelerator/particle_control.dm | 8 +- code/modules/projectiles/ammunition/energy.dm | 2 +- code/modules/projectiles/guns/energy/pulse.dm | 4 +- code/modules/projectiles/projectile.dm | 28 +- .../chemistry/reagents/food_reagents.dm | 0 .../reagents/reagent_containers/glass.dm | 0 .../reagents/reagent_containers/spray.dm | 14 +- code/modules/shuttle/ferry.dm | 2 +- code/modules/spells/spell_types/wizard.dm | 36 +- icons/emoji.dmi | Bin interface/skin.dmf | 2 +- tgstation.dme | 1 - 99 files changed, 8768 insertions(+), 8760 deletions(-) delete mode 100644 code/game/objects/items/weapons/explosives.dm mode change 100755 => 100644 code/modules/reagents/chemistry/reagents/food_reagents.dm mode change 100755 => 100644 code/modules/reagents/reagent_containers/glass.dm mode change 100755 => 100644 icons/emoji.dmi diff --git a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql index 0d0817bf1f..fee2b77921 100644 --- a/SQL/tgstation_schema.sql +++ b/SQL/tgstation_schema.sql @@ -267,8 +267,8 @@ CREATE TABLE `player` ( `ip` int(10) unsigned NOT NULL, `computerid` varchar(32) NOT NULL, `lastadminrank` varchar(32) NOT NULL DEFAULT 'Player', - `accountjoindate` DATE DEFAULT NULL, - PRIMARY KEY (`ckey`), + `accountjoindate` DATE DEFAULT NULL, + PRIMARY KEY (`ckey`), KEY `idx_player_cid_ckey` (`computerid`,`ckey`), KEY `idx_player_ip_ckey` (`ip`,`ckey`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; diff --git a/_maps/RandomZLevels/research.dmm b/_maps/RandomZLevels/research.dmm index 8ff8431a91..9bf2719f21 100644 --- a/_maps/RandomZLevels/research.dmm +++ b/_maps/RandomZLevels/research.dmm @@ -198,8 +198,8 @@ /area/awaymission/research/interior/engineering) "aL" = ( /obj/structure/table, -/obj/item/weapon/c4, -/obj/item/weapon/c4, +/obj/item/weapon/grenade/plastic/c4, +/obj/item/weapon/grenade/plastic/c4, /obj/item/weapon/storage/toolbox/syndicate, /turf/open/floor/mineral/plastitanium, /area/awaymission/research/interior/engineering) diff --git a/_maps/RandomZLevels/snowdin.dmm b/_maps/RandomZLevels/snowdin.dmm index a895d214ae..bac50dd89a 100644 --- a/_maps/RandomZLevels/snowdin.dmm +++ b/_maps/RandomZLevels/snowdin.dmm @@ -3948,7 +3948,7 @@ }, /area/awaymission/snowdin) "kB" = ( -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 2; pixel_y = 1 }, diff --git a/_maps/RandomZLevels/spacebattle.dmm b/_maps/RandomZLevels/spacebattle.dmm index 3ffc9f9a6a..699229e6db 100644 --- a/_maps/RandomZLevels/spacebattle.dmm +++ b/_maps/RandomZLevels/spacebattle.dmm @@ -357,7 +357,7 @@ /area/awaymission/spacebattle/syndicate2) "bl" = ( /obj/structure/table/reinforced, -/obj/item/weapon/c4, +/obj/item/weapon/grenade/plastic/c4, /turf/open/floor/mineral/plastitanium, /area/awaymission/spacebattle/syndicate3) "bm" = ( @@ -366,7 +366,7 @@ /area/awaymission/spacebattle/syndicate1) "bn" = ( /obj/structure/table/reinforced, -/obj/item/weapon/c4, +/obj/item/weapon/grenade/plastic/c4, /turf/open/floor/mineral/plastitanium, /area/awaymission/spacebattle/syndicate1) "bo" = ( diff --git a/_maps/map_files/Cerestation/cerestation.dmm b/_maps/map_files/Cerestation/cerestation.dmm index 1edf7431b1..1d73b4ba36 100644 --- a/_maps/map_files/Cerestation/cerestation.dmm +++ b/_maps/map_files/Cerestation/cerestation.dmm @@ -1073,23 +1073,23 @@ /area/ai_monitored/turret_protected/ai) "acq" = ( /obj/structure/table, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 2; pixel_y = -5 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = -3; pixel_y = 3 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 2; pixel_y = -3 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = -2; pixel_y = -1 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 3; pixel_y = 3 }, diff --git a/_maps/map_files/CitadelStation/CitadelStation-1.2.1.dmm b/_maps/map_files/CitadelStation/CitadelStation-1.2.1.dmm index 84fb806587..cbeb81de8f 100644 --- a/_maps/map_files/CitadelStation/CitadelStation-1.2.1.dmm +++ b/_maps/map_files/CitadelStation/CitadelStation-1.2.1.dmm @@ -114,7 +114,7 @@ "acj" = (/obj/machinery/light/small,/obj/structure/table,/turf/open/floor/plasteel,/area/security/processing{name = "Permabrig"}) "ack" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{req_access_txt = 1},/obj/machinery/atmospherics/components/unary/vent_scrubber{dir = 4;name = "regular air scrubber";on = 1;scrub_N2O = 0;scrub_Toxins = 0},/obj/machinery/computer/security/telescreen{desc = "Used for watching Prison Wing holding areas.";dir = 4;name = "Prison Monitor";network = list("Prison");pixel_x = -30;pixel_y = 0},/turf/open/floor/plasteel/red/side{dir = 8},/area/security/processing{name = "Permabrig"}) "acl" = (/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{dir = 4;initialize_directions = 11},/obj/structure/cable{tag = "icon-1-2";icon_state = "1-2"},/turf/open/floor/plasteel/red/side{dir = 4},/area/security/processing{name = "Permabrig"}) -"acm" = (/obj/structure/table,/obj/item/weapon/c4{pixel_x = 2;pixel_y = -5},/obj/item/weapon/c4{pixel_x = -3;pixel_y = 3},/obj/item/weapon/c4{pixel_x = 2;pixel_y = -3},/obj/item/weapon/c4{pixel_x = -2;pixel_y = -1},/obj/item/weapon/c4{pixel_x = 3;pixel_y = 3},/obj/machinery/light{dir = 4},/turf/open/floor/mineral/plastitanium,/area/shuttle/syndicate) +"acm" = (/obj/structure/table,/obj/item/weapon/grenade/plastic/c4{pixel_x = 2;pixel_y = -5},/obj/item/weapon/grenade/plastic/c4{pixel_x = -3;pixel_y = 3},/obj/item/weapon/grenade/plastic/c4{pixel_x = 2;pixel_y = -3},/obj/item/weapon/grenade/plastic/c4{pixel_x = -2;pixel_y = -1},/obj/item/weapon/grenade/plastic/c4{pixel_x = 3;pixel_y = 3},/obj/machinery/light{dir = 4},/turf/open/floor/mineral/plastitanium,/area/shuttle/syndicate) "acn" = (/obj/structure/grille,/obj/structure/window/reinforced/fulltile,/turf/open/floor/plating,/area/shuttle/syndicate) "aco" = (/obj/machinery/door/window{name = "Ready Room";req_access_txt = "150"},/turf/open/floor/mineral/plastitanium,/area/shuttle/syndicate) "acp" = (/obj/machinery/light{dir = 8},/obj/structure/sign/electricshock{pixel_x = -32},/turf/open/floor/plasteel/green,/area/security/processing{name = "Permabrig"}) diff --git a/_maps/map_files/TgStation/tgstation.2.1.3.dmm b/_maps/map_files/TgStation/tgstation.2.1.3.dmm index fb03151ef2..a1dbe47799 100644 --- a/_maps/map_files/TgStation/tgstation.2.1.3.dmm +++ b/_maps/map_files/TgStation/tgstation.2.1.3.dmm @@ -62007,11 +62007,26 @@ /obj/structure/window/reinforced{ dir = 4 }, -/obj/item/weapon/grenade/plastic/c4, -/obj/item/weapon/grenade/plastic/c4, -/obj/item/weapon/grenade/plastic/c4, -/obj/item/weapon/grenade/plastic/c4, -/obj/item/weapon/grenade/plastic/c4, +/obj/item/weapon/grenade/plastic/c4{ + pixel_x = 2; + pixel_y = -5 + }, +/obj/item/weapon/grenade/plastic/c4{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/weapon/grenade/plastic/c4{ + pixel_x = 2; + pixel_y = -3 + }, +/obj/item/weapon/grenade/plastic/c4{ + pixel_x = -2; + pixel_y = -1 + }, +/obj/item/weapon/grenade/plastic/c4{ + pixel_x = 3; + pixel_y = 3 + }, /turf/open/floor/plasteel/vault{ dir = 8 }, diff --git a/_maps/map_files/generic/Centcomm.dmm b/_maps/map_files/generic/Centcomm.dmm index e3870a49f8..ba62ea2357 100644 --- a/_maps/map_files/generic/Centcomm.dmm +++ b/_maps/map_files/generic/Centcomm.dmm @@ -8913,10 +8913,10 @@ /area/centcom/ferry) "vZ" = ( /obj/structure/table/reinforced, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = 6 }, -/obj/item/weapon/c4{ +/obj/item/weapon/grenade/plastic/c4{ pixel_x = -4 }, /obj/machinery/firealarm{ diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index cea01afc2a..ae58443302 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -32,7 +32,7 @@ #define BOTANIST (1<<2) #define COOK (1<<3) #define JANITOR (1<<4) -#define CURATOR (1<<5) +#define CURATOR (1<<5) #define QUARTERMASTER (1<<6) #define CARGOTECH (1<<7) #define MINER (1<<8) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 8f340bff46..7b5da91e03 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -457,7 +457,7 @@ #define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null) #define LAZYLEN(L) length(L) #define LAZYCLEARLIST(L) if(L) L.Cut() -#define SANITIZE_LIST(L) ( islist(L) ? L : list() ) +#define SANITIZE_LIST(L) ( islist(L) ? L : list() ) /* Definining a counter as a series of key -> numeric value entries diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index db405d2878..e93de5dccc 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -1,603 +1,603 @@ -//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a -#define RANGE_TURFS(RADIUS, CENTER) \ - block( \ - locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \ - locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \ - ) - -#define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL)) -#define CULT_POLL_WAIT 2400 - -/proc/get_area(atom/A) - if (!istype(A)) - return - for(A, A && !isarea(A), A=A.loc); //semicolon is for the empty statement - return A - -/proc/get_area_name(atom/X) - var/area/Y = get_area(X) - return Y.name - -/proc/get_area_by_name(N) //get area by its name - for(var/area/A in world) - if(A.name == N) - return A - return 0 - -/proc/get_areas_in_range(dist=0, atom/center=usr) - if(!dist) - var/turf/T = get_turf(center) - return T ? list(T.loc) : list() - if(!center) - return list() - - var/list/turfs = RANGE_TURFS(dist, center) - var/list/areas = list() - for(var/V in turfs) - var/turf/T = V - areas |= T.loc - return areas - -/proc/get_adjacent_areas(atom/center) - . = list(get_area(get_ranged_target_turf(center, NORTH, 1)), - get_area(get_ranged_target_turf(center, SOUTH, 1)), - get_area(get_ranged_target_turf(center, EAST, 1)), - get_area(get_ranged_target_turf(center, WEST, 1))) - listclearnulls(.) - -/proc/get_open_turf_in_dir(atom/center, dir) - var/turf/open/T = get_ranged_target_turf(center, dir, 1) - if(istype(T)) - return T - -/proc/get_adjacent_open_turfs(atom/center) - . = list(get_open_turf_in_dir(center, NORTH), - get_open_turf_in_dir(center, SOUTH), - get_open_turf_in_dir(center, EAST), - get_open_turf_in_dir(center, WEST)) - listclearnulls(.) - -/proc/get_adjacent_open_areas(atom/center) - . = list() - var/list/adjacent_turfs = get_adjacent_open_turfs(center) - for(var/I in adjacent_turfs) - . |= get_area(I) - -// Like view but bypasses luminosity check - -/proc/get_hear(range, atom/source) - - var/lum = source.luminosity - source.luminosity = 6 - - var/list/heard = view(range, source) - source.luminosity = lum - - return heard - -/proc/alone_in_area(area/the_area, mob/must_be_alone, check_type = /mob/living/carbon) - var/area/our_area = get_area(the_area) - for(var/C in GLOB.living_mob_list) - if(!istype(C, check_type)) - continue - if(C == must_be_alone) - continue - if(our_area == get_area(C)) - return 0 - return 1 - -//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster. -//And lo and behold, it is, and it's more accurate to boot. -/proc/cheap_hypotenuse(Ax,Ay,Bx,By) - return sqrt(abs(Ax - Bx)**2 + abs(Ay - By)**2) //A squared + B squared = C squared - -/proc/circlerange(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - - //turfs += centerturf - return turfs - -/proc/circleview(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/atoms = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/A in view(radius, centerturf)) - var/dx = A.x - centerturf.x - var/dy = A.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - atoms += A - - //turfs += centerturf - return atoms - -/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) - var/dx = Loc1.x - Loc2.x - var/dy = Loc1.y - Loc2.y - - var/dist = sqrt(dx**2 + dy**2) - - return dist - -/proc/circlerangeturfs(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - -/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in view(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - - -//This is the new version of recursive_mob_check, used for say(). -//The other proc was left intact because morgue trays use it. -//Sped this up again for real this time -/proc/recursive_hear_check(O) - var/list/processing_list = list(O) - . = list() - while(processing_list.len) - var/atom/A = processing_list[1] - if(A.flags & HEAR) - . += A - processing_list.Cut(1, 2) - processing_list += A.contents - -// Better recursive loop, technically sort of not actually recursive cause that shit is retarded, enjoy. -//No need for a recursive limit either -/proc/recursive_mob_check(atom/O,client_check=1,sight_check=1,include_radio=1) - - var/list/processing_list = list(O) - var/list/processed_list = list() - var/list/found_mobs = list() - - while(processing_list.len) - - var/atom/A = processing_list[1] - var/passed = 0 - - if(ismob(A)) - var/mob/A_tmp = A - passed=1 - - if(client_check && !A_tmp.client) - passed=0 - - if(sight_check && !isInSight(A_tmp, O)) - passed=0 - - else if(include_radio && istype(A, /obj/item/device/radio)) - passed=1 - - if(sight_check && !isInSight(A, O)) - passed=0 - - if(passed) - found_mobs |= A - - for(var/atom/B in A) - if(!processed_list[B]) - processing_list |= B - - processing_list.Cut(1, 2) - processed_list[A] = A - - return found_mobs - - -/proc/get_hearers_in_view(R, atom/source) - // Returns a list of hearers in view(R) from source (ignoring luminosity). Used in saycode. - var/turf/T = get_turf(source) - var/list/hear = list() - - if(!T) - return hear - - var/list/range = get_hear(R, T) - for(var/atom/movable/A in range) - hear |= recursive_hear_check(A) - - return hear - - -/proc/get_mobs_in_radio_ranges(list/obj/item/device/radio/radios) - - set background = BACKGROUND_ENABLED - - . = list() - // Returns a list of mobs who can hear any of the radios given in @radios - for(var/obj/item/device/radio/R in radios) - if(R) - . |= get_hearers_in_view(R.canhear_range, R) - - -#define SIGN(X) ((X<0)?-1:1) - -/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) - var/turf/T - if(X1==X2) - if(Y1==Y2) - return 1 //Light cannot be blocked on same tile - else - var/s = SIGN(Y2-Y1) - Y1+=s - while(Y1!=Y2) - T=locate(X1,Y1,Z) - if(T.opacity) - return 0 - Y1+=s - else - var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) - var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles - var/signX = SIGN(X2-X1) - var/signY = SIGN(Y2-Y1) - if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) - if(dy > 0) - return get_step(start, SOUTH) - else - return get_step(start, NORTH) - else - if(dx > 0) - return get_step(start, WEST) - else - return get_step(start, EAST) - -/proc/try_move_adjacent(atom/movable/AM) - var/turf/T = get_turf(AM) - for(var/direction in GLOB.cardinal) - if(AM.Move(get_step(T, direction))) - break - -/proc/get_mob_by_key(key) - for(var/mob/M in GLOB.mob_list) - if(M.ckey == lowertext(key)) - return M - return null - -// Will return a list of active candidates. It increases the buffer 5 times until it finds a candidate which is active within the buffer. - -/proc/get_candidates(be_special_type, afk_bracket = config.inactivity_period, jobbanType) - var/list/candidates = list() - // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) - while(!candidates.len && afk_bracket < config.afk_period) - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.client != null) - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) - if (jobbanType) - if(!(jobban_isbanned(G, jobbanType) || jobban_isbanned(G, "Syndicate"))) - candidates += G.client - else - candidates += G.client - afk_bracket += 600 // Add a minute to the bracket, for every attempt - return candidates - -/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) - if(!isobj(O)) - O = new /obj/screen/text() - O.maptext = maptext - O.maptext_height = maptext_height - O.maptext_width = maptext_width - O.screen_loc = screen_loc - return O - -/proc/remove_images_from_clients(image/I, list/show_to) - for(var/client/C in show_to) - C.images -= I - -/proc/flick_overlay(image/I, list/show_to, duration) - for(var/client/C in show_to) - C.images += I - addtimer(CALLBACK(GLOBAL_PROC, /.proc/remove_images_from_clients, I, show_to), duration) - -/proc/flick_overlay_view(image/I, atom/target, duration) //wrapper for the above, flicks to everyone who can see the target atom - var/list/viewing = list() - for(var/m in viewers(target)) - var/mob/M = m - if(M.client) - viewing += M.client - flick_overlay(I, viewing, duration) - -/proc/get_active_player_count(var/alive_check = 0, var/afk_check = 0, var/human_check = 0) - // Get active players who are playing in the round - var/active_players = 0 - for(var/i = 1; i <= GLOB.player_list.len; i++) - var/mob/M = GLOB.player_list[i] - if(M && M.client) - if(alive_check && M.stat) - continue - else if(afk_check && M.client.is_afk()) - continue - else if(human_check && !ishuman(M)) - continue - else if(isnewplayer(M)) // exclude people in the lobby - continue - else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers) - var/mob/dead/observer/O = M - if(O.started_as_observer) // Exclude people who started as observers - continue - active_players++ - return active_players - -/datum/projectile_data - var/src_x - var/src_y - var/time - var/distance - var/power_x - var/power_y - var/dest_x - var/dest_y - -/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ - var/power_x, var/power_y, var/dest_x, var/dest_y) - src.src_x = src_x - src.src_y = src_y - src.time = time - src.distance = distance - src.power_x = power_x - src.power_y = power_y - src.dest_x = dest_x - src.dest_y = dest_y - -/proc/projectile_trajectory(src_x, src_y, rotation, angle, power) - - // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], - // rotated at [rotation] and with the power of [power] - // Thanks to VistaPOWA for this function - - var/power_x = power * cos(angle) - var/power_y = power * sin(angle) - var/time = 2* power_y / 10 //10 = g - - var/distance = time * power_x - - var/dest_x = src_x + distance*sin(rotation); - var/dest_y = src_y + distance*cos(rotation); - - return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) - -/proc/showCandidatePollWindow(mob/M, poll_time, Question, list/candidates, ignore_category, time_passed, flashwindow = TRUE) - set waitfor = 0 - - M << 'sound/misc/notice2.ogg' //Alerting them to their consideration - if(flashwindow) - window_flash(M.client) - switch(ignore_category ? askuser(M,Question,"Please answer in [poll_time/10] seconds!","Yes","No","Never for this round", StealFocus=0, Timeout=poll_time) : askuser(M,Question,"Please answer in [poll_time/10] seconds!","Yes","No", StealFocus=0, Timeout=poll_time)) - if(1) - to_chat(M, "Choice registered: Yes.") - if((world.time-time_passed)>poll_time) - to_chat(M, "Sorry, you answered too late to be considered!") - M << 'sound/machines/buzz-sigh.ogg' - candidates -= M - else - candidates += M - if(2) - to_chat(M, "Choice registered: No.") - candidates -= M - if(3) - var/list/L = GLOB.poll_ignore[ignore_category] - if(!L) - GLOB.poll_ignore[ignore_category] = list() - GLOB.poll_ignore[ignore_category] += M.ckey - to_chat(M, "Choice registered: Never for this round.") - candidates -= M - else - candidates -= M - -/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE) - var/list/candidates = list() - - for(var/mob/dead/observer/G in GLOB.player_list) - candidates += G - - pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates) - - return candidates - -/proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null) - var/time_passed = world.time - if (!Question) - Question = "Would you like to be a special role?" - - for(var/m in group) - var/mob/M = m - if(!M.key || !M.client || (ignore_category && GLOB.poll_ignore[ignore_category] && M.ckey in GLOB.poll_ignore[ignore_category])) - continue - if(be_special_flag) - if(!(M.client.prefs) || !(be_special_flag in M.client.prefs.be_special)) - continue - if(gametypeCheck) - if(!gametypeCheck.age_check(M.client)) - continue - if(jobbanType) - if(jobban_isbanned(M, jobbanType) || jobban_isbanned(M, "Syndicate")) - continue - - showCandidatePollWindow(M, poll_time, Question, group, ignore_category, time_passed, flashwindow) - sleep(poll_time) - - //Check all our candidates, to make sure they didn't log off or get deleted during the wait period. - for(var/mob/M in group) - if(!M.key || !M.client) - group -= M - - listclearnulls(group) - - return group - -/proc/pollCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null) - var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) - if(!M || QDELETED(M) || !M.loc) - return list() - return L - -/proc/pollCandidatesForMobs(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null) - var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) - var/i=1 - for(var/v in mobs) - var/atom/A = v - if(!A || QDELETED(A) || !A.loc) - mobs.Cut(i,i+1) - else - ++i - return L - -/proc/pollCultists(var/mob/living/Nominee) // Cult Master Poll - if(world.time < CULT_POLL_WAIT) - to_chat(Nominee, "It would be premature to select a leader while everyone is still settling in, try again in [round((CULT_POLL_WAIT-world.time)/10)] seconds.") - return - for(var/datum/mind/B in SSticker.mode.cult) - if(B.current) - B.current.verbs -= /mob/living/proc/cult_master - if(!B.current.incapacitated()) - B.current << 'sound/hallucinations/im_here1.ogg' - to_chat(B.current, "Acolyte [Nominee] has asserted that they are worthy of leading the cult. A vote will be called shortly.") - sleep(250) - var/list/asked_cultists = list() - for(var/datum/mind/B in SSticker.mode.cult) - if(B.current && B.current != Nominee && !B.current.incapacitated()) - B.current << 'sound/magic/exit_blood.ogg' - asked_cultists += B.current - var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 1200, group = asked_cultists) - sleep(300) - if(QDELETED(Nominee) || Nominee.incapacitated()) - for(var/datum/mind/B in SSticker.mode.cult) - if(B.current) - B.current.verbs += /mob/living/proc/cult_master - if(!B.current.incapacitated()) - to_chat(B.current,"[Nominee] has died in the process of attempting to win the cult's support!") - return FALSE - if(!Nominee.mind) - for(var/datum/mind/B in SSticker.mode.cult) - if(B.current) - B.current.verbs += /mob/living/proc/cult_master - if(!B.current.incapacitated()) - to_chat(B.current,"[Nominee] has gone insane and catatonic in the process of attempting to win the cult's support!") - return FALSE - if(LAZYLEN(yes_voters) <= LAZYLEN(asked_cultists) * 0.5) - for(var/datum/mind/B in SSticker.mode.cult) - if(B.current) - B.current.verbs += /mob/living/proc/cult_master - if(!B.current.incapacitated()) - to_chat(B.current, "[Nominee] could not win the cult's support and shall continue to serve as an acolyte.") - return FALSE - SSticker.mode.remove_cultist(Nominee.mind, FALSE) - Nominee.mind.add_antag_datum(ANTAG_DATUM_CULT_MASTER) - GLOB.cult_mastered = TRUE - for(var/datum/mind/B in SSticker.mode.cult) - if(!B.current.incapacitated()) - to_chat(B.current,"[Nominee] has won the cult's support and is now their master. Follow [Nominee.p_their()] orders to the best of your ability!") - return TRUE - -/proc/poll_helper(var/mob/living/M) - -/proc/makeBody(mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character - if(!G_found || !G_found.key) - return - - //First we spawn a dude. - var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))//The mob being spawned. - - G_found.client.prefs.copy_to(new_character) - new_character.dna.update_dna_identity() - new_character.key = G_found.key - - return new_character - -/proc/send_to_playing_players(thing) //sends a whatever to all playing players; use instead of to_chat(world, where needed) - for(var/M in GLOB.player_list) - if(M && !isnewplayer(M)) - to_chat(M, thing) - -/proc/window_flash(client/C, ignorepref = FALSE) - if(ismob(C)) - var/mob/M = C - if(M.client) - C = M.client - if(!C || (!C.prefs.windowflashing && !ignorepref)) - return - winset(C, "mainwindow", "flash=5") - -/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank) - if(!SSticker.IsRoundInProgress() || !character) - return - var/area/A = get_area(character) - var/message = "\ - [character.real_name] ([rank]) has arrived at the station at \ - [A.name]." - deadchat_broadcast(message, follow_target = character, message_type=DEADCHAT_ARRIVALRATTLE) - if((!GLOB.announcement_systems.len) || (!character.mind)) - return - if((character.mind.assigned_role == "Cyborg") || (character.mind.assigned_role == character.mind.special_role)) - return - - var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems) - announcer.announce("ARRIVAL", character.real_name, rank, list()) //make the list empty to make it announce it in common - -/proc/GetRedPart(const/hexa) - return hex2num(copytext(hexa, 2, 4)) - -/proc/GetGreenPart(const/hexa) - return hex2num(copytext(hexa, 4, 6)) - -/proc/GetBluePart(const/hexa) - return hex2num(copytext(hexa, 6, 8)) +//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a +#define RANGE_TURFS(RADIUS, CENTER) \ + block( \ + locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \ + locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \ + ) + +#define Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL)) +#define CULT_POLL_WAIT 2400 + +/proc/get_area(atom/A) + if (!istype(A)) + return + for(A, A && !isarea(A), A=A.loc); //semicolon is for the empty statement + return A + +/proc/get_area_name(atom/X) + var/area/Y = get_area(X) + return Y.name + +/proc/get_area_by_name(N) //get area by its name + for(var/area/A in world) + if(A.name == N) + return A + return 0 + +/proc/get_areas_in_range(dist=0, atom/center=usr) + if(!dist) + var/turf/T = get_turf(center) + return T ? list(T.loc) : list() + if(!center) + return list() + + var/list/turfs = RANGE_TURFS(dist, center) + var/list/areas = list() + for(var/V in turfs) + var/turf/T = V + areas |= T.loc + return areas + +/proc/get_adjacent_areas(atom/center) + . = list(get_area(get_ranged_target_turf(center, NORTH, 1)), + get_area(get_ranged_target_turf(center, SOUTH, 1)), + get_area(get_ranged_target_turf(center, EAST, 1)), + get_area(get_ranged_target_turf(center, WEST, 1))) + listclearnulls(.) + +/proc/get_open_turf_in_dir(atom/center, dir) + var/turf/open/T = get_ranged_target_turf(center, dir, 1) + if(istype(T)) + return T + +/proc/get_adjacent_open_turfs(atom/center) + . = list(get_open_turf_in_dir(center, NORTH), + get_open_turf_in_dir(center, SOUTH), + get_open_turf_in_dir(center, EAST), + get_open_turf_in_dir(center, WEST)) + listclearnulls(.) + +/proc/get_adjacent_open_areas(atom/center) + . = list() + var/list/adjacent_turfs = get_adjacent_open_turfs(center) + for(var/I in adjacent_turfs) + . |= get_area(I) + +// Like view but bypasses luminosity check + +/proc/get_hear(range, atom/source) + + var/lum = source.luminosity + source.luminosity = 6 + + var/list/heard = view(range, source) + source.luminosity = lum + + return heard + +/proc/alone_in_area(area/the_area, mob/must_be_alone, check_type = /mob/living/carbon) + var/area/our_area = get_area(the_area) + for(var/C in GLOB.living_mob_list) + if(!istype(C, check_type)) + continue + if(C == must_be_alone) + continue + if(our_area == get_area(C)) + return 0 + return 1 + +//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster. +//And lo and behold, it is, and it's more accurate to boot. +/proc/cheap_hypotenuse(Ax,Ay,Bx,By) + return sqrt(abs(Ax - Bx)**2 + abs(Ay - By)**2) //A squared + B squared = C squared + +/proc/circlerange(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + + //turfs += centerturf + return turfs + +/proc/circleview(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/atoms = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/A in view(radius, centerturf)) + var/dx = A.x - centerturf.x + var/dy = A.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + atoms += A + + //turfs += centerturf + return atoms + +/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) + var/dx = Loc1.x - Loc2.x + var/dy = Loc1.y - Loc2.y + + var/dist = sqrt(dx**2 + dy**2) + + return dist + +/proc/circlerangeturfs(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + +/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in view(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + + +//This is the new version of recursive_mob_check, used for say(). +//The other proc was left intact because morgue trays use it. +//Sped this up again for real this time +/proc/recursive_hear_check(O) + var/list/processing_list = list(O) + . = list() + while(processing_list.len) + var/atom/A = processing_list[1] + if(A.flags & HEAR) + . += A + processing_list.Cut(1, 2) + processing_list += A.contents + +// Better recursive loop, technically sort of not actually recursive cause that shit is retarded, enjoy. +//No need for a recursive limit either +/proc/recursive_mob_check(atom/O,client_check=1,sight_check=1,include_radio=1) + + var/list/processing_list = list(O) + var/list/processed_list = list() + var/list/found_mobs = list() + + while(processing_list.len) + + var/atom/A = processing_list[1] + var/passed = 0 + + if(ismob(A)) + var/mob/A_tmp = A + passed=1 + + if(client_check && !A_tmp.client) + passed=0 + + if(sight_check && !isInSight(A_tmp, O)) + passed=0 + + else if(include_radio && istype(A, /obj/item/device/radio)) + passed=1 + + if(sight_check && !isInSight(A, O)) + passed=0 + + if(passed) + found_mobs |= A + + for(var/atom/B in A) + if(!processed_list[B]) + processing_list |= B + + processing_list.Cut(1, 2) + processed_list[A] = A + + return found_mobs + + +/proc/get_hearers_in_view(R, atom/source) + // Returns a list of hearers in view(R) from source (ignoring luminosity). Used in saycode. + var/turf/T = get_turf(source) + var/list/hear = list() + + if(!T) + return hear + + var/list/range = get_hear(R, T) + for(var/atom/movable/A in range) + hear |= recursive_hear_check(A) + + return hear + + +/proc/get_mobs_in_radio_ranges(list/obj/item/device/radio/radios) + + set background = BACKGROUND_ENABLED + + . = list() + // Returns a list of mobs who can hear any of the radios given in @radios + for(var/obj/item/device/radio/R in radios) + if(R) + . |= get_hearers_in_view(R.canhear_range, R) + + +#define SIGN(X) ((X<0)?-1:1) + +/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) + var/turf/T + if(X1==X2) + if(Y1==Y2) + return 1 //Light cannot be blocked on same tile + else + var/s = SIGN(Y2-Y1) + Y1+=s + while(Y1!=Y2) + T=locate(X1,Y1,Z) + if(T.opacity) + return 0 + Y1+=s + else + var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) + var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles + var/signX = SIGN(X2-X1) + var/signY = SIGN(Y2-Y1) + if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) + if(dy > 0) + return get_step(start, SOUTH) + else + return get_step(start, NORTH) + else + if(dx > 0) + return get_step(start, WEST) + else + return get_step(start, EAST) + +/proc/try_move_adjacent(atom/movable/AM) + var/turf/T = get_turf(AM) + for(var/direction in GLOB.cardinal) + if(AM.Move(get_step(T, direction))) + break + +/proc/get_mob_by_key(key) + for(var/mob/M in GLOB.mob_list) + if(M.ckey == lowertext(key)) + return M + return null + +// Will return a list of active candidates. It increases the buffer 5 times until it finds a candidate which is active within the buffer. + +/proc/get_candidates(be_special_type, afk_bracket = config.inactivity_period, jobbanType) + var/list/candidates = list() + // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) + while(!candidates.len && afk_bracket < config.afk_period) + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.client != null) + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) + if (jobbanType) + if(!(jobban_isbanned(G, jobbanType) || jobban_isbanned(G, "Syndicate"))) + candidates += G.client + else + candidates += G.client + afk_bracket += 600 // Add a minute to the bracket, for every attempt + return candidates + +/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) + if(!isobj(O)) + O = new /obj/screen/text() + O.maptext = maptext + O.maptext_height = maptext_height + O.maptext_width = maptext_width + O.screen_loc = screen_loc + return O + +/proc/remove_images_from_clients(image/I, list/show_to) + for(var/client/C in show_to) + C.images -= I + +/proc/flick_overlay(image/I, list/show_to, duration) + for(var/client/C in show_to) + C.images += I + addtimer(CALLBACK(GLOBAL_PROC, /.proc/remove_images_from_clients, I, show_to), duration) + +/proc/flick_overlay_view(image/I, atom/target, duration) //wrapper for the above, flicks to everyone who can see the target atom + var/list/viewing = list() + for(var/m in viewers(target)) + var/mob/M = m + if(M.client) + viewing += M.client + flick_overlay(I, viewing, duration) + +/proc/get_active_player_count(var/alive_check = 0, var/afk_check = 0, var/human_check = 0) + // Get active players who are playing in the round + var/active_players = 0 + for(var/i = 1; i <= GLOB.player_list.len; i++) + var/mob/M = GLOB.player_list[i] + if(M && M.client) + if(alive_check && M.stat) + continue + else if(afk_check && M.client.is_afk()) + continue + else if(human_check && !ishuman(M)) + continue + else if(isnewplayer(M)) // exclude people in the lobby + continue + else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers) + var/mob/dead/observer/O = M + if(O.started_as_observer) // Exclude people who started as observers + continue + active_players++ + return active_players + +/datum/projectile_data + var/src_x + var/src_y + var/time + var/distance + var/power_x + var/power_y + var/dest_x + var/dest_y + +/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ + var/power_x, var/power_y, var/dest_x, var/dest_y) + src.src_x = src_x + src.src_y = src_y + src.time = time + src.distance = distance + src.power_x = power_x + src.power_y = power_y + src.dest_x = dest_x + src.dest_y = dest_y + +/proc/projectile_trajectory(src_x, src_y, rotation, angle, power) + + // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], + // rotated at [rotation] and with the power of [power] + // Thanks to VistaPOWA for this function + + var/power_x = power * cos(angle) + var/power_y = power * sin(angle) + var/time = 2* power_y / 10 //10 = g + + var/distance = time * power_x + + var/dest_x = src_x + distance*sin(rotation); + var/dest_y = src_y + distance*cos(rotation); + + return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) + +/proc/showCandidatePollWindow(mob/M, poll_time, Question, list/candidates, ignore_category, time_passed, flashwindow = TRUE) + set waitfor = 0 + + M << 'sound/misc/notice2.ogg' //Alerting them to their consideration + if(flashwindow) + window_flash(M.client) + switch(ignore_category ? askuser(M,Question,"Please answer in [poll_time/10] seconds!","Yes","No","Never for this round", StealFocus=0, Timeout=poll_time) : askuser(M,Question,"Please answer in [poll_time/10] seconds!","Yes","No", StealFocus=0, Timeout=poll_time)) + if(1) + to_chat(M, "Choice registered: Yes.") + if((world.time-time_passed)>poll_time) + to_chat(M, "Sorry, you answered too late to be considered!") + M << 'sound/machines/buzz-sigh.ogg' + candidates -= M + else + candidates += M + if(2) + to_chat(M, "Choice registered: No.") + candidates -= M + if(3) + var/list/L = GLOB.poll_ignore[ignore_category] + if(!L) + GLOB.poll_ignore[ignore_category] = list() + GLOB.poll_ignore[ignore_category] += M.ckey + to_chat(M, "Choice registered: Never for this round.") + candidates -= M + else + candidates -= M + +/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE) + var/list/candidates = list() + + for(var/mob/dead/observer/G in GLOB.player_list) + candidates += G + + pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates) + + return candidates + +/proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null) + var/time_passed = world.time + if (!Question) + Question = "Would you like to be a special role?" + + for(var/m in group) + var/mob/M = m + if(!M.key || !M.client || (ignore_category && GLOB.poll_ignore[ignore_category] && M.ckey in GLOB.poll_ignore[ignore_category])) + continue + if(be_special_flag) + if(!(M.client.prefs) || !(be_special_flag in M.client.prefs.be_special)) + continue + if(gametypeCheck) + if(!gametypeCheck.age_check(M.client)) + continue + if(jobbanType) + if(jobban_isbanned(M, jobbanType) || jobban_isbanned(M, "Syndicate")) + continue + + showCandidatePollWindow(M, poll_time, Question, group, ignore_category, time_passed, flashwindow) + sleep(poll_time) + + //Check all our candidates, to make sure they didn't log off or get deleted during the wait period. + for(var/mob/M in group) + if(!M.key || !M.client) + group -= M + + listclearnulls(group) + + return group + +/proc/pollCandidatesForMob(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, mob/M, ignore_category = null) + var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) + if(!M || QDELETED(M) || !M.loc) + return list() + return L + +/proc/pollCandidatesForMobs(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, list/mobs, ignore_category = null) + var/list/L = pollGhostCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category) + var/i=1 + for(var/v in mobs) + var/atom/A = v + if(!A || QDELETED(A) || !A.loc) + mobs.Cut(i,i+1) + else + ++i + return L + +/proc/pollCultists(var/mob/living/Nominee) // Cult Master Poll + if(world.time < CULT_POLL_WAIT) + to_chat(Nominee, "It would be premature to select a leader while everyone is still settling in, try again in [round((CULT_POLL_WAIT-world.time)/10)] seconds.") + return + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current) + B.current.verbs -= /mob/living/proc/cult_master + if(!B.current.incapacitated()) + B.current << 'sound/hallucinations/im_here1.ogg' + to_chat(B.current, "Acolyte [Nominee] has asserted that they are worthy of leading the cult. A vote will be called shortly.") + sleep(250) + var/list/asked_cultists = list() + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current && B.current != Nominee && !B.current.incapacitated()) + B.current << 'sound/magic/exit_blood.ogg' + asked_cultists += B.current + var/list/yes_voters = pollCandidates("[Nominee] seeks to lead your cult, do you support [Nominee.p_them()]?", poll_time = 1200, group = asked_cultists) + sleep(300) + if(QDELETED(Nominee) || Nominee.incapacitated()) + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current) + B.current.verbs += /mob/living/proc/cult_master + if(!B.current.incapacitated()) + to_chat(B.current,"[Nominee] has died in the process of attempting to win the cult's support!") + return FALSE + if(!Nominee.mind) + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current) + B.current.verbs += /mob/living/proc/cult_master + if(!B.current.incapacitated()) + to_chat(B.current,"[Nominee] has gone insane and catatonic in the process of attempting to win the cult's support!") + return FALSE + if(LAZYLEN(yes_voters) <= LAZYLEN(asked_cultists) * 0.5) + for(var/datum/mind/B in SSticker.mode.cult) + if(B.current) + B.current.verbs += /mob/living/proc/cult_master + if(!B.current.incapacitated()) + to_chat(B.current, "[Nominee] could not win the cult's support and shall continue to serve as an acolyte.") + return FALSE + SSticker.mode.remove_cultist(Nominee.mind, FALSE) + Nominee.mind.add_antag_datum(ANTAG_DATUM_CULT_MASTER) + GLOB.cult_mastered = TRUE + for(var/datum/mind/B in SSticker.mode.cult) + if(!B.current.incapacitated()) + to_chat(B.current,"[Nominee] has won the cult's support and is now their master. Follow [Nominee.p_their()] orders to the best of your ability!") + return TRUE + +/proc/poll_helper(var/mob/living/M) + +/proc/makeBody(mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character + if(!G_found || !G_found.key) + return + + //First we spawn a dude. + var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))//The mob being spawned. + + G_found.client.prefs.copy_to(new_character) + new_character.dna.update_dna_identity() + new_character.key = G_found.key + + return new_character + +/proc/send_to_playing_players(thing) //sends a whatever to all playing players; use instead of to_chat(world, where needed) + for(var/M in GLOB.player_list) + if(M && !isnewplayer(M)) + to_chat(M, thing) + +/proc/window_flash(client/C, ignorepref = FALSE) + if(ismob(C)) + var/mob/M = C + if(M.client) + C = M.client + if(!C || (!C.prefs.windowflashing && !ignorepref)) + return + winset(C, "mainwindow", "flash=5") + +/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank) + if(!SSticker.IsRoundInProgress() || !character) + return + var/area/A = get_area(character) + var/message = "\ + [character.real_name] ([rank]) has arrived at the station at \ + [A.name]." + deadchat_broadcast(message, follow_target = character, message_type=DEADCHAT_ARRIVALRATTLE) + if((!GLOB.announcement_systems.len) || (!character.mind)) + return + if((character.mind.assigned_role == "Cyborg") || (character.mind.assigned_role == character.mind.special_role)) + return + + var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems) + announcer.announce("ARRIVAL", character.real_name, rank, list()) //make the list empty to make it announce it in common + +/proc/GetRedPart(const/hexa) + return hex2num(copytext(hexa, 2, 4)) + +/proc/GetGreenPart(const/hexa) + return hex2num(copytext(hexa, 4, 6)) + +/proc/GetBluePart(const/hexa) + return hex2num(copytext(hexa, 6, 8)) diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 00665bdfe2..06a3272ab7 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -1,34 +1,34 @@ -GLOBAL_VAR(log_directory) -GLOBAL_PROTECT(log_directory) -GLOBAL_VAR(world_game_log) -GLOBAL_PROTECT(world_game_log) -GLOBAL_VAR(world_runtime_log) -GLOBAL_PROTECT(world_runtime_log) -GLOBAL_VAR(world_attack_log) -GLOBAL_PROTECT(world_attack_log) -GLOBAL_VAR(world_href_log) -GLOBAL_PROTECT(world_href_log) -GLOBAL_VAR(round_id) -GLOBAL_PROTECT(round_id) -GLOBAL_VAR(config_error_log) -GLOBAL_PROTECT(config_error_log) - -GLOBAL_LIST_EMPTY(bombers) -GLOBAL_PROTECT(bombers) -GLOBAL_LIST_EMPTY(admin_log) -GLOBAL_PROTECT(admin_log) -GLOBAL_LIST_EMPTY(lastsignalers) //keeps last 100 signals here in format: "[src] used \ref[src] @ location [src.loc]: [freq]/[code]" -GLOBAL_PROTECT(lastsignalers) -GLOBAL_LIST_EMPTY(lawchanges) //Stores who uploaded laws to which silicon-based lifeform, and what the law was -GLOBAL_PROTECT(lawchanges) - -GLOBAL_LIST_EMPTY(combatlog) -GLOBAL_PROTECT(combatlog) -GLOBAL_LIST_EMPTY(IClog) -GLOBAL_PROTECT(IClog) -GLOBAL_LIST_EMPTY(OOClog) -GLOBAL_PROTECT(OOClog) -GLOBAL_LIST_EMPTY(adminlog) -GLOBAL_PROTECT(adminlog) - +GLOBAL_VAR(log_directory) +GLOBAL_PROTECT(log_directory) +GLOBAL_VAR(world_game_log) +GLOBAL_PROTECT(world_game_log) +GLOBAL_VAR(world_runtime_log) +GLOBAL_PROTECT(world_runtime_log) +GLOBAL_VAR(world_attack_log) +GLOBAL_PROTECT(world_attack_log) +GLOBAL_VAR(world_href_log) +GLOBAL_PROTECT(world_href_log) +GLOBAL_VAR(round_id) +GLOBAL_PROTECT(round_id) +GLOBAL_VAR(config_error_log) +GLOBAL_PROTECT(config_error_log) + +GLOBAL_LIST_EMPTY(bombers) +GLOBAL_PROTECT(bombers) +GLOBAL_LIST_EMPTY(admin_log) +GLOBAL_PROTECT(admin_log) +GLOBAL_LIST_EMPTY(lastsignalers) //keeps last 100 signals here in format: "[src] used \ref[src] @ location [src.loc]: [freq]/[code]" +GLOBAL_PROTECT(lastsignalers) +GLOBAL_LIST_EMPTY(lawchanges) //Stores who uploaded laws to which silicon-based lifeform, and what the law was +GLOBAL_PROTECT(lawchanges) + +GLOBAL_LIST_EMPTY(combatlog) +GLOBAL_PROTECT(combatlog) +GLOBAL_LIST_EMPTY(IClog) +GLOBAL_PROTECT(IClog) +GLOBAL_LIST_EMPTY(OOClog) +GLOBAL_PROTECT(OOClog) +GLOBAL_LIST_EMPTY(adminlog) +GLOBAL_PROTECT(adminlog) + GLOBAL_LIST_EMPTY(active_turfs_startlist) \ No newline at end of file diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 9272abbdb6..07dcfaee28 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -111,108 +111,108 @@ W.attack_self(src) update_inv_hands() return - - //These are always reachable. - //User itself, current loc, and user inventory - if(DirectAccess(A)) + + //These are always reachable. + //User itself, current loc, and user inventory + if(DirectAccess(A)) if(W) - melee_item_attack_chain(src,W,A,params) + melee_item_attack_chain(src,W,A,params) else if(ismob(A)) changeNext_move(CLICK_CD_MELEE) UnarmedAttack(A) return - - //Can't reach anything else in lockers or other weirdness - if(!loc.AllowClick()) + + //Can't reach anything else in lockers or other weirdness + if(!loc.AllowClick()) return - //Standard reach turf to turf or reaching inside storage - if(CanReach(A,W)) - if(W) - melee_item_attack_chain(src,W,A,params) - else - if(ismob(A)) - changeNext_move(CLICK_CD_MELEE) - UnarmedAttack(A,1) - else - if(W) - W.afterattack(A,src,0,params) - else - RangedAttack(A,params) - -/atom/movable/proc/CanReach(atom/target,obj/item/tool,view_only = FALSE) - if(isturf(target) || isturf(target.loc) || DirectAccess(target)) //Directly accessible atoms - if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks - return TRUE - else - //Things inside storage insde another storage - //Eg Contents of a box in a backpack - var/atom/outer_storage = get_atom_on_turf(target) - if(outer_storage == target) //whatever that is we don't want infinite loop. - return FALSE - if(outer_storage && CanReach(outer_storage,tool) && outer_storage.CanReachStorage(target,src,view_only ? STORAGE_VIEW_DEPTH : INVENTORY_DEPTH)) - return TRUE - return FALSE - -//Can [target] in this container be reached by [user], can't be more than [depth] levels deep -/atom/proc/CanReachStorage(atom/target,user,depth) - return FALSE - -/obj/item/weapon/storage/CanReachStorage(atom/target,user,depth) - while(target && depth > 0) - target = target.loc - depth-- - if(target == src) - return TRUE - return FALSE - -/atom/movable/proc/DirectAccess(atom/target) - if(target == src) - return TRUE - if(target == loc) - return TRUE - -/mob/DirectAccess(atom/target) - if(..()) - return TRUE - if(target in contents) //This could probably use moving down and restricting to inventory only - return TRUE - return FALSE - -/mob/living/DirectAccess(atom/target) - if(..()) //Lightweight checks first - return TRUE - if(target in GetAllContents()) - return TRUE - -/atom/proc/AllowClick() - return FALSE - -/turf/AllowClick() - return TRUE + //Standard reach turf to turf or reaching inside storage + if(CanReach(A,W)) + if(W) + melee_item_attack_chain(src,W,A,params) + else + if(ismob(A)) + changeNext_move(CLICK_CD_MELEE) + UnarmedAttack(A,1) + else + if(W) + W.afterattack(A,src,0,params) + else + RangedAttack(A,params) -/proc/CheckToolReach(atom/movable/here, atom/movable/there, reach) +/atom/movable/proc/CanReach(atom/target,obj/item/tool,view_only = FALSE) + if(isturf(target) || isturf(target.loc) || DirectAccess(target)) //Directly accessible atoms + if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks + return TRUE + else + //Things inside storage insde another storage + //Eg Contents of a box in a backpack + var/atom/outer_storage = get_atom_on_turf(target) + if(outer_storage == target) //whatever that is we don't want infinite loop. + return FALSE + if(outer_storage && CanReach(outer_storage,tool) && outer_storage.CanReachStorage(target,src,view_only ? STORAGE_VIEW_DEPTH : INVENTORY_DEPTH)) + return TRUE + return FALSE + +//Can [target] in this container be reached by [user], can't be more than [depth] levels deep +/atom/proc/CanReachStorage(atom/target,user,depth) + return FALSE + +/obj/item/weapon/storage/CanReachStorage(atom/target,user,depth) + while(target && depth > 0) + target = target.loc + depth-- + if(target == src) + return TRUE + return FALSE + +/atom/movable/proc/DirectAccess(atom/target) + if(target == src) + return TRUE + if(target == loc) + return TRUE + +/mob/DirectAccess(atom/target) + if(..()) + return TRUE + if(target in contents) //This could probably use moving down and restricting to inventory only + return TRUE + return FALSE + +/mob/living/DirectAccess(atom/target) + if(..()) //Lightweight checks first + return TRUE + if(target in GetAllContents()) + return TRUE + +/atom/proc/AllowClick() + return FALSE + +/turf/AllowClick() + return TRUE + +/proc/CheckToolReach(atom/movable/here, atom/movable/there, reach) if(!here || !there) return switch(reach) if(0) - return FALSE + return FALSE if(1) - return FALSE //here.Adjacent(there) + return FALSE //here.Adjacent(there) if(2 to INFINITY) - var/obj/dummy = new(get_turf(here)) + var/obj/dummy = new(get_turf(here)) dummy.pass_flags |= PASSTABLE - dummy.invisibility = INVISIBILITY_ABSTRACT + dummy.invisibility = INVISIBILITY_ABSTRACT for(var/i in 1 to reach) //Limit it to that many tries var/turf/T = get_step(dummy, get_dir(dummy, there)) - if(dummy.CanReach(there)) + if(dummy.CanReach(there)) qdel(dummy) - return TRUE + return TRUE if(!dummy.Move(T)) //we're blocked! qdel(dummy) return - qdel(dummy) + qdel(dummy) // Default behavior: ignore double clicks (the second click that makes the doubleclick call already calls for a normal click) /mob/proc/DblClickOn(atom/A, params) diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index ce7014d765..83ecedd319 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -1,162 +1,162 @@ -/* - These defines specificy screen locations. For more information, see the byond documentation on the screen_loc var. - - The short version: - - Everything is encoded as strings because apparently that's how Byond rolls. - - "1,1" is the bottom left square of the user's screen. This aligns perfectly with the turf grid. - "1:2,3:4" is the square (1,3) with pixel offsets (+2, +4); slightly right and slightly above the turf grid. - Pixel offsets are used so you don't perfectly hide the turf under them, that would be crappy. - - In addition, the keywords NORTH, SOUTH, EAST, WEST and CENTER can be used to represent their respective - screen borders. NORTH-1, for example, is the row just below the upper edge. Useful if you want your - UI to scale with screen size. - - The size of the user's screen is defined by client.view (indirectly by world.view), in our case "15x15". - Therefore, the top right corner (except during admin shenanigans) is at "15,15" -*/ - -//Lower left, persistent menu -#define ui_inventory "WEST:6,SOUTH:5" - -//Middle left indicators -#define ui_lingchemdisplay "WEST,CENTER-1:15" -#define ui_lingstingdisplay "WEST:6,CENTER-3:11" -#define ui_crafting "12:-10,1:5" -#define ui_building "12:-10,1:21" -#define ui_language_menu "11:6,2:-11" - -#define ui_devilsouldisplay "WEST:6,CENTER-1:15" - -//Lower center, persistent menu -#define ui_sstore1 "CENTER-5:10,SOUTH:5" -#define ui_id "CENTER-4:12,SOUTH:5" -#define ui_belt "CENTER-3:14,SOUTH:5" -#define ui_back "CENTER-2:14,SOUTH:5" - -/proc/ui_hand_position(i) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5) - var/x_off = -(!(i % 2)) - var/y_off = round((i-1) / 2) - return"CENTER+[x_off]:16,SOUTH+[y_off]:5" - -/proc/ui_equip_position(mob/M) - var/y_off = round((M.held_items.len-1) / 2) //values based on old equip ui position (CENTER: +/-16,SOUTH+1:5) - return "CENTER:-16,SOUTH+[y_off+1]:5" - -/proc/ui_swaphand_position(mob/M, which = 1) //values based on old swaphand ui positions (CENTER: +/-16,SOUTH+1:5) - var/x_off = which == 1 ? -1 : 0 - var/y_off = round((M.held_items.len-1) / 2) - return "CENTER+[x_off]:16,SOUTH+[y_off+1]:5" - -#define ui_storage1 "CENTER+1:18,SOUTH:5" -#define ui_storage2 "CENTER+2:20,SOUTH:5" - -#define ui_borg_sensor "CENTER-3:16, SOUTH:5" //borgs -#define ui_borg_lamp "CENTER-4:16, SOUTH:5" //borgs -#define ui_borg_thrusters "CENTER-5:16, SOUTH:5" //borgs -#define ui_inv1 "CENTER-2:16,SOUTH:5" //borgs -#define ui_inv2 "CENTER-1 :16,SOUTH:5" //borgs -#define ui_inv3 "CENTER :16,SOUTH:5" //borgs -#define ui_borg_module "CENTER+1:16,SOUTH:5" //borgs -#define ui_borg_store "CENTER+2:16,SOUTH:5" //borgs -#define ui_borg_camera "CENTER+3:21,SOUTH:5" //borgs -#define ui_borg_album "CENTER+4:21,SOUTH:5" //borgs -#define ui_borg_language_menu "CENTER+4:21,SOUTH+1:5" //borgs - -#define ui_monkey_head "CENTER-5:13,SOUTH:5" //monkey -#define ui_monkey_mask "CENTER-4:14,SOUTH:5" //monkey -#define ui_monkey_neck "CENTER-3:15,SOUTH:5" //monkey -#define ui_monkey_back "CENTER-2:16,SOUTH:5" //monkey - -#define ui_alien_storage_l "CENTER-2:14,SOUTH:5"//alien -#define ui_alien_storage_r "CENTER+1:18,SOUTH:5"//alien -#define ui_alien_language_menu "EAST-3:26,SOUTH:5" //alien - -#define ui_drone_drop "CENTER+1:18,SOUTH:5" //maintenance drones -#define ui_drone_pull "CENTER+2:2,SOUTH:5" //maintenance drones -#define ui_drone_storage "CENTER-2:14,SOUTH:5" //maintenance drones -#define ui_drone_head "CENTER-3:14,SOUTH:5" //maintenance drones - -//Lower right, persistent menu -#define ui_drop_throw "EAST-1:28,SOUTH+1:7" -#define ui_pull_resist "EAST-2:26,SOUTH+1:7" -#define ui_movi "EAST-2:26,SOUTH:5" -#define ui_acti "EAST-3:24,SOUTH:5" -#define ui_zonesel "EAST-1:28,SOUTH:5" -#define ui_acti_alt "EAST-1:28,SOUTH:5" //alternative intent switcher for when the interface is hidden (F12) - -#define ui_borg_pull "EAST-2:26,SOUTH+1:7" -#define ui_borg_radio "EAST-1:28,SOUTH+1:7" -#define ui_borg_intents "EAST-2:26,SOUTH:5" - - -//Upper-middle right (alerts) -#define ui_alert1 "EAST-1:28,CENTER+5:27" -#define ui_alert2 "EAST-1:28,CENTER+4:25" -#define ui_alert3 "EAST-1:28,CENTER+3:23" -#define ui_alert4 "EAST-1:28,CENTER+2:21" -#define ui_alert5 "EAST-1:28,CENTER+1:19" - - -//Middle right (status indicators) -#define ui_healthdoll "EAST-1:28,CENTER-2:13" -#define ui_health "EAST-1:28,CENTER-1:15" -#define ui_internal "EAST-1:28,CENTER:17" - -//borgs -#define ui_borg_health "EAST-1:28,CENTER-1:15" //borgs have the health display where humans have the pressure damage indicator. - -//aliens -#define ui_alien_health "EAST,CENTER-1:15" //aliens have the health display where humans have the pressure damage indicator. -#define ui_alienplasmadisplay "EAST,CENTER-2:15" -#define ui_alien_queen_finder "EAST,CENTER-3:15" - -//constructs -#define ui_construct_pull "EAST,CENTER-2:15" -#define ui_construct_health "EAST,CENTER:15" //same as borgs and humans - -// AI - -#define ui_ai_core "SOUTH:6,WEST" -#define ui_ai_camera_list "SOUTH:6,WEST+1" -#define ui_ai_track_with_camera "SOUTH:6,WEST+2" -#define ui_ai_camera_light "SOUTH:6,WEST+3" -#define ui_ai_crew_monitor "SOUTH:6,WEST+4" -#define ui_ai_crew_manifest "SOUTH:6,WEST+5" -#define ui_ai_alerts "SOUTH:6,WEST+6" -#define ui_ai_announcement "SOUTH:6,WEST+7" -#define ui_ai_shuttle "SOUTH:6,WEST+8" -#define ui_ai_state_laws "SOUTH:6,WEST+9" -#define ui_ai_pda_send "SOUTH:6,WEST+10" -#define ui_ai_pda_log "SOUTH:6,WEST+11" -#define ui_ai_take_picture "SOUTH:6,WEST+12" -#define ui_ai_view_images "SOUTH:6,WEST+13" -#define ui_ai_sensor "SOUTH:6,WEST+14" - -//Pop-up inventory -#define ui_shoes "WEST+1:8,SOUTH:5" - -#define ui_iclothing "WEST:6,SOUTH+1:7" -#define ui_oclothing "WEST+1:8,SOUTH+1:7" -#define ui_gloves "WEST+2:10,SOUTH+1:7" - -#define ui_glasses "WEST:6,SOUTH+3:11" -#define ui_mask "WEST+1:8,SOUTH+2:9" -#define ui_ears "WEST+2:10,SOUTH+2:9" -#define ui_neck "WEST:6,SOUTH+2:9" -#define ui_head "WEST+1:8,SOUTH+3:11" - -//Ghosts - -#define ui_ghost_jumptomob "SOUTH:6,CENTER-2:24" -#define ui_ghost_orbit "SOUTH:6,CENTER-1:24" -#define ui_ghost_reenter_corpse "SOUTH:6,CENTER:24" -#define ui_ghost_teleport "SOUTH:6,CENTER+1:24" -#define ui_ghost_pai "SOUTH: 6, CENTER+2:24" - -//Hand of God, god - -#define ui_deityhealth "EAST-1:28,CENTER-2:13" -#define ui_deitypower "EAST-1:28,CENTER-1:15" -#define ui_deityfollowers "EAST-1:28,CENTER:17" +/* + These defines specificy screen locations. For more information, see the byond documentation on the screen_loc var. + + The short version: + + Everything is encoded as strings because apparently that's how Byond rolls. + + "1,1" is the bottom left square of the user's screen. This aligns perfectly with the turf grid. + "1:2,3:4" is the square (1,3) with pixel offsets (+2, +4); slightly right and slightly above the turf grid. + Pixel offsets are used so you don't perfectly hide the turf under them, that would be crappy. + + In addition, the keywords NORTH, SOUTH, EAST, WEST and CENTER can be used to represent their respective + screen borders. NORTH-1, for example, is the row just below the upper edge. Useful if you want your + UI to scale with screen size. + + The size of the user's screen is defined by client.view (indirectly by world.view), in our case "15x15". + Therefore, the top right corner (except during admin shenanigans) is at "15,15" +*/ + +//Lower left, persistent menu +#define ui_inventory "WEST:6,SOUTH:5" + +//Middle left indicators +#define ui_lingchemdisplay "WEST,CENTER-1:15" +#define ui_lingstingdisplay "WEST:6,CENTER-3:11" +#define ui_crafting "12:-10,1:5" +#define ui_building "12:-10,1:21" +#define ui_language_menu "11:6,2:-11" + +#define ui_devilsouldisplay "WEST:6,CENTER-1:15" + +//Lower center, persistent menu +#define ui_sstore1 "CENTER-5:10,SOUTH:5" +#define ui_id "CENTER-4:12,SOUTH:5" +#define ui_belt "CENTER-3:14,SOUTH:5" +#define ui_back "CENTER-2:14,SOUTH:5" + +/proc/ui_hand_position(i) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5) + var/x_off = -(!(i % 2)) + var/y_off = round((i-1) / 2) + return"CENTER+[x_off]:16,SOUTH+[y_off]:5" + +/proc/ui_equip_position(mob/M) + var/y_off = round((M.held_items.len-1) / 2) //values based on old equip ui position (CENTER: +/-16,SOUTH+1:5) + return "CENTER:-16,SOUTH+[y_off+1]:5" + +/proc/ui_swaphand_position(mob/M, which = 1) //values based on old swaphand ui positions (CENTER: +/-16,SOUTH+1:5) + var/x_off = which == 1 ? -1 : 0 + var/y_off = round((M.held_items.len-1) / 2) + return "CENTER+[x_off]:16,SOUTH+[y_off+1]:5" + +#define ui_storage1 "CENTER+1:18,SOUTH:5" +#define ui_storage2 "CENTER+2:20,SOUTH:5" + +#define ui_borg_sensor "CENTER-3:16, SOUTH:5" //borgs +#define ui_borg_lamp "CENTER-4:16, SOUTH:5" //borgs +#define ui_borg_thrusters "CENTER-5:16, SOUTH:5" //borgs +#define ui_inv1 "CENTER-2:16,SOUTH:5" //borgs +#define ui_inv2 "CENTER-1 :16,SOUTH:5" //borgs +#define ui_inv3 "CENTER :16,SOUTH:5" //borgs +#define ui_borg_module "CENTER+1:16,SOUTH:5" //borgs +#define ui_borg_store "CENTER+2:16,SOUTH:5" //borgs +#define ui_borg_camera "CENTER+3:21,SOUTH:5" //borgs +#define ui_borg_album "CENTER+4:21,SOUTH:5" //borgs +#define ui_borg_language_menu "CENTER+4:21,SOUTH+1:5" //borgs + +#define ui_monkey_head "CENTER-5:13,SOUTH:5" //monkey +#define ui_monkey_mask "CENTER-4:14,SOUTH:5" //monkey +#define ui_monkey_neck "CENTER-3:15,SOUTH:5" //monkey +#define ui_monkey_back "CENTER-2:16,SOUTH:5" //monkey + +#define ui_alien_storage_l "CENTER-2:14,SOUTH:5"//alien +#define ui_alien_storage_r "CENTER+1:18,SOUTH:5"//alien +#define ui_alien_language_menu "EAST-3:26,SOUTH:5" //alien + +#define ui_drone_drop "CENTER+1:18,SOUTH:5" //maintenance drones +#define ui_drone_pull "CENTER+2:2,SOUTH:5" //maintenance drones +#define ui_drone_storage "CENTER-2:14,SOUTH:5" //maintenance drones +#define ui_drone_head "CENTER-3:14,SOUTH:5" //maintenance drones + +//Lower right, persistent menu +#define ui_drop_throw "EAST-1:28,SOUTH+1:7" +#define ui_pull_resist "EAST-2:26,SOUTH+1:7" +#define ui_movi "EAST-2:26,SOUTH:5" +#define ui_acti "EAST-3:24,SOUTH:5" +#define ui_zonesel "EAST-1:28,SOUTH:5" +#define ui_acti_alt "EAST-1:28,SOUTH:5" //alternative intent switcher for when the interface is hidden (F12) + +#define ui_borg_pull "EAST-2:26,SOUTH+1:7" +#define ui_borg_radio "EAST-1:28,SOUTH+1:7" +#define ui_borg_intents "EAST-2:26,SOUTH:5" + + +//Upper-middle right (alerts) +#define ui_alert1 "EAST-1:28,CENTER+5:27" +#define ui_alert2 "EAST-1:28,CENTER+4:25" +#define ui_alert3 "EAST-1:28,CENTER+3:23" +#define ui_alert4 "EAST-1:28,CENTER+2:21" +#define ui_alert5 "EAST-1:28,CENTER+1:19" + + +//Middle right (status indicators) +#define ui_healthdoll "EAST-1:28,CENTER-2:13" +#define ui_health "EAST-1:28,CENTER-1:15" +#define ui_internal "EAST-1:28,CENTER:17" + +//borgs +#define ui_borg_health "EAST-1:28,CENTER-1:15" //borgs have the health display where humans have the pressure damage indicator. + +//aliens +#define ui_alien_health "EAST,CENTER-1:15" //aliens have the health display where humans have the pressure damage indicator. +#define ui_alienplasmadisplay "EAST,CENTER-2:15" +#define ui_alien_queen_finder "EAST,CENTER-3:15" + +//constructs +#define ui_construct_pull "EAST,CENTER-2:15" +#define ui_construct_health "EAST,CENTER:15" //same as borgs and humans + +// AI + +#define ui_ai_core "SOUTH:6,WEST" +#define ui_ai_camera_list "SOUTH:6,WEST+1" +#define ui_ai_track_with_camera "SOUTH:6,WEST+2" +#define ui_ai_camera_light "SOUTH:6,WEST+3" +#define ui_ai_crew_monitor "SOUTH:6,WEST+4" +#define ui_ai_crew_manifest "SOUTH:6,WEST+5" +#define ui_ai_alerts "SOUTH:6,WEST+6" +#define ui_ai_announcement "SOUTH:6,WEST+7" +#define ui_ai_shuttle "SOUTH:6,WEST+8" +#define ui_ai_state_laws "SOUTH:6,WEST+9" +#define ui_ai_pda_send "SOUTH:6,WEST+10" +#define ui_ai_pda_log "SOUTH:6,WEST+11" +#define ui_ai_take_picture "SOUTH:6,WEST+12" +#define ui_ai_view_images "SOUTH:6,WEST+13" +#define ui_ai_sensor "SOUTH:6,WEST+14" + +//Pop-up inventory +#define ui_shoes "WEST+1:8,SOUTH:5" + +#define ui_iclothing "WEST:6,SOUTH+1:7" +#define ui_oclothing "WEST+1:8,SOUTH+1:7" +#define ui_gloves "WEST+2:10,SOUTH+1:7" + +#define ui_glasses "WEST:6,SOUTH+3:11" +#define ui_mask "WEST+1:8,SOUTH+2:9" +#define ui_ears "WEST+2:10,SOUTH+2:9" +#define ui_neck "WEST:6,SOUTH+2:9" +#define ui_head "WEST+1:8,SOUTH+3:11" + +//Ghosts + +#define ui_ghost_jumptomob "SOUTH:6,CENTER-2:24" +#define ui_ghost_orbit "SOUTH:6,CENTER-1:24" +#define ui_ghost_reenter_corpse "SOUTH:6,CENTER:24" +#define ui_ghost_teleport "SOUTH:6,CENTER+1:24" +#define ui_ghost_pai "SOUTH: 6, CENTER+2:24" + +//Hand of God, god + +#define ui_deityhealth "EAST-1:28,CENTER-2:13" +#define ui_deitypower "EAST-1:28,CENTER-1:15" +#define ui_deityfollowers "EAST-1:28,CENTER:17" diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm index c2448ceb87..acda7f36e5 100644 --- a/code/_onclick/hud/ghost.dm +++ b/code/_onclick/hud/ghost.dm @@ -68,10 +68,10 @@ using.screen_loc = ui_ghost_pai static_inventory += using - using = new /obj/screen/language_menu - using.icon = ui_style - static_inventory += using - + using = new /obj/screen/language_menu + using.icon = ui_style + static_inventory += using + /datum/hud/ghost/show_hud(version = 0, mob/viewmob) ..() if(!mymob.client.prefs.ghost_hud) diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index f6a3688626..30db96c79f 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -265,6 +265,6 @@ E.screen_loc = ui_equip_position(mymob) if(mymob.hud_used) show_hud(HUD_STYLE_STANDARD,mymob) - -/datum/hud/proc/update_locked_slots() + +/datum/hud/proc/update_locked_slots() return \ No newline at end of file diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index 8453b12085..e759b0c111 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -313,18 +313,18 @@ inv_slots[inv.slot_id] = inv inv.update_icon() -/datum/hud/human/update_locked_slots() - if(!mymob) - return - var/mob/living/carbon/human/H = mymob - var/datum/species/S = H.dna.species - for(var/obj/screen/inventory/inv in (static_inventory + toggleable_inventory)) - if(inv.slot_id) - if(inv.slot_id in S.no_equip) - inv.alpha = 128 - else - inv.alpha = initial(inv.alpha) - +/datum/hud/human/update_locked_slots() + if(!mymob) + return + var/mob/living/carbon/human/H = mymob + var/datum/species/S = H.dna.species + for(var/obj/screen/inventory/inv in (static_inventory + toggleable_inventory)) + if(inv.slot_id) + if(inv.slot_id in S.no_equip) + inv.alpha = 128 + else + inv.alpha = initial(inv.alpha) + /datum/hud/human/hidden_inventory_update(mob/viewer) if(!mymob) return diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm index 66085de7d1..10599ca25b 100644 --- a/code/datums/holocall.dm +++ b/code/datums/holocall.dm @@ -1,160 +1,160 @@ - -#define HOLOPAD_MAX_DIAL_TIME 200 - -/mob/camera/aiEye/remote/holo/setLoc() - . = ..() - var/obj/machinery/holopad/H = origin - H.move_hologram(eye_user, loc) - -//this datum manages it's own references - -/datum/holocall - var/mob/living/user //the one that called - var/obj/machinery/holopad/calling_holopad //the one that sent the call - var/obj/machinery/holopad/connected_holopad //the one that answered the call (may be null) - var/list/dialed_holopads //all things called, will be cleared out to just connected_holopad once answered - - var/mob/camera/aiEye/remote/holo/eye //user's eye, once connected - var/obj/effect/overlay/holo_pad_hologram/hologram //user's hologram, once connected - - var/call_start_time - -//creates a holocall made by `caller` from `calling_pad` to `callees` -/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees) - call_start_time = world.time - user = caller - calling_pad.outgoing_call = src - calling_holopad = calling_pad - dialed_holopads = list() - - for(var/I in callees) - var/obj/machinery/holopad/H = I - if(!QDELETED(H) && H.is_operational()) - dialed_holopads += H - LAZYADD(H.holo_calls, src) - - if(!dialed_holopads.len) - calling_pad.say("Connection failure.") - qdel(src) - return - - testing("Holocall started") - -//cleans up ALL references :) -/datum/holocall/Destroy() - QDEL_NULL(eye) - - user.reset_perspective() - - user = null - hologram.HC = null - hologram = null - calling_holopad.outgoing_call = null - - for(var/I in dialed_holopads) - var/obj/machinery/holopad/H = I - LAZYREMOVE(H.holo_calls, src) - dialed_holopads.Cut() - - if(calling_holopad) - calling_holopad.SetLightsAndPower() - calling_holopad = null - if(connected_holopad) - connected_holopad.SetLightsAndPower() - connected_holopad = null - - testing("Holocall destroyed") - - return ..() - -//Gracefully disconnects a holopad `H` from a call. Pads not in the call are ignored. Notifies participants of the disconnection -/datum/holocall/proc/Disconnect(obj/machinery/holopad/H) - testing("Holocall disconnect") - if(H == connected_holopad) - calling_holopad.say("[usr] disconnected.") - else if(H == calling_holopad && connected_holopad) - connected_holopad.say("[usr] disconnected.") - - ConnectionFailure(H, TRUE) - -//Forcefully disconnects a holopad `H` from a call. Pads not in the call are ignored. -/datum/holocall/proc/ConnectionFailure(obj/machinery/holopad/H, graceful = FALSE) - testing("Holocall connection failure: graceful [graceful]") - if(H == connected_holopad || H == calling_holopad) - if(!graceful) - calling_holopad.say("Connection failure.") - qdel(src) - return - - LAZYREMOVE(H.holo_calls, src) - dialed_holopads -= H - if(!dialed_holopads.len) - if(graceful) - calling_holopad.say("Call rejected.") - testing("No recipients, terminating") - qdel(src) - -//Answers a call made to a holopad `H` which cannot be the calling holopad. Pads not in the call are ignored -/datum/holocall/proc/Answer(obj/machinery/holopad/H) - testing("Holocall answer") - if(H == calling_holopad) - CRASH("How cute, a holopad tried to answer itself.") - - if(!(H in dialed_holopads)) - return - - if(connected_holopad) - CRASH("Multi-connection holocall") - - for(var/I in dialed_holopads) - if(I == H) - continue - Disconnect(I) - - for(var/I in H.holo_calls) - var/datum/holocall/HC = I - if(HC != src) - HC.Disconnect(H) - - connected_holopad = H - - if(!Check()) - return - - hologram = H.activate_holo(user) - hologram.HC = src - - //eyeobj code is horrid, this is the best copypasta I could make - eye = new - eye.origin = H - eye.eye_initialized = TRUE - eye.eye_user = user - eye.name = "Camera Eye ([user.name])" - user.remote_control = eye - user.reset_perspective(eye) - eye.setLoc(H.loc) - -//Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise -/datum/holocall/proc/Check() - for(var/I in dialed_holopads) - var/obj/machinery/holopad/H = I - if(!H.is_operational()) - ConnectionFailure(H) - - if(QDELETED(src)) - return FALSE - - . = !QDELETED(user) && !user.incapacitated() && !QDELETED(calling_holopad) && calling_holopad.is_operational() && user.loc == calling_holopad.loc - - if(.) - if(connected_holopad) - . = !QDELETED(connected_holopad) && connected_holopad.is_operational() - else - . = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME) - if(!.) - calling_holopad.say("No answer recieved.") - calling_holopad.temp = "" - - if(!.) - testing("Holocall Check fail") - qdel(src) + +#define HOLOPAD_MAX_DIAL_TIME 200 + +/mob/camera/aiEye/remote/holo/setLoc() + . = ..() + var/obj/machinery/holopad/H = origin + H.move_hologram(eye_user, loc) + +//this datum manages it's own references + +/datum/holocall + var/mob/living/user //the one that called + var/obj/machinery/holopad/calling_holopad //the one that sent the call + var/obj/machinery/holopad/connected_holopad //the one that answered the call (may be null) + var/list/dialed_holopads //all things called, will be cleared out to just connected_holopad once answered + + var/mob/camera/aiEye/remote/holo/eye //user's eye, once connected + var/obj/effect/overlay/holo_pad_hologram/hologram //user's hologram, once connected + + var/call_start_time + +//creates a holocall made by `caller` from `calling_pad` to `callees` +/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees) + call_start_time = world.time + user = caller + calling_pad.outgoing_call = src + calling_holopad = calling_pad + dialed_holopads = list() + + for(var/I in callees) + var/obj/machinery/holopad/H = I + if(!QDELETED(H) && H.is_operational()) + dialed_holopads += H + LAZYADD(H.holo_calls, src) + + if(!dialed_holopads.len) + calling_pad.say("Connection failure.") + qdel(src) + return + + testing("Holocall started") + +//cleans up ALL references :) +/datum/holocall/Destroy() + QDEL_NULL(eye) + + user.reset_perspective() + + user = null + hologram.HC = null + hologram = null + calling_holopad.outgoing_call = null + + for(var/I in dialed_holopads) + var/obj/machinery/holopad/H = I + LAZYREMOVE(H.holo_calls, src) + dialed_holopads.Cut() + + if(calling_holopad) + calling_holopad.SetLightsAndPower() + calling_holopad = null + if(connected_holopad) + connected_holopad.SetLightsAndPower() + connected_holopad = null + + testing("Holocall destroyed") + + return ..() + +//Gracefully disconnects a holopad `H` from a call. Pads not in the call are ignored. Notifies participants of the disconnection +/datum/holocall/proc/Disconnect(obj/machinery/holopad/H) + testing("Holocall disconnect") + if(H == connected_holopad) + calling_holopad.say("[usr] disconnected.") + else if(H == calling_holopad && connected_holopad) + connected_holopad.say("[usr] disconnected.") + + ConnectionFailure(H, TRUE) + +//Forcefully disconnects a holopad `H` from a call. Pads not in the call are ignored. +/datum/holocall/proc/ConnectionFailure(obj/machinery/holopad/H, graceful = FALSE) + testing("Holocall connection failure: graceful [graceful]") + if(H == connected_holopad || H == calling_holopad) + if(!graceful) + calling_holopad.say("Connection failure.") + qdel(src) + return + + LAZYREMOVE(H.holo_calls, src) + dialed_holopads -= H + if(!dialed_holopads.len) + if(graceful) + calling_holopad.say("Call rejected.") + testing("No recipients, terminating") + qdel(src) + +//Answers a call made to a holopad `H` which cannot be the calling holopad. Pads not in the call are ignored +/datum/holocall/proc/Answer(obj/machinery/holopad/H) + testing("Holocall answer") + if(H == calling_holopad) + CRASH("How cute, a holopad tried to answer itself.") + + if(!(H in dialed_holopads)) + return + + if(connected_holopad) + CRASH("Multi-connection holocall") + + for(var/I in dialed_holopads) + if(I == H) + continue + Disconnect(I) + + for(var/I in H.holo_calls) + var/datum/holocall/HC = I + if(HC != src) + HC.Disconnect(H) + + connected_holopad = H + + if(!Check()) + return + + hologram = H.activate_holo(user) + hologram.HC = src + + //eyeobj code is horrid, this is the best copypasta I could make + eye = new + eye.origin = H + eye.eye_initialized = TRUE + eye.eye_user = user + eye.name = "Camera Eye ([user.name])" + user.remote_control = eye + user.reset_perspective(eye) + eye.setLoc(H.loc) + +//Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise +/datum/holocall/proc/Check() + for(var/I in dialed_holopads) + var/obj/machinery/holopad/H = I + if(!H.is_operational()) + ConnectionFailure(H) + + if(QDELETED(src)) + return FALSE + + . = !QDELETED(user) && !user.incapacitated() && !QDELETED(calling_holopad) && calling_holopad.is_operational() && user.loc == calling_holopad.loc + + if(.) + if(connected_holopad) + . = !QDELETED(connected_holopad) && connected_holopad.is_operational() + else + . = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME) + if(!.) + calling_holopad.say("No answer recieved.") + calling_holopad.temp = "" + + if(!.) + testing("Holocall Check fail") + qdel(src) diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 4dba11e02b..32690d20b3 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1,1642 +1,1642 @@ -/* Note from Carnie: - The way datum/mind stuff works has been changed a lot. - Minds now represent IC characters rather than following a client around constantly. - - Guidelines for using minds properly: - - - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! - ghost.mind is however used as a reference to the ghost's corpse - - - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) - the existing mind of the old mob should be transfered to the new mob like so: - - mind.transfer_to(new_mob) - - - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. - By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing - the player. - - - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. - - - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting - a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. - - new_mob.key = key - - The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! - However if you want that mind to have any special properties like being a traitor etc you will have to do that - yourself. - -*/ - -/datum/mind - var/key - var/name //replaces mob/var/original_name - var/mob/living/current - var/active = 0 - - var/memory - - var/assigned_role - var/special_role - var/list/restricted_roles = list() - - var/datum/job/assigned_job - - var/list/datum/objective/objectives = list() - - var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. - - var/datum/faction/faction //associated faction - var/datum/changeling/changeling //changeling holder - var/linglink - - var/miming = 0 // Mime's vow of silence - var/list/antag_datums - var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state - var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD - var/datum/gang/gang_datum //Which gang this mind belongs to, if any - var/datum/devilinfo/devilinfo //Information about the devil, if any. - var/damnation_type = 0 - var/datum/mind/soulOwner //who owns the soul. Under normal circumstances, this will point to src - var/isholy = FALSE //is this person a chaplain or admin role allowed to use bibles - - var/mob/living/enslaved_to //If this mind's master is another mob (i.e. adamantine golems) - - var/datum/language_holder/language_holder - -/datum/mind/New(var/key) - src.key = key - soulOwner = src - -/datum/mind/Destroy() - SSticker.minds -= src - if(islist(antag_datums)) - for(var/i in antag_datums) - qdel(i) - antag_datums = null - return ..() - -/datum/mind/proc/get_language_holder() - if(!language_holder) - var/datum/language_holder/L = current.get_language_holder(shadow=FALSE) - language_holder = L.copy(src) - - return language_holder - -/datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0) - if(current) // remove ourself from our old body's mind variable - current.mind = null - SStgui.on_transfer(current, new_character) - - if(!language_holder) - var/datum/language_holder/mob_holder = new_character.get_language_holder(shadow = FALSE) - language_holder = mob_holder.copy(src) - - if(key) - if(new_character.key != key) //if we're transfering into a body with a key associated which is not ours - new_character.ghostize(1) //we'll need to ghostize so that key isn't mobless. - else - key = new_character.key - - if(new_character.mind) //disassociate any mind currently in our new body's mind variable - new_character.mind.current = null - - var/datum/atom_hud/antag/hud_to_transfer = antag_hud//we need this because leave_hud() will clear this list - var/mob/living/old_current = current - current = new_character //associate ourself with our new body - new_character.mind = src //and associate our new body with ourself - for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body - var/datum/antagonist/A = a - A.on_body_transfer(old_current, current) - if(iscarbon(new_character)) - var/mob/living/carbon/C = new_character - C.last_mind = src - transfer_antag_huds(hud_to_transfer) //inherit the antag HUD - transfer_actions(new_character) - - if(active || force_key_move) - new_character.key = key //now transfer the key to link the client to our new body - -/datum/mind/proc/store_memory(new_text) - memory += "[new_text]
" - -/datum/mind/proc/wipe_memory() - memory = null - -// Datum antag mind procs -/datum/mind/proc/add_antag_datum(datum_type) - if(!datum_type) - return - var/datum/antagonist/A = new datum_type(src) - if(!A.can_be_owned(src)) - qdel(A) - return - LAZYADD(antag_datums, A) - A.on_gain() - return A - -/datum/mind/proc/remove_antag_datum(datum_type) - if(!datum_type) - return - var/datum/antagonist/A = has_antag_datum(datum_type) - if(A) - A.on_removal() - return TRUE - -/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us. - for(var/a in antag_datums) - var/datum/antagonist/A = a - A.on_removal() - -/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE) - if(!datum_type) - return - . = FALSE - for(var/a in antag_datums) - var/datum/antagonist/A = a - if(check_subtypes && istype(A, datum_type)) - return A - else if(A.type == datum_type) - return A - -/* - Removes antag type's references from a mind. - objectives, uplinks, powers etc are all handled. -*/ - -/datum/mind/proc/remove_objectives() - if(objectives.len) - for(var/datum/objective/O in objectives) - objectives -= O - qdel(O) - -/datum/mind/proc/remove_changeling() - if(src in SSticker.mode.changelings) - SSticker.mode.changelings -= src - current.remove_changeling_powers() - if(changeling) - qdel(changeling) - changeling = null - special_role = null - remove_antag_equip() - SSticker.mode.update_changeling_icons_removed(src) - -/datum/mind/proc/remove_traitor() - if(src in SSticker.mode.traitors) - SSticker.mode.traitors -= src - if(isAI(current)) - var/mob/living/silicon/ai/A = current - A.set_zeroth_law("") - A.verbs -= /mob/living/silicon/ai/proc/choose_modules - A.malf_picker.remove_verbs(A) - qdel(A.malf_picker) - special_role = null - remove_antag_equip() - SSticker.mode.update_traitor_icons_removed(src) - -/datum/mind/proc/remove_nukeop() - if(src in SSticker.mode.syndicates) - SSticker.mode.syndicates -= src - SSticker.mode.update_synd_icons_removed(src) - special_role = null - remove_objectives() - remove_antag_equip() - -/datum/mind/proc/remove_wizard() - if(src in SSticker.mode.wizards) - SSticker.mode.wizards -= src - current.spellremove(current) - special_role = null - remove_antag_equip() - -/datum/mind/proc/remove_cultist() - if(src in SSticker.mode.cult) - SSticker.mode.remove_cultist(src, 0, 0) - special_role = null - remove_objectives() - remove_antag_equip() - -/datum/mind/proc/remove_rev() - if(src in SSticker.mode.revolutionaries) - SSticker.mode.revolutionaries -= src - SSticker.mode.update_rev_icons_removed(src) - if(src in SSticker.mode.head_revolutionaries) - SSticker.mode.head_revolutionaries -= src - SSticker.mode.update_rev_icons_removed(src) - special_role = null - remove_objectives() - remove_antag_equip() - - -/datum/mind/proc/remove_gang() - SSticker.mode.remove_gangster(src,0,1,1) - remove_objectives() - -/datum/mind/proc/remove_antag_equip() - var/list/Mob_Contents = current.get_contents() - for(var/obj/item/I in Mob_Contents) - if(istype(I, /obj/item/device/pda)) - var/obj/item/device/pda/P = I - P.lock_code = "" - - else if(istype(I, /obj/item/device/radio)) - var/obj/item/device/radio/R = I - R.traitor_frequency = 0 - -/datum/mind/proc/remove_all_antag() //For the Lazy amongst us. - remove_changeling() - remove_traitor() - remove_nukeop() - remove_wizard() - remove_cultist() - remove_rev() - remove_gang() - SSticker.mode.update_changeling_icons_removed(src) - SSticker.mode.update_traitor_icons_removed(src) - SSticker.mode.update_wiz_icons_removed(src) - SSticker.mode.update_cult_icons_removed(src) - SSticker.mode.update_rev_icons_removed(src) - if(gang_datum) - gang_datum.remove_gang_hud(src) - - -//Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does. - -/datum/mind/proc/enslave_mind_to_creator(mob/living/creator) - if(iscultist(creator)) - SSticker.mode.add_cultist(src) - - else if(is_gangster(creator)) - SSticker.mode.add_gangster(src, creator.mind.gang_datum, TRUE) - - else if(is_revolutionary_in_general(creator)) - SSticker.mode.add_revolutionary(src) - - else if(is_servant_of_ratvar(creator)) - add_servant_of_ratvar(current) - - else if(is_nuclear_operative(creator)) - make_Nuke(null, null, 0, FALSE) - - enslaved_to = creator - - current.faction |= creator.faction - creator.faction |= current.faction - - if(creator.mind.special_role) - message_admins("[ADMIN_LOOKUPFLW(current)] has been created by [ADMIN_LOOKUPFLW(creator)], an antagonist.") - to_chat(current, "Despite your creators current allegiances, your true master remains [creator.real_name]. If their loyalities change, so do yours. This will never change unless your creator's body is destroyed.") - -/datum/mind/proc/show_memory(mob/recipient, window=1) - if(!recipient) - recipient = current - var/output = "[current.real_name]'s Memories:
" - output += memory - - if(objectives.len) - output += "Objectives:" - var/obj_count = 1 - for(var/datum/objective/objective in objectives) - output += "
Objective #[obj_count++]: [objective.explanation_text]" - - if(window) - recipient << browse(output,"window=memory") - else if(objectives.len || memory) - to_chat(recipient, "[output]") - -/datum/mind/proc/edit_memory() - if(!SSticker.HasRoundStarted()) - alert("Not before round-start!", "Alert") - return - - var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
" - out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
" - out += "Assigned role: [assigned_role]. Edit
" - out += "Faction and special role: [special_role]
" - - var/list/sections = list( - "revolution", - "gang", - "cult", - "wizard", - "changeling", - "nuclear", - "traitor", // "traitorchan", - "monkey", - "clockcult" - ) - var/text = "" - - if(ishuman(current)) - /** REVOLUTION ***/ - text = "revolution" - if (SSticker.mode.config_tag=="revolution") - text = uppertext(text) - text = "[text]: " - if (assigned_role in GLOB.command_positions) - text += "HEAD|loyal|employee|headrev|rev" - else if (src in SSticker.mode.head_revolutionaries) - text += "head|loyal|employee|HEADREV|rev" - text += "
Flash: give" - - var/list/L = current.get_contents() - var/obj/item/device/assembly/flash/flash = locate() in L - if (flash) - if(!flash.crit_fail) - text += "|take." - else - text += "|take|repair." - else - text += "." - - text += " Reequip (gives traitor uplink)." - if (objectives.len==0) - text += "
Objectives are empty! Set to kill all heads." - else if(current.isloyal()) - text += "head|LOYAL|employee|headrev|rev" - else if (src in SSticker.mode.revolutionaries) - text += "head|loyal|employee|headrev|REV" - else - text += "head|loyal|EMPLOYEE|headrev|rev" - - if(current && current.client && (ROLE_REV in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["revolution"] = text - - /** GANG ***/ - text = "gang" - if (SSticker.mode.config_tag=="gang") - text = uppertext(text) - text = "[text]: " - text += "[current.isloyal() ? "LOYAL" : "loyal"]|" - if(src in SSticker.mode.get_all_gangsters()) - text += "none" - else - text += "NONE" - - if(current && current.client && (ROLE_GANG in current.client.prefs.be_special)) - text += "|Enabled in Prefs
" - else - text += "|Disabled in Prefs
" - - for(var/datum/gang/G in SSticker.mode.gangs) - text += "[G.name]: " - if(src in (G.gangsters)) - text += "GANGSTER" - else - text += "gangster" - text += "|" - if(src in (G.bosses)) - text += "GANG LEADER" - text += "|Equipment: give" - var/list/L = current.get_contents() - var/obj/item/device/gangtool/gangtool = locate() in L - if (gangtool) - text += "|take" - - else - text += "gang leader" - text += "
" - - if(GLOB.gang_colors_pool.len) - text += "Create New Gang" - - sections["gang"] = text - - /** Abductors **/ - text = "Abductor" - if(SSticker.mode.config_tag == "abductor") - text = uppertext(text) - text = "[text]: " - if(src in SSticker.mode.abductors) - text += "Abductor|human" - text += "|undress|equip" - else - text += "Abductor|human" - - if(current && current.client && (ROLE_ABDUCTOR in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["abductor"] = text - - /** NUCLEAR ***/ - text = "nuclear" - if (SSticker.mode.config_tag=="nuclear") - text = uppertext(text) - text = "[text]: " - if (src in SSticker.mode.syndicates) - text += "OPERATIVE|nanotrasen" - text += "
To shuttle, undress, dress up." - var/code - for (var/obj/machinery/nuclearbomb/bombue in GLOB.machines) - if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") - code = bombue.r_code - break - if (code) - text += " Code is [code]. tell the code." - else - text += "operative|NANOTRASEN" - - if(current && current.client && (ROLE_OPERATIVE in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["nuclear"] = text - - /** WIZARD ***/ - text = "wizard" - if (SSticker.mode.config_tag=="wizard") - text = uppertext(text) - text = "[text]: " - if ((src in SSticker.mode.wizards) || (src in SSticker.mode.apprentices)) - text += "YES|no" - text += "
To lair, undress, dress up, let choose name." - if (objectives.len==0) - text += "
Objectives are empty! Randomize!" - else - text += "yes|NO" - - if(current && current.client && (ROLE_WIZARD in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["wizard"] = text - - /** CULT ***/ - text = "cult" - if (SSticker.mode.config_tag=="cult") - text = uppertext(text) - text = "[text]: " - if(iscultist(current)) - text += "loyal|employee|CULTIST" - text += "
Give tome|amulet." - - else if(current.isloyal()) - text += "LOYAL|employee|cultist" - else if(is_convertable_to_cult(current)) - text += "loyal|EMPLOYEE|cultist" - else - text += "loyal|EMPLOYEE|cannot serve Nar-Sie" - - if(current && current.client && (ROLE_CULTIST in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["cult"] = text - - /** CLOCKWORK CULT **/ - text = "clockwork cult" - if(SSticker.mode.config_tag == "clockwork cult") - text = uppertext(text) - text = "[text]: " - if(is_servant_of_ratvar(current)) - text += "loyal|employee|SERVANT" - text += "
Give slab" - else if(current.isloyal()) - text += "LOYAL|employee|servant" - else if(is_eligible_servant(current)) - text += "loyal|EMPLOYEE|servant" - else - text += "loyal|EMPLOYEE|cannot serve Ratvar" - - if(current && current.client && (ROLE_SERVANT_OF_RATVAR in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["clockcult"] = text - - /** TRAITOR ***/ - text = "traitor" - if (SSticker.mode.config_tag=="traitor" || SSticker.mode.config_tag=="traitorchan") - text = uppertext(text) - text = "[text]: " - if (src in SSticker.mode.traitors) - text += "TRAITOR|loyal" - if (objectives.len==0) - text += "
Objectives are empty! Randomize!" - else - text += "traitor|LOYAL" - - if(current && current.client && (ROLE_TRAITOR in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["traitor"] = text - - if(ishuman(current) || ismonkey(current)) - - /** CHANGELING ***/ - text = "changeling" - if (SSticker.mode.config_tag=="changeling" || SSticker.mode.config_tag=="traitorchan") - text = uppertext(text) - text = "[text]: " - if ((src in SSticker.mode.changelings) && special_role) - text += "YES|no" - if (objectives.len==0) - text += "
Objectives are empty! Randomize!" - if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) - text += "
Transform to initial appearance." - else if(src in SSticker.mode.changelings) //Station Aligned Changeling - text += "YES (but not an antag)|no" - if (objectives.len==0) - text += "
Objectives are empty! Randomize!" - if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) - text += "
Transform to initial appearance." - else - text += "yes|NO" - - if(current && current.client && (ROLE_CHANGELING in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["changeling"] = text - - /** MONKEY ***/ - text = "monkey" - if (SSticker.mode.config_tag=="monkey") - text = uppertext(text) - text = "[text]: " - if (ishuman(current)) - text += "healthy|infected|HUMAN|other" - else if (ismonkey(current)) - var/found = 0 - for(var/datum/disease/D in current.viruses) - if(istype(D, /datum/disease/transformation/jungle_fever)) found = 1 - - if(found) - text += "healthy|INFECTED|human|other" - else - text += "HEALTHY|infected|human|other" - - else - text += "healthy|infected|human|OTHER" - - if(current && current.client && (ROLE_MONKEY in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - - sections["monkey"] = text - - /** devil ***/ - text = "devil" - if(SSticker.mode.config_tag == "devil") - text = uppertext(text) - text = "[text]: " - if(src in SSticker.mode.devils) - if(devilinfo && !devilinfo.ascendable) - text += "DEVIL|Ascendable Devil|sintouched|human" - else - text += "DEVIL|ASCENDABLE DEVIL|sintouched|human" - else if(src in SSticker.mode.sintouched) - text += "devil|Ascendable Devil|SINTOUCHED|human" - else - text += "devil|Ascendable Devil|sintouched|HUMAN" - - if(current && current.client && (ROLE_DEVIL in current.client.prefs.be_special)) - text += "|Enabled in Prefs" - else - text += "|Disabled in Prefs" - sections["devil"] = text - - - /** SILICON ***/ - - if(issilicon(current)) - text = "silicon" - var/mob/living/silicon/robot/robot = current - if (istype(robot) && robot.emagged) - text += "
Cyborg: Is emagged! Unemag!
0th law: [robot.laws.zeroth]" - var/mob/living/silicon/ai/ai = current - if (istype(ai) && ai.connected_robots.len) - var/n_e_robots = 0 - for (var/mob/living/silicon/robot/R in ai.connected_robots) - if (R.emagged) - n_e_robots++ - text += "
[n_e_robots] of [ai.connected_robots.len] slaved cyborgs are emagged. Unemag" - if (SSticker.mode.config_tag == "traitorchan") - if (sections["traitor"]) - out += sections["traitor"]+"
" - if (sections["changeling"]) - out += sections["changeling"]+"

" - sections -= "traitor" - sections -= "changeling" - else - if (sections[SSticker.mode.config_tag]) - out += sections[SSticker.mode.config_tag]+"

" - sections -= SSticker.mode.config_tag - for (var/i in sections) - if (sections[i]) - out += sections[i]+"
" - - - if(((src in SSticker.mode.head_revolutionaries) || (src in SSticker.mode.traitors) || (src in SSticker.mode.syndicates)) && ishuman(current)) - - text = "Uplink: give" - var/obj/item/device/uplink/U = find_syndicate_uplink() - if(U) - text += "|take" - if (check_rights(R_FUN, 0)) - text += ", [U.telecrystals] TC" - else - text += ", [U.telecrystals] TC" - text += "." //hiel grammar - out += text - - out += "

" - - out += "Memory:
" - out += memory - out += "
Edit memory
" - out += "Objectives:
" - if (objectives.len == 0) - out += "EMPTY
" - else - var/obj_count = 1 - for(var/datum/objective/objective in objectives) - out += "[obj_count]: [objective.explanation_text] Edit Delete Toggle Completion
" - obj_count++ - out += "Add objective

" - - out += "Announce objectives

" - - usr << browse(out, "window=edit_memory[src];size=500x600") - - -/datum/mind/Topic(href, href_list) - if(!check_rights(R_ADMIN)) - return - - if (href_list["role_edit"]) - var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in get_all_jobs() - if (!new_role) - return - assigned_role = new_role - - else if (href_list["memory_edit"]) - var/new_memo = copytext(sanitize(input("Write new memory", "Memory", memory) as null|message),1,MAX_MESSAGE_LEN) - if (isnull(new_memo)) - return - memory = new_memo - - else if (href_list["obj_edit"] || href_list["obj_add"]) - var/datum/objective/objective - var/objective_pos - var/def_value - - if (href_list["obj_edit"]) - objective = locate(href_list["obj_edit"]) - if (!objective) - return - objective_pos = objectives.Find(objective) - - //Text strings are easy to manipulate. Revised for simplicity. - var/temp_obj_type = "[objective.type]"//Convert path into a text string. - def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. - if(!def_value)//If it's a custom objective, it will be an empty string. - def_value = "custom" - - var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "maroon", "debrain", "protect", "destroy", "prevent", "hijack", "escape", "survive", "martyr", "steal", "download", "nuclear", "capture", "absorb", "custom") - if (!new_obj_type) - return - - var/datum/objective/new_objective = null - - switch (new_obj_type) - if ("assassinate","protect","debrain","maroon") - var/list/possible_targets = list("Free objective") - for(var/datum/mind/possible_target in SSticker.minds) - if ((possible_target != src) && ishuman(possible_target.current)) - possible_targets += possible_target.current - - var/mob/def_target = null - var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain, /datum/objective/maroon) - if (objective&&(objective.type in objective_list) && objective:target) - def_target = objective:target.current - - var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets - if (!new_target) - return - - var/objective_path = text2path("/datum/objective/[new_obj_type]") - if (new_target == "Free objective") - new_objective = new objective_path - new_objective.owner = src - new_objective:target = null - new_objective.explanation_text = "Free objective" - else - new_objective = new objective_path - new_objective.owner = src - new_objective:target = new_target:mind - //Will display as special role if the target is set as MODE. Ninjas/commandos/nuke ops. - new_objective.update_explanation_text() - - if ("destroy") - var/list/possible_targets = active_ais(1) - if(possible_targets.len) - var/mob/new_target = input("Select target:", "Objective target") as null|anything in possible_targets - new_objective = new /datum/objective/destroy - new_objective.target = new_target.mind - new_objective.owner = src - new_objective.update_explanation_text() - else - to_chat(usr, "No active AIs with minds") - - if ("prevent") - new_objective = new /datum/objective/block - new_objective.owner = src - - if ("hijack") - new_objective = new /datum/objective/hijack - new_objective.owner = src - - if ("escape") - new_objective = new /datum/objective/escape - new_objective.owner = src - - if ("survive") - new_objective = new /datum/objective/survive - new_objective.owner = src - - if("martyr") - new_objective = new /datum/objective/martyr - new_objective.owner = src - - if ("nuclear") - new_objective = new /datum/objective/nuclear - new_objective.owner = src - - if ("steal") - if (!istype(objective, /datum/objective/steal)) - new_objective = new /datum/objective/steal - new_objective.owner = src - else - new_objective = objective - var/datum/objective/steal/steal = new_objective - if (!steal.select_target()) - return - - if("download","capture","absorb") - var/def_num - if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) - def_num = objective.target_amount - - var/target_number = input("Input target number:", "Objective", def_num) as num|null - if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. - return - - switch(new_obj_type) - if("download") - new_objective = new /datum/objective/download - new_objective.explanation_text = "Download [target_number] research levels." - if("capture") - new_objective = new /datum/objective/capture - new_objective.explanation_text = "Capture [target_number] lifeforms with an energy net. Live, rare specimens are worth more." - if("absorb") - new_objective = new /datum/objective/absorb - new_objective.explanation_text = "Absorb [target_number] compatible genomes." - new_objective.owner = src - new_objective.target_amount = target_number - - if ("custom") - var/expl = stripped_input(usr, "Custom objective:", "Objective", objective ? objective.explanation_text : "") - if (!expl) - return - new_objective = new /datum/objective - new_objective.owner = src - new_objective.explanation_text = expl - - if (!new_objective) - return - - if (objective) - objectives -= objective - objectives.Insert(objective_pos, new_objective) - message_admins("[key_name_admin(usr)] edited [current]'s objective to [new_objective.explanation_text]") - log_admin("[key_name(usr)] edited [current]'s objective to [new_objective.explanation_text]") - else - objectives += new_objective - message_admins("[key_name_admin(usr)] added a new objective for [current]: [new_objective.explanation_text]") - log_admin("[key_name(usr)] added a new objective for [current]: [new_objective.explanation_text]") - - else if (href_list["obj_delete"]) - var/datum/objective/objective = locate(href_list["obj_delete"]) - if(!istype(objective)) - return - objectives -= objective - message_admins("[key_name_admin(usr)] removed an objective for [current]: [objective.explanation_text]") - log_admin("[key_name(usr)] removed an objective for [current]: [objective.explanation_text]") - - else if(href_list["obj_completed"]) - var/datum/objective/objective = locate(href_list["obj_completed"]) - if(!istype(objective)) - return - objective.completed = !objective.completed - log_admin("[key_name(usr)] toggled the win state for [current]'s objective: [objective.explanation_text]") - - else if (href_list["revolution"]) - switch(href_list["revolution"]) - if("clear") - remove_rev() - to_chat(current, "You have been brainwashed! You are no longer a revolutionary!") - message_admins("[key_name_admin(usr)] has de-rev'ed [current].") - log_admin("[key_name(usr)] has de-rev'ed [current].") - if("rev") - if(src in SSticker.mode.head_revolutionaries) - SSticker.mode.head_revolutionaries -= src - SSticker.mode.update_rev_icons_removed(src) - to_chat(current, "Revolution has been disappointed of your leader traits! You are a regular revolutionary now!") - else if(!(src in SSticker.mode.revolutionaries)) - to_chat(current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") - else - return - SSticker.mode.revolutionaries += src - SSticker.mode.update_rev_icons_added(src) - special_role = "Revolutionary" - message_admins("[key_name_admin(usr)] has rev'ed [current].") - log_admin("[key_name(usr)] has rev'ed [current].") - - if("headrev") - if(src in SSticker.mode.revolutionaries) - SSticker.mode.revolutionaries -= src - SSticker.mode.update_rev_icons_removed(src) - to_chat(current, "You have proved your devotion to revoltion! Yea are a head revolutionary now!") - else if(!(src in SSticker.mode.head_revolutionaries)) - to_chat(current, "You are a member of the revolutionaries' leadership now!") - else - return - if (SSticker.mode.head_revolutionaries.len>0) - // copy targets - var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries - if (valid_head) - for (var/datum/objective/mutiny/O in valid_head.objectives) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = src - rev_obj.target = O.target - rev_obj.explanation_text = "Assassinate [O.target.name], the [O.target.assigned_role]." - objectives += rev_obj - SSticker.mode.greet_revolutionary(src,0) - SSticker.mode.head_revolutionaries += src - SSticker.mode.update_rev_icons_added(src) - special_role = "Head Revolutionary" - message_admins("[key_name_admin(usr)] has head-rev'ed [current].") - log_admin("[key_name(usr)] has head-rev'ed [current].") - - if("autoobjectives") - SSticker.mode.forge_revolutionary_objectives(src) - SSticker.mode.greet_revolutionary(src,0) - to_chat(usr, "The objectives for revolution have been generated and shown to [key]") - - if("flash") - if (!SSticker.mode.equip_revolutionary(current)) - to_chat(usr, "Spawning flash failed!") - - if("takeflash") - var/list/L = current.get_contents() - var/obj/item/device/assembly/flash/flash = locate() in L - if (!flash) - to_chat(usr, "Deleting flash failed!") - qdel(flash) - - if("repairflash") - var/list/L = current.get_contents() - var/obj/item/device/assembly/flash/flash = locate() in L - if (!flash) - to_chat(usr, "Repairing flash failed!") - else - flash.crit_fail = 0 - flash.update_icon() - - - -//////////////////// GANG MODE - - else if (href_list["gang"]) - switch(href_list["gang"]) - if("clear") - remove_gang() - message_admins("[key_name_admin(usr)] has de-gang'ed [current].") - log_admin("[key_name(usr)] has de-gang'ed [current].") - - if("equip") - switch(SSticker.mode.equip_gang(current,gang_datum)) - if(1) - to_chat(usr, "Unable to equip territory spraycan!") - if(2) - to_chat(usr, "Unable to equip recruitment pen and spraycan!") - if(3) - to_chat(usr, "Unable to equip gangtool, pen, and spraycan!") - - if("takeequip") - var/list/L = current.get_contents() - for(var/obj/item/weapon/pen/gang/pen in L) - qdel(pen) - for(var/obj/item/device/gangtool/gangtool in L) - qdel(gangtool) - for(var/obj/item/toy/crayon/spraycan/gang/SC in L) - qdel(SC) - - if("new") - if(GLOB.gang_colors_pool.len) - var/list/names = list("Random") + GLOB.gang_name_pool - var/gangname = input("Pick a gang name.","Select Name") as null|anything in names - if(gangname && GLOB.gang_colors_pool.len) //Check again just in case another admin made max gangs at the same time - if(!(gangname in GLOB.gang_name_pool)) - gangname = null - var/datum/gang/newgang = new(null,gangname) - SSticker.mode.gangs += newgang - message_admins("[key_name_admin(usr)] has created the [newgang.name] Gang.") - log_admin("[key_name(usr)] has created the [newgang.name] Gang.") - - else if (href_list["gangboss"]) - var/datum/gang/G = locate(href_list["gangboss"]) in SSticker.mode.gangs - if(!G || (src in G.bosses)) - return - SSticker.mode.remove_gangster(src,0,2,1) - G.bosses += src - gang_datum = G - special_role = "[G.name] Gang Boss" - G.add_gang_hud(src) - to_chat(current, "You are a [G.name] Gang Boss!") - message_admins("[key_name_admin(usr)] has added [current] to the [G.name] Gang leadership.") - log_admin("[key_name(usr)] has added [current] to the [G.name] Gang leadership.") - SSticker.mode.forge_gang_objectives(src) - SSticker.mode.greet_gang(src,0) - - else if (href_list["gangster"]) - var/datum/gang/G = locate(href_list["gangster"]) in SSticker.mode.gangs - if(!G || (src in G.gangsters)) - return - SSticker.mode.remove_gangster(src,0,2,1) - SSticker.mode.add_gangster(src,G,0) - message_admins("[key_name_admin(usr)] has added [current] to the [G.name] Gang (A).") - log_admin("[key_name(usr)] has added [current] to the [G.name] Gang (A).") - -///////////////////////////////// - - - - else if (href_list["cult"]) - switch(href_list["cult"]) - if("clear") - remove_cultist() - message_admins("[key_name_admin(usr)] has de-cult'ed [current].") - log_admin("[key_name(usr)] has de-cult'ed [current].") - if("cultist") - if(!(src in SSticker.mode.cult)) - SSticker.mode.add_cultist(src, 0) - message_admins("[key_name_admin(usr)] has cult'ed [current].") - log_admin("[key_name(usr)] has cult'ed [current].") - if("tome") - if (!SSticker.mode.equip_cultist(current,1)) - to_chat(usr, "Spawning tome failed!") - - if("amulet") - if (!SSticker.mode.equip_cultist(current)) - to_chat(usr, "Spawning amulet failed!") - - else if(href_list["clockcult"]) - switch(href_list["clockcult"]) - if("clear") - remove_servant_of_ratvar(current, TRUE) - message_admins("[key_name_admin(usr)] has removed clockwork servant status from [current].") - log_admin("[key_name(usr)] has removed clockwork servant status from [current].") - if("servant") - if(!is_servant_of_ratvar(current)) - add_servant_of_ratvar(current, TRUE) - message_admins("[key_name_admin(usr)] has made [current] into a servant of Ratvar.") - log_admin("[key_name(usr)] has made [current] into a servant of Ratvar.") - if("slab") - if(!SSticker.mode.equip_servant(current)) - to_chat(usr, "Failed to outfit [current] with a slab!") - else - to_chat(usr, "Successfully gave [current] a clockwork slab!") - - else if (href_list["wizard"]) - switch(href_list["wizard"]) - if("clear") - remove_wizard() - to_chat(current, "You have been brainwashed! You are no longer a wizard!") - log_admin("[key_name(usr)] has de-wizard'ed [current].") - SSticker.mode.update_wiz_icons_removed(src) - if("wizard") - if(!(src in SSticker.mode.wizards)) - SSticker.mode.wizards += src - special_role = "Wizard" - //SSticker.mode.learn_basic_spells(current) - to_chat(current, "You are the Space Wizard!") - message_admins("[key_name_admin(usr)] has wizard'ed [current].") - log_admin("[key_name(usr)] has wizard'ed [current].") - SSticker.mode.update_wiz_icons_added(src) - if("lair") - current.loc = pick(GLOB.wizardstart) - if("dressup") - SSticker.mode.equip_wizard(current) - if("name") - SSticker.mode.name_wizard(current) - if("autoobjectives") - SSticker.mode.forge_wizard_objectives(src) - to_chat(usr, "The objectives for wizard [key] have been generated. You can edit them and anounce manually.") - - else if (href_list["changeling"]) - switch(href_list["changeling"]) - if("clear") - remove_changeling() - to_chat(current, "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!") - message_admins("[key_name_admin(usr)] has de-changeling'ed [current].") - log_admin("[key_name(usr)] has de-changeling'ed [current].") - if("changeling") - if(!(src in SSticker.mode.changelings)) - SSticker.mode.changelings += src - current.make_changeling() - special_role = "Changeling" - to_chat(current, "Your powers are awoken. A flash of memory returns to us...we are [changeling.changelingID], a changeling!") - message_admins("[key_name_admin(usr)] has changeling'ed [current].") - log_admin("[key_name(usr)] has changeling'ed [current].") - SSticker.mode.update_changeling_icons_added(src) - if("autoobjectives") - SSticker.mode.forge_changeling_objectives(src) - to_chat(usr, "The objectives for changeling [key] have been generated. You can edit them and anounce manually.") - - if("initialdna") - if( !changeling || !changeling.stored_profiles.len || !istype(current, /mob/living/carbon)) - to_chat(usr, "Resetting DNA failed!") - else - var/mob/living/carbon/C = current - changeling.first_prof.dna.transfer_identity(C, transfer_SE=1) - C.real_name = changeling.first_prof.name - C.updateappearance(mutcolor_update=1) - C.domutcheck() - - else if (href_list["nuclear"]) - switch(href_list["nuclear"]) - if("clear") - remove_nukeop() - to_chat(current, "You have been brainwashed! You are no longer a syndicate operative!") - message_admins("[key_name_admin(usr)] has de-nuke op'ed [current].") - log_admin("[key_name(usr)] has de-nuke op'ed [current].") - if("nuclear") - if(!(src in SSticker.mode.syndicates)) - SSticker.mode.syndicates += src - SSticker.mode.update_synd_icons_added(src) - if (SSticker.mode.syndicates.len==1) - SSticker.mode.prepare_syndicate_leader(src) - else - current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" - special_role = "Syndicate" - assigned_role = "Syndicate" - to_chat(current, "You are a [syndicate_name()] agent!") - SSticker.mode.forge_syndicate_objectives(src) - SSticker.mode.greet_syndicate(src) - message_admins("[key_name_admin(usr)] has nuke op'ed [current].") - log_admin("[key_name(usr)] has nuke op'ed [current].") - if("lair") - current.loc = get_turf(locate("landmark*Syndicate-Spawn")) - if("dressup") - var/mob/living/carbon/human/H = current - qdel(H.belt) - qdel(H.back) - qdel(H.ears) - qdel(H.gloves) - qdel(H.head) - qdel(H.shoes) - qdel(H.wear_id) - qdel(H.wear_suit) - qdel(H.w_uniform) - - if (!SSticker.mode.equip_syndicate(current)) - to_chat(usr, "Equipping a syndicate failed!") - if("tellcode") - var/code - for (var/obj/machinery/nuclearbomb/bombue in GLOB.machines) - if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") - code = bombue.r_code - break - if (code) - store_memory("Syndicate Nuclear Bomb Code: [code]", 0, 0) - to_chat(current, "The nuclear authorization code is: [code]") - else - to_chat(usr, "No valid nuke found!") - - else if (href_list["traitor"]) - switch(href_list["traitor"]) - if("clear") - remove_traitor() - to_chat(current, "You have been brainwashed! You are no longer a traitor!") - message_admins("[key_name_admin(usr)] has de-traitor'ed [current].") - log_admin("[key_name(usr)] has de-traitor'ed [current].") - SSticker.mode.update_traitor_icons_removed(src) - - if("traitor") - if(!(src in SSticker.mode.traitors)) - SSticker.mode.traitors += src - special_role = "traitor" - to_chat(current, "You are a traitor!") - message_admins("[key_name_admin(usr)] has traitor'ed [current].") - log_admin("[key_name(usr)] has traitor'ed [current].") - if(isAI(current)) - var/mob/living/silicon/ai/A = current - SSticker.mode.add_law_zero(A) - SSticker.mode.update_traitor_icons_added(src) - - if("autoobjectives") - SSticker.mode.forge_traitor_objectives(src) - to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and anounce manually.") - - else if(href_list["devil"]) - switch(href_list["devil"]) - if("clear") - if(src in SSticker.mode.devils) - if(istype(current,/mob/living/carbon/true_devil/)) - if(devilinfo) - devilinfo.regress_blood_lizard() - else - to_chat(usr, "Something went wrong with removing the devil, we were unable to find an attached devilinfo..") - SSticker.mode.devils -= src - special_role = null - to_chat(current, "Your infernal link has been severed! You are no longer a devil!") - RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt) - RemoveSpell(/obj/effect/proc_holder/spell/aimed/fireball/hellish) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/violin) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/greater) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/ascended) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_dancefloor) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch/ascended) - message_admins("[key_name_admin(usr)] has de-devil'ed [current].") - devilinfo = null - if(issilicon(current)) - var/mob/living/silicon/S = current - S.clear_law_sixsixsix(current) - log_admin("[key_name(usr)] has de-devil'ed [current].") - else if(src in SSticker.mode.sintouched) - SSticker.mode.sintouched -= src - message_admins("[key_name_admin(usr)] has de-sintouch'ed [current].") - log_admin("[key_name(usr)] has de-sintouch'ed [current].") - if("devil") - if(devilinfo) - devilinfo.ascendable = FALSE - message_admins("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") - log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") - return - if(!ishuman(current) && !iscyborg(current)) - to_chat(usr, "This only works on humans and cyborgs!") - return - SSticker.mode.devils += src - special_role = "devil" - SSticker.mode.finalize_devil(src, FALSE) - SSticker.mode.add_devil_objectives(src, 2) - announceDevilLaws() - announce_objectives() - message_admins("[key_name_admin(usr)] has devil'ed [current].") - log_admin("[key_name(usr)] has devil'ed [current].") - if("ascendable_devil") - if(devilinfo) - devilinfo.ascendable = TRUE - message_admins("[key_name_admin(usr)] has made [current] able to ascend as a devil.") - log_admin("[key_name_admin(usr)] has made [current] able to ascend as a devil.") - return - if(!ishuman(current) && !iscyborg(current)) - to_chat(usr, "This only works on humans and cyborgs!") - return - SSticker.mode.devils += src - special_role = "devil" - SSticker.mode.finalize_devil(src, TRUE) - SSticker.mode.add_devil_objectives(src, 2) - announceDevilLaws() - announce_objectives() - message_admins("[key_name_admin(usr)] has devil'ed [current]. The devil has been marked as ascendable.") - log_admin("[key_name(usr)] has devil'ed [current]. The devil has been marked as ascendable.") - if("sintouched") - if(ishuman(current)) - var/mob/living/carbon/human/H = current - H.influenceSin() - message_admins("[key_name_admin(usr)] has sintouch'ed [current].") - else - to_chat(usr, "This only works on humans!") - return - - else if(href_list["abductor"]) - switch(href_list["abductor"]) - if("clear") - to_chat(usr, "Not implemented yet. Sorry!") - //SSticker.mode.update_abductor_icons_removed(src) - if("abductor") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - make_Abductor() - log_admin("[key_name(usr)] turned [current] into abductor.") - SSticker.mode.update_abductor_icons_added(src) - if("equip") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - - var/mob/living/carbon/human/H = current - var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist") - if(gear) - if(gear=="Agent") - H.equipOutfit(/datum/outfit/abductor/agent) - else - H.equipOutfit(/datum/outfit/abductor/scientist) - - else if (href_list["monkey"]) - var/mob/living/L = current - if (L.notransform) - return - switch(href_list["monkey"]) - if("healthy") - if (check_rights(R_ADMIN)) - var/mob/living/carbon/human/H = current - var/mob/living/carbon/monkey/M = current - if (istype(H)) - log_admin("[key_name(usr)] attempting to monkeyize [key_name(current)]") - message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(current)]") - src = null - M = H.monkeyize() - src = M.mind - //to_chat(world, "DEBUG: \"healthy\": M=[M], M.mind=[M.mind], src=[src]!") - else if (istype(M) && length(M.viruses)) - for(var/datum/disease/D in M.viruses) - D.cure(0) - sleep(0) //because deleting of virus is done through spawn(0) - if("infected") - if (check_rights(R_ADMIN, 0)) - var/mob/living/carbon/human/H = current - var/mob/living/carbon/monkey/M = current - if (istype(H)) - log_admin("[key_name(usr)] attempting to monkeyize and infect [key_name(current)]") - message_admins("[key_name_admin(usr)] attempting to monkeyize and infect [key_name_admin(current)]") - src = null - M = H.monkeyize() - src = M.mind - current.ForceContractDisease(new /datum/disease/transformation/jungle_fever) - else if (istype(M)) - current.ForceContractDisease(new /datum/disease/transformation/jungle_fever) - if("human") - if (check_rights(R_ADMIN, 0)) - var/mob/living/carbon/human/H = current - var/mob/living/carbon/monkey/M = current - if (istype(M)) - for(var/datum/disease/D in M.viruses) - if (istype(D,/datum/disease/transformation/jungle_fever)) - D.cure(0) - sleep(0) //because deleting of virus is doing throught spawn(0) - log_admin("[key_name(usr)] attempting to humanize [key_name(current)]") - message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(current)]") - H = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG) - if(H) - src = H.mind - - else if (href_list["silicon"]) - switch(href_list["silicon"]) - if("unemag") - var/mob/living/silicon/robot/R = current - if (istype(R)) - R.SetEmagged(0) - message_admins("[key_name_admin(usr)] has unemag'ed [R].") - log_admin("[key_name(usr)] has unemag'ed [R].") - - if("unemagcyborgs") - if(isAI(current)) - var/mob/living/silicon/ai/ai = current - for (var/mob/living/silicon/robot/R in ai.connected_robots) - R.SetEmagged(0) - message_admins("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.") - log_admin("[key_name(usr)] has unemag'ed [ai]'s Cyborgs.") - - else if (href_list["common"]) - switch(href_list["common"]) - if("undress") - for(var/obj/item/W in current) - current.dropItemToGround(W, TRUE) //The 1 forces all items to drop, since this is an admin undress. - if("takeuplink") - take_uplink() - memory = null//Remove any memory they may have had. - log_admin("[key_name(usr)] removed [current]'s uplink.") - if("crystals") - if(check_rights(R_FUN, 0)) - var/obj/item/device/uplink/U = find_syndicate_uplink() - if(U) - var/crystals = input("Amount of telecrystals for [key]","Syndicate uplink", U.telecrystals) as null|num - if(!isnull(crystals)) - U.telecrystals = crystals - message_admins("[key_name_admin(usr)] changed [current]'s telecrystal count to [crystals].") - log_admin("[key_name(usr)] changed [current]'s telecrystal count to [crystals].") - if("uplink") - if(!SSticker.mode.equip_traitor(current, !(src in SSticker.mode.traitors))) - to_chat(usr, "Equipping a syndicate failed!") - log_admin("[key_name(usr)] attempted to give [current] an uplink.") - - else if (href_list["obj_announce"]) - announce_objectives() - - edit_memory() - -/datum/mind/proc/announce_objectives() - var/obj_count = 1 - to_chat(current, "Your current objectives:") - for(var/objective in objectives) - var/datum/objective/O = objective - to_chat(current, "Objective #[obj_count]: [O.explanation_text]") - obj_count++ - -/datum/mind/proc/find_syndicate_uplink() - var/list/L = current.get_contents() - for (var/obj/item/I in L) - if (I.hidden_uplink) - return I.hidden_uplink - return null - -/datum/mind/proc/take_uplink() - var/obj/item/device/uplink/H = find_syndicate_uplink() - if(H) - qdel(H) - -/datum/mind/proc/make_Traitor() - if(!(src in SSticker.mode.traitors)) - SSticker.mode.traitors += src - special_role = "traitor" - SSticker.mode.forge_traitor_objectives(src) - SSticker.mode.finalize_traitor(src) - SSticker.mode.greet_traitor(src) - -/datum/mind/proc/make_Nuke(turf/spawnloc, nuke_code, leader=0, telecrystals = TRUE) - if(!(src in SSticker.mode.syndicates)) - SSticker.mode.syndicates += src - SSticker.mode.update_synd_icons_added(src) - special_role = "Syndicate" - SSticker.mode.forge_syndicate_objectives(src) - SSticker.mode.greet_syndicate(src) - current.faction |= "syndicate" - - if(spawnloc) - current.loc = spawnloc - - if(ishuman(current)) - var/mob/living/carbon/human/H = current - qdel(H.belt) - qdel(H.back) - qdel(H.ears) - qdel(H.gloves) - qdel(H.head) - qdel(H.shoes) - qdel(H.wear_id) - qdel(H.wear_suit) - qdel(H.w_uniform) - - SSticker.mode.equip_syndicate(current, telecrystals) - - if (nuke_code) - store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) - to_chat(current, "The nuclear authorization code is: [nuke_code]") - else - var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in GLOB.nuke_list - if(nuke) - store_memory("Syndicate Nuclear Bomb Code: [nuke.r_code]", 0, 0) - to_chat(current, "The nuclear authorization code is: nuke.r_code") - else - to_chat(current, "You were not provided with a nuclear code. Trying asking your team leader or contacting syndicate command.") - - if (leader) - SSticker.mode.prepare_syndicate_leader(src,nuke_code) - else - current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" - -/datum/mind/proc/make_Changling() - if(!(src in SSticker.mode.changelings)) - SSticker.mode.changelings += src - current.make_changeling() - special_role = "Changeling" - SSticker.mode.forge_changeling_objectives(src) - SSticker.mode.greet_changeling(src) - SSticker.mode.update_changeling_icons_added(src) - -/datum/mind/proc/make_Wizard() - if(!(src in SSticker.mode.wizards)) - SSticker.mode.wizards += src - special_role = "Wizard" - assigned_role = "Wizard" - if(!GLOB.wizardstart.len) - current.loc = pick(GLOB.latejoin) - to_chat(current, "HOT INSERTION, GO GO GO") - else - current.loc = pick(GLOB.wizardstart) - - SSticker.mode.equip_wizard(current) - SSticker.mode.name_wizard(current) - SSticker.mode.forge_wizard_objectives(src) - SSticker.mode.greet_wizard(src) - - -/datum/mind/proc/make_Cultist() - if(!(src in SSticker.mode.cult)) - SSticker.mode.add_cultist(src,FALSE) - special_role = "Cultist" - to_chat(current, "You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of Nar-Sie.") - to_chat(current, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.") - var/datum/antagonist/cult/C - C.cult_memorization(src) - var/mob/living/carbon/human/H = current - if (!SSticker.mode.equip_cultist(current)) - to_chat(H, "Spawning an amulet from your Master failed.") - -/datum/mind/proc/make_Rev() - if (SSticker.mode.head_revolutionaries.len>0) - // copy targets - var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries - if (valid_head) - for (var/datum/objective/mutiny/O in valid_head.objectives) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = src - rev_obj.target = O.target - rev_obj.explanation_text = "Assassinate [O.target.current.real_name], the [O.target.assigned_role]." - objectives += rev_obj - SSticker.mode.greet_revolutionary(src,0) - SSticker.mode.head_revolutionaries += src - SSticker.mode.update_rev_icons_added(src) - special_role = "Head Revolutionary" - - SSticker.mode.forge_revolutionary_objectives(src) - SSticker.mode.greet_revolutionary(src,0) - - var/list/L = current.get_contents() - var/obj/item/device/assembly/flash/flash = locate() in L - qdel(flash) - take_uplink() - var/fail = 0 -// fail |= !SSticker.mode.equip_traitor(current, 1) - fail |= !SSticker.mode.equip_revolutionary(current) - - -/datum/mind/proc/make_Gang(datum/gang/G) - special_role = "[G.name] Gang Boss" - G.bosses += src - gang_datum = G - G.add_gang_hud(src) - SSticker.mode.forge_gang_objectives(src) - SSticker.mode.greet_gang(src) - SSticker.mode.equip_gang(current,G) - -/datum/mind/proc/make_Abductor() - var/role = alert("Abductor Role ?","Role","Agent","Scientist") - var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4) - var/teleport = alert("Teleport to ship ?","Teleport","Yes","No") - - if(!role || !team || !teleport) - return - - if(!ishuman(current)) - return - - SSticker.mode.abductors |= src - - var/datum/objective/experiment/O = new - O.owner = src - objectives += O - - var/mob/living/carbon/human/H = current - - H.set_species(/datum/species/abductor) - var/datum/species/abductor/S = H.dna.species - - if(role == "Scientist") - S.scientist = TRUE - S.team = team - - var/list/obj/effect/landmark/abductor/agent_landmarks = new - var/list/obj/effect/landmark/abductor/scientist_landmarks = new - agent_landmarks.len = 4 - scientist_landmarks.len = 4 - for(var/obj/effect/landmark/abductor/A in GLOB.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/obj/effect/landmark/L - if(teleport=="Yes") - switch(role) - if("Agent") - L = agent_landmarks[team] - if("Scientist") - L = scientist_landmarks[team] - H.forceMove(L.loc) - -/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S) - spell_list += S - S.action.Grant(current) - -//To remove a specific spell from a mind -/datum/mind/proc/RemoveSpell(obj/effect/proc_holder/spell/spell) - if(!spell) - return - for(var/X in spell_list) - var/obj/effect/proc_holder/spell/S = X - if(istype(S, spell)) - spell_list -= S - qdel(S) - -/datum/mind/proc/transfer_actions(mob/living/new_character) - if(current && current.actions) - for(var/datum/action/A in current.actions) - A.Grant(new_character) - transfer_mindbound_actions(new_character) - -/datum/mind/proc/transfer_mindbound_actions(mob/living/new_character) - for(var/X in spell_list) - var/obj/effect/proc_holder/spell/S = X - S.action.Grant(new_character) - -/datum/mind/proc/disrupt_spells(delay, list/exceptions = New()) - for(var/X in spell_list) - var/obj/effect/proc_holder/spell/S = X - for(var/type in exceptions) - if(istype(S, type)) - continue - S.charge_counter = delay - INVOKE_ASYNC(S, /obj/effect/proc_holder/spell.proc/start_recharge) - -/datum/mind/proc/get_ghost(even_if_they_cant_reenter) - for(var/mob/dead/observer/G in GLOB.dead_mob_list) - if(G.mind == src) - if(G.can_reenter_corpse || even_if_they_cant_reenter) - return G - break - -/datum/mind/proc/grab_ghost(force) - var/mob/dead/observer/G = get_ghost(even_if_they_cant_reenter = force) - . = G - if(G) - G.reenter_corpse() - -/mob/proc/sync_mind() - mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) - mind.active = 1 //indicates that the mind is currently synced with a client - -/mob/dead/new_player/sync_mind() - return - -/mob/dead/observer/sync_mind() - return - -//Initialisation procs -/mob/proc/mind_initialize() - if(mind) - mind.key = key - - else - mind = new /datum/mind(key) - if(SSticker) - SSticker.minds += mind - else - stack_trace("mind_initialize(): No SSticker ready") - if(!mind.name) - mind.name = real_name - mind.current = src - -/mob/living/carbon/mind_initialize() - ..() - last_mind = mind - -//HUMAN -/mob/living/carbon/human/mind_initialize() - ..() - if(!mind.assigned_role) - mind.assigned_role = "Assistant" //defualt - -//XENO -/mob/living/carbon/alien/mind_initialize() - ..() - mind.special_role = "Alien" - -//AI -/mob/living/silicon/ai/mind_initialize() - ..() - mind.assigned_role = "AI" - -//BORG -/mob/living/silicon/robot/mind_initialize() - ..() - mind.assigned_role = "Cyborg" - -//PAI -/mob/living/silicon/pai/mind_initialize() - ..() - mind.assigned_role = "pAI" - mind.special_role = "" +/* Note from Carnie: + The way datum/mind stuff works has been changed a lot. + Minds now represent IC characters rather than following a client around constantly. + + Guidelines for using minds properly: + + - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! + ghost.mind is however used as a reference to the ghost's corpse + + - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) + the existing mind of the old mob should be transfered to the new mob like so: + + mind.transfer_to(new_mob) + + - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. + By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing + the player. + + - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. + + - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting + a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. + + new_mob.key = key + + The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! + However if you want that mind to have any special properties like being a traitor etc you will have to do that + yourself. + +*/ + +/datum/mind + var/key + var/name //replaces mob/var/original_name + var/mob/living/current + var/active = 0 + + var/memory + + var/assigned_role + var/special_role + var/list/restricted_roles = list() + + var/datum/job/assigned_job + + var/list/datum/objective/objectives = list() + + var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. + + var/datum/faction/faction //associated faction + var/datum/changeling/changeling //changeling holder + var/linglink + + var/miming = 0 // Mime's vow of silence + var/list/antag_datums + var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state + var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD + var/datum/gang/gang_datum //Which gang this mind belongs to, if any + var/datum/devilinfo/devilinfo //Information about the devil, if any. + var/damnation_type = 0 + var/datum/mind/soulOwner //who owns the soul. Under normal circumstances, this will point to src + var/isholy = FALSE //is this person a chaplain or admin role allowed to use bibles + + var/mob/living/enslaved_to //If this mind's master is another mob (i.e. adamantine golems) + + var/datum/language_holder/language_holder + +/datum/mind/New(var/key) + src.key = key + soulOwner = src + +/datum/mind/Destroy() + SSticker.minds -= src + if(islist(antag_datums)) + for(var/i in antag_datums) + qdel(i) + antag_datums = null + return ..() + +/datum/mind/proc/get_language_holder() + if(!language_holder) + var/datum/language_holder/L = current.get_language_holder(shadow=FALSE) + language_holder = L.copy(src) + + return language_holder + +/datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0) + if(current) // remove ourself from our old body's mind variable + current.mind = null + SStgui.on_transfer(current, new_character) + + if(!language_holder) + var/datum/language_holder/mob_holder = new_character.get_language_holder(shadow = FALSE) + language_holder = mob_holder.copy(src) + + if(key) + if(new_character.key != key) //if we're transfering into a body with a key associated which is not ours + new_character.ghostize(1) //we'll need to ghostize so that key isn't mobless. + else + key = new_character.key + + if(new_character.mind) //disassociate any mind currently in our new body's mind variable + new_character.mind.current = null + + var/datum/atom_hud/antag/hud_to_transfer = antag_hud//we need this because leave_hud() will clear this list + var/mob/living/old_current = current + current = new_character //associate ourself with our new body + new_character.mind = src //and associate our new body with ourself + for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body + var/datum/antagonist/A = a + A.on_body_transfer(old_current, current) + if(iscarbon(new_character)) + var/mob/living/carbon/C = new_character + C.last_mind = src + transfer_antag_huds(hud_to_transfer) //inherit the antag HUD + transfer_actions(new_character) + + if(active || force_key_move) + new_character.key = key //now transfer the key to link the client to our new body + +/datum/mind/proc/store_memory(new_text) + memory += "[new_text]
" + +/datum/mind/proc/wipe_memory() + memory = null + +// Datum antag mind procs +/datum/mind/proc/add_antag_datum(datum_type) + if(!datum_type) + return + var/datum/antagonist/A = new datum_type(src) + if(!A.can_be_owned(src)) + qdel(A) + return + LAZYADD(antag_datums, A) + A.on_gain() + return A + +/datum/mind/proc/remove_antag_datum(datum_type) + if(!datum_type) + return + var/datum/antagonist/A = has_antag_datum(datum_type) + if(A) + A.on_removal() + return TRUE + +/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us. + for(var/a in antag_datums) + var/datum/antagonist/A = a + A.on_removal() + +/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE) + if(!datum_type) + return + . = FALSE + for(var/a in antag_datums) + var/datum/antagonist/A = a + if(check_subtypes && istype(A, datum_type)) + return A + else if(A.type == datum_type) + return A + +/* + Removes antag type's references from a mind. + objectives, uplinks, powers etc are all handled. +*/ + +/datum/mind/proc/remove_objectives() + if(objectives.len) + for(var/datum/objective/O in objectives) + objectives -= O + qdel(O) + +/datum/mind/proc/remove_changeling() + if(src in SSticker.mode.changelings) + SSticker.mode.changelings -= src + current.remove_changeling_powers() + if(changeling) + qdel(changeling) + changeling = null + special_role = null + remove_antag_equip() + SSticker.mode.update_changeling_icons_removed(src) + +/datum/mind/proc/remove_traitor() + if(src in SSticker.mode.traitors) + SSticker.mode.traitors -= src + if(isAI(current)) + var/mob/living/silicon/ai/A = current + A.set_zeroth_law("") + A.verbs -= /mob/living/silicon/ai/proc/choose_modules + A.malf_picker.remove_verbs(A) + qdel(A.malf_picker) + special_role = null + remove_antag_equip() + SSticker.mode.update_traitor_icons_removed(src) + +/datum/mind/proc/remove_nukeop() + if(src in SSticker.mode.syndicates) + SSticker.mode.syndicates -= src + SSticker.mode.update_synd_icons_removed(src) + special_role = null + remove_objectives() + remove_antag_equip() + +/datum/mind/proc/remove_wizard() + if(src in SSticker.mode.wizards) + SSticker.mode.wizards -= src + current.spellremove(current) + special_role = null + remove_antag_equip() + +/datum/mind/proc/remove_cultist() + if(src in SSticker.mode.cult) + SSticker.mode.remove_cultist(src, 0, 0) + special_role = null + remove_objectives() + remove_antag_equip() + +/datum/mind/proc/remove_rev() + if(src in SSticker.mode.revolutionaries) + SSticker.mode.revolutionaries -= src + SSticker.mode.update_rev_icons_removed(src) + if(src in SSticker.mode.head_revolutionaries) + SSticker.mode.head_revolutionaries -= src + SSticker.mode.update_rev_icons_removed(src) + special_role = null + remove_objectives() + remove_antag_equip() + + +/datum/mind/proc/remove_gang() + SSticker.mode.remove_gangster(src,0,1,1) + remove_objectives() + +/datum/mind/proc/remove_antag_equip() + var/list/Mob_Contents = current.get_contents() + for(var/obj/item/I in Mob_Contents) + if(istype(I, /obj/item/device/pda)) + var/obj/item/device/pda/P = I + P.lock_code = "" + + else if(istype(I, /obj/item/device/radio)) + var/obj/item/device/radio/R = I + R.traitor_frequency = 0 + +/datum/mind/proc/remove_all_antag() //For the Lazy amongst us. + remove_changeling() + remove_traitor() + remove_nukeop() + remove_wizard() + remove_cultist() + remove_rev() + remove_gang() + SSticker.mode.update_changeling_icons_removed(src) + SSticker.mode.update_traitor_icons_removed(src) + SSticker.mode.update_wiz_icons_removed(src) + SSticker.mode.update_cult_icons_removed(src) + SSticker.mode.update_rev_icons_removed(src) + if(gang_datum) + gang_datum.remove_gang_hud(src) + + +//Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does. + +/datum/mind/proc/enslave_mind_to_creator(mob/living/creator) + if(iscultist(creator)) + SSticker.mode.add_cultist(src) + + else if(is_gangster(creator)) + SSticker.mode.add_gangster(src, creator.mind.gang_datum, TRUE) + + else if(is_revolutionary_in_general(creator)) + SSticker.mode.add_revolutionary(src) + + else if(is_servant_of_ratvar(creator)) + add_servant_of_ratvar(current) + + else if(is_nuclear_operative(creator)) + make_Nuke(null, null, 0, FALSE) + + enslaved_to = creator + + current.faction |= creator.faction + creator.faction |= current.faction + + if(creator.mind.special_role) + message_admins("[ADMIN_LOOKUPFLW(current)] has been created by [ADMIN_LOOKUPFLW(creator)], an antagonist.") + to_chat(current, "Despite your creators current allegiances, your true master remains [creator.real_name]. If their loyalities change, so do yours. This will never change unless your creator's body is destroyed.") + +/datum/mind/proc/show_memory(mob/recipient, window=1) + if(!recipient) + recipient = current + var/output = "[current.real_name]'s Memories:
" + output += memory + + if(objectives.len) + output += "Objectives:" + var/obj_count = 1 + for(var/datum/objective/objective in objectives) + output += "
Objective #[obj_count++]: [objective.explanation_text]" + + if(window) + recipient << browse(output,"window=memory") + else if(objectives.len || memory) + to_chat(recipient, "[output]") + +/datum/mind/proc/edit_memory() + if(!SSticker.HasRoundStarted()) + alert("Not before round-start!", "Alert") + return + + var/out = "[name][(current&&(current.real_name!=name))?" (as [current.real_name])":""]
" + out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]
" + out += "Assigned role: [assigned_role]. Edit
" + out += "Faction and special role: [special_role]
" + + var/list/sections = list( + "revolution", + "gang", + "cult", + "wizard", + "changeling", + "nuclear", + "traitor", // "traitorchan", + "monkey", + "clockcult" + ) + var/text = "" + + if(ishuman(current)) + /** REVOLUTION ***/ + text = "revolution" + if (SSticker.mode.config_tag=="revolution") + text = uppertext(text) + text = "[text]: " + if (assigned_role in GLOB.command_positions) + text += "HEAD|loyal|employee|headrev|rev" + else if (src in SSticker.mode.head_revolutionaries) + text += "head|loyal|employee|HEADREV|rev" + text += "
Flash: give" + + var/list/L = current.get_contents() + var/obj/item/device/assembly/flash/flash = locate() in L + if (flash) + if(!flash.crit_fail) + text += "|take." + else + text += "|take|repair." + else + text += "." + + text += " Reequip (gives traitor uplink)." + if (objectives.len==0) + text += "
Objectives are empty! Set to kill all heads." + else if(current.isloyal()) + text += "head|LOYAL|employee|headrev|rev" + else if (src in SSticker.mode.revolutionaries) + text += "head|loyal|employee|headrev|REV" + else + text += "head|loyal|EMPLOYEE|headrev|rev" + + if(current && current.client && (ROLE_REV in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["revolution"] = text + + /** GANG ***/ + text = "gang" + if (SSticker.mode.config_tag=="gang") + text = uppertext(text) + text = "[text]: " + text += "[current.isloyal() ? "LOYAL" : "loyal"]|" + if(src in SSticker.mode.get_all_gangsters()) + text += "none" + else + text += "NONE" + + if(current && current.client && (ROLE_GANG in current.client.prefs.be_special)) + text += "|Enabled in Prefs
" + else + text += "|Disabled in Prefs
" + + for(var/datum/gang/G in SSticker.mode.gangs) + text += "[G.name]: " + if(src in (G.gangsters)) + text += "GANGSTER" + else + text += "gangster" + text += "|" + if(src in (G.bosses)) + text += "GANG LEADER" + text += "|Equipment: give" + var/list/L = current.get_contents() + var/obj/item/device/gangtool/gangtool = locate() in L + if (gangtool) + text += "|take" + + else + text += "gang leader" + text += "
" + + if(GLOB.gang_colors_pool.len) + text += "Create New Gang" + + sections["gang"] = text + + /** Abductors **/ + text = "Abductor" + if(SSticker.mode.config_tag == "abductor") + text = uppertext(text) + text = "[text]: " + if(src in SSticker.mode.abductors) + text += "Abductor|human" + text += "|undress|equip" + else + text += "Abductor|human" + + if(current && current.client && (ROLE_ABDUCTOR in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["abductor"] = text + + /** NUCLEAR ***/ + text = "nuclear" + if (SSticker.mode.config_tag=="nuclear") + text = uppertext(text) + text = "[text]: " + if (src in SSticker.mode.syndicates) + text += "OPERATIVE|nanotrasen" + text += "
To shuttle, undress, dress up." + var/code + for (var/obj/machinery/nuclearbomb/bombue in GLOB.machines) + if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") + code = bombue.r_code + break + if (code) + text += " Code is [code]. tell the code." + else + text += "operative|NANOTRASEN" + + if(current && current.client && (ROLE_OPERATIVE in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["nuclear"] = text + + /** WIZARD ***/ + text = "wizard" + if (SSticker.mode.config_tag=="wizard") + text = uppertext(text) + text = "[text]: " + if ((src in SSticker.mode.wizards) || (src in SSticker.mode.apprentices)) + text += "YES|no" + text += "
To lair, undress, dress up, let choose name." + if (objectives.len==0) + text += "
Objectives are empty! Randomize!" + else + text += "yes|NO" + + if(current && current.client && (ROLE_WIZARD in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["wizard"] = text + + /** CULT ***/ + text = "cult" + if (SSticker.mode.config_tag=="cult") + text = uppertext(text) + text = "[text]: " + if(iscultist(current)) + text += "loyal|employee|CULTIST" + text += "
Give tome|amulet." + + else if(current.isloyal()) + text += "LOYAL|employee|cultist" + else if(is_convertable_to_cult(current)) + text += "loyal|EMPLOYEE|cultist" + else + text += "loyal|EMPLOYEE|cannot serve Nar-Sie" + + if(current && current.client && (ROLE_CULTIST in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["cult"] = text + + /** CLOCKWORK CULT **/ + text = "clockwork cult" + if(SSticker.mode.config_tag == "clockwork cult") + text = uppertext(text) + text = "[text]: " + if(is_servant_of_ratvar(current)) + text += "loyal|employee|SERVANT" + text += "
Give slab" + else if(current.isloyal()) + text += "LOYAL|employee|servant" + else if(is_eligible_servant(current)) + text += "loyal|EMPLOYEE|servant" + else + text += "loyal|EMPLOYEE|cannot serve Ratvar" + + if(current && current.client && (ROLE_SERVANT_OF_RATVAR in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["clockcult"] = text + + /** TRAITOR ***/ + text = "traitor" + if (SSticker.mode.config_tag=="traitor" || SSticker.mode.config_tag=="traitorchan") + text = uppertext(text) + text = "[text]: " + if (src in SSticker.mode.traitors) + text += "TRAITOR|loyal" + if (objectives.len==0) + text += "
Objectives are empty! Randomize!" + else + text += "traitor|LOYAL" + + if(current && current.client && (ROLE_TRAITOR in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["traitor"] = text + + if(ishuman(current) || ismonkey(current)) + + /** CHANGELING ***/ + text = "changeling" + if (SSticker.mode.config_tag=="changeling" || SSticker.mode.config_tag=="traitorchan") + text = uppertext(text) + text = "[text]: " + if ((src in SSticker.mode.changelings) && special_role) + text += "YES|no" + if (objectives.len==0) + text += "
Objectives are empty! Randomize!" + if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) + text += "
Transform to initial appearance." + else if(src in SSticker.mode.changelings) //Station Aligned Changeling + text += "YES (but not an antag)|no" + if (objectives.len==0) + text += "
Objectives are empty! Randomize!" + if(changeling && changeling.stored_profiles.len && (current.real_name != changeling.first_prof.name) ) + text += "
Transform to initial appearance." + else + text += "yes|NO" + + if(current && current.client && (ROLE_CHANGELING in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["changeling"] = text + + /** MONKEY ***/ + text = "monkey" + if (SSticker.mode.config_tag=="monkey") + text = uppertext(text) + text = "[text]: " + if (ishuman(current)) + text += "healthy|infected|HUMAN|other" + else if (ismonkey(current)) + var/found = 0 + for(var/datum/disease/D in current.viruses) + if(istype(D, /datum/disease/transformation/jungle_fever)) found = 1 + + if(found) + text += "healthy|INFECTED|human|other" + else + text += "HEALTHY|infected|human|other" + + else + text += "healthy|infected|human|OTHER" + + if(current && current.client && (ROLE_MONKEY in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + + sections["monkey"] = text + + /** devil ***/ + text = "devil" + if(SSticker.mode.config_tag == "devil") + text = uppertext(text) + text = "[text]: " + if(src in SSticker.mode.devils) + if(devilinfo && !devilinfo.ascendable) + text += "DEVIL|Ascendable Devil|sintouched|human" + else + text += "DEVIL|ASCENDABLE DEVIL|sintouched|human" + else if(src in SSticker.mode.sintouched) + text += "devil|Ascendable Devil|SINTOUCHED|human" + else + text += "devil|Ascendable Devil|sintouched|HUMAN" + + if(current && current.client && (ROLE_DEVIL in current.client.prefs.be_special)) + text += "|Enabled in Prefs" + else + text += "|Disabled in Prefs" + sections["devil"] = text + + + /** SILICON ***/ + + if(issilicon(current)) + text = "silicon" + var/mob/living/silicon/robot/robot = current + if (istype(robot) && robot.emagged) + text += "
Cyborg: Is emagged! Unemag!
0th law: [robot.laws.zeroth]" + var/mob/living/silicon/ai/ai = current + if (istype(ai) && ai.connected_robots.len) + var/n_e_robots = 0 + for (var/mob/living/silicon/robot/R in ai.connected_robots) + if (R.emagged) + n_e_robots++ + text += "
[n_e_robots] of [ai.connected_robots.len] slaved cyborgs are emagged. Unemag" + if (SSticker.mode.config_tag == "traitorchan") + if (sections["traitor"]) + out += sections["traitor"]+"
" + if (sections["changeling"]) + out += sections["changeling"]+"

" + sections -= "traitor" + sections -= "changeling" + else + if (sections[SSticker.mode.config_tag]) + out += sections[SSticker.mode.config_tag]+"

" + sections -= SSticker.mode.config_tag + for (var/i in sections) + if (sections[i]) + out += sections[i]+"
" + + + if(((src in SSticker.mode.head_revolutionaries) || (src in SSticker.mode.traitors) || (src in SSticker.mode.syndicates)) && ishuman(current)) + + text = "Uplink: give" + var/obj/item/device/uplink/U = find_syndicate_uplink() + if(U) + text += "|take" + if (check_rights(R_FUN, 0)) + text += ", [U.telecrystals] TC" + else + text += ", [U.telecrystals] TC" + text += "." //hiel grammar + out += text + + out += "

" + + out += "Memory:
" + out += memory + out += "
Edit memory
" + out += "Objectives:
" + if (objectives.len == 0) + out += "EMPTY
" + else + var/obj_count = 1 + for(var/datum/objective/objective in objectives) + out += "[obj_count]: [objective.explanation_text] Edit Delete Toggle Completion
" + obj_count++ + out += "Add objective

" + + out += "Announce objectives

" + + usr << browse(out, "window=edit_memory[src];size=500x600") + + +/datum/mind/Topic(href, href_list) + if(!check_rights(R_ADMIN)) + return + + if (href_list["role_edit"]) + var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in get_all_jobs() + if (!new_role) + return + assigned_role = new_role + + else if (href_list["memory_edit"]) + var/new_memo = copytext(sanitize(input("Write new memory", "Memory", memory) as null|message),1,MAX_MESSAGE_LEN) + if (isnull(new_memo)) + return + memory = new_memo + + else if (href_list["obj_edit"] || href_list["obj_add"]) + var/datum/objective/objective + var/objective_pos + var/def_value + + if (href_list["obj_edit"]) + objective = locate(href_list["obj_edit"]) + if (!objective) + return + objective_pos = objectives.Find(objective) + + //Text strings are easy to manipulate. Revised for simplicity. + var/temp_obj_type = "[objective.type]"//Convert path into a text string. + def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. + if(!def_value)//If it's a custom objective, it will be an empty string. + def_value = "custom" + + var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "maroon", "debrain", "protect", "destroy", "prevent", "hijack", "escape", "survive", "martyr", "steal", "download", "nuclear", "capture", "absorb", "custom") + if (!new_obj_type) + return + + var/datum/objective/new_objective = null + + switch (new_obj_type) + if ("assassinate","protect","debrain","maroon") + var/list/possible_targets = list("Free objective") + for(var/datum/mind/possible_target in SSticker.minds) + if ((possible_target != src) && ishuman(possible_target.current)) + possible_targets += possible_target.current + + var/mob/def_target = null + var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain, /datum/objective/maroon) + if (objective&&(objective.type in objective_list) && objective:target) + def_target = objective:target.current + + var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets + if (!new_target) + return + + var/objective_path = text2path("/datum/objective/[new_obj_type]") + if (new_target == "Free objective") + new_objective = new objective_path + new_objective.owner = src + new_objective:target = null + new_objective.explanation_text = "Free objective" + else + new_objective = new objective_path + new_objective.owner = src + new_objective:target = new_target:mind + //Will display as special role if the target is set as MODE. Ninjas/commandos/nuke ops. + new_objective.update_explanation_text() + + if ("destroy") + var/list/possible_targets = active_ais(1) + if(possible_targets.len) + var/mob/new_target = input("Select target:", "Objective target") as null|anything in possible_targets + new_objective = new /datum/objective/destroy + new_objective.target = new_target.mind + new_objective.owner = src + new_objective.update_explanation_text() + else + to_chat(usr, "No active AIs with minds") + + if ("prevent") + new_objective = new /datum/objective/block + new_objective.owner = src + + if ("hijack") + new_objective = new /datum/objective/hijack + new_objective.owner = src + + if ("escape") + new_objective = new /datum/objective/escape + new_objective.owner = src + + if ("survive") + new_objective = new /datum/objective/survive + new_objective.owner = src + + if("martyr") + new_objective = new /datum/objective/martyr + new_objective.owner = src + + if ("nuclear") + new_objective = new /datum/objective/nuclear + new_objective.owner = src + + if ("steal") + if (!istype(objective, /datum/objective/steal)) + new_objective = new /datum/objective/steal + new_objective.owner = src + else + new_objective = objective + var/datum/objective/steal/steal = new_objective + if (!steal.select_target()) + return + + if("download","capture","absorb") + var/def_num + if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) + def_num = objective.target_amount + + var/target_number = input("Input target number:", "Objective", def_num) as num|null + if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. + return + + switch(new_obj_type) + if("download") + new_objective = new /datum/objective/download + new_objective.explanation_text = "Download [target_number] research levels." + if("capture") + new_objective = new /datum/objective/capture + new_objective.explanation_text = "Capture [target_number] lifeforms with an energy net. Live, rare specimens are worth more." + if("absorb") + new_objective = new /datum/objective/absorb + new_objective.explanation_text = "Absorb [target_number] compatible genomes." + new_objective.owner = src + new_objective.target_amount = target_number + + if ("custom") + var/expl = stripped_input(usr, "Custom objective:", "Objective", objective ? objective.explanation_text : "") + if (!expl) + return + new_objective = new /datum/objective + new_objective.owner = src + new_objective.explanation_text = expl + + if (!new_objective) + return + + if (objective) + objectives -= objective + objectives.Insert(objective_pos, new_objective) + message_admins("[key_name_admin(usr)] edited [current]'s objective to [new_objective.explanation_text]") + log_admin("[key_name(usr)] edited [current]'s objective to [new_objective.explanation_text]") + else + objectives += new_objective + message_admins("[key_name_admin(usr)] added a new objective for [current]: [new_objective.explanation_text]") + log_admin("[key_name(usr)] added a new objective for [current]: [new_objective.explanation_text]") + + else if (href_list["obj_delete"]) + var/datum/objective/objective = locate(href_list["obj_delete"]) + if(!istype(objective)) + return + objectives -= objective + message_admins("[key_name_admin(usr)] removed an objective for [current]: [objective.explanation_text]") + log_admin("[key_name(usr)] removed an objective for [current]: [objective.explanation_text]") + + else if(href_list["obj_completed"]) + var/datum/objective/objective = locate(href_list["obj_completed"]) + if(!istype(objective)) + return + objective.completed = !objective.completed + log_admin("[key_name(usr)] toggled the win state for [current]'s objective: [objective.explanation_text]") + + else if (href_list["revolution"]) + switch(href_list["revolution"]) + if("clear") + remove_rev() + to_chat(current, "You have been brainwashed! You are no longer a revolutionary!") + message_admins("[key_name_admin(usr)] has de-rev'ed [current].") + log_admin("[key_name(usr)] has de-rev'ed [current].") + if("rev") + if(src in SSticker.mode.head_revolutionaries) + SSticker.mode.head_revolutionaries -= src + SSticker.mode.update_rev_icons_removed(src) + to_chat(current, "Revolution has been disappointed of your leader traits! You are a regular revolutionary now!") + else if(!(src in SSticker.mode.revolutionaries)) + to_chat(current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") + else + return + SSticker.mode.revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = "Revolutionary" + message_admins("[key_name_admin(usr)] has rev'ed [current].") + log_admin("[key_name(usr)] has rev'ed [current].") + + if("headrev") + if(src in SSticker.mode.revolutionaries) + SSticker.mode.revolutionaries -= src + SSticker.mode.update_rev_icons_removed(src) + to_chat(current, "You have proved your devotion to revoltion! Yea are a head revolutionary now!") + else if(!(src in SSticker.mode.head_revolutionaries)) + to_chat(current, "You are a member of the revolutionaries' leadership now!") + else + return + if (SSticker.mode.head_revolutionaries.len>0) + // copy targets + var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries + if (valid_head) + for (var/datum/objective/mutiny/O in valid_head.objectives) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = src + rev_obj.target = O.target + rev_obj.explanation_text = "Assassinate [O.target.name], the [O.target.assigned_role]." + objectives += rev_obj + SSticker.mode.greet_revolutionary(src,0) + SSticker.mode.head_revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = "Head Revolutionary" + message_admins("[key_name_admin(usr)] has head-rev'ed [current].") + log_admin("[key_name(usr)] has head-rev'ed [current].") + + if("autoobjectives") + SSticker.mode.forge_revolutionary_objectives(src) + SSticker.mode.greet_revolutionary(src,0) + to_chat(usr, "The objectives for revolution have been generated and shown to [key]") + + if("flash") + if (!SSticker.mode.equip_revolutionary(current)) + to_chat(usr, "Spawning flash failed!") + + if("takeflash") + var/list/L = current.get_contents() + var/obj/item/device/assembly/flash/flash = locate() in L + if (!flash) + to_chat(usr, "Deleting flash failed!") + qdel(flash) + + if("repairflash") + var/list/L = current.get_contents() + var/obj/item/device/assembly/flash/flash = locate() in L + if (!flash) + to_chat(usr, "Repairing flash failed!") + else + flash.crit_fail = 0 + flash.update_icon() + + + +//////////////////// GANG MODE + + else if (href_list["gang"]) + switch(href_list["gang"]) + if("clear") + remove_gang() + message_admins("[key_name_admin(usr)] has de-gang'ed [current].") + log_admin("[key_name(usr)] has de-gang'ed [current].") + + if("equip") + switch(SSticker.mode.equip_gang(current,gang_datum)) + if(1) + to_chat(usr, "Unable to equip territory spraycan!") + if(2) + to_chat(usr, "Unable to equip recruitment pen and spraycan!") + if(3) + to_chat(usr, "Unable to equip gangtool, pen, and spraycan!") + + if("takeequip") + var/list/L = current.get_contents() + for(var/obj/item/weapon/pen/gang/pen in L) + qdel(pen) + for(var/obj/item/device/gangtool/gangtool in L) + qdel(gangtool) + for(var/obj/item/toy/crayon/spraycan/gang/SC in L) + qdel(SC) + + if("new") + if(GLOB.gang_colors_pool.len) + var/list/names = list("Random") + GLOB.gang_name_pool + var/gangname = input("Pick a gang name.","Select Name") as null|anything in names + if(gangname && GLOB.gang_colors_pool.len) //Check again just in case another admin made max gangs at the same time + if(!(gangname in GLOB.gang_name_pool)) + gangname = null + var/datum/gang/newgang = new(null,gangname) + SSticker.mode.gangs += newgang + message_admins("[key_name_admin(usr)] has created the [newgang.name] Gang.") + log_admin("[key_name(usr)] has created the [newgang.name] Gang.") + + else if (href_list["gangboss"]) + var/datum/gang/G = locate(href_list["gangboss"]) in SSticker.mode.gangs + if(!G || (src in G.bosses)) + return + SSticker.mode.remove_gangster(src,0,2,1) + G.bosses += src + gang_datum = G + special_role = "[G.name] Gang Boss" + G.add_gang_hud(src) + to_chat(current, "You are a [G.name] Gang Boss!") + message_admins("[key_name_admin(usr)] has added [current] to the [G.name] Gang leadership.") + log_admin("[key_name(usr)] has added [current] to the [G.name] Gang leadership.") + SSticker.mode.forge_gang_objectives(src) + SSticker.mode.greet_gang(src,0) + + else if (href_list["gangster"]) + var/datum/gang/G = locate(href_list["gangster"]) in SSticker.mode.gangs + if(!G || (src in G.gangsters)) + return + SSticker.mode.remove_gangster(src,0,2,1) + SSticker.mode.add_gangster(src,G,0) + message_admins("[key_name_admin(usr)] has added [current] to the [G.name] Gang (A).") + log_admin("[key_name(usr)] has added [current] to the [G.name] Gang (A).") + +///////////////////////////////// + + + + else if (href_list["cult"]) + switch(href_list["cult"]) + if("clear") + remove_cultist() + message_admins("[key_name_admin(usr)] has de-cult'ed [current].") + log_admin("[key_name(usr)] has de-cult'ed [current].") + if("cultist") + if(!(src in SSticker.mode.cult)) + SSticker.mode.add_cultist(src, 0) + message_admins("[key_name_admin(usr)] has cult'ed [current].") + log_admin("[key_name(usr)] has cult'ed [current].") + if("tome") + if (!SSticker.mode.equip_cultist(current,1)) + to_chat(usr, "Spawning tome failed!") + + if("amulet") + if (!SSticker.mode.equip_cultist(current)) + to_chat(usr, "Spawning amulet failed!") + + else if(href_list["clockcult"]) + switch(href_list["clockcult"]) + if("clear") + remove_servant_of_ratvar(current, TRUE) + message_admins("[key_name_admin(usr)] has removed clockwork servant status from [current].") + log_admin("[key_name(usr)] has removed clockwork servant status from [current].") + if("servant") + if(!is_servant_of_ratvar(current)) + add_servant_of_ratvar(current, TRUE) + message_admins("[key_name_admin(usr)] has made [current] into a servant of Ratvar.") + log_admin("[key_name(usr)] has made [current] into a servant of Ratvar.") + if("slab") + if(!SSticker.mode.equip_servant(current)) + to_chat(usr, "Failed to outfit [current] with a slab!") + else + to_chat(usr, "Successfully gave [current] a clockwork slab!") + + else if (href_list["wizard"]) + switch(href_list["wizard"]) + if("clear") + remove_wizard() + to_chat(current, "You have been brainwashed! You are no longer a wizard!") + log_admin("[key_name(usr)] has de-wizard'ed [current].") + SSticker.mode.update_wiz_icons_removed(src) + if("wizard") + if(!(src in SSticker.mode.wizards)) + SSticker.mode.wizards += src + special_role = "Wizard" + //SSticker.mode.learn_basic_spells(current) + to_chat(current, "You are the Space Wizard!") + message_admins("[key_name_admin(usr)] has wizard'ed [current].") + log_admin("[key_name(usr)] has wizard'ed [current].") + SSticker.mode.update_wiz_icons_added(src) + if("lair") + current.loc = pick(GLOB.wizardstart) + if("dressup") + SSticker.mode.equip_wizard(current) + if("name") + SSticker.mode.name_wizard(current) + if("autoobjectives") + SSticker.mode.forge_wizard_objectives(src) + to_chat(usr, "The objectives for wizard [key] have been generated. You can edit them and anounce manually.") + + else if (href_list["changeling"]) + switch(href_list["changeling"]) + if("clear") + remove_changeling() + to_chat(current, "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!") + message_admins("[key_name_admin(usr)] has de-changeling'ed [current].") + log_admin("[key_name(usr)] has de-changeling'ed [current].") + if("changeling") + if(!(src in SSticker.mode.changelings)) + SSticker.mode.changelings += src + current.make_changeling() + special_role = "Changeling" + to_chat(current, "Your powers are awoken. A flash of memory returns to us...we are [changeling.changelingID], a changeling!") + message_admins("[key_name_admin(usr)] has changeling'ed [current].") + log_admin("[key_name(usr)] has changeling'ed [current].") + SSticker.mode.update_changeling_icons_added(src) + if("autoobjectives") + SSticker.mode.forge_changeling_objectives(src) + to_chat(usr, "The objectives for changeling [key] have been generated. You can edit them and anounce manually.") + + if("initialdna") + if( !changeling || !changeling.stored_profiles.len || !istype(current, /mob/living/carbon)) + to_chat(usr, "Resetting DNA failed!") + else + var/mob/living/carbon/C = current + changeling.first_prof.dna.transfer_identity(C, transfer_SE=1) + C.real_name = changeling.first_prof.name + C.updateappearance(mutcolor_update=1) + C.domutcheck() + + else if (href_list["nuclear"]) + switch(href_list["nuclear"]) + if("clear") + remove_nukeop() + to_chat(current, "You have been brainwashed! You are no longer a syndicate operative!") + message_admins("[key_name_admin(usr)] has de-nuke op'ed [current].") + log_admin("[key_name(usr)] has de-nuke op'ed [current].") + if("nuclear") + if(!(src in SSticker.mode.syndicates)) + SSticker.mode.syndicates += src + SSticker.mode.update_synd_icons_added(src) + if (SSticker.mode.syndicates.len==1) + SSticker.mode.prepare_syndicate_leader(src) + else + current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" + special_role = "Syndicate" + assigned_role = "Syndicate" + to_chat(current, "You are a [syndicate_name()] agent!") + SSticker.mode.forge_syndicate_objectives(src) + SSticker.mode.greet_syndicate(src) + message_admins("[key_name_admin(usr)] has nuke op'ed [current].") + log_admin("[key_name(usr)] has nuke op'ed [current].") + if("lair") + current.loc = get_turf(locate("landmark*Syndicate-Spawn")) + if("dressup") + var/mob/living/carbon/human/H = current + qdel(H.belt) + qdel(H.back) + qdel(H.ears) + qdel(H.gloves) + qdel(H.head) + qdel(H.shoes) + qdel(H.wear_id) + qdel(H.wear_suit) + qdel(H.w_uniform) + + if (!SSticker.mode.equip_syndicate(current)) + to_chat(usr, "Equipping a syndicate failed!") + if("tellcode") + var/code + for (var/obj/machinery/nuclearbomb/bombue in GLOB.machines) + if (length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") + code = bombue.r_code + break + if (code) + store_memory("Syndicate Nuclear Bomb Code: [code]", 0, 0) + to_chat(current, "The nuclear authorization code is: [code]") + else + to_chat(usr, "No valid nuke found!") + + else if (href_list["traitor"]) + switch(href_list["traitor"]) + if("clear") + remove_traitor() + to_chat(current, "You have been brainwashed! You are no longer a traitor!") + message_admins("[key_name_admin(usr)] has de-traitor'ed [current].") + log_admin("[key_name(usr)] has de-traitor'ed [current].") + SSticker.mode.update_traitor_icons_removed(src) + + if("traitor") + if(!(src in SSticker.mode.traitors)) + SSticker.mode.traitors += src + special_role = "traitor" + to_chat(current, "You are a traitor!") + message_admins("[key_name_admin(usr)] has traitor'ed [current].") + log_admin("[key_name(usr)] has traitor'ed [current].") + if(isAI(current)) + var/mob/living/silicon/ai/A = current + SSticker.mode.add_law_zero(A) + SSticker.mode.update_traitor_icons_added(src) + + if("autoobjectives") + SSticker.mode.forge_traitor_objectives(src) + to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and anounce manually.") + + else if(href_list["devil"]) + switch(href_list["devil"]) + if("clear") + if(src in SSticker.mode.devils) + if(istype(current,/mob/living/carbon/true_devil/)) + if(devilinfo) + devilinfo.regress_blood_lizard() + else + to_chat(usr, "Something went wrong with removing the devil, we were unable to find an attached devilinfo..") + SSticker.mode.devils -= src + special_role = null + to_chat(current, "Your infernal link has been severed! You are no longer a devil!") + RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt) + RemoveSpell(/obj/effect/proc_holder/spell/aimed/fireball/hellish) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/violin) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/greater) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/summon_pitchfork/ascended) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_dancefloor) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch/ascended) + message_admins("[key_name_admin(usr)] has de-devil'ed [current].") + devilinfo = null + if(issilicon(current)) + var/mob/living/silicon/S = current + S.clear_law_sixsixsix(current) + log_admin("[key_name(usr)] has de-devil'ed [current].") + else if(src in SSticker.mode.sintouched) + SSticker.mode.sintouched -= src + message_admins("[key_name_admin(usr)] has de-sintouch'ed [current].") + log_admin("[key_name(usr)] has de-sintouch'ed [current].") + if("devil") + if(devilinfo) + devilinfo.ascendable = FALSE + message_admins("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") + log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") + return + if(!ishuman(current) && !iscyborg(current)) + to_chat(usr, "This only works on humans and cyborgs!") + return + SSticker.mode.devils += src + special_role = "devil" + SSticker.mode.finalize_devil(src, FALSE) + SSticker.mode.add_devil_objectives(src, 2) + announceDevilLaws() + announce_objectives() + message_admins("[key_name_admin(usr)] has devil'ed [current].") + log_admin("[key_name(usr)] has devil'ed [current].") + if("ascendable_devil") + if(devilinfo) + devilinfo.ascendable = TRUE + message_admins("[key_name_admin(usr)] has made [current] able to ascend as a devil.") + log_admin("[key_name_admin(usr)] has made [current] able to ascend as a devil.") + return + if(!ishuman(current) && !iscyborg(current)) + to_chat(usr, "This only works on humans and cyborgs!") + return + SSticker.mode.devils += src + special_role = "devil" + SSticker.mode.finalize_devil(src, TRUE) + SSticker.mode.add_devil_objectives(src, 2) + announceDevilLaws() + announce_objectives() + message_admins("[key_name_admin(usr)] has devil'ed [current]. The devil has been marked as ascendable.") + log_admin("[key_name(usr)] has devil'ed [current]. The devil has been marked as ascendable.") + if("sintouched") + if(ishuman(current)) + var/mob/living/carbon/human/H = current + H.influenceSin() + message_admins("[key_name_admin(usr)] has sintouch'ed [current].") + else + to_chat(usr, "This only works on humans!") + return + + else if(href_list["abductor"]) + switch(href_list["abductor"]) + if("clear") + to_chat(usr, "Not implemented yet. Sorry!") + //SSticker.mode.update_abductor_icons_removed(src) + if("abductor") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + make_Abductor() + log_admin("[key_name(usr)] turned [current] into abductor.") + SSticker.mode.update_abductor_icons_added(src) + if("equip") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + + var/mob/living/carbon/human/H = current + var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist") + if(gear) + if(gear=="Agent") + H.equipOutfit(/datum/outfit/abductor/agent) + else + H.equipOutfit(/datum/outfit/abductor/scientist) + + else if (href_list["monkey"]) + var/mob/living/L = current + if (L.notransform) + return + switch(href_list["monkey"]) + if("healthy") + if (check_rights(R_ADMIN)) + var/mob/living/carbon/human/H = current + var/mob/living/carbon/monkey/M = current + if (istype(H)) + log_admin("[key_name(usr)] attempting to monkeyize [key_name(current)]") + message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(current)]") + src = null + M = H.monkeyize() + src = M.mind + //to_chat(world, "DEBUG: \"healthy\": M=[M], M.mind=[M.mind], src=[src]!") + else if (istype(M) && length(M.viruses)) + for(var/datum/disease/D in M.viruses) + D.cure(0) + sleep(0) //because deleting of virus is done through spawn(0) + if("infected") + if (check_rights(R_ADMIN, 0)) + var/mob/living/carbon/human/H = current + var/mob/living/carbon/monkey/M = current + if (istype(H)) + log_admin("[key_name(usr)] attempting to monkeyize and infect [key_name(current)]") + message_admins("[key_name_admin(usr)] attempting to monkeyize and infect [key_name_admin(current)]") + src = null + M = H.monkeyize() + src = M.mind + current.ForceContractDisease(new /datum/disease/transformation/jungle_fever) + else if (istype(M)) + current.ForceContractDisease(new /datum/disease/transformation/jungle_fever) + if("human") + if (check_rights(R_ADMIN, 0)) + var/mob/living/carbon/human/H = current + var/mob/living/carbon/monkey/M = current + if (istype(M)) + for(var/datum/disease/D in M.viruses) + if (istype(D,/datum/disease/transformation/jungle_fever)) + D.cure(0) + sleep(0) //because deleting of virus is doing throught spawn(0) + log_admin("[key_name(usr)] attempting to humanize [key_name(current)]") + message_admins("[key_name_admin(usr)] attempting to humanize [key_name_admin(current)]") + H = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_DEFAULTMSG) + if(H) + src = H.mind + + else if (href_list["silicon"]) + switch(href_list["silicon"]) + if("unemag") + var/mob/living/silicon/robot/R = current + if (istype(R)) + R.SetEmagged(0) + message_admins("[key_name_admin(usr)] has unemag'ed [R].") + log_admin("[key_name(usr)] has unemag'ed [R].") + + if("unemagcyborgs") + if(isAI(current)) + var/mob/living/silicon/ai/ai = current + for (var/mob/living/silicon/robot/R in ai.connected_robots) + R.SetEmagged(0) + message_admins("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.") + log_admin("[key_name(usr)] has unemag'ed [ai]'s Cyborgs.") + + else if (href_list["common"]) + switch(href_list["common"]) + if("undress") + for(var/obj/item/W in current) + current.dropItemToGround(W, TRUE) //The 1 forces all items to drop, since this is an admin undress. + if("takeuplink") + take_uplink() + memory = null//Remove any memory they may have had. + log_admin("[key_name(usr)] removed [current]'s uplink.") + if("crystals") + if(check_rights(R_FUN, 0)) + var/obj/item/device/uplink/U = find_syndicate_uplink() + if(U) + var/crystals = input("Amount of telecrystals for [key]","Syndicate uplink", U.telecrystals) as null|num + if(!isnull(crystals)) + U.telecrystals = crystals + message_admins("[key_name_admin(usr)] changed [current]'s telecrystal count to [crystals].") + log_admin("[key_name(usr)] changed [current]'s telecrystal count to [crystals].") + if("uplink") + if(!SSticker.mode.equip_traitor(current, !(src in SSticker.mode.traitors))) + to_chat(usr, "Equipping a syndicate failed!") + log_admin("[key_name(usr)] attempted to give [current] an uplink.") + + else if (href_list["obj_announce"]) + announce_objectives() + + edit_memory() + +/datum/mind/proc/announce_objectives() + var/obj_count = 1 + to_chat(current, "Your current objectives:") + for(var/objective in objectives) + var/datum/objective/O = objective + to_chat(current, "Objective #[obj_count]: [O.explanation_text]") + obj_count++ + +/datum/mind/proc/find_syndicate_uplink() + var/list/L = current.get_contents() + for (var/obj/item/I in L) + if (I.hidden_uplink) + return I.hidden_uplink + return null + +/datum/mind/proc/take_uplink() + var/obj/item/device/uplink/H = find_syndicate_uplink() + if(H) + qdel(H) + +/datum/mind/proc/make_Traitor() + if(!(src in SSticker.mode.traitors)) + SSticker.mode.traitors += src + special_role = "traitor" + SSticker.mode.forge_traitor_objectives(src) + SSticker.mode.finalize_traitor(src) + SSticker.mode.greet_traitor(src) + +/datum/mind/proc/make_Nuke(turf/spawnloc, nuke_code, leader=0, telecrystals = TRUE) + if(!(src in SSticker.mode.syndicates)) + SSticker.mode.syndicates += src + SSticker.mode.update_synd_icons_added(src) + special_role = "Syndicate" + SSticker.mode.forge_syndicate_objectives(src) + SSticker.mode.greet_syndicate(src) + current.faction |= "syndicate" + + if(spawnloc) + current.loc = spawnloc + + if(ishuman(current)) + var/mob/living/carbon/human/H = current + qdel(H.belt) + qdel(H.back) + qdel(H.ears) + qdel(H.gloves) + qdel(H.head) + qdel(H.shoes) + qdel(H.wear_id) + qdel(H.wear_suit) + qdel(H.w_uniform) + + SSticker.mode.equip_syndicate(current, telecrystals) + + if (nuke_code) + store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) + to_chat(current, "The nuclear authorization code is: [nuke_code]") + else + var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in GLOB.nuke_list + if(nuke) + store_memory("Syndicate Nuclear Bomb Code: [nuke.r_code]", 0, 0) + to_chat(current, "The nuclear authorization code is: nuke.r_code") + else + to_chat(current, "You were not provided with a nuclear code. Trying asking your team leader or contacting syndicate command.") + + if (leader) + SSticker.mode.prepare_syndicate_leader(src,nuke_code) + else + current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" + +/datum/mind/proc/make_Changling() + if(!(src in SSticker.mode.changelings)) + SSticker.mode.changelings += src + current.make_changeling() + special_role = "Changeling" + SSticker.mode.forge_changeling_objectives(src) + SSticker.mode.greet_changeling(src) + SSticker.mode.update_changeling_icons_added(src) + +/datum/mind/proc/make_Wizard() + if(!(src in SSticker.mode.wizards)) + SSticker.mode.wizards += src + special_role = "Wizard" + assigned_role = "Wizard" + if(!GLOB.wizardstart.len) + current.loc = pick(GLOB.latejoin) + to_chat(current, "HOT INSERTION, GO GO GO") + else + current.loc = pick(GLOB.wizardstart) + + SSticker.mode.equip_wizard(current) + SSticker.mode.name_wizard(current) + SSticker.mode.forge_wizard_objectives(src) + SSticker.mode.greet_wizard(src) + + +/datum/mind/proc/make_Cultist() + if(!(src in SSticker.mode.cult)) + SSticker.mode.add_cultist(src,FALSE) + special_role = "Cultist" + to_chat(current, "You catch a glimpse of the Realm of Nar-Sie, The Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of Nar-Sie.") + to_chat(current, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.") + var/datum/antagonist/cult/C + C.cult_memorization(src) + var/mob/living/carbon/human/H = current + if (!SSticker.mode.equip_cultist(current)) + to_chat(H, "Spawning an amulet from your Master failed.") + +/datum/mind/proc/make_Rev() + if (SSticker.mode.head_revolutionaries.len>0) + // copy targets + var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries + if (valid_head) + for (var/datum/objective/mutiny/O in valid_head.objectives) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = src + rev_obj.target = O.target + rev_obj.explanation_text = "Assassinate [O.target.current.real_name], the [O.target.assigned_role]." + objectives += rev_obj + SSticker.mode.greet_revolutionary(src,0) + SSticker.mode.head_revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = "Head Revolutionary" + + SSticker.mode.forge_revolutionary_objectives(src) + SSticker.mode.greet_revolutionary(src,0) + + var/list/L = current.get_contents() + var/obj/item/device/assembly/flash/flash = locate() in L + qdel(flash) + take_uplink() + var/fail = 0 +// fail |= !SSticker.mode.equip_traitor(current, 1) + fail |= !SSticker.mode.equip_revolutionary(current) + + +/datum/mind/proc/make_Gang(datum/gang/G) + special_role = "[G.name] Gang Boss" + G.bosses += src + gang_datum = G + G.add_gang_hud(src) + SSticker.mode.forge_gang_objectives(src) + SSticker.mode.greet_gang(src) + SSticker.mode.equip_gang(current,G) + +/datum/mind/proc/make_Abductor() + var/role = alert("Abductor Role ?","Role","Agent","Scientist") + var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4) + var/teleport = alert("Teleport to ship ?","Teleport","Yes","No") + + if(!role || !team || !teleport) + return + + if(!ishuman(current)) + return + + SSticker.mode.abductors |= src + + var/datum/objective/experiment/O = new + O.owner = src + objectives += O + + var/mob/living/carbon/human/H = current + + H.set_species(/datum/species/abductor) + var/datum/species/abductor/S = H.dna.species + + if(role == "Scientist") + S.scientist = TRUE + S.team = team + + var/list/obj/effect/landmark/abductor/agent_landmarks = new + var/list/obj/effect/landmark/abductor/scientist_landmarks = new + agent_landmarks.len = 4 + scientist_landmarks.len = 4 + for(var/obj/effect/landmark/abductor/A in GLOB.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/obj/effect/landmark/L + if(teleport=="Yes") + switch(role) + if("Agent") + L = agent_landmarks[team] + if("Scientist") + L = scientist_landmarks[team] + H.forceMove(L.loc) + +/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S) + spell_list += S + S.action.Grant(current) + +//To remove a specific spell from a mind +/datum/mind/proc/RemoveSpell(obj/effect/proc_holder/spell/spell) + if(!spell) + return + for(var/X in spell_list) + var/obj/effect/proc_holder/spell/S = X + if(istype(S, spell)) + spell_list -= S + qdel(S) + +/datum/mind/proc/transfer_actions(mob/living/new_character) + if(current && current.actions) + for(var/datum/action/A in current.actions) + A.Grant(new_character) + transfer_mindbound_actions(new_character) + +/datum/mind/proc/transfer_mindbound_actions(mob/living/new_character) + for(var/X in spell_list) + var/obj/effect/proc_holder/spell/S = X + S.action.Grant(new_character) + +/datum/mind/proc/disrupt_spells(delay, list/exceptions = New()) + for(var/X in spell_list) + var/obj/effect/proc_holder/spell/S = X + for(var/type in exceptions) + if(istype(S, type)) + continue + S.charge_counter = delay + INVOKE_ASYNC(S, /obj/effect/proc_holder/spell.proc/start_recharge) + +/datum/mind/proc/get_ghost(even_if_they_cant_reenter) + for(var/mob/dead/observer/G in GLOB.dead_mob_list) + if(G.mind == src) + if(G.can_reenter_corpse || even_if_they_cant_reenter) + return G + break + +/datum/mind/proc/grab_ghost(force) + var/mob/dead/observer/G = get_ghost(even_if_they_cant_reenter = force) + . = G + if(G) + G.reenter_corpse() + +/mob/proc/sync_mind() + mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) + mind.active = 1 //indicates that the mind is currently synced with a client + +/mob/dead/new_player/sync_mind() + return + +/mob/dead/observer/sync_mind() + return + +//Initialisation procs +/mob/proc/mind_initialize() + if(mind) + mind.key = key + + else + mind = new /datum/mind(key) + if(SSticker) + SSticker.minds += mind + else + stack_trace("mind_initialize(): No SSticker ready") + if(!mind.name) + mind.name = real_name + mind.current = src + +/mob/living/carbon/mind_initialize() + ..() + last_mind = mind + +//HUMAN +/mob/living/carbon/human/mind_initialize() + ..() + if(!mind.assigned_role) + mind.assigned_role = "Assistant" //defualt + +//XENO +/mob/living/carbon/alien/mind_initialize() + ..() + mind.special_role = "Alien" + +//AI +/mob/living/silicon/ai/mind_initialize() + ..() + mind.assigned_role = "AI" + +//BORG +/mob/living/silicon/robot/mind_initialize() + ..() + mind.assigned_role = "Cyborg" + +//PAI +/mob/living/silicon/pai/mind_initialize() + ..() + mind.assigned_role = "pAI" + mind.special_role = "" diff --git a/code/datums/wires/explosive.dm b/code/datums/wires/explosive.dm index eefc270cc9..0b75d86905 100644 --- a/code/datums/wires/explosive.dm +++ b/code/datums/wires/explosive.dm @@ -13,16 +13,16 @@ /datum/wires/explosive/c4 - holder_type = /obj/item/weapon/c4 + holder_type = /obj/item/weapon/grenade/plastic/c4 randomize = TRUE //Same behaviour since no wire actually disarms it /datum/wires/explosive/c4/interactable(mob/user) - var/obj/item/weapon/c4/P = holder + var/obj/item/weapon/grenade/plastic/c4/P = holder if(P.open_panel) return TRUE /datum/wires/explosive/c4/explode() - var/obj/item/weapon/c4/P = holder + var/obj/item/weapon/grenade/plastic/c4/P = holder P.explode() diff --git a/code/game/gamemodes/antag_spawner.dm b/code/game/gamemodes/antag_spawner.dm index 5b568970d9..6f5afd5767 100644 --- a/code/game/gamemodes/antag_spawner.dm +++ b/code/game/gamemodes/antag_spawner.dm @@ -155,8 +155,8 @@ if(!(check_usability(user))) return - to_chat(user, "You activate [src] and wait for confirmation.") - var/list/nuke_candidates = pollCandidatesForMob("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, POLL_IGNORE_SYNDICATE, src) + to_chat(user, "You activate [src] and wait for confirmation.") + var/list/nuke_candidates = pollCandidatesForMob("Do you want to play as a syndicate [borg_to_spawn ? "[lowertext(borg_to_spawn)] cyborg":"operative"]?", ROLE_OPERATIVE, null, ROLE_OPERATIVE, 150, POLL_IGNORE_SYNDICATE, src) if(nuke_candidates.len) if(!(check_usability(user))) return diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index f3ddb6d4be..1587f233d9 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -17,7 +17,7 @@ var/atom/movable/constant_target = null //The thing we're always focused on, if we're in the right mode var/target_x = 0 //The target coordinates if we're tracking those var/target_y = 0 - var/minimum_range = 0 //at what range the pinpointer declares you to be at your destination + var/minimum_range = 0 //at what range the pinpointer declares you to be at your destination var/nuke_warning = FALSE // If we've set off a miniature alarm about an armed nuke var/mode = TRACK_NUKE_DISK //What are we looking for? @@ -112,7 +112,7 @@ var/mob/living/closest_operative = get_closest_atom(/mob/living/carbon/human, possible_targets, here) if(closest_operative) target = closest_operative - if(TRACK_ATOM) + if(TRACK_ATOM) if(constant_target) target = constant_target if(TRACK_COORDINATES) @@ -130,7 +130,7 @@ if(here.z != there.z) icon_state = "pinon[nuke_warning ? "alert" : ""]null" return - if(get_dist_euclidian(here,there)<=minimum_range) + if(get_dist_euclidian(here,there)<=minimum_range) icon_state = "pinon[nuke_warning ? "alert" : ""]direct" else setDir(get_dir(here, there)) @@ -172,6 +172,6 @@ desc = "An integrated tracking device, jury-rigged to search for living Syndicate operatives." mode = TRACK_OPERATIVES flags = NODROP - - - + + + diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index aef2e14446..143d60be91 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -1,916 +1,916 @@ -/datum/spellbook_entry - var/name = "Entry Name" - - var/spell_type = null - var/desc = "" - var/category = "Offensive" - var/cost = 2 - var/refundable = 1 - var/surplus = -1 // -1 for infinite, not used by anything atm - var/obj/effect/proc_holder/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell - var/buy_word = "Learn" - var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook - var/list/no_coexistance_typecache //Used so you can't have specific spells together - -/datum/spellbook_entry/New() - ..() - no_coexistance_typecache = typecacheof(no_coexistance_typecache) - -/datum/spellbook_entry/proc/IsAvailible() // For config prefs / gamemode restrictions - these are round applied - return 1 - -/datum/spellbook_entry/proc/CanBuy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) // Specific circumstances - if(book.uses= aspell.level_max) - to_chat(user, "This spell cannot be improved further.") - return 0 - else - aspell.name = initial(aspell.name) - aspell.spell_level++ - aspell.charge_max = round(initial(aspell.charge_max) - aspell.spell_level * (initial(aspell.charge_max) - aspell.cooldown_min)/ aspell.level_max) - if(aspell.charge_max < aspell.charge_counter) - aspell.charge_counter = aspell.charge_max - switch(aspell.spell_level) - if(1) - to_chat(user, "You have improved [aspell.name] into Efficient [aspell.name].") - aspell.name = "Efficient [aspell.name]" - if(2) - to_chat(user, "You have further improved [aspell.name] into Quickened [aspell.name].") - aspell.name = "Quickened [aspell.name]" - if(3) - to_chat(user, "You have further improved [aspell.name] into Free [aspell.name].") - aspell.name = "Free [aspell.name]" - if(4) - to_chat(user, "You have further improved [aspell.name] into Instant [aspell.name].") - aspell.name = "Instant [aspell.name]" - if(aspell.spell_level >= aspell.level_max) - to_chat(user, "This spell cannot be strengthened any further.") - SSblackbox.add_details("wizard_spell_improved", "[name]|[aspell.level]") - return 1 - //No same spell found - just learn it - SSblackbox.add_details("wizard_spell_learned", name) - user.mind.AddSpell(S) - to_chat(user, "You have learned [S.name].") - return 1 - -/datum/spellbook_entry/proc/CanRefund(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) - if(!refundable) - return 0 - if(!S) - S = new spell_type() - for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) - if(initial(S.name) == initial(aspell.name)) - return 1 - return 0 - -/datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) //return point value or -1 for failure - var/area/wizard_station/A = locate() - if(!(user in A.contents)) - to_chat(user, "You can only refund spells at the wizard lair") - return -1 - if(!S) - S = new spell_type() - var/spell_levels = 0 - for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) - if(initial(S.name) == initial(aspell.name)) - spell_levels = aspell.spell_level - user.mind.spell_list.Remove(aspell) - qdel(S) - return cost * (spell_levels+1) - return -1 -/datum/spellbook_entry/proc/GetInfo() - if(!S) - S = new spell_type() - var/dat ="" - dat += "[initial(S.name)]" - if(S.charge_type == "recharge") - dat += " Cooldown:[S.charge_max/10]" - dat += " Cost:[cost]
" - dat += "[S.desc][desc]
" - dat += "[S.clothes_req?"Needs wizard garb":"Can be cast without wizard garb"]
" - return dat - -/datum/spellbook_entry/fireball - name = "Fireball" - spell_type = /obj/effect/proc_holder/spell/aimed/fireball - -/* -/datum/spellbook_entry/rod_form - name = "Rod Form" - spell_type = /obj/effect/proc_holder/spell/targeted/rod_form */ - -/datum/spellbook_entry/magicm - name = "Magic Missile" - spell_type = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile - category = "Defensive" - -/datum/spellbook_entry/disintegrate - name = "Disintegrate" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/disintegrate - cost = 6 - -/datum/spellbook_entry/disabletech - name = "Disable Tech" - spell_type = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/repulse - name = "Repulse" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/repulse - category = "Defensive" - -/datum/spellbook_entry/lightningPacket - name = "Lightning bolt! Lightning bolt!" - spell_type = /obj/effect/proc_holder/spell/targeted/conjure_item/spellpacket - category = "Defensive" - -/datum/spellbook_entry/timestop - name = "Time Stop" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop - category = "Defensive" - cost = 4 - -/datum/spellbook_entry/smoke - name = "Smoke" - spell_type = /obj/effect/proc_holder/spell/targeted/smoke - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/blind - name = "Blind" - spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind - cost = 1 - -/datum/spellbook_entry/mindswap - name = "Mindswap" - spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer - category = "Mobility" - -/datum/spellbook_entry/forcewall - name = "Force Wall" - spell_type = /obj/effect/proc_holder/spell/targeted/forcewall - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/blink - name = "Blink" - spell_type = /obj/effect/proc_holder/spell/targeted/turf_teleport/blink - category = "Mobility" - -/datum/spellbook_entry/teleport - name = "Teleport" - spell_type = /obj/effect/proc_holder/spell/targeted/area_teleport/teleport - category = "Mobility" - -/datum/spellbook_entry/mutate - name = "Mutate" - spell_type = /obj/effect/proc_holder/spell/targeted/genetic/mutate - -/datum/spellbook_entry/jaunt - name = "Ethereal Jaunt" - spell_type = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt - category = "Mobility" - -/datum/spellbook_entry/knock - name = "Knock" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock - category = "Mobility" - cost = 1 - -/datum/spellbook_entry/fleshtostone - name = "Flesh to Stone" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/flesh_to_stone - -/datum/spellbook_entry/summonitem - name = "Summon Item" - spell_type = /obj/effect/proc_holder/spell/targeted/summonitem - category = "Assistance" - cost = 1 - -/datum/spellbook_entry/lichdom - name = "Bind Soul" - spell_type = /obj/effect/proc_holder/spell/targeted/lichdom - category = "Defensive" - cost = 4 - -/datum/spellbook_entry/teslablast - name = "Tesla Blast" - spell_type = /obj/effect/proc_holder/spell/targeted/tesla - -/datum/spellbook_entry/lightningbolt - name = "Lightning Bolt" - spell_type = /obj/effect/proc_holder/spell/aimed/lightningbolt - cost = 3 - -/datum/spellbook_entry/lightningbolt/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) //return 1 on success - . = ..() - SET_SECONDARY_FLAG(user, TESLA_IGNORE) - -/datum/spellbook_entry/infinite_guns - name = "Lesser Summon Guns" - spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns/gun - cost = 3 - no_coexistance_typecache = /obj/effect/proc_holder/spell/targeted/infinite_guns/arcane_barrage - -/datum/spellbook_entry/arcane_barrage - name = "Arcane Barrage" - spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns/arcane_barrage - cost = 3 - no_coexistance_typecache = /obj/effect/proc_holder/spell/targeted/infinite_guns/gun - -/datum/spellbook_entry/barnyard - name = "Barnyard Curse" - spell_type = /obj/effect/proc_holder/spell/targeted/barnyardcurse - -/datum/spellbook_entry/charge - name = "Charge" - spell_type = /obj/effect/proc_holder/spell/targeted/charge - category = "Assistance" - cost = 1 - -/datum/spellbook_entry/shapeshift - name = "Wild Shapeshift" - spell_type = /obj/effect/proc_holder/spell/targeted/shapeshift - category = "Assistance" - cost = 1 - -/datum/spellbook_entry/spacetime_dist - name = "Spacetime Distortion" - spell_type = /obj/effect/proc_holder/spell/spacetime_dist - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/the_traps - name = "The Traps!" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/the_traps - category = "Defensive" - cost = 1 - - -/datum/spellbook_entry/item - name = "Buy Item" - refundable = 0 - buy_word = "Summon" - var/item_path= null - - -/datum/spellbook_entry/item/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) - new item_path(get_turf(user)) - SSblackbox.add_details("wizard_spell_learned", name) - return 1 - -/datum/spellbook_entry/item/GetInfo() - var/dat ="" - dat += "[name]" - dat += " Cost:[cost]
" - dat += "[desc]
" - if(surplus>=0) - dat += "[surplus] left.
" - return dat -/* // these have never been fun, like, ever. -/datum/spellbook_entry/item/staffchange - name = "Staff of Change" - desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself." - item_path = /obj/item/weapon/gun/magic/staff/change - -/datum/spellbook_entry/item/staffanimation - name = "Staff of Animation" - desc = "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines." - item_path = /obj/item/weapon/gun/magic/staff/animate - category = "Assistance" */ - -/datum/spellbook_entry/item/staffchaos - name = "Staff of Chaos" - desc = "A caprious tool that can fire all sorts of magic without any rhyme or reason. Using it on people you care about is not recommended." - item_path = /obj/item/weapon/gun/magic/staff/chaos - cost = 4 - -/datum/spellbook_entry/item/spellblade - name = "Spellblade" - desc = "A sword capable of firing blasts of energy which rip targets limb from limb." - item_path = /obj/item/weapon/gun/magic/staff/spellblade - -/datum/spellbook_entry/item/staffdoor - name = "Staff of Door Creation" - desc = "A particular staff that can mold solid metal into ornate doors. Useful for getting around in the absence of other transportation. Does not work on glass." - item_path = /obj/item/weapon/gun/magic/staff/door - cost = 1 - category = "Mobility" - -/datum/spellbook_entry/item/staffhealing - name = "Staff of Healing" - desc = "An altruistic staff that can heal the lame and raise the dead." - item_path = /obj/item/weapon/gun/magic/staff/healing - cost = 1 - category = "Defensive" - -/datum/spellbook_entry/item/scryingorb - name = "Scrying Orb" - desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you x-ray vision." - item_path = /obj/item/weapon/scrying - category = "Defensive" - -/datum/spellbook_entry/item/scryingorb/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) - if(..()) - if (!(user.dna.check_mutation(XRAY))) - user.dna.add_mutation(XRAY) - return 1 - -/datum/spellbook_entry/item/soulstones - name = "Six Soul Stone Shards and the spell Artificer" - desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. The spell Artificer allows you to create arcane machines for the captured souls to pilot." - item_path = /obj/item/weapon/storage/belt/soulstone/full - category = "Assistance" - -/datum/spellbook_entry/item/soulstones/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) - . =..() - if(.) - user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/construct(null)) - return . - -/datum/spellbook_entry/item/necrostone - name = "A Necromantic Stone" - desc = "A Necromantic stone is able to resurrect three dead individuals as skeletal thralls for you to command." - item_path = /obj/item/device/necromantic_stone - category = "Assistance" - -/datum/spellbook_entry/item/wands - name = "Wand Assortment" - desc = "A collection of wands that allow for a wide variety of utility. Wands have a limited number of charges, so be conservative in use. Comes in a handy belt." - item_path = /obj/item/weapon/storage/belt/wands/full - category = "Defensive" - -/datum/spellbook_entry/item/armor - name = "Mastercrafted Armor Set" - desc = "An artefact suit of armor that allows you to cast spells while providing more protection against attacks and the void of space." - item_path = /obj/item/clothing/suit/space/hardsuit/wizard - category = "Defensive" - -/datum/spellbook_entry/item/armor/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) - . = ..() - if(.) - new /obj/item/clothing/shoes/sandal/magic(get_turf(user)) //In case they've lost them. - new /obj/item/clothing/gloves/color/purple(get_turf(user))//To complete the outfit - -/datum/spellbook_entry/item/contract - name = "Contract of Apprenticeship" - desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side." - item_path = /obj/item/weapon/antag_spawner/contract - category = "Assistance" - -/datum/spellbook_entry/item/guardian - name = "Guardian Deck" - desc = "A deck of guardian tarot cards, capable of binding a personal guardian to your body. There are multiple types of guardian available, but all of them will transfer some amount of damage to you. \ - It would be wise to avoid buying these with anything capable of causing you to swap bodies with others." - item_path = /obj/item/weapon/guardiancreator/choose/wizard - category = "Assistance" - -/datum/spellbook_entry/item/guardian/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) - . = ..() - if(.) - new /obj/item/weapon/paper/guardian/wizard(get_turf(user)) - -/datum/spellbook_entry/item/bloodbottle - name = "Bottle of Blood" - desc = "A bottle of magically infused blood, the smell of which will attract extradimensional beings when broken. Be careful though, the kinds of creatures summoned by blood magic are indiscriminate in their killing, and you yourself may become a victim." - item_path = /obj/item/weapon/antag_spawner/slaughter_demon - limit = 3 - category = "Assistance" - -/datum/spellbook_entry/item/hugbottle - name = "Bottle of Tickles" - desc = "A bottle of magically infused fun, the smell of which will \ - attract adorable extradimensional beings when broken. These beings \ - are similar to slaughter demons, but they do not permamently kill \ - their victims, instead putting them in an extradimensional hugspace, \ - to be released on the demon's death. Chaotic, but not ultimately \ - damaging. The crew's reaction to the other hand could be very \ - destructive." - item_path = /obj/item/weapon/antag_spawner/slaughter_demon/laughter - cost = 1 //non-destructive; it's just a jape, sibling! - limit = 3 - category = "Assistance" - -/datum/spellbook_entry/item/mjolnir - name = "Mjolnir" - desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power." - item_path = /obj/item/weapon/twohanded/mjollnir - -/datum/spellbook_entry/item/singularity_hammer - name = "Singularity Hammer" - desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everything nearby to the point of impact." - item_path = /obj/item/weapon/twohanded/singularityhammer - -/datum/spellbook_entry/item/battlemage - name = "Battlemage Armour" - desc = "An ensorcelled suit of armour, protected by a powerful shield. The shield can completly negate sixteen attacks before being permanently depleted." - item_path = /obj/item/clothing/suit/space/hardsuit/shielded/wizard - limit = 1 - category = "Defensive" - -/datum/spellbook_entry/item/battlemage_charge - name = "Battlemage Armour Charges" - desc = "A powerful defensive rune, it will grant eight additional charges to a suit of battlemage armour." - item_path = /obj/item/wizard_armour_charge - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/item/warpwhistle - name = "Warp Whistle" - desc = "A strange whistle that will transport you to a distant safe place on the station. There is a window of vulnerability at the begining of every use." - item_path = /obj/item/warpwhistle - category = "Mobility" - cost = 1 - -/datum/spellbook_entry/summon - name = "Summon Stuff" - category = "Rituals" - refundable = 0 - buy_word = "Cast" - var/active = 0 - -/datum/spellbook_entry/summon/CanBuy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) - return ..() && !active - -/datum/spellbook_entry/summon/GetInfo() - var/dat ="" - dat += "[name]" - if(cost>0) - dat += " Cost:[cost]
" - else - dat += " No Cost
" - dat += "[desc]
" - if(active) - dat += "Already cast!
" - return dat - -/datum/spellbook_entry/summon/ghosts - name = "Summon Ghosts" - desc = "Spook the crew out by making them see dead people. Be warned, ghosts are capricious and occasionally vindicative, and some will use their incredibly minor abilties to frustrate you." - cost = 0 - -/datum/spellbook_entry/summon/ghosts/IsAvailible() - if(!SSticker.mode) - return FALSE - else - return TRUE - -/datum/spellbook_entry/summon/ghosts/Buy(mob/living/carbon/human/user, obj/item/weapon/spellbook/book) - SSblackbox.add_details("wizard_spell_learned", name) - new /datum/round_event/wizard/ghost() - active = TRUE - to_chat(user, "You have cast summon ghosts!") - playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1) - return TRUE - -/datum/spellbook_entry/summon/guns - name = "Summon Guns" - desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. Just be careful not to stand still too long!" - -/datum/spellbook_entry/summon/guns/IsAvailible() - if(!SSticker.mode) // In case spellbook is placed on map - return 0 - return (SSticker.mode.name != "ragin' mages" && !config.no_summon_guns) - -/datum/spellbook_entry/summon/guns/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) - SSblackbox.add_details("wizard_spell_learned", name) - rightandwrong(0, user, 25) - active = 1 - playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1) - to_chat(user, "You have cast summon guns!") - return 1 - -/datum/spellbook_entry/summon/magic - name = "Summon Magic" - desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time." - -/datum/spellbook_entry/summon/magic/IsAvailible() - if(!SSticker.mode) // In case spellbook is placed on map - return 0 - return (SSticker.mode.name != "ragin' mages" && !config.no_summon_magic) - -/datum/spellbook_entry/summon/magic/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) - SSblackbox.add_details("wizard_spell_learned", name) - rightandwrong(1, user, 25) - active = 1 - playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1) - to_chat(user, "You have cast summon magic!") - return 1 - -/datum/spellbook_entry/summon/events - name = "Summon Events" - desc = "Give Murphy's law a little push and replace all events with special wizard ones that will confound and confuse everyone. Multiple castings increase the rate of these events." - var/times = 0 - -/datum/spellbook_entry/summon/events/IsAvailible() - if(!SSticker.mode) // In case spellbook is placed on map - return 0 - return (SSticker.mode.name != "ragin' mages" && !config.no_summon_events) - -/datum/spellbook_entry/summon/events/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) - SSblackbox.add_details("wizard_spell_learned", name) - summonevents() - times++ - playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1) - to_chat(user, "You have cast summon events.") - return 1 - -/datum/spellbook_entry/summon/events/GetInfo() - . = ..() - if(times>0) - . += "You cast it [times] times.
" - return . - -/obj/item/weapon/spellbook - name = "spell book" - desc = "An unearthly tome that glows with power." - icon = 'icons/obj/library.dmi' - icon_state ="book" - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_TINY - persistence_replacement = /obj/item/weapon/spellbook/oneuse/random - var/uses = 10 - var/temp = null - var/tab = null - var/mob/living/carbon/human/owner - var/list/datum/spellbook_entry/entries = list() - var/list/categories = list() - -/obj/item/weapon/spellbook/examine(mob/user) - ..() - if(owner) - to_chat(user, "There is a small signature on the front cover: \"[owner]\".") - else - to_chat(user, "It appears to have no author.") - -/obj/item/weapon/spellbook/Initialize() - ..() - prepare_spells() - -/obj/item/weapon/spellbook/proc/prepare_spells() - var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon - for(var/T in entry_types) - var/datum/spellbook_entry/E = new T - if(E.IsAvailible()) - entries |= E - categories |= E.category - else - qdel(E) - tab = categories[1] - -/obj/item/weapon/spellbook/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/weapon/antag_spawner/contract)) - var/obj/item/weapon/antag_spawner/contract/contract = O - if(contract.used) - to_chat(user, "The contract has been used, you can't get your points back now!") - else - to_chat(user, "You feed the contract back into the spellbook, refunding your points.") - uses++ - for(var/datum/spellbook_entry/item/contract/CT in entries) - if(!isnull(CT.limit)) - CT.limit++ - qdel(O) - else if(istype(O, /obj/item/weapon/antag_spawner/slaughter_demon)) - to_chat(user, "On second thought, maybe summoning a demon is a bad idea. You refund your points.") - uses++ - for(var/datum/spellbook_entry/item/bloodbottle/BB in entries) - if(!isnull(BB.limit)) - BB.limit++ - qdel(O) - -/obj/item/weapon/spellbook/proc/GetCategoryHeader(category) - var/dat = "" - switch(category) - if("Offensive") - dat += "Spells and items geared towards debilitating and destroying.

" - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
" - dat += "For spells: the number after the spell name is the cooldown time.
" - dat += "You can reduce this number by spending more points on the spell.
" - if("Defensive") - dat += "Spells and items geared towards improving your survivabilty or reducing foes' ability to attack.

" - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
" - dat += "For spells: the number after the spell name is the cooldown time.
" - dat += "You can reduce this number by spending more points on the spell.
" - if("Mobility") - dat += "Spells and items geared towards improving your ability to move. It is a good idea to take at least one.

" - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
" - dat += "For spells: the number after the spell name is the cooldown time.
" - dat += "You can reduce this number by spending more points on the spell.
" - if("Assistance") - dat += "Spells and items geared towards bringing in outside forces to aid you or improving upon your other items and abilties.

" - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
" - dat += "For spells: the number after the spell name is the cooldown time.
" - dat += "You can reduce this number by spending more points on the spell.
" - if("Challenges") - dat += "The Wizard Federation typically has hard limits on the potency and number of spells brought to the station based on risk.
" - dat += "Arming the station against you will increases the risk, but will grant you one more charge for your spellbook.
" - if("Rituals") - dat += "These powerful spells change the very fabric of reality. Not always in your favour.
" - return dat - -/obj/item/weapon/spellbook/proc/wrap(content) - var/dat = "" - dat +="Spellbook" - dat += {" - - - - "} - dat += {"[content]"} - return dat - -/obj/item/weapon/spellbook/attack_self(mob/user) - if(!owner) - to_chat(user, "You bind the spellbook to yourself.") - owner = user - return - if(user != owner) - to_chat(user, "The [name] does not recognize you as its owner and refuses to open!") - return - user.set_machine(src) - var/dat = "" - - dat += "" - - var/datum/spellbook_entry/E - for(var/i=1,i<=entries.len,i++) - var/spell_info = "" - E = entries[i] - spell_info += E.GetInfo() - if(E.CanBuy(user,src)) - spell_info+= "[E.buy_word]
" - else - spell_info+= "Can't [E.buy_word]
" - if(E.CanRefund(user,src)) - spell_info+= "Refund
" - spell_info += "
" - if(cat_dat[E.category]) - cat_dat[E.category] += spell_info - - for(var/category in categories) - dat += "
" - dat += GetCategoryHeader(category) - dat += cat_dat[category] - dat += "
" - - user << browse(wrap(dat), "window=spellbook;size=700x500") - onclose(user, "spellbook") - return - -/obj/item/weapon/spellbook/Topic(href, href_list) - ..() - var/mob/living/carbon/human/H = usr - - if(H.stat || H.restrained()) - return - if(!ishuman(H)) - return 1 - - if(H.mind.special_role == "apprentice") - temp = "If you got caught sneaking a peek from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not." - return - - var/datum/spellbook_entry/E = null - if(loc == H || (in_range(src, H) && isturf(loc))) - H.set_machine(src) - if(href_list["buy"]) - E = entries[text2num(href_list["buy"])] - if(E && E.CanBuy(H,src)) - if(E.Buy(H,src)) - if(E.limit) - E.limit-- - uses -= E.cost - else if(href_list["refund"]) - E = entries[text2num(href_list["refund"])] - if(E && E.refundable) - var/result = E.Refund(H,src) - if(result > 0) - if(!isnull(E.limit)) - E.limit += result - uses += result - else if(href_list["page"]) - tab = sanitize(href_list["page"]) - attack_self(H) - return - -//Single Use Spellbooks// - -/obj/item/weapon/spellbook/oneuse - var/spell = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile //just a placeholder to avoid runtimes if someone spawned the generic - var/spellname = "sandbox" - var/used = 0 - name = "spellbook of " - uses = 1 - desc = "This template spellbook was never meant for the eyes of man..." - persistence_replacement = null - -/obj/item/weapon/spellbook/oneuse/prepare_spells() - name += spellname - -/obj/item/weapon/spellbook/oneuse/attack_self(mob/user) - var/obj/effect/proc_holder/spell/S = new spell - for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list) - if(knownspell.type == S.type) - if(user.mind) - if(user.mind.special_role == "apprentice" || user.mind.special_role == "Wizard") - to_chat(user,"You're already far more versed in this spell than this flimsy how-to book can provide.") - else - to_chat(user,"You've already read this one.") - return - if(used) - recoil(user) - else - user.mind.AddSpell(S) - to_chat(user,"You rapidly read through the arcane book. Suddenly you realize you understand [spellname]!") - user.log_message("learned the spell [spellname] ([S]).", INDIVIDUAL_ATTACK_LOG) - onlearned(user) - -/obj/item/weapon/spellbook/oneuse/proc/recoil(mob/user) - user.visible_message("[src] glows in a black light!") - -/obj/item/weapon/spellbook/oneuse/proc/onlearned(mob/user) - used = 1 - user.visible_message("[src] glows dark for a second!") - -/obj/item/weapon/spellbook/oneuse/attackby() - return - -/obj/item/weapon/spellbook/oneuse/fireball - spell = /obj/effect/proc_holder/spell/aimed/fireball - spellname = "fireball" - icon_state ="bookfireball" - desc = "This book feels warm to the touch." - -/obj/item/weapon/spellbook/oneuse/fireball/recoil(mob/user) - ..() - explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) - qdel(src) - -/obj/item/weapon/spellbook/oneuse/smoke - spell = /obj/effect/proc_holder/spell/targeted/smoke - spellname = "smoke" - icon_state ="booksmoke" - desc = "This book is overflowing with the dank arts." - -/obj/item/weapon/spellbook/oneuse/smoke/lesser //Chaplain smoke book - spell = /obj/effect/proc_holder/spell/targeted/smoke/lesser - -/obj/item/weapon/spellbook/oneuse/smoke/recoil(mob/user) - ..() - to_chat(user,"Your stomach rumbles...") - if(user.nutrition) - user.nutrition -= 200 - if(user.nutrition <= 0) - user.nutrition = 0 - - -/obj/item/weapon/spellbook/oneuse/blind - spell = /obj/effect/proc_holder/spell/targeted/trigger/blind - spellname = "blind" - icon_state ="bookblind" - desc = "This book looks blurry, no matter how you look at it." - -/obj/item/weapon/spellbook/oneuse/blind/recoil(mob/user) - ..() - to_chat(user,"You go blind!") - user.blind_eyes(10) - -/obj/item/weapon/spellbook/oneuse/mindswap - spell = /obj/effect/proc_holder/spell/targeted/mind_transfer - spellname = "mindswap" - icon_state ="bookmindswap" - desc = "This book's cover is pristine, though its pages look ragged and torn." - var/mob/stored_swap = null //Used in used book recoils to store an identity for mindswaps - -/obj/item/weapon/spellbook/oneuse/mindswap/onlearned() - spellname = pick("fireball","smoke","blind","forcewall","knock","barnyard","charge") - icon_state = "book[spellname]" - name = "spellbook of [spellname]" //Note, desc doesn't change by design - ..() - -/obj/item/weapon/spellbook/oneuse/mindswap/recoil(mob/user) - ..() - if(stored_swap in GLOB.dead_mob_list) - stored_swap = null - if(!stored_swap) - stored_swap = user - to_chat(user,"For a moment you feel like you don't even know who you are anymore.") - return - if(stored_swap == user) - to_chat(user,"You stare at the book some more, but there doesn't seem to be anything else to learn...") - return - - var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new - swapper.cast(user, stored_swap, 1) - - to_chat(stored_swap,"You're suddenly somewhere else... and someone else?!") - to_chat(user,"Suddenly you're staring at [src] again... where are you, who are you?!") - stored_swap = null - -/obj/item/weapon/spellbook/oneuse/forcewall - spell = /obj/effect/proc_holder/spell/targeted/forcewall - spellname = "forcewall" - icon_state ="bookforcewall" - desc = "This book has a dedication to mimes everywhere inside the front cover." - -/obj/item/weapon/spellbook/oneuse/forcewall/recoil(mob/user) - ..() - to_chat(user,"You suddenly feel very solid!") - user.Stun(2) - user.petrify(30) - -/obj/item/weapon/spellbook/oneuse/knock - spell = /obj/effect/proc_holder/spell/aoe_turf/knock - spellname = "knock" - icon_state ="bookknock" - desc = "This book is hard to hold closed properly." - -/obj/item/weapon/spellbook/oneuse/knock/recoil(mob/user) - ..() - to_chat(user,"You're knocked down!") - user.Weaken(20) - -/obj/item/weapon/spellbook/oneuse/barnyard - spell = /obj/effect/proc_holder/spell/targeted/barnyardcurse - spellname = "barnyard" - icon_state ="bookhorses" - desc = "This book is more horse than your mind has room for." - -/obj/item/weapon/spellbook/oneuse/barnyard/recoil(mob/living/carbon/user) - if(ishuman(user)) - to_chat(user,"HOR-SIE HAS RISEN") - var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead - magichead.flags |= NODROP //curses! - magichead.flags_inv &= ~HIDEFACE //so you can still see their face - magichead.voicechange = 1 //NEEEEIIGHH - if(!user.dropItemToGround(user.wear_mask)) - qdel(user.wear_mask) - user.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) - qdel(src) - else - to_chat(user,"I say thee neigh") //It still lives here - -/obj/item/weapon/spellbook/oneuse/charge - spell = /obj/effect/proc_holder/spell/targeted/charge - spellname = "charging" - icon_state ="bookcharge" - desc = "This book is made of 100% post-consumer wizard." - -/obj/item/weapon/spellbook/oneuse/charge/recoil(mob/user) - ..() - to_chat(user,"[src] suddenly feels very warm!") - empulse(src, 1, 1) - -/obj/item/weapon/spellbook/oneuse/summonitem - spell = /obj/effect/proc_holder/spell/targeted/summonitem - spellname = "instant summons" - icon_state ="booksummons" - desc = "This book is bright and garish, very hard to miss." - -/obj/item/weapon/spellbook/oneuse/summonitem/recoil(mob/user) - ..() - to_chat(user,"[src] suddenly vanishes!") - qdel(src) - -/obj/item/weapon/spellbook/oneuse/random/Initialize() - ..() - var/static/banned_spells = list(/obj/item/weapon/spellbook/oneuse/mimery_blockade,/obj/item/weapon/spellbook/oneuse/mimery_guns) - var/real_type = pick(subtypesof(/obj/item/weapon/spellbook/oneuse) - banned_spells) - new real_type(loc) - qdel(src) - -/obj/item/weapon/spellbook/oneuse/sacredflame - spell = /obj/effect/proc_holder/spell/targeted/sacred_flame - spellname = "sacred flame" - icon_state ="booksacredflame" - desc = "Become one with the flames that burn within... and invite others to do so as well." +/datum/spellbook_entry + var/name = "Entry Name" + + var/spell_type = null + var/desc = "" + var/category = "Offensive" + var/cost = 2 + var/refundable = 1 + var/surplus = -1 // -1 for infinite, not used by anything atm + var/obj/effect/proc_holder/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell + var/buy_word = "Learn" + var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook + var/list/no_coexistance_typecache //Used so you can't have specific spells together + +/datum/spellbook_entry/New() + ..() + no_coexistance_typecache = typecacheof(no_coexistance_typecache) + +/datum/spellbook_entry/proc/IsAvailible() // For config prefs / gamemode restrictions - these are round applied + return 1 + +/datum/spellbook_entry/proc/CanBuy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) // Specific circumstances + if(book.uses= aspell.level_max) + to_chat(user, "This spell cannot be improved further.") + return 0 + else + aspell.name = initial(aspell.name) + aspell.spell_level++ + aspell.charge_max = round(initial(aspell.charge_max) - aspell.spell_level * (initial(aspell.charge_max) - aspell.cooldown_min)/ aspell.level_max) + if(aspell.charge_max < aspell.charge_counter) + aspell.charge_counter = aspell.charge_max + switch(aspell.spell_level) + if(1) + to_chat(user, "You have improved [aspell.name] into Efficient [aspell.name].") + aspell.name = "Efficient [aspell.name]" + if(2) + to_chat(user, "You have further improved [aspell.name] into Quickened [aspell.name].") + aspell.name = "Quickened [aspell.name]" + if(3) + to_chat(user, "You have further improved [aspell.name] into Free [aspell.name].") + aspell.name = "Free [aspell.name]" + if(4) + to_chat(user, "You have further improved [aspell.name] into Instant [aspell.name].") + aspell.name = "Instant [aspell.name]" + if(aspell.spell_level >= aspell.level_max) + to_chat(user, "This spell cannot be strengthened any further.") + SSblackbox.add_details("wizard_spell_improved", "[name]|[aspell.level]") + return 1 + //No same spell found - just learn it + SSblackbox.add_details("wizard_spell_learned", name) + user.mind.AddSpell(S) + to_chat(user, "You have learned [S.name].") + return 1 + +/datum/spellbook_entry/proc/CanRefund(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) + if(!refundable) + return 0 + if(!S) + S = new spell_type() + for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) + if(initial(S.name) == initial(aspell.name)) + return 1 + return 0 + +/datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) //return point value or -1 for failure + var/area/wizard_station/A = locate() + if(!(user in A.contents)) + to_chat(user, "You can only refund spells at the wizard lair") + return -1 + if(!S) + S = new spell_type() + var/spell_levels = 0 + for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) + if(initial(S.name) == initial(aspell.name)) + spell_levels = aspell.spell_level + user.mind.spell_list.Remove(aspell) + qdel(S) + return cost * (spell_levels+1) + return -1 +/datum/spellbook_entry/proc/GetInfo() + if(!S) + S = new spell_type() + var/dat ="" + dat += "[initial(S.name)]" + if(S.charge_type == "recharge") + dat += " Cooldown:[S.charge_max/10]" + dat += " Cost:[cost]
" + dat += "[S.desc][desc]
" + dat += "[S.clothes_req?"Needs wizard garb":"Can be cast without wizard garb"]
" + return dat + +/datum/spellbook_entry/fireball + name = "Fireball" + spell_type = /obj/effect/proc_holder/spell/aimed/fireball + +/* +/datum/spellbook_entry/rod_form + name = "Rod Form" + spell_type = /obj/effect/proc_holder/spell/targeted/rod_form */ + +/datum/spellbook_entry/magicm + name = "Magic Missile" + spell_type = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile + category = "Defensive" + +/datum/spellbook_entry/disintegrate + name = "Disintegrate" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/disintegrate + cost = 6 + +/datum/spellbook_entry/disabletech + name = "Disable Tech" + spell_type = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/repulse + name = "Repulse" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/repulse + category = "Defensive" + +/datum/spellbook_entry/lightningPacket + name = "Lightning bolt! Lightning bolt!" + spell_type = /obj/effect/proc_holder/spell/targeted/conjure_item/spellpacket + category = "Defensive" + +/datum/spellbook_entry/timestop + name = "Time Stop" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop + category = "Defensive" + cost = 4 + +/datum/spellbook_entry/smoke + name = "Smoke" + spell_type = /obj/effect/proc_holder/spell/targeted/smoke + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/blind + name = "Blind" + spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind + cost = 1 + +/datum/spellbook_entry/mindswap + name = "Mindswap" + spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer + category = "Mobility" + +/datum/spellbook_entry/forcewall + name = "Force Wall" + spell_type = /obj/effect/proc_holder/spell/targeted/forcewall + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/blink + name = "Blink" + spell_type = /obj/effect/proc_holder/spell/targeted/turf_teleport/blink + category = "Mobility" + +/datum/spellbook_entry/teleport + name = "Teleport" + spell_type = /obj/effect/proc_holder/spell/targeted/area_teleport/teleport + category = "Mobility" + +/datum/spellbook_entry/mutate + name = "Mutate" + spell_type = /obj/effect/proc_holder/spell/targeted/genetic/mutate + +/datum/spellbook_entry/jaunt + name = "Ethereal Jaunt" + spell_type = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt + category = "Mobility" + +/datum/spellbook_entry/knock + name = "Knock" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock + category = "Mobility" + cost = 1 + +/datum/spellbook_entry/fleshtostone + name = "Flesh to Stone" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/flesh_to_stone + +/datum/spellbook_entry/summonitem + name = "Summon Item" + spell_type = /obj/effect/proc_holder/spell/targeted/summonitem + category = "Assistance" + cost = 1 + +/datum/spellbook_entry/lichdom + name = "Bind Soul" + spell_type = /obj/effect/proc_holder/spell/targeted/lichdom + category = "Defensive" + cost = 4 + +/datum/spellbook_entry/teslablast + name = "Tesla Blast" + spell_type = /obj/effect/proc_holder/spell/targeted/tesla + +/datum/spellbook_entry/lightningbolt + name = "Lightning Bolt" + spell_type = /obj/effect/proc_holder/spell/aimed/lightningbolt + cost = 3 + +/datum/spellbook_entry/lightningbolt/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) //return 1 on success + . = ..() + SET_SECONDARY_FLAG(user, TESLA_IGNORE) + +/datum/spellbook_entry/infinite_guns + name = "Lesser Summon Guns" + spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns/gun + cost = 3 + no_coexistance_typecache = /obj/effect/proc_holder/spell/targeted/infinite_guns/arcane_barrage + +/datum/spellbook_entry/arcane_barrage + name = "Arcane Barrage" + spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns/arcane_barrage + cost = 3 + no_coexistance_typecache = /obj/effect/proc_holder/spell/targeted/infinite_guns/gun + +/datum/spellbook_entry/barnyard + name = "Barnyard Curse" + spell_type = /obj/effect/proc_holder/spell/targeted/barnyardcurse + +/datum/spellbook_entry/charge + name = "Charge" + spell_type = /obj/effect/proc_holder/spell/targeted/charge + category = "Assistance" + cost = 1 + +/datum/spellbook_entry/shapeshift + name = "Wild Shapeshift" + spell_type = /obj/effect/proc_holder/spell/targeted/shapeshift + category = "Assistance" + cost = 1 + +/datum/spellbook_entry/spacetime_dist + name = "Spacetime Distortion" + spell_type = /obj/effect/proc_holder/spell/spacetime_dist + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/the_traps + name = "The Traps!" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/the_traps + category = "Defensive" + cost = 1 + + +/datum/spellbook_entry/item + name = "Buy Item" + refundable = 0 + buy_word = "Summon" + var/item_path= null + + +/datum/spellbook_entry/item/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) + new item_path(get_turf(user)) + SSblackbox.add_details("wizard_spell_learned", name) + return 1 + +/datum/spellbook_entry/item/GetInfo() + var/dat ="" + dat += "[name]" + dat += " Cost:[cost]
" + dat += "[desc]
" + if(surplus>=0) + dat += "[surplus] left.
" + return dat +/* // these have never been fun, like, ever. +/datum/spellbook_entry/item/staffchange + name = "Staff of Change" + desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself." + item_path = /obj/item/weapon/gun/magic/staff/change + +/datum/spellbook_entry/item/staffanimation + name = "Staff of Animation" + desc = "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines." + item_path = /obj/item/weapon/gun/magic/staff/animate + category = "Assistance" */ + +/datum/spellbook_entry/item/staffchaos + name = "Staff of Chaos" + desc = "A caprious tool that can fire all sorts of magic without any rhyme or reason. Using it on people you care about is not recommended." + item_path = /obj/item/weapon/gun/magic/staff/chaos + cost = 4 + +/datum/spellbook_entry/item/spellblade + name = "Spellblade" + desc = "A sword capable of firing blasts of energy which rip targets limb from limb." + item_path = /obj/item/weapon/gun/magic/staff/spellblade + +/datum/spellbook_entry/item/staffdoor + name = "Staff of Door Creation" + desc = "A particular staff that can mold solid metal into ornate doors. Useful for getting around in the absence of other transportation. Does not work on glass." + item_path = /obj/item/weapon/gun/magic/staff/door + cost = 1 + category = "Mobility" + +/datum/spellbook_entry/item/staffhealing + name = "Staff of Healing" + desc = "An altruistic staff that can heal the lame and raise the dead." + item_path = /obj/item/weapon/gun/magic/staff/healing + cost = 1 + category = "Defensive" + +/datum/spellbook_entry/item/scryingorb + name = "Scrying Orb" + desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you x-ray vision." + item_path = /obj/item/weapon/scrying + category = "Defensive" + +/datum/spellbook_entry/item/scryingorb/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) + if(..()) + if (!(user.dna.check_mutation(XRAY))) + user.dna.add_mutation(XRAY) + return 1 + +/datum/spellbook_entry/item/soulstones + name = "Six Soul Stone Shards and the spell Artificer" + desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. The spell Artificer allows you to create arcane machines for the captured souls to pilot." + item_path = /obj/item/weapon/storage/belt/soulstone/full + category = "Assistance" + +/datum/spellbook_entry/item/soulstones/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) + . =..() + if(.) + user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/construct(null)) + return . + +/datum/spellbook_entry/item/necrostone + name = "A Necromantic Stone" + desc = "A Necromantic stone is able to resurrect three dead individuals as skeletal thralls for you to command." + item_path = /obj/item/device/necromantic_stone + category = "Assistance" + +/datum/spellbook_entry/item/wands + name = "Wand Assortment" + desc = "A collection of wands that allow for a wide variety of utility. Wands have a limited number of charges, so be conservative in use. Comes in a handy belt." + item_path = /obj/item/weapon/storage/belt/wands/full + category = "Defensive" + +/datum/spellbook_entry/item/armor + name = "Mastercrafted Armor Set" + desc = "An artefact suit of armor that allows you to cast spells while providing more protection against attacks and the void of space." + item_path = /obj/item/clothing/suit/space/hardsuit/wizard + category = "Defensive" + +/datum/spellbook_entry/item/armor/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) + . = ..() + if(.) + new /obj/item/clothing/shoes/sandal/magic(get_turf(user)) //In case they've lost them. + new /obj/item/clothing/gloves/color/purple(get_turf(user))//To complete the outfit + +/datum/spellbook_entry/item/contract + name = "Contract of Apprenticeship" + desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side." + item_path = /obj/item/weapon/antag_spawner/contract + category = "Assistance" + +/datum/spellbook_entry/item/guardian + name = "Guardian Deck" + desc = "A deck of guardian tarot cards, capable of binding a personal guardian to your body. There are multiple types of guardian available, but all of them will transfer some amount of damage to you. \ + It would be wise to avoid buying these with anything capable of causing you to swap bodies with others." + item_path = /obj/item/weapon/guardiancreator/choose/wizard + category = "Assistance" + +/datum/spellbook_entry/item/guardian/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) + . = ..() + if(.) + new /obj/item/weapon/paper/guardian/wizard(get_turf(user)) + +/datum/spellbook_entry/item/bloodbottle + name = "Bottle of Blood" + desc = "A bottle of magically infused blood, the smell of which will attract extradimensional beings when broken. Be careful though, the kinds of creatures summoned by blood magic are indiscriminate in their killing, and you yourself may become a victim." + item_path = /obj/item/weapon/antag_spawner/slaughter_demon + limit = 3 + category = "Assistance" + +/datum/spellbook_entry/item/hugbottle + name = "Bottle of Tickles" + desc = "A bottle of magically infused fun, the smell of which will \ + attract adorable extradimensional beings when broken. These beings \ + are similar to slaughter demons, but they do not permamently kill \ + their victims, instead putting them in an extradimensional hugspace, \ + to be released on the demon's death. Chaotic, but not ultimately \ + damaging. The crew's reaction to the other hand could be very \ + destructive." + item_path = /obj/item/weapon/antag_spawner/slaughter_demon/laughter + cost = 1 //non-destructive; it's just a jape, sibling! + limit = 3 + category = "Assistance" + +/datum/spellbook_entry/item/mjolnir + name = "Mjolnir" + desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power." + item_path = /obj/item/weapon/twohanded/mjollnir + +/datum/spellbook_entry/item/singularity_hammer + name = "Singularity Hammer" + desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everything nearby to the point of impact." + item_path = /obj/item/weapon/twohanded/singularityhammer + +/datum/spellbook_entry/item/battlemage + name = "Battlemage Armour" + desc = "An ensorcelled suit of armour, protected by a powerful shield. The shield can completly negate sixteen attacks before being permanently depleted." + item_path = /obj/item/clothing/suit/space/hardsuit/shielded/wizard + limit = 1 + category = "Defensive" + +/datum/spellbook_entry/item/battlemage_charge + name = "Battlemage Armour Charges" + desc = "A powerful defensive rune, it will grant eight additional charges to a suit of battlemage armour." + item_path = /obj/item/wizard_armour_charge + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/item/warpwhistle + name = "Warp Whistle" + desc = "A strange whistle that will transport you to a distant safe place on the station. There is a window of vulnerability at the begining of every use." + item_path = /obj/item/warpwhistle + category = "Mobility" + cost = 1 + +/datum/spellbook_entry/summon + name = "Summon Stuff" + category = "Rituals" + refundable = 0 + buy_word = "Cast" + var/active = 0 + +/datum/spellbook_entry/summon/CanBuy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) + return ..() && !active + +/datum/spellbook_entry/summon/GetInfo() + var/dat ="" + dat += "[name]" + if(cost>0) + dat += " Cost:[cost]
" + else + dat += " No Cost
" + dat += "[desc]
" + if(active) + dat += "Already cast!
" + return dat + +/datum/spellbook_entry/summon/ghosts + name = "Summon Ghosts" + desc = "Spook the crew out by making them see dead people. Be warned, ghosts are capricious and occasionally vindicative, and some will use their incredibly minor abilties to frustrate you." + cost = 0 + +/datum/spellbook_entry/summon/ghosts/IsAvailible() + if(!SSticker.mode) + return FALSE + else + return TRUE + +/datum/spellbook_entry/summon/ghosts/Buy(mob/living/carbon/human/user, obj/item/weapon/spellbook/book) + SSblackbox.add_details("wizard_spell_learned", name) + new /datum/round_event/wizard/ghost() + active = TRUE + to_chat(user, "You have cast summon ghosts!") + playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1) + return TRUE + +/datum/spellbook_entry/summon/guns + name = "Summon Guns" + desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. Just be careful not to stand still too long!" + +/datum/spellbook_entry/summon/guns/IsAvailible() + if(!SSticker.mode) // In case spellbook is placed on map + return 0 + return (SSticker.mode.name != "ragin' mages" && !config.no_summon_guns) + +/datum/spellbook_entry/summon/guns/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) + SSblackbox.add_details("wizard_spell_learned", name) + rightandwrong(0, user, 25) + active = 1 + playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1) + to_chat(user, "You have cast summon guns!") + return 1 + +/datum/spellbook_entry/summon/magic + name = "Summon Magic" + desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time." + +/datum/spellbook_entry/summon/magic/IsAvailible() + if(!SSticker.mode) // In case spellbook is placed on map + return 0 + return (SSticker.mode.name != "ragin' mages" && !config.no_summon_magic) + +/datum/spellbook_entry/summon/magic/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) + SSblackbox.add_details("wizard_spell_learned", name) + rightandwrong(1, user, 25) + active = 1 + playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1) + to_chat(user, "You have cast summon magic!") + return 1 + +/datum/spellbook_entry/summon/events + name = "Summon Events" + desc = "Give Murphy's law a little push and replace all events with special wizard ones that will confound and confuse everyone. Multiple castings increase the rate of these events." + var/times = 0 + +/datum/spellbook_entry/summon/events/IsAvailible() + if(!SSticker.mode) // In case spellbook is placed on map + return 0 + return (SSticker.mode.name != "ragin' mages" && !config.no_summon_events) + +/datum/spellbook_entry/summon/events/Buy(mob/living/carbon/human/user,obj/item/weapon/spellbook/book) + SSblackbox.add_details("wizard_spell_learned", name) + summonevents() + times++ + playsound(get_turf(user), 'sound/magic/CastSummon.ogg', 50, 1) + to_chat(user, "You have cast summon events.") + return 1 + +/datum/spellbook_entry/summon/events/GetInfo() + . = ..() + if(times>0) + . += "You cast it [times] times.
" + return . + +/obj/item/weapon/spellbook + name = "spell book" + desc = "An unearthly tome that glows with power." + icon = 'icons/obj/library.dmi' + icon_state ="book" + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_TINY + persistence_replacement = /obj/item/weapon/spellbook/oneuse/random + var/uses = 10 + var/temp = null + var/tab = null + var/mob/living/carbon/human/owner + var/list/datum/spellbook_entry/entries = list() + var/list/categories = list() + +/obj/item/weapon/spellbook/examine(mob/user) + ..() + if(owner) + to_chat(user, "There is a small signature on the front cover: \"[owner]\".") + else + to_chat(user, "It appears to have no author.") + +/obj/item/weapon/spellbook/Initialize() + ..() + prepare_spells() + +/obj/item/weapon/spellbook/proc/prepare_spells() + var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon + for(var/T in entry_types) + var/datum/spellbook_entry/E = new T + if(E.IsAvailible()) + entries |= E + categories |= E.category + else + qdel(E) + tab = categories[1] + +/obj/item/weapon/spellbook/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/weapon/antag_spawner/contract)) + var/obj/item/weapon/antag_spawner/contract/contract = O + if(contract.used) + to_chat(user, "The contract has been used, you can't get your points back now!") + else + to_chat(user, "You feed the contract back into the spellbook, refunding your points.") + uses++ + for(var/datum/spellbook_entry/item/contract/CT in entries) + if(!isnull(CT.limit)) + CT.limit++ + qdel(O) + else if(istype(O, /obj/item/weapon/antag_spawner/slaughter_demon)) + to_chat(user, "On second thought, maybe summoning a demon is a bad idea. You refund your points.") + uses++ + for(var/datum/spellbook_entry/item/bloodbottle/BB in entries) + if(!isnull(BB.limit)) + BB.limit++ + qdel(O) + +/obj/item/weapon/spellbook/proc/GetCategoryHeader(category) + var/dat = "" + switch(category) + if("Offensive") + dat += "Spells and items geared towards debilitating and destroying.

" + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
" + dat += "For spells: the number after the spell name is the cooldown time.
" + dat += "You can reduce this number by spending more points on the spell.
" + if("Defensive") + dat += "Spells and items geared towards improving your survivabilty or reducing foes' ability to attack.

" + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
" + dat += "For spells: the number after the spell name is the cooldown time.
" + dat += "You can reduce this number by spending more points on the spell.
" + if("Mobility") + dat += "Spells and items geared towards improving your ability to move. It is a good idea to take at least one.

" + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
" + dat += "For spells: the number after the spell name is the cooldown time.
" + dat += "You can reduce this number by spending more points on the spell.
" + if("Assistance") + dat += "Spells and items geared towards bringing in outside forces to aid you or improving upon your other items and abilties.

" + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
" + dat += "For spells: the number after the spell name is the cooldown time.
" + dat += "You can reduce this number by spending more points on the spell.
" + if("Challenges") + dat += "The Wizard Federation typically has hard limits on the potency and number of spells brought to the station based on risk.
" + dat += "Arming the station against you will increases the risk, but will grant you one more charge for your spellbook.
" + if("Rituals") + dat += "These powerful spells change the very fabric of reality. Not always in your favour.
" + return dat + +/obj/item/weapon/spellbook/proc/wrap(content) + var/dat = "" + dat +="Spellbook" + dat += {" + + + + "} + dat += {"[content]"} + return dat + +/obj/item/weapon/spellbook/attack_self(mob/user) + if(!owner) + to_chat(user, "You bind the spellbook to yourself.") + owner = user + return + if(user != owner) + to_chat(user, "The [name] does not recognize you as its owner and refuses to open!") + return + user.set_machine(src) + var/dat = "" + + dat += "" + + var/datum/spellbook_entry/E + for(var/i=1,i<=entries.len,i++) + var/spell_info = "" + E = entries[i] + spell_info += E.GetInfo() + if(E.CanBuy(user,src)) + spell_info+= "[E.buy_word]
" + else + spell_info+= "Can't [E.buy_word]
" + if(E.CanRefund(user,src)) + spell_info+= "Refund
" + spell_info += "
" + if(cat_dat[E.category]) + cat_dat[E.category] += spell_info + + for(var/category in categories) + dat += "
" + dat += GetCategoryHeader(category) + dat += cat_dat[category] + dat += "
" + + user << browse(wrap(dat), "window=spellbook;size=700x500") + onclose(user, "spellbook") + return + +/obj/item/weapon/spellbook/Topic(href, href_list) + ..() + var/mob/living/carbon/human/H = usr + + if(H.stat || H.restrained()) + return + if(!ishuman(H)) + return 1 + + if(H.mind.special_role == "apprentice") + temp = "If you got caught sneaking a peek from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not." + return + + var/datum/spellbook_entry/E = null + if(loc == H || (in_range(src, H) && isturf(loc))) + H.set_machine(src) + if(href_list["buy"]) + E = entries[text2num(href_list["buy"])] + if(E && E.CanBuy(H,src)) + if(E.Buy(H,src)) + if(E.limit) + E.limit-- + uses -= E.cost + else if(href_list["refund"]) + E = entries[text2num(href_list["refund"])] + if(E && E.refundable) + var/result = E.Refund(H,src) + if(result > 0) + if(!isnull(E.limit)) + E.limit += result + uses += result + else if(href_list["page"]) + tab = sanitize(href_list["page"]) + attack_self(H) + return + +//Single Use Spellbooks// + +/obj/item/weapon/spellbook/oneuse + var/spell = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile //just a placeholder to avoid runtimes if someone spawned the generic + var/spellname = "sandbox" + var/used = 0 + name = "spellbook of " + uses = 1 + desc = "This template spellbook was never meant for the eyes of man..." + persistence_replacement = null + +/obj/item/weapon/spellbook/oneuse/prepare_spells() + name += spellname + +/obj/item/weapon/spellbook/oneuse/attack_self(mob/user) + var/obj/effect/proc_holder/spell/S = new spell + for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list) + if(knownspell.type == S.type) + if(user.mind) + if(user.mind.special_role == "apprentice" || user.mind.special_role == "Wizard") + to_chat(user,"You're already far more versed in this spell than this flimsy how-to book can provide.") + else + to_chat(user,"You've already read this one.") + return + if(used) + recoil(user) + else + user.mind.AddSpell(S) + to_chat(user,"You rapidly read through the arcane book. Suddenly you realize you understand [spellname]!") + user.log_message("learned the spell [spellname] ([S]).", INDIVIDUAL_ATTACK_LOG) + onlearned(user) + +/obj/item/weapon/spellbook/oneuse/proc/recoil(mob/user) + user.visible_message("[src] glows in a black light!") + +/obj/item/weapon/spellbook/oneuse/proc/onlearned(mob/user) + used = 1 + user.visible_message("[src] glows dark for a second!") + +/obj/item/weapon/spellbook/oneuse/attackby() + return + +/obj/item/weapon/spellbook/oneuse/fireball + spell = /obj/effect/proc_holder/spell/aimed/fireball + spellname = "fireball" + icon_state ="bookfireball" + desc = "This book feels warm to the touch." + +/obj/item/weapon/spellbook/oneuse/fireball/recoil(mob/user) + ..() + explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) + qdel(src) + +/obj/item/weapon/spellbook/oneuse/smoke + spell = /obj/effect/proc_holder/spell/targeted/smoke + spellname = "smoke" + icon_state ="booksmoke" + desc = "This book is overflowing with the dank arts." + +/obj/item/weapon/spellbook/oneuse/smoke/lesser //Chaplain smoke book + spell = /obj/effect/proc_holder/spell/targeted/smoke/lesser + +/obj/item/weapon/spellbook/oneuse/smoke/recoil(mob/user) + ..() + to_chat(user,"Your stomach rumbles...") + if(user.nutrition) + user.nutrition -= 200 + if(user.nutrition <= 0) + user.nutrition = 0 + + +/obj/item/weapon/spellbook/oneuse/blind + spell = /obj/effect/proc_holder/spell/targeted/trigger/blind + spellname = "blind" + icon_state ="bookblind" + desc = "This book looks blurry, no matter how you look at it." + +/obj/item/weapon/spellbook/oneuse/blind/recoil(mob/user) + ..() + to_chat(user,"You go blind!") + user.blind_eyes(10) + +/obj/item/weapon/spellbook/oneuse/mindswap + spell = /obj/effect/proc_holder/spell/targeted/mind_transfer + spellname = "mindswap" + icon_state ="bookmindswap" + desc = "This book's cover is pristine, though its pages look ragged and torn." + var/mob/stored_swap = null //Used in used book recoils to store an identity for mindswaps + +/obj/item/weapon/spellbook/oneuse/mindswap/onlearned() + spellname = pick("fireball","smoke","blind","forcewall","knock","barnyard","charge") + icon_state = "book[spellname]" + name = "spellbook of [spellname]" //Note, desc doesn't change by design + ..() + +/obj/item/weapon/spellbook/oneuse/mindswap/recoil(mob/user) + ..() + if(stored_swap in GLOB.dead_mob_list) + stored_swap = null + if(!stored_swap) + stored_swap = user + to_chat(user,"For a moment you feel like you don't even know who you are anymore.") + return + if(stored_swap == user) + to_chat(user,"You stare at the book some more, but there doesn't seem to be anything else to learn...") + return + + var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new + swapper.cast(user, stored_swap, 1) + + to_chat(stored_swap,"You're suddenly somewhere else... and someone else?!") + to_chat(user,"Suddenly you're staring at [src] again... where are you, who are you?!") + stored_swap = null + +/obj/item/weapon/spellbook/oneuse/forcewall + spell = /obj/effect/proc_holder/spell/targeted/forcewall + spellname = "forcewall" + icon_state ="bookforcewall" + desc = "This book has a dedication to mimes everywhere inside the front cover." + +/obj/item/weapon/spellbook/oneuse/forcewall/recoil(mob/user) + ..() + to_chat(user,"You suddenly feel very solid!") + user.Stun(2) + user.petrify(30) + +/obj/item/weapon/spellbook/oneuse/knock + spell = /obj/effect/proc_holder/spell/aoe_turf/knock + spellname = "knock" + icon_state ="bookknock" + desc = "This book is hard to hold closed properly." + +/obj/item/weapon/spellbook/oneuse/knock/recoil(mob/user) + ..() + to_chat(user,"You're knocked down!") + user.Weaken(20) + +/obj/item/weapon/spellbook/oneuse/barnyard + spell = /obj/effect/proc_holder/spell/targeted/barnyardcurse + spellname = "barnyard" + icon_state ="bookhorses" + desc = "This book is more horse than your mind has room for." + +/obj/item/weapon/spellbook/oneuse/barnyard/recoil(mob/living/carbon/user) + if(ishuman(user)) + to_chat(user,"HOR-SIE HAS RISEN") + var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead + magichead.flags |= NODROP //curses! + magichead.flags_inv &= ~HIDEFACE //so you can still see their face + magichead.voicechange = 1 //NEEEEIIGHH + if(!user.dropItemToGround(user.wear_mask)) + qdel(user.wear_mask) + user.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) + qdel(src) + else + to_chat(user,"I say thee neigh") //It still lives here + +/obj/item/weapon/spellbook/oneuse/charge + spell = /obj/effect/proc_holder/spell/targeted/charge + spellname = "charging" + icon_state ="bookcharge" + desc = "This book is made of 100% post-consumer wizard." + +/obj/item/weapon/spellbook/oneuse/charge/recoil(mob/user) + ..() + to_chat(user,"[src] suddenly feels very warm!") + empulse(src, 1, 1) + +/obj/item/weapon/spellbook/oneuse/summonitem + spell = /obj/effect/proc_holder/spell/targeted/summonitem + spellname = "instant summons" + icon_state ="booksummons" + desc = "This book is bright and garish, very hard to miss." + +/obj/item/weapon/spellbook/oneuse/summonitem/recoil(mob/user) + ..() + to_chat(user,"[src] suddenly vanishes!") + qdel(src) + +/obj/item/weapon/spellbook/oneuse/random/Initialize() + ..() + var/static/banned_spells = list(/obj/item/weapon/spellbook/oneuse/mimery_blockade,/obj/item/weapon/spellbook/oneuse/mimery_guns) + var/real_type = pick(subtypesof(/obj/item/weapon/spellbook/oneuse) - banned_spells) + new real_type(loc) + qdel(src) + +/obj/item/weapon/spellbook/oneuse/sacredflame + spell = /obj/effect/proc_holder/spell/targeted/sacred_flame + spellname = "sacred flame" + icon_state ="booksacredflame" + desc = "Become one with the flames that burn within... and invite others to do so as well." diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 0e5b78af7c..b95dbff56d 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -1,499 +1,499 @@ -//Cloning revival method. -//The pod handles the actual cloning while the computer manages the clone profiles - -//Potential replacement for genetics revives or something I dunno (?) - -#define CLONE_INITIAL_DAMAGE 190 //Clones in clonepods start with 190 cloneloss damage and 190 brainloss damage, thats just logical -#define MINIMUM_HEAL_LEVEL 40 - -#define SPEAK(message) radio.talk_into(src, message, radio_channel, get_spans(), get_default_language()) - -/obj/machinery/clonepod - anchored = 1 - name = "cloning pod" - desc = "An electronically-lockable pod for growing organic tissue." - density = 1 - icon = 'icons/obj/cloning.dmi' - icon_state = "pod_0" - req_access = list(GLOB.access_cloning) //For premature unlocking. - verb_say = "states" - var/heal_level //The clone is released once its health reaches this level. - var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. - var/mess = FALSE //Need to clean out it if it's full of exploded clone. - var/attempting = FALSE //One clone attempt at a time thanks - var/speed_coeff - var/efficiency - - var/datum/mind/clonemind - var/grab_ghost_when = CLONER_MATURE_CLONE - - var/obj/item/device/radio/radio - var/radio_key = /obj/item/device/encryptionkey/headset_med - var/radio_channel = "Medical" - - var/obj/effect/countdown/clonepod/countdown - - var/list/unattached_flesh - var/flesh_number = 0 - - // The "brine" is the reagents that are automatically added in small - // amounts to the occupant. - var/static/list/brine_types = list( - "salbutamol", // anti-oxyloss - "bicaridine", // NOBREATHE species take brute in crit - "corazone", // prevents cardiac arrest damage - "mimesbane") // stops them gasping from lack of air. - -/obj/machinery/clonepod/New() - ..() - var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/clonepod(null) - B.apply_default_parts(src) - - countdown = new(src) - - radio = new(src) - radio.keyslot = new radio_key - radio.subspace_transmission = 1 - radio.canhear_range = 0 - radio.recalculateChannels() - -/obj/machinery/clonepod/Destroy() - go_out() - qdel(radio) - radio = null - qdel(countdown) - countdown = null - if(connected) - connected.DetachCloner(src) - for(var/i in unattached_flesh) - qdel(i) - LAZYCLEARLIST(unattached_flesh) - unattached_flesh = null - . = ..() - -/obj/machinery/clonepod/RefreshParts() - speed_coeff = 0 - efficiency = 0 - for(var/obj/item/weapon/stock_parts/scanning_module/S in component_parts) - efficiency += S.rating - for(var/obj/item/weapon/stock_parts/manipulator/P in component_parts) - speed_coeff += P.rating - heal_level = (efficiency * 15) + 10 - if(heal_level < MINIMUM_HEAL_LEVEL) - heal_level = MINIMUM_HEAL_LEVEL - if(heal_level > 100) - heal_level = 100 - -/obj/item/weapon/circuitboard/machine/clonepod - name = "Clone Pod (Machine Board)" - build_path = /obj/machinery/clonepod - origin_tech = "programming=2;biotech=2" - req_components = list( - /obj/item/stack/cable_coil = 2, - /obj/item/weapon/stock_parts/scanning_module = 2, - /obj/item/weapon/stock_parts/manipulator = 2, - /obj/item/stack/sheet/glass = 1) - -//The return of data disks?? Just for transferring between genetics machine/cloning machine. -//TO-DO: Make the genetics machine accept them. -/obj/item/weapon/disk/data - name = "cloning data disk" - icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. - var/list/fields = list() - var/read_only = 0 //Well,it's still a floppy disk - -//Disk stuff. -/obj/item/weapon/disk/data/New() - ..() - icon_state = "datadisk[rand(0,6)]" - add_overlay("datadisk_gene") - -/obj/item/weapon/disk/data/attack_self(mob/user) - read_only = !read_only - to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") - -/obj/item/weapon/disk/data/examine(mob/user) - ..() - to_chat(user, "The write-protect tab is set to [read_only ? "protected" : "unprotected"].") - - -//Clonepod - -/obj/machinery/clonepod/examine(mob/user) - ..() - var/mob/living/mob_occupant = occupant - if(mess) - to_chat(user, "It's filled with blood and viscera. You swear you can see it moving...") - if(is_operational() && mob_occupant) - if(mob_occupant.stat != DEAD) - to_chat(user, "Current clone cycle is [round(get_completion())]% complete.") - -/obj/machinery/clonepod/return_air() - // We want to simulate the clone not being in contact with - // the atmosphere, so we'll put them in a constant pressure - // nitrogen. They'll breathe through the chemicals we pump into them. - var/static/datum/gas_mixture/immutable/cloner/GM //global so that there's only one instance made for all cloning pods - if(!GM) - GM = new - return GM - -/obj/machinery/clonepod/proc/get_completion() - . = FALSE - var/mob/living/mob_occupant = occupant - if(mob_occupant) - . = (100 * ((mob_occupant.health + 100) / (heal_level + 100))) - -/obj/machinery/clonepod/attack_ai(mob/user) - return examine(user) - -//Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions) - if(panel_open) - return FALSE - if(mess || attempting) - return FALSE - clonemind = locate(mindref) - if(!istype(clonemind)) //not a mind - return FALSE - if( clonemind.current && clonemind.current.stat != DEAD ) //mind is associated with a non-dead body - return FALSE - if(clonemind.active) //somebody is using that mind - if( ckey(clonemind.key)!=ckey ) - return FALSE - else - // get_ghost() will fail if they're unable to reenter their body - var/mob/dead/observer/G = clonemind.get_ghost() - if(!G) - return FALSE - if(clonemind.damnation_type) //Can't clone the damned. - INVOKE_ASYNC(src, .proc/horrifyingsound) - mess = TRUE - icon_state = "pod_g" - update_icon() - return FALSE - - attempting = TRUE //One at a time!! - countdown.start() - - var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) - - if(clonemind.changeling) - var/obj/item/organ/brain/B = H.getorganslot("brain") - B.vital = FALSE - B.decoy_override = TRUE - - H.hardset_dna(ui, se, H.real_name, null, mrace, features) - - if(efficiency > 2) - var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations) - H.dna.remove_mutation_group(unclean_mutations) - if(efficiency > 5 && prob(20)) - H.randmutvg() - if(efficiency < 3 && prob(50)) - var/mob/M = H.randmutb() - if(ismob(M)) - H = M - - H.silent = 20 //Prevents an extreme edge case where clones could speak if they said something at exactly the right moment. - occupant = H - - if(!clonename) //to prevent null names - clonename = "clone ([rand(0,999)])" - H.real_name = clonename - - icon_state = "pod_1" - //Get the clone body ready - maim_clone(H) - check_brine() // put in chemicals NOW to stop death via cardiac arrest - H.Paralyse(4) - - clonemind.transfer_to(H) - - if(grab_ghost_when == CLONER_FRESH_CLONE) - H.grab_ghost() - to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?
") - - if(grab_ghost_when == CLONER_MATURE_CLONE) - H.ghostize(TRUE) //Only does anything if they were still in their old body and not already a ghost - to_chat(H.get_ghost(TRUE), "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.") - - if(H) - H.faction |= factions - - H.set_cloned_appearance() - - H.suiciding = FALSE - attempting = FALSE - return TRUE - -//Grow clones to maturity then kick them out. FREELOADERS -/obj/machinery/clonepod/process() - var/mob/living/mob_occupant = occupant - - if(!is_operational()) //Autoeject if power is lost - if(mob_occupant) - go_out() - connected_message("Clone Ejected: Loss of power.") - - else if(mob_occupant && (mob_occupant.loc == src)) - if((mob_occupant.stat == DEAD) || (mob_occupant.suiciding) || mob_occupant.hellbound) //Autoeject corpses and suiciding dudes. - connected_message("Clone Rejected: Deceased.") - SPEAK("The cloning of [mob_occupant.real_name] has been \ - aborted due to unrecoverable tissue failure.") - go_out() - - else if(mob_occupant.cloneloss > (100 - heal_level)) - mob_occupant.Paralyse(4) - - //Slowly get that clone healed and finished. - mob_occupant.adjustCloneLoss(-((speed_coeff/2) * config.damage_multiplier)) - var/progress = CLONE_INITIAL_DAMAGE - mob_occupant.getCloneLoss() - // To avoid the default cloner making incomplete clones - progress += (100 - MINIMUM_HEAL_LEVEL) - var/milestone = CLONE_INITIAL_DAMAGE / flesh_number - var/installed = flesh_number - unattached_flesh.len - - if((progress / milestone) >= installed) - // attach some flesh - var/obj/item/I = pick_n_take(unattached_flesh) - if(isorgan(I)) - var/obj/item/organ/O = I - O.Insert(mob_occupant) - else if(isbodypart(I)) - var/obj/item/bodypart/BP = I - BP.attach_limb(mob_occupant) - - //Premature clones may have brain damage. - mob_occupant.adjustBrainLoss(-((speed_coeff/2) * config.damage_multiplier)) - - check_brine() - - use_power(7500) //This might need tweaking. - - else if((mob_occupant.cloneloss <= (100 - heal_level))) - connected_message("Cloning Process Complete.") - SPEAK("The cloning cycle of [mob_occupant.real_name] is complete.") - go_out() - - else if (!mob_occupant || mob_occupant.loc != src) - occupant = null - if (!mess && !panel_open) - icon_state = "pod_0" - use_power(200) - -//Let's unlock this early I guess. Might be too early, needs tweaking. -/obj/machinery/clonepod/attackby(obj/item/weapon/W, mob/user, params) - if(!(occupant || mess)) - if(default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]",W)) - return - - if(exchange_parts(user, W)) - return - - if(default_deconstruction_crowbar(W)) - return - - if(istype(W,/obj/item/device/multitool)) - var/obj/item/device/multitool/P = W - - if(istype(P.buffer, /obj/machinery/computer/cloning)) - if(get_area(P.buffer) != get_area(src)) - to_chat(user, "-% Cannot link machines across power zones. Buffer cleared %-") - P.buffer = null - return - to_chat(user, "-% Successfully linked [P.buffer] with [src] %-") - var/obj/machinery/computer/cloning/comp = P.buffer - if(connected) - connected.DetachCloner(src) - comp.AttachCloner(src) - else - P.buffer = src - to_chat(user, "-% Successfully stored \ref[P.buffer] [P.buffer.name] in buffer %-") - return - - var/mob/living/mob_occupant = occupant - if(W.GetID()) - if(!check_access(W)) - to_chat(user, "Access Denied.") - return - if(!(mob_occupant || mess)) - to_chat(user, "Error: Pod has no occupant.") - return - else - connected_message("Authorized Ejection") - - SPEAK("An authorized ejection of [clonemind.name] has occurred.") - - to_chat(user, "You force an emergency ejection. ") - go_out() - else - return ..() - -/obj/machinery/clonepod/emag_act(mob/user) - if(!occupant) - return - to_chat(user, "You corrupt the genetic compiler.") - malfunction() - -//Put messages in the connected computer's temp var for display. -/obj/machinery/clonepod/proc/connected_message(message) - if ((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) - return FALSE - if (!message) - return FALSE - - connected.temp = message - connected.updateUsrDialog() - return TRUE - -/obj/machinery/clonepod/proc/go_out() - countdown.stop() - var/mob/living/mob_occupant = occupant - - if(mess) //Clean that mess and dump those gibs! - mess = FALSE - new /obj/effect/gibspawner/generic(loc) - audible_message("You hear a splat.") - icon_state = "pod_0" - return - - if(!mob_occupant) - return - - - if(grab_ghost_when == CLONER_MATURE_CLONE) - mob_occupant.grab_ghost() - to_chat(occupant, "There is a bright flash!
You feel like a new being.
") - mob_occupant.flash_act() - - var/turf/T = get_turf(src) - occupant.forceMove(T) - icon_state = "pod_0" - mob_occupant.domutcheck(1) //Waiting until they're out before possible monkeyizing. The 1 argument forces powers to manifest. - - occupant = null - -/obj/machinery/clonepod/proc/malfunction() - var/mob/living/mob_occupant = occupant - if(mob_occupant) - connected_message("Critical Error!") - SPEAK("Critical error! Please contact a Thinktronic Systems \ - technician, as your warranty may be affected.") - mess = TRUE - - for(var/obj/item/O in unattached_flesh) - qdel(O) - - icon_state = "pod_g" - if(mob_occupant.mind != clonemind) - clonemind.transfer_to(mob_occupant) - mob_occupant.grab_ghost() // We really just want to make you suffer. - flash_color(mob_occupant, flash_color="#960000", flash_time=100) - to_chat(mob_occupant, "Agony blazes across your consciousness as your body is torn apart.
Is this what dying is like? Yes it is.
") - playsound(src.loc, 'sound/machines/warning-buzzer.ogg', 50, 0) - mob_occupant << sound('sound/hallucinations/veryfar_noise.ogg',0,1,50) - QDEL_IN(mob_occupant, 40) - -/obj/machinery/clonepod/relaymove(mob/user) - if(user.stat == CONSCIOUS) - go_out() - -/obj/machinery/clonepod/emp_act(severity) - - var/mob/living/mob_occupant = occupant - if(mob_occupant && prob(100/(severity*efficiency))) - connected_message(Gibberish("EMP-caused Accidental Ejection", 0)) - SPEAK(Gibberish("Exposure to electromagnetic fields has caused the ejection of [mob_occupant.real_name] prematurely." ,0)) - - go_out() - ..() - -/obj/machinery/clonepod/ex_act(severity, target) - ..() - if(!QDELETED(src)) - go_out() - -/obj/machinery/clonepod/handle_atom_del(atom/A) - if(A == occupant) - occupant = null - countdown.stop() - -/obj/machinery/clonepod/proc/horrifyingsound() - for(var/i in 1 to 5) - playsound(loc,pick('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg'), 100, rand(0.95,1.05)) - sleep(1) - sleep(10) - playsound(loc,'sound/hallucinations/wail.ogg',100,1) - -/obj/machinery/clonepod/deconstruct(disassembled = TRUE) - if(occupant) - go_out() - ..() - -/obj/machinery/clonepod/proc/maim_clone(mob/living/carbon/human/H) - if(!unattached_flesh) - unattached_flesh = list() - else - for(var/fl in unattached_flesh) - qdel(fl) - unattached_flesh.Cut() - - H.setCloneLoss(CLONE_INITIAL_DAMAGE) //Yeah, clones start with very low health, not with random, because why would they start with random health - H.setBrainLoss(CLONE_INITIAL_DAMAGE) - // In addition to being cellularly damaged and having barely any - // brain function, they also have no limbs or internal organs. - var/static/list/zones = list("r_arm", "l_arm", "r_leg", "l_leg") - for(var/zone in zones) - var/obj/item/bodypart/BP = H.get_bodypart(zone) - BP.drop_limb() - BP.forceMove(src) - unattached_flesh += BP - - for(var/o in H.internal_organs) - var/obj/item/organ/organ = o - if(!istype(organ) || organ.vital) - continue - organ.Remove(H, special=TRUE) - organ.forceMove(src) - unattached_flesh += organ - - flesh_number = unattached_flesh.len - -/obj/machinery/clonepod/proc/check_brine() - // Clones are in a pickled bath of mild chemicals, keeping - // them alive, despite their lack of internal organs - for(var/bt in brine_types) - if(occupant.reagents.get_reagent_amount(bt) < 1) - occupant.reagents.add_reagent(bt, 1) - -/* - * Manual -- A big ol' manual. - */ - -/obj/item/weapon/paper/Cloning - name = "paper - 'H-87 Cloning Apparatus Manual" - info = {"

Getting Started

- Congratulations, your station has purchased the H-87 industrial cloning device!
- Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
- That's all there is to it!
- Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.
-

Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. - Profile Deletion has been restricted to \[Station Head\] level access.

-

Cloning from a profile

- Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
- Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.
-
-

The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. - The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


- Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.
-

Profile Management

-

The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. - These diskettes are used to transfer genetic information between machines and profiles. - A load/save dialog will become available in each profile if a disk is inserted.


- A good diskette is a great way to counter aforementioned genetic drift!
-
- This technology produced under license from Thinktronic Systems, LTD."} - -#undef CLONE_INITIAL_DAMAGE -#undef SPEAK -#undef MINIMUM_HEAL_LEVEL +//Cloning revival method. +//The pod handles the actual cloning while the computer manages the clone profiles + +//Potential replacement for genetics revives or something I dunno (?) + +#define CLONE_INITIAL_DAMAGE 190 //Clones in clonepods start with 190 cloneloss damage and 190 brainloss damage, thats just logical +#define MINIMUM_HEAL_LEVEL 40 + +#define SPEAK(message) radio.talk_into(src, message, radio_channel, get_spans(), get_default_language()) + +/obj/machinery/clonepod + anchored = 1 + name = "cloning pod" + desc = "An electronically-lockable pod for growing organic tissue." + density = 1 + icon = 'icons/obj/cloning.dmi' + icon_state = "pod_0" + req_access = list(GLOB.access_cloning) //For premature unlocking. + verb_say = "states" + var/heal_level //The clone is released once its health reaches this level. + var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. + var/mess = FALSE //Need to clean out it if it's full of exploded clone. + var/attempting = FALSE //One clone attempt at a time thanks + var/speed_coeff + var/efficiency + + var/datum/mind/clonemind + var/grab_ghost_when = CLONER_MATURE_CLONE + + var/obj/item/device/radio/radio + var/radio_key = /obj/item/device/encryptionkey/headset_med + var/radio_channel = "Medical" + + var/obj/effect/countdown/clonepod/countdown + + var/list/unattached_flesh + var/flesh_number = 0 + + // The "brine" is the reagents that are automatically added in small + // amounts to the occupant. + var/static/list/brine_types = list( + "salbutamol", // anti-oxyloss + "bicaridine", // NOBREATHE species take brute in crit + "corazone", // prevents cardiac arrest damage + "mimesbane") // stops them gasping from lack of air. + +/obj/machinery/clonepod/New() + ..() + var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/clonepod(null) + B.apply_default_parts(src) + + countdown = new(src) + + radio = new(src) + radio.keyslot = new radio_key + radio.subspace_transmission = 1 + radio.canhear_range = 0 + radio.recalculateChannels() + +/obj/machinery/clonepod/Destroy() + go_out() + qdel(radio) + radio = null + qdel(countdown) + countdown = null + if(connected) + connected.DetachCloner(src) + for(var/i in unattached_flesh) + qdel(i) + LAZYCLEARLIST(unattached_flesh) + unattached_flesh = null + . = ..() + +/obj/machinery/clonepod/RefreshParts() + speed_coeff = 0 + efficiency = 0 + for(var/obj/item/weapon/stock_parts/scanning_module/S in component_parts) + efficiency += S.rating + for(var/obj/item/weapon/stock_parts/manipulator/P in component_parts) + speed_coeff += P.rating + heal_level = (efficiency * 15) + 10 + if(heal_level < MINIMUM_HEAL_LEVEL) + heal_level = MINIMUM_HEAL_LEVEL + if(heal_level > 100) + heal_level = 100 + +/obj/item/weapon/circuitboard/machine/clonepod + name = "Clone Pod (Machine Board)" + build_path = /obj/machinery/clonepod + origin_tech = "programming=2;biotech=2" + req_components = list( + /obj/item/stack/cable_coil = 2, + /obj/item/weapon/stock_parts/scanning_module = 2, + /obj/item/weapon/stock_parts/manipulator = 2, + /obj/item/stack/sheet/glass = 1) + +//The return of data disks?? Just for transferring between genetics machine/cloning machine. +//TO-DO: Make the genetics machine accept them. +/obj/item/weapon/disk/data + name = "cloning data disk" + icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. + var/list/fields = list() + var/read_only = 0 //Well,it's still a floppy disk + +//Disk stuff. +/obj/item/weapon/disk/data/New() + ..() + icon_state = "datadisk[rand(0,6)]" + add_overlay("datadisk_gene") + +/obj/item/weapon/disk/data/attack_self(mob/user) + read_only = !read_only + to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") + +/obj/item/weapon/disk/data/examine(mob/user) + ..() + to_chat(user, "The write-protect tab is set to [read_only ? "protected" : "unprotected"].") + + +//Clonepod + +/obj/machinery/clonepod/examine(mob/user) + ..() + var/mob/living/mob_occupant = occupant + if(mess) + to_chat(user, "It's filled with blood and viscera. You swear you can see it moving...") + if(is_operational() && mob_occupant) + if(mob_occupant.stat != DEAD) + to_chat(user, "Current clone cycle is [round(get_completion())]% complete.") + +/obj/machinery/clonepod/return_air() + // We want to simulate the clone not being in contact with + // the atmosphere, so we'll put them in a constant pressure + // nitrogen. They'll breathe through the chemicals we pump into them. + var/static/datum/gas_mixture/immutable/cloner/GM //global so that there's only one instance made for all cloning pods + if(!GM) + GM = new + return GM + +/obj/machinery/clonepod/proc/get_completion() + . = FALSE + var/mob/living/mob_occupant = occupant + if(mob_occupant) + . = (100 * ((mob_occupant.health + 100) / (heal_level + 100))) + +/obj/machinery/clonepod/attack_ai(mob/user) + return examine(user) + +//Start growing a human clone in the pod! +/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions) + if(panel_open) + return FALSE + if(mess || attempting) + return FALSE + clonemind = locate(mindref) + if(!istype(clonemind)) //not a mind + return FALSE + if( clonemind.current && clonemind.current.stat != DEAD ) //mind is associated with a non-dead body + return FALSE + if(clonemind.active) //somebody is using that mind + if( ckey(clonemind.key)!=ckey ) + return FALSE + else + // get_ghost() will fail if they're unable to reenter their body + var/mob/dead/observer/G = clonemind.get_ghost() + if(!G) + return FALSE + if(clonemind.damnation_type) //Can't clone the damned. + INVOKE_ASYNC(src, .proc/horrifyingsound) + mess = TRUE + icon_state = "pod_g" + update_icon() + return FALSE + + attempting = TRUE //One at a time!! + countdown.start() + + var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) + + if(clonemind.changeling) + var/obj/item/organ/brain/B = H.getorganslot("brain") + B.vital = FALSE + B.decoy_override = TRUE + + H.hardset_dna(ui, se, H.real_name, null, mrace, features) + + if(efficiency > 2) + var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations) + H.dna.remove_mutation_group(unclean_mutations) + if(efficiency > 5 && prob(20)) + H.randmutvg() + if(efficiency < 3 && prob(50)) + var/mob/M = H.randmutb() + if(ismob(M)) + H = M + + H.silent = 20 //Prevents an extreme edge case where clones could speak if they said something at exactly the right moment. + occupant = H + + if(!clonename) //to prevent null names + clonename = "clone ([rand(0,999)])" + H.real_name = clonename + + icon_state = "pod_1" + //Get the clone body ready + maim_clone(H) + check_brine() // put in chemicals NOW to stop death via cardiac arrest + H.Paralyse(4) + + clonemind.transfer_to(H) + + if(grab_ghost_when == CLONER_FRESH_CLONE) + H.grab_ghost() + to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?
") + + if(grab_ghost_when == CLONER_MATURE_CLONE) + H.ghostize(TRUE) //Only does anything if they were still in their old body and not already a ghost + to_chat(H.get_ghost(TRUE), "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.") + + if(H) + H.faction |= factions + + H.set_cloned_appearance() + + H.suiciding = FALSE + attempting = FALSE + return TRUE + +//Grow clones to maturity then kick them out. FREELOADERS +/obj/machinery/clonepod/process() + var/mob/living/mob_occupant = occupant + + if(!is_operational()) //Autoeject if power is lost + if(mob_occupant) + go_out() + connected_message("Clone Ejected: Loss of power.") + + else if(mob_occupant && (mob_occupant.loc == src)) + if((mob_occupant.stat == DEAD) || (mob_occupant.suiciding) || mob_occupant.hellbound) //Autoeject corpses and suiciding dudes. + connected_message("Clone Rejected: Deceased.") + SPEAK("The cloning of [mob_occupant.real_name] has been \ + aborted due to unrecoverable tissue failure.") + go_out() + + else if(mob_occupant.cloneloss > (100 - heal_level)) + mob_occupant.Paralyse(4) + + //Slowly get that clone healed and finished. + mob_occupant.adjustCloneLoss(-((speed_coeff/2) * config.damage_multiplier)) + var/progress = CLONE_INITIAL_DAMAGE - mob_occupant.getCloneLoss() + // To avoid the default cloner making incomplete clones + progress += (100 - MINIMUM_HEAL_LEVEL) + var/milestone = CLONE_INITIAL_DAMAGE / flesh_number + var/installed = flesh_number - unattached_flesh.len + + if((progress / milestone) >= installed) + // attach some flesh + var/obj/item/I = pick_n_take(unattached_flesh) + if(isorgan(I)) + var/obj/item/organ/O = I + O.Insert(mob_occupant) + else if(isbodypart(I)) + var/obj/item/bodypart/BP = I + BP.attach_limb(mob_occupant) + + //Premature clones may have brain damage. + mob_occupant.adjustBrainLoss(-((speed_coeff/2) * config.damage_multiplier)) + + check_brine() + + use_power(7500) //This might need tweaking. + + else if((mob_occupant.cloneloss <= (100 - heal_level))) + connected_message("Cloning Process Complete.") + SPEAK("The cloning cycle of [mob_occupant.real_name] is complete.") + go_out() + + else if (!mob_occupant || mob_occupant.loc != src) + occupant = null + if (!mess && !panel_open) + icon_state = "pod_0" + use_power(200) + +//Let's unlock this early I guess. Might be too early, needs tweaking. +/obj/machinery/clonepod/attackby(obj/item/weapon/W, mob/user, params) + if(!(occupant || mess)) + if(default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]",W)) + return + + if(exchange_parts(user, W)) + return + + if(default_deconstruction_crowbar(W)) + return + + if(istype(W,/obj/item/device/multitool)) + var/obj/item/device/multitool/P = W + + if(istype(P.buffer, /obj/machinery/computer/cloning)) + if(get_area(P.buffer) != get_area(src)) + to_chat(user, "-% Cannot link machines across power zones. Buffer cleared %-") + P.buffer = null + return + to_chat(user, "-% Successfully linked [P.buffer] with [src] %-") + var/obj/machinery/computer/cloning/comp = P.buffer + if(connected) + connected.DetachCloner(src) + comp.AttachCloner(src) + else + P.buffer = src + to_chat(user, "-% Successfully stored \ref[P.buffer] [P.buffer.name] in buffer %-") + return + + var/mob/living/mob_occupant = occupant + if(W.GetID()) + if(!check_access(W)) + to_chat(user, "Access Denied.") + return + if(!(mob_occupant || mess)) + to_chat(user, "Error: Pod has no occupant.") + return + else + connected_message("Authorized Ejection") + + SPEAK("An authorized ejection of [clonemind.name] has occurred.") + + to_chat(user, "You force an emergency ejection. ") + go_out() + else + return ..() + +/obj/machinery/clonepod/emag_act(mob/user) + if(!occupant) + return + to_chat(user, "You corrupt the genetic compiler.") + malfunction() + +//Put messages in the connected computer's temp var for display. +/obj/machinery/clonepod/proc/connected_message(message) + if ((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) + return FALSE + if (!message) + return FALSE + + connected.temp = message + connected.updateUsrDialog() + return TRUE + +/obj/machinery/clonepod/proc/go_out() + countdown.stop() + var/mob/living/mob_occupant = occupant + + if(mess) //Clean that mess and dump those gibs! + mess = FALSE + new /obj/effect/gibspawner/generic(loc) + audible_message("You hear a splat.") + icon_state = "pod_0" + return + + if(!mob_occupant) + return + + + if(grab_ghost_when == CLONER_MATURE_CLONE) + mob_occupant.grab_ghost() + to_chat(occupant, "There is a bright flash!
You feel like a new being.
") + mob_occupant.flash_act() + + var/turf/T = get_turf(src) + occupant.forceMove(T) + icon_state = "pod_0" + mob_occupant.domutcheck(1) //Waiting until they're out before possible monkeyizing. The 1 argument forces powers to manifest. + + occupant = null + +/obj/machinery/clonepod/proc/malfunction() + var/mob/living/mob_occupant = occupant + if(mob_occupant) + connected_message("Critical Error!") + SPEAK("Critical error! Please contact a Thinktronic Systems \ + technician, as your warranty may be affected.") + mess = TRUE + + for(var/obj/item/O in unattached_flesh) + qdel(O) + + icon_state = "pod_g" + if(mob_occupant.mind != clonemind) + clonemind.transfer_to(mob_occupant) + mob_occupant.grab_ghost() // We really just want to make you suffer. + flash_color(mob_occupant, flash_color="#960000", flash_time=100) + to_chat(mob_occupant, "Agony blazes across your consciousness as your body is torn apart.
Is this what dying is like? Yes it is.
") + playsound(src.loc, 'sound/machines/warning-buzzer.ogg', 50, 0) + mob_occupant << sound('sound/hallucinations/veryfar_noise.ogg',0,1,50) + QDEL_IN(mob_occupant, 40) + +/obj/machinery/clonepod/relaymove(mob/user) + if(user.stat == CONSCIOUS) + go_out() + +/obj/machinery/clonepod/emp_act(severity) + + var/mob/living/mob_occupant = occupant + if(mob_occupant && prob(100/(severity*efficiency))) + connected_message(Gibberish("EMP-caused Accidental Ejection", 0)) + SPEAK(Gibberish("Exposure to electromagnetic fields has caused the ejection of [mob_occupant.real_name] prematurely." ,0)) + + go_out() + ..() + +/obj/machinery/clonepod/ex_act(severity, target) + ..() + if(!QDELETED(src)) + go_out() + +/obj/machinery/clonepod/handle_atom_del(atom/A) + if(A == occupant) + occupant = null + countdown.stop() + +/obj/machinery/clonepod/proc/horrifyingsound() + for(var/i in 1 to 5) + playsound(loc,pick('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg'), 100, rand(0.95,1.05)) + sleep(1) + sleep(10) + playsound(loc,'sound/hallucinations/wail.ogg',100,1) + +/obj/machinery/clonepod/deconstruct(disassembled = TRUE) + if(occupant) + go_out() + ..() + +/obj/machinery/clonepod/proc/maim_clone(mob/living/carbon/human/H) + if(!unattached_flesh) + unattached_flesh = list() + else + for(var/fl in unattached_flesh) + qdel(fl) + unattached_flesh.Cut() + + H.setCloneLoss(CLONE_INITIAL_DAMAGE) //Yeah, clones start with very low health, not with random, because why would they start with random health + H.setBrainLoss(CLONE_INITIAL_DAMAGE) + // In addition to being cellularly damaged and having barely any + // brain function, they also have no limbs or internal organs. + var/static/list/zones = list("r_arm", "l_arm", "r_leg", "l_leg") + for(var/zone in zones) + var/obj/item/bodypart/BP = H.get_bodypart(zone) + BP.drop_limb() + BP.forceMove(src) + unattached_flesh += BP + + for(var/o in H.internal_organs) + var/obj/item/organ/organ = o + if(!istype(organ) || organ.vital) + continue + organ.Remove(H, special=TRUE) + organ.forceMove(src) + unattached_flesh += organ + + flesh_number = unattached_flesh.len + +/obj/machinery/clonepod/proc/check_brine() + // Clones are in a pickled bath of mild chemicals, keeping + // them alive, despite their lack of internal organs + for(var/bt in brine_types) + if(occupant.reagents.get_reagent_amount(bt) < 1) + occupant.reagents.add_reagent(bt, 1) + +/* + * Manual -- A big ol' manual. + */ + +/obj/item/weapon/paper/Cloning + name = "paper - 'H-87 Cloning Apparatus Manual" + info = {"

Getting Started

+ Congratulations, your station has purchased the H-87 industrial cloning device!
+ Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
+ That's all there is to it!
+ Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.
+

Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. + Profile Deletion has been restricted to \[Station Head\] level access.

+

Cloning from a profile

+ Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
+ Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.
+
+

The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. + The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


+ Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.
+

Profile Management

+

The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. + These diskettes are used to transfer genetic information between machines and profiles. + A load/save dialog will become available in each profile if a disk is inserted.


+ A good diskette is a great way to counter aforementioned genetic drift!
+
+ This technology produced under license from Thinktronic Systems, LTD."} + +#undef CLONE_INITIAL_DAMAGE +#undef SPEAK +#undef MINIMUM_HEAL_LEVEL diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 48819f91e2..f20e92b721 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -186,23 +186,23 @@ // Scanner if (!isnull(src.scanner)) - var/mob/living/scanner_occupant = scanner.occupant - + var/mob/living/scanner_occupant = scanner.occupant + dat += "

Scanner Functions

" dat += "
" - if(!scanner_occupant) + if(!scanner_occupant) dat += "Scanner Unoccupied" else if(loading) - dat += "[scanner_occupant] => Scanning..." + dat += "[scanner_occupant] => Scanning..." else - if(scanner_occupant.ckey != scantemp_ckey) + if(scanner_occupant.ckey != scantemp_ckey) scantemp = "Ready to Scan" - scantemp_ckey = scanner_occupant.ckey - dat += "[scanner_occupant] => [scantemp]" + scantemp_ckey = scanner_occupant.ckey + dat += "[scanner_occupant] => [scantemp]" dat += "
" - if(scanner_occupant) + if(scanner_occupant) dat += "Start Scan" dat += "
[src.scanner.locked ? "Unlock Scanner" : "Lock Scanner"]" else diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 0c2894f056..79d6aa0b52 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -113,8 +113,8 @@ to_chat(R.connected_ai, "

ALERT - Cyborg detonation detected: [R.name]
") R.ResetSecurityCodes() else - var/turf/T = get_turf(R) - message_admins("[ADMIN_LOOKUPFLW(usr)] detonated [key_name(R, R.client)][ADMIN_JMP(T)]!") + var/turf/T = get_turf(R) + message_admins("[ADMIN_LOOKUPFLW(usr)] detonated [key_name(R, R.client)][ADMIN_JMP(T)]!") log_game("\[key_name(usr)] detonated [key_name(R)]!") if(R.connected_ai) to_chat(R.connected_ai, "

ALERT - Cyborg detonation detected: [R.name]
") @@ -128,7 +128,7 @@ if(can_control(usr, R)) var/choice = input("Are you certain you wish to [R.canmove ? "lock down" : "release"] [R.name]?") in list("Confirm", "Abort") if(choice == "Confirm" && can_control(usr, R) && !..()) - message_admins("[ADMIN_LOOKUPFLW(usr)] [R.canmove ? "locked down" : "released"] [key_name(R, R.client)][ADMIN_LOOKUPFLW(R)]!") + message_admins("[ADMIN_LOOKUPFLW(usr)] [R.canmove ? "locked down" : "released"] [key_name(R, R.client)][ADMIN_LOOKUPFLW(R)]!") log_game("[key_name(usr)] [R.canmove ? "locked down" : "released"] [key_name(R)]!") R.SetLockdown(!R.lockcharge) to_chat(R, "[!R.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") diff --git a/code/game/machinery/computer/telecrystalconsoles.dm b/code/game/machinery/computer/telecrystalconsoles.dm index fd026e4686..cfbf112bba 100644 --- a/code/game/machinery/computer/telecrystalconsoles.dm +++ b/code/game/machinery/computer/telecrystalconsoles.dm @@ -58,12 +58,12 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E /obj/machinery/computer/telecrystals/uplinker/proc/donateTC(amt, addLog = 1) if(uplinkholder && linkedboss) - if(amt < 0) - linkedboss.storedcrystals += uplinkholder.hidden_uplink.telecrystals - if(addLog) - linkedboss.logTransfer("[src] donated [uplinkholder.hidden_uplink.telecrystals] telecrystals to [linkedboss].") - uplinkholder.hidden_uplink.telecrystals = 0 - else if(amt <= uplinkholder.hidden_uplink.telecrystals) + if(amt < 0) + linkedboss.storedcrystals += uplinkholder.hidden_uplink.telecrystals + if(addLog) + linkedboss.logTransfer("[src] donated [uplinkholder.hidden_uplink.telecrystals] telecrystals to [linkedboss].") + uplinkholder.hidden_uplink.telecrystals = 0 + else if(amt <= uplinkholder.hidden_uplink.telecrystals) uplinkholder.hidden_uplink.telecrystals -= amt linkedboss.storedcrystals += amt if(addLog) @@ -71,12 +71,12 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E /obj/machinery/computer/telecrystals/uplinker/proc/giveTC(amt, addLog = 1) if(uplinkholder && linkedboss) - if(amt < 0) - uplinkholder.hidden_uplink.telecrystals += linkedboss.storedcrystals - if(addLog) - linkedboss.logTransfer("[src] received [linkedboss.storedcrystals] telecrystals from [linkedboss].") - linkedboss.storedcrystals = 0 - else if(amt <= linkedboss.storedcrystals) + if(amt < 0) + uplinkholder.hidden_uplink.telecrystals += linkedboss.storedcrystals + if(addLog) + linkedboss.logTransfer("[src] received [linkedboss.storedcrystals] telecrystals from [linkedboss].") + linkedboss.storedcrystals = 0 + else if(amt <= linkedboss.storedcrystals) uplinkholder.hidden_uplink.telecrystals += amt linkedboss.storedcrystals -= amt if(addLog) @@ -99,7 +99,7 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E if(uplinkholder) dat += "[uplinkholder.hidden_uplink.telecrystals] telecrystals remain in this uplink.
" if(linkedboss) - dat += "Donate TC: 1 | 5 | All" + dat += "Donate TC: 1 | 5 | All" dat += "
Eject Uplink" @@ -113,9 +113,9 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E if(..()) return - if(href_list["donate"]) - var/tcamt = text2num(href_list["donate"]) - donateTC(tcamt) + if(href_list["donate"]) + var/tcamt = text2num(href_list["donate"]) + donateTC(tcamt) if(href_list["eject"]) ejectuplink() @@ -170,7 +170,7 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E var/dat = "" dat += "Scan for TC stations.
" - dat += "[storedcrystals] telecrystals are available for distribution.
" + dat += "[storedcrystals] telecrystals are available for distribution.
" dat += "

" @@ -179,10 +179,10 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E if(A.uplinkholder) dat += "[A.uplinkholder.hidden_uplink.telecrystals] telecrystals." if(storedcrystals) - dat+= "
Add TC: 1 | 5 | 10 | All" + dat+= "
Add TC: 1 | 5 | 10 | All" dat += "
" - if(TCstations.len && storedcrystals) + if(TCstations.len && storedcrystals) dat += "

Evenly distribute remaining TC.

" @@ -203,11 +203,11 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E if(href_list["scan"]) scanUplinkers() - if(href_list["give"]) - var/tcamt = text2num(href_list["give"]) - if(TCstations.len) // sanity - var/obj/machinery/computer/telecrystals/uplinker/A = locate(href_list["target"]) in TCstations - A.giveTC(tcamt) + if(href_list["give"]) + var/tcamt = text2num(href_list["give"]) + if(TCstations.len) // sanity + var/obj/machinery/computer/telecrystals/uplinker/A = locate(href_list["target"]) in TCstations + A.giveTC(tcamt) if(href_list["distrib"]) var/sanity = 0 diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index c65c0029ff..fdeb6598e5 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -121,7 +121,7 @@ var/atom/movable/AM = i var/obj/item/bodypart/head/as_head = AM var/obj/item/device/mmi/as_mmi = AM - var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || istype(AM, /mob/living/brain) + var/brain_holder = istype(AM, /obj/item/organ/brain) || (istype(as_head) && as_head.brain) || (istype(as_mmi) && as_mmi.brain) || istype(AM, /mob/living/brain) if(isliving(AM) || brain_holder) if(emagged) if(!brain_holder) diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index e81774e147..18d2a1b5bd 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -211,13 +211,13 @@ uv = TRUE locked = TRUE update_icon() - if(occupant) - var/mob/living/mob_occupant = occupant + if(occupant) + var/mob/living/mob_occupant = occupant if(uv_super) - mob_occupant.adjustFireLoss(rand(20, 36)) + mob_occupant.adjustFireLoss(rand(20, 36)) else - mob_occupant.adjustFireLoss(rand(10, 16)) - mob_occupant.emote("scream") + mob_occupant.adjustFireLoss(rand(10, 16)) + mob_occupant.emote("scream") addtimer(CALLBACK(src, .proc/cook), 50) else uv_cycles = initial(uv_cycles) @@ -368,15 +368,15 @@ else if(!helmet && !mask && !suit && !storage && !occupant) return else - if(occupant) - var/mob/living/mob_occupant = occupant - to_chat(mob_occupant, "[src]'s confines grow warm, then hot, then scorching. You're being burned [!mob_occupant.stat ? "alive" : "away"]!") + if(occupant) + var/mob/living/mob_occupant = occupant + to_chat(mob_occupant, "[src]'s confines grow warm, then hot, then scorching. You're being burned [!mob_occupant.stat ? "alive" : "away"]!") cook() . = TRUE if("dispense") if(!state_open) return - + var/static/list/valid_items = list("helmet", "suit", "mask", "storage") var/item_name = params["item"] if(item_name in valid_items) diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index bf90f0a569..d04f88a9e7 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -107,7 +107,7 @@ icon_state = "mecha_ion" origin_tech = "materials=4;engineering=4;combat=6;magnets=6" energy_drain = 500 - projectile = /obj/item/projectile/energy/tesla/cannon + projectile = /obj/item/projectile/energy/tesla/cannon fire_sound = 'sound/magic/lightningbolt.ogg' @@ -337,8 +337,8 @@ /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/proj_init(var/obj/item/weapon/grenade/flashbang/F) var/turf/T = get_turf(src) - message_admins("[ADMIN_LOOKUPFLW(chassis.occupant)] fired a [src] in [ADMIN_COORDJMP(T)]",0,1) - log_game("[key_name(chassis.occupant)] fired a [src] [COORD(T)]") + message_admins("[ADMIN_LOOKUPFLW(chassis.occupant)] fired a [src] in [ADMIN_COORDJMP(T)]",0,1) + log_game("[key_name(chassis.occupant)] fired a [src] [COORD(T)]") addtimer(CALLBACK(F, /obj/item/weapon/grenade/flashbang.proc/prime), det_time) /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang/clusterbang //Because I am a heartless bastard -Sieve //Heartless? for making the poor man's honkblast? - Kaze diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 86d62eacea..2bb55410e1 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -94,7 +94,7 @@ name = "Head of Personnel" /obj/effect/landmark/start/librarian - name = "Curator" + name = "Curator" /obj/effect/landmark/start/lawyer name = "Lawyer" diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 2290456d78..7f66b0ab1a 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -63,7 +63,7 @@ return if (istype(M, /atom/movable)) if(ismegafauna(M)) - message_admins("[M] [ADMIN_FLW(M)] has teleported through [src].") + message_admins("[M] [ADMIN_FLW(M)] has teleported through [src].") do_teleport(M, target, precision) ///You will appear adjacent to the beacon diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index 6f3491ac9c..4eb36f6fbd 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -108,7 +108,7 @@ access_mime = 1 var/mime_charges = 5 -/obj/item/weapon/cartridge/curator +/obj/item/weapon/cartridge/curator name = "\improper Lib-Tweet cartridge" icon_state = "cart-s" access_newscaster = 1 diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index e1cf6bb8c8..65489a89c9 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -12,9 +12,9 @@ GLOBAL_LIST_EMPTY(GPS_list) var/emped = FALSE var/turf/locked_location var/tracking = TRUE - var/updating = TRUE //Automatic updating of GPS list. Can be set to manual by user. - var/global_mode = TRUE //If disabled, only GPS signals of the same Z level are shown - + var/updating = TRUE //Automatic updating of GPS list. Can be set to manual by user. + var/global_mode = TRUE //If disabled, only GPS signals of the same Z level are shown + /obj/item/device/gps/Initialize() ..() @@ -31,7 +31,7 @@ GLOBAL_LIST_EMPTY(GPS_list) cut_overlay("working") add_overlay("emp") addtimer(CALLBACK(src, .proc/reboot), 300, TIMER_OVERRIDE) //if a new EMP happens, remove the old timer so it doesn't reactivate early - SStgui.close_uis(src) //Close the UI control if it is open. + SStgui.close_uis(src) //Close the UI control if it is open. /obj/item/device/gps/proc/reboot() emped = FALSE @@ -39,9 +39,9 @@ GLOBAL_LIST_EMPTY(GPS_list) add_overlay("working") /obj/item/device/gps/AltClick(mob/user) - toggletracking(user) - -/obj/item/device/gps/proc/toggletracking(mob/user) + toggletracking(user) + +/obj/item/device/gps/proc/toggletracking(mob/user) if(!user.canUseTopic(src, be_close=TRUE)) return //user not valid to use gps if(emped) @@ -57,80 +57,80 @@ GLOBAL_LIST_EMPTY(GPS_list) tracking = TRUE -/obj/item/device/gps/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state. +/obj/item/device/gps/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state. if(emped) - to_chat(user, "[src] fizzles weakly.") - return - ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - var/gps_window_height = 300 + GLOB.GPS_list.len * 20 // Variable window height, depending on how many GPS units there are to show - ui = new(user, src, ui_key, "gps", "Global Positioning System", 600, gps_window_height, master_ui, state) //width, height - ui.open() - - ui.set_autoupdate(state = updating) - - -/obj/item/device/gps/ui_data(mob/user) - var/list/data = list() - data["power"] = tracking - data["tag"] = gpstag - data["updating"] = updating - data["globalmode"] = global_mode - if(!tracking || emped) //Do not bother scanning if the GPS is off or EMPed - return data - - var/turf/curr = get_turf(src) - data["current"] = "[get_area_name(curr)] ([curr.x], [curr.y], [curr.z])" - - var/list/signals = list() - data["signals"] = list() - - for(var/gps in GLOB.GPS_list) - var/obj/item/device/gps/G = gps - if(G.emped || !G.tracking || G == src) - continue - var/turf/pos = get_turf(G) - if(!global_mode && pos.z != curr.z) - continue - var/area/gps_area = get_area_name(G) - var/list/signal = list() - signal["entrytag"] = G.gpstag //Name or 'tag' of the GPS - signal["area"] = format_text(gps_area) - signal["coord"] = "[pos.x], [pos.y], [pos.z]" - if(pos.z == curr.z) //Distance/Direction calculations for same z-level only - signal["dist"] = max(get_dist(curr, pos), 0) //Distance between the src and remote GPS turfs - signal["degrees"] = round(Get_Angle(curr, pos)) //0-360 degree directional bearing, for more precision. - var/direction = uppertext(dir2text(get_dir(curr, pos))) //Direction text (East, etc). Not as precise, but still helpful. - if(!direction) - direction = "CENTER" - signal["degrees"] = "N/A" - signal["direction"] = direction - - signals += list(signal) //Add this signal to the list of signals - data["signals"] = signals - return data - - - -/obj/item/device/gps/ui_act(action, params) - if(..()) - return - switch(action) - if("rename") - var/a = input("Please enter desired tag.", name, gpstag) as text - a = uppertext(copytext(sanitize(a), 1, 5)) - gpstag = a - name = "global positioning system ([gpstag])" - . = TRUE - if("power") - toggletracking(usr) - . = TRUE - if("updating") - updating = !updating - . = TRUE - if("globalmode") - global_mode = !global_mode - . = TRUE + to_chat(user, "[src] fizzles weakly.") + return + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + var/gps_window_height = 300 + GLOB.GPS_list.len * 20 // Variable window height, depending on how many GPS units there are to show + ui = new(user, src, ui_key, "gps", "Global Positioning System", 600, gps_window_height, master_ui, state) //width, height + ui.open() + + ui.set_autoupdate(state = updating) + + +/obj/item/device/gps/ui_data(mob/user) + var/list/data = list() + data["power"] = tracking + data["tag"] = gpstag + data["updating"] = updating + data["globalmode"] = global_mode + if(!tracking || emped) //Do not bother scanning if the GPS is off or EMPed + return data + + var/turf/curr = get_turf(src) + data["current"] = "[get_area_name(curr)] ([curr.x], [curr.y], [curr.z])" + + var/list/signals = list() + data["signals"] = list() + + for(var/gps in GLOB.GPS_list) + var/obj/item/device/gps/G = gps + if(G.emped || !G.tracking || G == src) + continue + var/turf/pos = get_turf(G) + if(!global_mode && pos.z != curr.z) + continue + var/area/gps_area = get_area_name(G) + var/list/signal = list() + signal["entrytag"] = G.gpstag //Name or 'tag' of the GPS + signal["area"] = format_text(gps_area) + signal["coord"] = "[pos.x], [pos.y], [pos.z]" + if(pos.z == curr.z) //Distance/Direction calculations for same z-level only + signal["dist"] = max(get_dist(curr, pos), 0) //Distance between the src and remote GPS turfs + signal["degrees"] = round(Get_Angle(curr, pos)) //0-360 degree directional bearing, for more precision. + var/direction = uppertext(dir2text(get_dir(curr, pos))) //Direction text (East, etc). Not as precise, but still helpful. + if(!direction) + direction = "CENTER" + signal["degrees"] = "N/A" + signal["direction"] = direction + + signals += list(signal) //Add this signal to the list of signals + data["signals"] = signals + return data + + + +/obj/item/device/gps/ui_act(action, params) + if(..()) + return + switch(action) + if("rename") + var/a = input("Please enter desired tag.", name, gpstag) as text + a = uppertext(copytext(sanitize(a), 1, 5)) + gpstag = a + name = "global positioning system ([gpstag])" + . = TRUE + if("power") + toggletracking(usr) + . = TRUE + if("updating") + updating = !updating + . = TRUE + if("globalmode") + global_mode = !global_mode + . = TRUE /obj/item/device/gps/Topic(href, href_list) ..() diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index f7d11d2f47..e03c85c09a 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -95,8 +95,8 @@ "[user] activates \the [src]!", \ "You activate \the [src].", "You hear a click.") - message_admins("Power sink activated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_COORDJMP(src)]") - log_game("Power sink activated by [key_name(user)] at [COORD(src)]") + message_admins("Power sink activated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_COORDJMP(src)]") + log_game("Power sink activated by [key_name(user)] at [COORD(src)]") set_mode(OPERATING) if(OPERATING) diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 8d37640b6d..635338fe96 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -178,21 +178,21 @@ var/log_attacher = "" if(attacher) - log_attacher = "[ADMIN_QUE(attacher)] [ADMIN_FLW(attacher)]" + log_attacher = "[ADMIN_QUE(attacher)] [ADMIN_FLW(attacher)]" var/mob/mob = get_mob_by_key(src.fingerprintslast) var/last_touch_info = "" if(mob) - last_touch_info = "[ADMIN_QUE(mob)] [ADMIN_FLW(mob)]" + last_touch_info = "[ADMIN_QUE(mob)] [ADMIN_FLW(mob)]" var/log_str3 = " Last touched by: [key_name_admin(mob)]" - var/bomb_message = "[log_str1] [A.name][ADMIN_JMP(bombturf)] [log_str2][log_attacher] [log_str3][last_touch_info]" + var/bomb_message = "[log_str1] [A.name][ADMIN_JMP(bombturf)] [log_str2][log_attacher] [log_str3][last_touch_info]" GLOB.bombers += bomb_message message_admins(bomb_message, 0, 1) - log_game("[log_str1] [A.name][COORD(bombturf)] [log_str2] [log_str3]") + log_game("[log_str1] [A.name][COORD(bombturf)] [log_str2] [log_str3]") merge_gases() spawn(20) // In case one tank bursts for (var/i=0,i<5,i++) diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 29463b5a5c..3c7464d4dc 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -237,7 +237,7 @@ /obj/item/borg/upgrade/selfrepair/proc/check_dropped() if(loc != cyborg) toggle_action.Remove(cyborg) - QDEL_NULL(toggle_action) + QDEL_NULL(toggle_action) cyborg = null deactivate() @@ -400,4 +400,4 @@ return R.make_shell(src) - return TRUE + return TRUE diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm deleted file mode 100644 index 2ae93fa774..0000000000 --- a/code/game/objects/items/weapons/explosives.dm +++ /dev/null @@ -1,115 +0,0 @@ -//In this file: C4 - -/obj/item/weapon/c4 - name = "C-4" - desc = "Used to put holes in specific areas without too much extra hole." - gender = PLURAL - icon = 'icons/obj/grenade.dmi' - icon_state = "plastic-explosive0" - item_state = "plasticx" - flags = NOBLUDGEON - w_class = WEIGHT_CLASS_SMALL - origin_tech = "syndicate=1" - var/timer = 10 - var/open_panel = 0 - parent_type = /obj/item/weapon/grenade/plastic/c4 - -/obj/item/weapon/c4/New() - wires = new /datum/wires/explosive/c4(src) - plastic_overlay = mutable_appearance(icon, "plastic-explosive2") - ..() - -/obj/item/weapon/c4/Destroy() - qdel(wires) - wires = null - target = null - return ..() - -/obj/item/weapon/c4/suicide_act(mob/user) - user.visible_message("[user] activates the [src.name] and holds it above [user.p_their()] head! It looks like [user.p_theyre()] going out with a bang!") - var/message_say = "FOR NO RAISIN!" - if(user.mind) - if(user.mind.special_role) - var/role = lowertext(user.mind.special_role) - if(role == "traitor" || role == "syndicate") - message_say = "FOR THE SYNDICATE!" - else if(role == "changeling") - message_say = "FOR THE HIVE!" - else if(role == "cultist") - message_say = "FOR NAR-SIE!" - else if(role == "revolutionary" || role == "head revolutionary") - message_say = "VIVA LA REVOLUTION!" - else if(user.mind.gang_datum) - message_say = "[uppertext(user.mind.gang_datum.name)] RULES!" - user.say(message_say) - target = user - message_admins("[ADMIN_LOOKUPFLW(user)] suicided with [name] at [ADMIN_COORDJMP(src)]",0,1) - message_admins("[key_name(user)] suicided with [name] at ([x],[y],[z])") - sleep(10) - explode(get_turf(user)) - user.gib(1, 1) - -/obj/item/weapon/c4/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/weapon/screwdriver)) - open_panel = !open_panel - to_chat(user, "You [open_panel ? "open" : "close"] the wire panel.") - else if(is_wire_tool(I)) - wires.interact(user) - else - return ..() - -/obj/item/weapon/c4/attack_self(mob/user) - var/newtime = input(usr, "Please set the timer.", "Timer", 10) as num - if(user.get_active_held_item() == src) - newtime = Clamp(newtime, 10, 60000) - timer = newtime - to_chat(user, "Timer set for [timer] seconds.") - -/obj/item/weapon/c4/afterattack(atom/movable/AM, mob/user, flag) - if (!flag) - return - if (ismob(AM)) - return - if(loc == AM) - return - if((istype(AM, /obj/item/weapon/storage/)) && !((istype(AM, /obj/item/weapon/storage/secure)) || (istype(AM, /obj/item/weapon/storage/lockbox)))) //If its storage but not secure storage OR a lockbox, then place it inside. - return - if((istype(AM,/obj/item/weapon/storage/secure)) || (istype(AM, /obj/item/weapon/storage/lockbox))) - var/obj/item/weapon/storage/secure/S = AM - if(!S.locked) //Literal hacks, this works for lockboxes despite incorrect type casting, because they both share the locked var. But if its unlocked, place it inside, otherwise PLANTING C4! - return - - to_chat(user, "You start planting the bomb...") - - if(do_after(user, 50, target = AM)) - if(!user.temporarilyRemoveItemFromInventory(src)) - return - src.target = AM - forceMove(null) - - var/message = "[ADMIN_LOOKUPFLW(user)] planted [name] on [target.name] at [ADMIN_COORDJMP(target)] with [timer] second fuse" - GLOB.bombers += message - message_admins(message,0,1) - log_game("[key_name(user)] planted [name] on [target.name] at [COORD(target)] with [timer] second fuse") - - target.add_overlay(plastic_overlay, 1) - to_chat(user, "You plant the bomb. Timer counting down from [timer].") - addtimer(CALLBACK(src, .proc/explode), timer * 10) - -/obj/item/weapon/c4/proc/explode() - if(QDELETED(src)) - return - var/turf/location - if(target) - if(!QDELETED(target)) - location = get_turf(target) - target.cut_overlay(plastic_overlay, TRUE) - else - location = get_turf(src) - if(location) - location.ex_act(2, target) - explosion(location,0,0,3) - qdel(src) - -/obj/item/weapon/c4/attack(mob/M, mob/user, def_zone) - return diff --git a/code/game/objects/items/weapons/grenades/plastic.dm b/code/game/objects/items/weapons/grenades/plastic.dm index 09b9d6aa33..3118d731f2 100644 --- a/code/game/objects/items/weapons/grenades/plastic.dm +++ b/code/game/objects/items/weapons/grenades/plastic.dm @@ -154,6 +154,115 @@ /obj/item/weapon/grenade/plastic/c4 name = "C4" desc = "Used to put holes in specific areas without too much extra hole. A saboteur's favorite." + gender = PLURAL + icon = 'icons/obj/grenade.dmi' + icon_state = "plastic-explosive0" + item_state = "plasticx" + flags = NOBLUDGEON + w_class = WEIGHT_CLASS_SMALL + origin_tech = "syndicate=1" + var/timer = 10 + var/open_panel = 0 + +/obj/item/weapon/grenade/plastic/c4/New() + wires = new /datum/wires/explosive/c4(src) + plastic_overlay = mutable_appearance(icon, "plastic-explosive2") + ..() + +/obj/item/weapon/grenade/plastic/c4/Destroy() + qdel(wires) + wires = null + target = null + return ..() + +/obj/item/weapon/grenade/plastic/c4/suicide_act(mob/user) + user.visible_message("[user] activates the [src.name] and holds it above [user.p_their()] head! It looks like [user.p_theyre()] going out with a bang!") + var/message_say = "FOR NO RAISIN!" + if(user.mind) + if(user.mind.special_role) + var/role = lowertext(user.mind.special_role) + if(role == "traitor" || role == "syndicate") + message_say = "FOR THE SYNDICATE!" + else if(role == "changeling") + message_say = "FOR THE HIVE!" + else if(role == "cultist") + message_say = "FOR NAR-SIE!" + else if(role == "revolutionary" || role == "head revolutionary") + message_say = "VIVA LA REVOLUTION!" + else if(user.mind.gang_datum) + message_say = "[uppertext(user.mind.gang_datum.name)] RULES!" + user.say(message_say) + target = user + message_admins("[ADMIN_LOOKUPFLW(user)] suicided with [name] at [ADMIN_COORDJMP(src)]",0,1) + message_admins("[key_name(user)] suicided with [name] at ([x],[y],[z])") + sleep(10) + explode(get_turf(user)) + user.gib(1, 1) + +/obj/item/weapon/grenade/plastic/c4/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/weapon/screwdriver)) + open_panel = !open_panel + to_chat(user, "You [open_panel ? "open" : "close"] the wire panel.") + else if(is_wire_tool(I)) + wires.interact(user) + else + return ..() + +/obj/item/weapon/grenade/plastic/c4/attack_self(mob/user) + var/newtime = input(usr, "Please set the timer.", "Timer", 10) as num + if(user.get_active_held_item() == src) + newtime = Clamp(newtime, 10, 60000) + timer = newtime + to_chat(user, "Timer set for [timer] seconds.") + +/obj/item/weapon/grenade/plastic/c4/afterattack(atom/movable/AM, mob/user, flag) + if (!flag) + return + if (ismob(AM)) + return + if(loc == AM) + return + if((istype(AM, /obj/item/weapon/storage/)) && !((istype(AM, /obj/item/weapon/storage/secure)) || (istype(AM, /obj/item/weapon/storage/lockbox)))) //If its storage but not secure storage OR a lockbox, then place it inside. + return + if((istype(AM,/obj/item/weapon/storage/secure)) || (istype(AM, /obj/item/weapon/storage/lockbox))) + var/obj/item/weapon/storage/secure/S = AM + if(!S.locked) //Literal hacks, this works for lockboxes despite incorrect type casting, because they both share the locked var. But if its unlocked, place it inside, otherwise PLANTING C4! + return + + to_chat(user, "You start planting the bomb...") + + if(do_after(user, 50, target = AM)) + if(!user.temporarilyRemoveItemFromInventory(src)) + return + src.target = AM + forceMove(null) + + var/message = "[ADMIN_LOOKUPFLW(user)] planted [name] on [target.name] at [ADMIN_COORDJMP(target)] with [timer] second fuse" + GLOB.bombers += message + message_admins(message,0,1) + log_game("[key_name(user)] planted [name] on [target.name] at [COORD(target)] with [timer] second fuse") + + target.add_overlay(plastic_overlay, 1) + to_chat(user, "You plant the bomb. Timer counting down from [timer].") + addtimer(CALLBACK(src, .proc/explode), timer * 10) + +/obj/item/weapon/grenade/plastic/c4/proc/explode() + if(QDELETED(src)) + return + var/turf/location + if(target) + if(!QDELETED(target)) + location = get_turf(target) + target.cut_overlay(plastic_overlay, TRUE) + else + location = get_turf(src) + if(location) + location.ex_act(2, target) + explosion(location,0,0,3) + qdel(src) + +/obj/item/weapon/grenade/plastic/c4/attack(mob/M, mob/user, def_zone) + return // X4 is an upgraded directional variant of c4 which is relatively safe to be standing next to. And much less safe to be standing on the other side of. // C4 is intended to be used for infiltration, and destroying tech. X4 is intended to be used for heavy breaching and tight spaces. diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index 86c01d745e..f0566e06a0 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -1,5 +1,5 @@ /obj/item/weapon/grenade/spawnergrenade - desc = "It will unleash an unspecified anomaly into the vicinity." + desc = "It will unleash an unspecified anomaly into the vicinity." name = "delivery grenade" icon = 'icons/obj/grenade.dmi' icon_state = "delivery" diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm index a3b72d55bd..9d7516faac 100644 --- a/code/game/objects/items/weapons/implants/implantchair.dm +++ b/code/game/objects/items/weapons/implants/implantchair.dm @@ -41,10 +41,10 @@ data["open"] = state_open data["occupant"] = list() - if(occupant) - var/mob/living/mob_occupant = occupant - data["occupant"]["name"] = mob_occupant.name - data["occupant"]["stat"] = mob_occupant.stat + if(occupant) + var/mob/living/mob_occupant = occupant + data["occupant"]["name"] = mob_occupant.name + data["occupant"]["stat"] = mob_occupant.stat data["special_name"] = special ? special_name : null data["ready_implants"] = ready_implants diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index fd8c5483a2..4fbc7572d5 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -16,7 +16,7 @@ var/brightness_on = 3 /obj/item/weapon/melee/energy/Initialize() - . = ..() + . = ..() if(LAZYLEN(possible_colors)) item_color = pick(possible_colors) switch(item_color)//Only run this check if the color was picked randomly, so that colors can be manually set for non-random colored energy weapons. @@ -183,8 +183,8 @@ light_color = "#40ceff" possible_colors = null -/obj/item/weapon/melee/energy/sword/cyborg/saw/Initialize() - . = ..() +/obj/item/weapon/melee/energy/sword/cyborg/saw/Initialize() + . = ..() icon_state = "esaw_0" item_color = null @@ -207,9 +207,9 @@ /obj/item/weapon/melee/energy/sword/saber/attackby(obj/item/weapon/W, mob/living/user, params) - if(istype(W, /obj/item/device/multitool)) - if(!hacked) - hacked = TRUE + if(istype(W, /obj/item/device/multitool)) + if(!hacked) + hacked = TRUE item_color = "rainbow" to_chat(user, "RNBW_ENGAGE") @@ -243,8 +243,8 @@ sharpness = IS_SHARP //Most of the other special functions are handled in their own files. aka special snowflake code so kewl -/obj/item/weapon/melee/energy/blade/Initialize() - . = ..() +/obj/item/weapon/melee/energy/blade/Initialize() + . = ..() spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, src) spark_system.attach(src) diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 589cf3270d..64e2a5e8fd 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -296,7 +296,7 @@ /obj/item/weapon/lighter, /obj/item/device/multitool, /obj/item/weapon/reagent_containers/food/drinks/bottle/molotov, - /obj/item/weapon/c4, + /obj/item/weapon/grenade/plastic/c4, ) /obj/item/weapon/storage/belt/grenade/full/PopulateContents() new /obj/item/weapon/grenade/flashbang(src) diff --git a/code/game/objects/items/weapons/vending_items.dm b/code/game/objects/items/weapons/vending_items.dm index c93dcaca3b..b8a9314f54 100644 --- a/code/game/objects/items/weapons/vending_items.dm +++ b/code/game/objects/items/weapons/vending_items.dm @@ -71,9 +71,9 @@ icon_state = "refill_clothes" charges = list(31, 4, 4)// of 101 standard, 12 contraband, 10 premium(?) init_charges = list(31, 4, 4) - -/obj/item/weapon/vending_refill/medical - machine_name = "NanoMed" - icon_state = "refill_medical" - charges = list(26, 5, 3)// of 76 standard, 13 contraband, 8 premium + +/obj/item/weapon/vending_refill/medical + machine_name = "NanoMed" + icon_state = "refill_medical" + charges = list(26, 5, 3)// of 76 standard, 13 contraband, 8 premium init_charges = list(26, 5, 3) \ No newline at end of file diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index e28c916ae4..084ac19f63 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -390,7 +390,7 @@ desc = "A chainsaw that has replaced your arm." icon_state = "chainsaw_on" item_state = "mounted_chainsaw" - flags = NODROP | ABSTRACT | DROPDEL + flags = NODROP | ABSTRACT | DROPDEL w_class = WEIGHT_CLASS_HUGE force = 21 throwforce = 0 @@ -400,17 +400,17 @@ attack_verb = list("sawed", "torn", "cut", "chopped", "diced") hitsound = 'sound/weapons/chainsawhit.ogg' -/obj/item/weapon/mounted_chainsaw/Destroy() - var/obj/item/bodypart/part +/obj/item/weapon/mounted_chainsaw/Destroy() + var/obj/item/bodypart/part new /obj/item/weapon/twohanded/required/chainsaw(get_turf(src)) - if(iscarbon(loc)) - var/mob/living/carbon/holder = loc - var/index = holder.get_held_index_of_item(src) - if(index) - part = holder.hand_bodyparts[index] - . = ..() - if(part) - part.drop_limb() + if(iscarbon(loc)) + var/mob/living/carbon/holder = loc + var/index = holder.get_held_index_of_item(src) + if(index) + part = holder.hand_bodyparts[index] + . = ..() + if(part) + part.drop_limb() /obj/item/weapon/statuebust name = "bust" diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index 89c815692d..36730239ea 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -106,11 +106,11 @@ LINEN BINS icon_state = "sheetrd" item_color = "director" -// for Free Golems. -/obj/item/weapon/bedsheet/rd/royal_cape - name = "Royal Cape of the Liberator" - desc = "Majestic." - +// for Free Golems. +/obj/item/weapon/bedsheet/rd/royal_cape + name = "Royal Cape of the Liberator" + desc = "Majestic." + /obj/item/weapon/bedsheet/medical name = "medical blanket" desc = "It's a sterilized* blanket commonly used in the Medbay. *Sterilization is voided if a virologist is present onboard the station." diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index 5e1ea02509..98a52994e9 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -11,7 +11,7 @@ else new /obj/item/weapon/storage/backpack/satchel/cap(src) new /obj/item/clothing/neck/cloak/cap(src) - new /obj/item/weapon/storage/daki(src) + new /obj/item/weapon/storage/daki(src) new /obj/item/weapon/storage/backpack/dufflebag/captain(src) new /obj/item/clothing/head/crown/fancy(src) new /obj/item/clothing/suit/captunic(src) diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index 43fbdde5ee..17aea280e0 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -1,361 +1,361 @@ -/obj/structure/displaycase - name = "display case" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "glassbox0" - desc = "A display case for prized possessions." - density = 1 - anchored = 1 - resistance_flags = ACID_PROOF - armor = list(melee = 30, bullet = 0, laser = 0, energy = 0, bomb = 10, bio = 0, rad = 0, fire = 70, acid = 100) - obj_integrity = 200 - max_integrity = 200 - integrity_failure = 50 - var/obj/item/showpiece = null - var/alert = TRUE - var/open = FALSE - var/openable = TRUE - var/obj/item/weapon/electronics/airlock/electronics - var/start_showpiece_type = null //add type for items on display - -/obj/structure/displaycase/Initialize() - . = ..() - if(start_showpiece_type) - showpiece = new start_showpiece_type (src) - update_icon() - -/obj/structure/displaycase/Destroy() - if(electronics) - QDEL_NULL(electronics) - if(showpiece) - QDEL_NULL(showpiece) - return ..() - -/obj/structure/displaycase/examine(mob/user) - ..() - if(alert) - to_chat(user, "Hooked up with an anti-theft system.") - if(showpiece) - to_chat(user, "There's [showpiece] inside.") - - -/obj/structure/displaycase/proc/dump() - if (showpiece) - showpiece.forceMove(loc) - showpiece = null - -/obj/structure/displaycase/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) - if(BURN) - playsound(src.loc, 'sound/items/Welder.ogg', 100, 1) - -/obj/structure/displaycase/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - dump() - if(!disassembled) - new /obj/item/weapon/shard( src.loc ) - trigger_alarm() - qdel(src) - -/obj/structure/displaycase/obj_break(damage_flag) - if(!broken && !(flags & NODECONSTRUCT)) - density = 0 - broken = 1 - new /obj/item/weapon/shard( src.loc ) - playsound(src, "shatter", 70, 1) - update_icon() - trigger_alarm() - -/obj/structure/displaycase/proc/trigger_alarm() - //Activate Anti-theft - if(alert) - var/area/alarmed = get_area(src) - alarmed.burglaralert(src) - playsound(src, 'sound/effects/alert.ogg', 50, 1) - -/* - -*/ - -/obj/structure/displaycase/proc/is_directional(atom/A) - try - getFlatIcon(A,defdir=4) - catch - return FALSE - return TRUE - -/obj/structure/displaycase/proc/get_flat_icon_directional(atom/A) - //Get flatIcon even if dir is mismatched for directionless icons - //SLOW - var/icon/I - if(is_directional(A)) - I = getFlatIcon(A) - else - var/old_dir = A.dir - A.setDir(2) - I = getFlatIcon(A) - A.setDir(old_dir) - return I - -/obj/structure/displaycase/update_icon() - var/icon/I - if(open) - I = icon('icons/obj/stationobjs.dmi',"glassbox_open") - else - I = icon('icons/obj/stationobjs.dmi',"glassbox0") - if(broken) - I = icon('icons/obj/stationobjs.dmi',"glassboxb0") - if(showpiece) - var/icon/S = get_flat_icon_directional(showpiece) - S.Scale(17,17) - I.Blend(S,ICON_UNDERLAY,8,8) - src.icon = I - return - -/obj/structure/displaycase/attackby(obj/item/weapon/W, mob/user, params) - if(W.GetID() && !broken && openable) - if(allowed(user)) - to_chat(user, "You [open ? "close":"open"] the [src]") - toggle_lock(user) - else - to_chat(user, "Access denied.") - else if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent == INTENT_HELP && !broken) - var/obj/item/weapon/weldingtool/WT = W - if(obj_integrity < max_integrity && WT.remove_fuel(5, user)) - to_chat(user, "You begin repairing [src].") - playsound(loc, WT.usesound, 40, 1) - if(do_after(user, 40*W.toolspeed, target = src)) - obj_integrity = max_integrity - playsound(loc, 'sound/items/Welder2.ogg', 50, 1) - update_icon() - to_chat(user, "You repair [src].") - else - to_chat(user, "[src] is already in good condition!") - return - else if(!alert && istype(W,/obj/item/weapon/crowbar) && openable) //Only applies to the lab cage and player made display cases - if(broken) - if(showpiece) - to_chat(user, "Remove the displayed object first.") - else - to_chat(user, "You remove the destroyed case") - qdel(src) - else - to_chat(user, "You start to [open ? "close":"open"] the [src]") - if(do_after(user, 20*W.toolspeed, target = src)) - to_chat(user, "You [open ? "close":"open"] the [src]") - toggle_lock(user) - else if(open && !showpiece) - if(user.transferItemToLoc(W, src)) - showpiece = W - to_chat(user, "You put [W] on display") - update_icon() - else if(istype(W, /obj/item/stack/sheet/glass) && broken) - var/obj/item/stack/sheet/glass/G = W - if(G.get_amount() < 2) - to_chat(user, "You need two glass sheets to fix the case!") - return - to_chat(user, "You start fixing [src]...") - if(do_after(user, 20, target = src)) - G.use(2) - broken = 0 - obj_integrity = max_integrity - update_icon() - else - return ..() - -/obj/structure/displaycase/proc/toggle_lock(mob/user) - open = !open - update_icon() - -/obj/structure/displaycase/attack_paw(mob/user) - return src.attack_hand(user) - -/obj/structure/displaycase/attack_hand(mob/user) - user.changeNext_move(CLICK_CD_MELEE) - if (showpiece && (broken || open)) - to_chat(user, "You deactivate the hover field built into the case.") - dump() - src.add_fingerprint(user) - update_icon() - return - else - //prevents remote "kicks" with TK - if (!Adjacent(user)) - return - user.visible_message("[user] kicks the display case.", null, null, COMBAT_MESSAGE_RANGE) - user.do_attack_animation(src, ATTACK_EFFECT_KICK) - take_damage(2) - - - -/obj/structure/displaycase_chassis - anchored = 1 - density = 0 - name = "display case chassis" - desc = "wooden base of display case" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "glassbox_chassis" - var/obj/item/weapon/electronics/airlock/electronics - - -/obj/structure/displaycase_chassis/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/weapon/wrench)) //The player can only deconstruct the wooden frame - to_chat(user, "You start disassembling [src]...") - playsound(src.loc, I.usesound, 50, 1) - if(do_after(user, 30*I.toolspeed, target = src)) - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - new /obj/item/stack/sheet/mineral/wood(get_turf(src), 5) - qdel(src) - - else if(istype(I, /obj/item/weapon/electronics/airlock)) - to_chat(user, "You start installing the electronics into [src]...") - playsound(src.loc, I.usesound, 50, 1) - if(do_after(user, 30, target = src) && user.transferItemToLoc(I,src)) - electronics = I - to_chat(user, "You install the airlock electronics.") - - else if(istype(I, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = I - if(G.get_amount() < 10) - to_chat(user, "You need ten glass sheets to do this!") - return - to_chat(user, "You start adding [G] to [src]...") - if(do_after(user, 20, target = src)) - G.use(10) - var/obj/structure/displaycase/display = new(src.loc) - if(electronics) - electronics.loc = display - display.electronics = electronics - if(electronics.one_access) - display.req_one_access = electronics.accesses - else - display.req_access = electronics.accesses - qdel(src) - else - return ..() - -//The captains display case requiring specops ID access is intentional. -//The lab cage and captains display case do not spawn with electronics, which is why req_access is needed. -/obj/structure/displaycase/captain - alert = 1 - start_showpiece_type = /obj/item/weapon/gun/energy/laser/captain - req_access = list(GLOB.access_cent_specops) - -/obj/structure/displaycase/labcage - name = "lab cage" - desc = "A glass lab container for storing interesting creatures." - start_showpiece_type = /obj/item/clothing/mask/facehugger/lamarr - req_access = list(GLOB.access_rd) - -/obj/structure/displaycase/trophy - name = "trophy display case" - desc = "Store your trophies of accomplishment in here, and they will stay forever." - var/trophy_message = "" - var/placer_key = "" - var/added_roundstart = TRUE - var/is_locked = TRUE - - alert = TRUE - integrity_failure = 0 - openable = FALSE - -/obj/structure/displaycase/trophy/Initialize() - . = ..() - GLOB.trophy_cases += src - -/obj/structure/displaycase/trophy/Destroy() - GLOB.trophy_cases -= src - return ..() - -/obj/structure/displaycase/trophy/examine(mob/user) - ..() - if(trophy_message) - to_chat(user, "The plaque reads:") - to_chat(user, trophy_message) - -/obj/structure/displaycase/trophy/attackby(obj/item/weapon/W, mob/user, params) - - if(!user.Adjacent(src)) //no TK museology - return - if(user.a_intent == INTENT_HARM) - return ..() - - if(user.is_holding_item_of_type(/obj/item/key/displaycase)) - if(added_roundstart) - is_locked = !is_locked - to_chat(user, "You [!is_locked ? "un" : ""]lock the case.") - else - to_chat(user, "The lock is stuck shut!") - return - - if(is_locked) - to_chat(user, "The case is shut tight with an old fashioned physical lock. Maybe you should ask the curator for the key?") - return - - if(!added_roundstart) - to_chat(user, "You've already put something new in this case.") - return - - if(is_type_in_typecache(W, GLOB.blacklisted_cargo_types)) - to_chat(user, "The case rejects the [W].") - return - - for(var/a in W.GetAllContents()) - if(is_type_in_typecache(a, GLOB.blacklisted_cargo_types)) - to_chat(user, "The case rejects the [W].") - return - - if(user.transferItemToLoc(W, src)) - - if(showpiece) - to_chat(user, "You press a button, and [showpiece] descends into the floor of the case.") - QDEL_NULL(showpiece) - - to_chat(user, "You insert [W] into the case.") - showpiece = W - added_roundstart = FALSE - update_icon() - - placer_key = user.ckey - - trophy_message = W.desc //default value - - var/chosen_plaque = stripped_input(user, "What would you like the plaque to say? Default value is item's description.", "Trophy Plaque") - if(chosen_plaque) - if(user.Adjacent(src)) - trophy_message = chosen_plaque - to_chat(user, "You set the plaque's text.") - else - to_chat(user, "You are too far to set the plaque's text.") - - SSpersistence.SaveTrophy(src) - return TRUE - - else - to_chat(user, "\The [W] is stuck to your hand, you can't put it in the [src.name]!") - - return - -/obj/structure/displaycase/trophy/dump() - if (showpiece) - if(added_roundstart) - visible_message("The [showpiece] crumbles to dust!") - new /obj/effect/decal/cleanable/ash(loc) - QDEL_NULL(showpiece) - else - ..() - -/obj/item/key/displaycase - name = "display case key" - desc = "The key to the curator's display cases." - -/obj/item/showpiece_dummy - name = "Cheap replica" - -/obj/item/showpiece_dummy/Initialize(mapload, path) - . = ..() - var/obj/item/I = path - name = initial(I.name) - icon = initial(I.icon) - icon_state = initial(I.icon_state) +/obj/structure/displaycase + name = "display case" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "glassbox0" + desc = "A display case for prized possessions." + density = 1 + anchored = 1 + resistance_flags = ACID_PROOF + armor = list(melee = 30, bullet = 0, laser = 0, energy = 0, bomb = 10, bio = 0, rad = 0, fire = 70, acid = 100) + obj_integrity = 200 + max_integrity = 200 + integrity_failure = 50 + var/obj/item/showpiece = null + var/alert = TRUE + var/open = FALSE + var/openable = TRUE + var/obj/item/weapon/electronics/airlock/electronics + var/start_showpiece_type = null //add type for items on display + +/obj/structure/displaycase/Initialize() + . = ..() + if(start_showpiece_type) + showpiece = new start_showpiece_type (src) + update_icon() + +/obj/structure/displaycase/Destroy() + if(electronics) + QDEL_NULL(electronics) + if(showpiece) + QDEL_NULL(showpiece) + return ..() + +/obj/structure/displaycase/examine(mob/user) + ..() + if(alert) + to_chat(user, "Hooked up with an anti-theft system.") + if(showpiece) + to_chat(user, "There's [showpiece] inside.") + + +/obj/structure/displaycase/proc/dump() + if (showpiece) + showpiece.forceMove(loc) + showpiece = null + +/obj/structure/displaycase/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1) + if(BURN) + playsound(src.loc, 'sound/items/Welder.ogg', 100, 1) + +/obj/structure/displaycase/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + dump() + if(!disassembled) + new /obj/item/weapon/shard( src.loc ) + trigger_alarm() + qdel(src) + +/obj/structure/displaycase/obj_break(damage_flag) + if(!broken && !(flags & NODECONSTRUCT)) + density = 0 + broken = 1 + new /obj/item/weapon/shard( src.loc ) + playsound(src, "shatter", 70, 1) + update_icon() + trigger_alarm() + +/obj/structure/displaycase/proc/trigger_alarm() + //Activate Anti-theft + if(alert) + var/area/alarmed = get_area(src) + alarmed.burglaralert(src) + playsound(src, 'sound/effects/alert.ogg', 50, 1) + +/* + +*/ + +/obj/structure/displaycase/proc/is_directional(atom/A) + try + getFlatIcon(A,defdir=4) + catch + return FALSE + return TRUE + +/obj/structure/displaycase/proc/get_flat_icon_directional(atom/A) + //Get flatIcon even if dir is mismatched for directionless icons + //SLOW + var/icon/I + if(is_directional(A)) + I = getFlatIcon(A) + else + var/old_dir = A.dir + A.setDir(2) + I = getFlatIcon(A) + A.setDir(old_dir) + return I + +/obj/structure/displaycase/update_icon() + var/icon/I + if(open) + I = icon('icons/obj/stationobjs.dmi',"glassbox_open") + else + I = icon('icons/obj/stationobjs.dmi',"glassbox0") + if(broken) + I = icon('icons/obj/stationobjs.dmi',"glassboxb0") + if(showpiece) + var/icon/S = get_flat_icon_directional(showpiece) + S.Scale(17,17) + I.Blend(S,ICON_UNDERLAY,8,8) + src.icon = I + return + +/obj/structure/displaycase/attackby(obj/item/weapon/W, mob/user, params) + if(W.GetID() && !broken && openable) + if(allowed(user)) + to_chat(user, "You [open ? "close":"open"] the [src]") + toggle_lock(user) + else + to_chat(user, "Access denied.") + else if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent == INTENT_HELP && !broken) + var/obj/item/weapon/weldingtool/WT = W + if(obj_integrity < max_integrity && WT.remove_fuel(5, user)) + to_chat(user, "You begin repairing [src].") + playsound(loc, WT.usesound, 40, 1) + if(do_after(user, 40*W.toolspeed, target = src)) + obj_integrity = max_integrity + playsound(loc, 'sound/items/Welder2.ogg', 50, 1) + update_icon() + to_chat(user, "You repair [src].") + else + to_chat(user, "[src] is already in good condition!") + return + else if(!alert && istype(W,/obj/item/weapon/crowbar) && openable) //Only applies to the lab cage and player made display cases + if(broken) + if(showpiece) + to_chat(user, "Remove the displayed object first.") + else + to_chat(user, "You remove the destroyed case") + qdel(src) + else + to_chat(user, "You start to [open ? "close":"open"] the [src]") + if(do_after(user, 20*W.toolspeed, target = src)) + to_chat(user, "You [open ? "close":"open"] the [src]") + toggle_lock(user) + else if(open && !showpiece) + if(user.transferItemToLoc(W, src)) + showpiece = W + to_chat(user, "You put [W] on display") + update_icon() + else if(istype(W, /obj/item/stack/sheet/glass) && broken) + var/obj/item/stack/sheet/glass/G = W + if(G.get_amount() < 2) + to_chat(user, "You need two glass sheets to fix the case!") + return + to_chat(user, "You start fixing [src]...") + if(do_after(user, 20, target = src)) + G.use(2) + broken = 0 + obj_integrity = max_integrity + update_icon() + else + return ..() + +/obj/structure/displaycase/proc/toggle_lock(mob/user) + open = !open + update_icon() + +/obj/structure/displaycase/attack_paw(mob/user) + return src.attack_hand(user) + +/obj/structure/displaycase/attack_hand(mob/user) + user.changeNext_move(CLICK_CD_MELEE) + if (showpiece && (broken || open)) + to_chat(user, "You deactivate the hover field built into the case.") + dump() + src.add_fingerprint(user) + update_icon() + return + else + //prevents remote "kicks" with TK + if (!Adjacent(user)) + return + user.visible_message("[user] kicks the display case.", null, null, COMBAT_MESSAGE_RANGE) + user.do_attack_animation(src, ATTACK_EFFECT_KICK) + take_damage(2) + + + +/obj/structure/displaycase_chassis + anchored = 1 + density = 0 + name = "display case chassis" + desc = "wooden base of display case" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "glassbox_chassis" + var/obj/item/weapon/electronics/airlock/electronics + + +/obj/structure/displaycase_chassis/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/weapon/wrench)) //The player can only deconstruct the wooden frame + to_chat(user, "You start disassembling [src]...") + playsound(src.loc, I.usesound, 50, 1) + if(do_after(user, 30*I.toolspeed, target = src)) + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + new /obj/item/stack/sheet/mineral/wood(get_turf(src), 5) + qdel(src) + + else if(istype(I, /obj/item/weapon/electronics/airlock)) + to_chat(user, "You start installing the electronics into [src]...") + playsound(src.loc, I.usesound, 50, 1) + if(do_after(user, 30, target = src) && user.transferItemToLoc(I,src)) + electronics = I + to_chat(user, "You install the airlock electronics.") + + else if(istype(I, /obj/item/stack/sheet/glass)) + var/obj/item/stack/sheet/glass/G = I + if(G.get_amount() < 10) + to_chat(user, "You need ten glass sheets to do this!") + return + to_chat(user, "You start adding [G] to [src]...") + if(do_after(user, 20, target = src)) + G.use(10) + var/obj/structure/displaycase/display = new(src.loc) + if(electronics) + electronics.loc = display + display.electronics = electronics + if(electronics.one_access) + display.req_one_access = electronics.accesses + else + display.req_access = electronics.accesses + qdel(src) + else + return ..() + +//The captains display case requiring specops ID access is intentional. +//The lab cage and captains display case do not spawn with electronics, which is why req_access is needed. +/obj/structure/displaycase/captain + alert = 1 + start_showpiece_type = /obj/item/weapon/gun/energy/laser/captain + req_access = list(GLOB.access_cent_specops) + +/obj/structure/displaycase/labcage + name = "lab cage" + desc = "A glass lab container for storing interesting creatures." + start_showpiece_type = /obj/item/clothing/mask/facehugger/lamarr + req_access = list(GLOB.access_rd) + +/obj/structure/displaycase/trophy + name = "trophy display case" + desc = "Store your trophies of accomplishment in here, and they will stay forever." + var/trophy_message = "" + var/placer_key = "" + var/added_roundstart = TRUE + var/is_locked = TRUE + + alert = TRUE + integrity_failure = 0 + openable = FALSE + +/obj/structure/displaycase/trophy/Initialize() + . = ..() + GLOB.trophy_cases += src + +/obj/structure/displaycase/trophy/Destroy() + GLOB.trophy_cases -= src + return ..() + +/obj/structure/displaycase/trophy/examine(mob/user) + ..() + if(trophy_message) + to_chat(user, "The plaque reads:") + to_chat(user, trophy_message) + +/obj/structure/displaycase/trophy/attackby(obj/item/weapon/W, mob/user, params) + + if(!user.Adjacent(src)) //no TK museology + return + if(user.a_intent == INTENT_HARM) + return ..() + + if(user.is_holding_item_of_type(/obj/item/key/displaycase)) + if(added_roundstart) + is_locked = !is_locked + to_chat(user, "You [!is_locked ? "un" : ""]lock the case.") + else + to_chat(user, "The lock is stuck shut!") + return + + if(is_locked) + to_chat(user, "The case is shut tight with an old fashioned physical lock. Maybe you should ask the curator for the key?") + return + + if(!added_roundstart) + to_chat(user, "You've already put something new in this case.") + return + + if(is_type_in_typecache(W, GLOB.blacklisted_cargo_types)) + to_chat(user, "The case rejects the [W].") + return + + for(var/a in W.GetAllContents()) + if(is_type_in_typecache(a, GLOB.blacklisted_cargo_types)) + to_chat(user, "The case rejects the [W].") + return + + if(user.transferItemToLoc(W, src)) + + if(showpiece) + to_chat(user, "You press a button, and [showpiece] descends into the floor of the case.") + QDEL_NULL(showpiece) + + to_chat(user, "You insert [W] into the case.") + showpiece = W + added_roundstart = FALSE + update_icon() + + placer_key = user.ckey + + trophy_message = W.desc //default value + + var/chosen_plaque = stripped_input(user, "What would you like the plaque to say? Default value is item's description.", "Trophy Plaque") + if(chosen_plaque) + if(user.Adjacent(src)) + trophy_message = chosen_plaque + to_chat(user, "You set the plaque's text.") + else + to_chat(user, "You are too far to set the plaque's text.") + + SSpersistence.SaveTrophy(src) + return TRUE + + else + to_chat(user, "\The [W] is stuck to your hand, you can't put it in the [src.name]!") + + return + +/obj/structure/displaycase/trophy/dump() + if (showpiece) + if(added_roundstart) + visible_message("The [showpiece] crumbles to dust!") + new /obj/effect/decal/cleanable/ash(loc) + QDEL_NULL(showpiece) + else + ..() + +/obj/item/key/displaycase + name = "display case key" + desc = "The key to the curator's display cases." + +/obj/item/showpiece_dummy + name = "Cheap replica" + +/obj/item/showpiece_dummy/Initialize(mapload, path) + . = ..() + var/obj/item/I = path + name = initial(I.name) + icon = initial(I.icon) + icon_state = initial(I.icon_state) diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm index 039f3da5d7..4c57c55e6a 100644 --- a/code/game/objects/structures/mineral_doors.dm +++ b/code/game/objects/structures/mineral_doors.dm @@ -188,9 +188,9 @@ /obj/structure/mineral_door/transparent/plasma/attackby(obj/item/weapon/W, mob/user, params) if(W.is_hot()) - var/turf/T = get_turf(src) - message_admins("Plasma mineral door ignited by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(T)]",0,1) - log_game("Plasma mineral door ignited by [key_name(user)] in [COORD(T)]") + var/turf/T = get_turf(src) + message_admins("Plasma mineral door ignited by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(T)]",0,1) + log_game("Plasma mineral door ignited by [key_name(user)] in [COORD(T)]") TemperatureAct() else return ..() diff --git a/code/modules/admin/DB_ban/functions.dm b/code/modules/admin/DB_ban/functions.dm index 67d3b394c6..0df3434c8e 100644 --- a/code/modules/admin/DB_ban/functions.dm +++ b/code/modules/admin/DB_ban/functions.dm @@ -71,13 +71,13 @@ computerid = bancid ip = banip - var/datum/DBQuery/query_add_ban_get_ckey = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[ckey]'") - if(!query_add_ban_get_ckey.warn_execute()) + var/datum/DBQuery/query_add_ban_get_ckey = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[ckey]'") + if(!query_add_ban_get_ckey.warn_execute()) return - if(!query_add_ban_get_ckey.NextRow()) + if(!query_add_ban_get_ckey.NextRow()) if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key))) - if(alert(usr, "[ckey] has not been seen before, are you sure you want to create a ban for them?", "Unknown ckey", "Yes", "No", "Cancel") != "Yes") - return + if(alert(usr, "[ckey] has not been seen before, are you sure you want to create a ban for them?", "Unknown ckey", "Yes", "No", "Cancel") != "Yes") + return var/a_ckey var/a_computerid diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 7196319568..44417bdf27 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -1,821 +1,821 @@ - -//////////////////////////////// -/proc/message_admins(msg) - msg = "ADMIN LOG: [msg]" - to_chat(GLOB.admins, msg) - -/proc/relay_msg_admins(msg) - msg = "RELAY: [msg]" - to_chat(GLOB.admins, msg) - - -///////////////////////////////////////////////////////////////////////////////////////////////Panels - -/datum/admins/proc/show_player_panel(mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Show Player Panel" - set desc="Edit player (respawn, ban, heal, etc)" - - if(!check_rights()) - return - - if(!isobserver(usr)) - log_game("[key_name_admin(usr)] checked the player panel while in game.") - - if(!M) - to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.") - return - - var/body = "Options for [M.key]" - body += "Options panel for [M]" - if(M.client) - body += " played by [M.client] " - body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" - - if(isnewplayer(M)) - body += " Hasn't Entered Game " - else - body += " \[Heal\] " - - if(M.client) - body += "
\[First Seen: [M.client.player_join_date]\]\[Byond account registered on: [M.client.account_join_date]\]" - - - - body += "

\[ " - body += "VV - " - body += "TP - " - body += "PM - " - body += "SM - " - body += "FLW - " - body += "LOGS\]
" - - body += "Mob type = [M.type]

" - - body += "Kick | " - body += "Ban | " - body += "Jobban | " - body += "Identity Ban | " - if(jobban_isbanned(M, "OOC")) - body+= "OOCBan | " - else - body+= "OOCBan | " - if(jobban_isbanned(M, "emote")) - body+= "EmoteBan | " - else - body+= "Emoteban | " - - body += "Notes | Messages | Watchlist | " - if(M.client) - body += "| Prison | " - body += "\ Send back to Lobby | " - var/muted = M.client.prefs.muted - body += "
Mute: " - body += "\[IC | " - body += "OOC | " - body += "PRAY | " - body += "ADMINHELP | " - body += "DEADCHAT\]" - body += "(toggle all)" - - body += "

" - body += "Jump to | " - body += "Get | " - body += "Send To" - - body += "

" - body += "Traitor panel | " - body += "Narrate to | " - body += "Subtle message | " - body += "Language Menu" - - if (M.client) - if(!isnewplayer(M)) - body += "

" - body += "Transformation:" - body += "
" - - //Human - if(ishuman(M)) - body += "Human | " - else - body += "Humanize | " - - //Monkey - if(ismonkey(M)) - body += "Monkeyized | " - else - body += "Monkeyize | " - - //Corgi - if(iscorgi(M)) - body += "Corgized | " - else - body += "Corgize | " - - //AI / Cyborg - if(isAI(M)) - body += "Is an AI " - else if(ishuman(M)) - body += "Make AI | " - body += "Make Robot | " - body += "Make Alien | " - body += "Make Slime | " - body += "Make Blob | " - - //Simple Animals - if(isanimal(M)) - body += "Re-Animalize | " - else - body += "Animalize | " - - body += "

" - body += "Rudimentary transformation:
These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.

" - body += "Observer | " - body += "\[ Alien: Drone, " - body += "Hunter, " - body += "Sentinel, " - body += "Praetorian, " - body += "Queen, " - body += "Larva \] " - body += "Human " - body += "\[ slime: Baby, " - body += "Adult \] " - body += "Monkey | " - body += "Cyborg | " - body += "Cat | " - body += "Runtime | " - body += "Corgi | " - body += "Ian | " - body += "Crab | " - body += "Coffee | " - //body += "Parrot | " - //body += "Poly | " - body += "\[ Construct: Juggernaut , " - body += "Artificer , " - body += "Wraith \] " - body += "Shade" - body += "
" - - if (M.client) - body += "

" - body += "Other actions:" - body += "
" - body += "Forcesay | " - body += "Thunderdome 1 | " - body += "Thunderdome 2 | " - body += "Thunderdome Admin | " - body += "Thunderdome Observer | " - - body += "
" - body += "" - - usr << browse(body, "window=adminplayeropts-\ref[M];size=550x515") - SSblackbox.add_details("admin_verb","Player Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/datum/admins/proc/access_news_network() //MARKER - set category = "Fun" - set name = "Access Newscaster Network" - set desc = "Allows you to view, add and edit news feeds." - - if (!istype(src,/datum/admins)) - src = usr.client.holder - if (!istype(src,/datum/admins)) - to_chat(usr, "Error: you are not an admin!") - return - var/dat - dat = text("Admin Newscaster

Admin Newscaster Unit

") - - switch(admincaster_screen) - if(0) - dat += "Welcome to the admin newscaster.
Here you can add, edit and censor every newspiece on the network." - dat += "
Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units." - dat += "
Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things!" - if(GLOB.news_network.wanted_issue.active) - dat+= "
Read Wanted Issue" - dat+= "

Create Feed Channel" - dat+= "
View Feed Channels" - dat+= "
Submit new Feed story" - dat+= "

Exit" - var/wanted_already = 0 - if(GLOB.news_network.wanted_issue.active) - wanted_already = 1 - dat+="
Feed Security functions:
" - dat+="
[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue" - dat+="
Censor Feed Stories" - dat+="
Mark Feed Channel with Nanotrasen D-Notice (disables and locks the channel)." - dat+="

The newscaster recognises you as:
[src.admin_signature]
" - if(1) - dat+= "Station Feed Channels
" - if( isemptylist(GLOB.news_network.network_channels) ) - dat+="No active channels found..." - else - for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels) - if(CHANNEL.is_admin_channel) - dat+="[CHANNEL.channel_name]
" - else - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
" - dat+="

Refresh" - dat+="
Back" - if(2) - dat+="Creating new Feed Channel..." - dat+="
Channel Name: [src.admincaster_feed_channel.channel_name]
" - dat+="Channel Author: [src.admin_signature]
" - dat+="Will Accept Public Feeds: [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]

" - dat+="
Submit

Cancel
" - if(3) - dat+="Creating new Feed Message..." - dat+="
Receiving Channel: [src.admincaster_feed_channel.channel_name]
" //MARK - dat+="Message Author: [src.admin_signature]
" - dat+="Message Body: [src.admincaster_feed_message.returnBody(-1)]
" - dat+="
Submit

Cancel
" - if(4) - dat+="Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].

" - dat+="
Return
" - if(5) - dat+="Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.

" - dat+="
Return
" - if(6) - dat+="ERROR: Could not submit Feed story to Network.

" - if(src.admincaster_feed_channel.channel_name=="") - dat+="•Invalid receiving channel name.
" - if(src.admincaster_feed_message.returnBody(-1) == "" || src.admincaster_feed_message.returnBody(-1) == "\[REDACTED\]") - dat+="•Invalid message body.
" - dat+="
Return
" - if(7) - dat+="ERROR: Could not submit Feed Channel to Network.

" - if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]") - dat+="•Invalid channel name.
" - var/check = 0 - for(var/datum/newscaster/feed_channel/FC in GLOB.news_network.network_channels) - if(FC.channel_name == src.admincaster_feed_channel.channel_name) - check = 1 - break - if(check) - dat+="•Channel name already in use.
" - dat+="
Return
" - if(9) - dat+="[admincaster_feed_channel.channel_name]: \[created by: [admincaster_feed_channel.returnAuthor(-1)]\]
" - if(src.admincaster_feed_channel.censored) - dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
" - dat+="No further feed story additions are allowed while the D-Notice is in effect.

" - else - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
" - else - var/i = 0 - for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - i++ - dat+="-[MESSAGE.returnBody(-1)]
" - if(MESSAGE.img) - usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png") - dat+="

" - dat+="\[Story by [MESSAGE.returnAuthor(-1)]\]
" - dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]:
" - for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments) - dat+="[comment.body]
[comment.author] [comment.time_stamp]
" - dat+="
" - dat+="

Refresh" - dat+="
Back" - if(10) - dat+="Nanotrasen Feed Censorship Tool
" - dat+="NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.
" - dat+="Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.
" - dat+="
Select Feed channel to get Stories from:
" - if(isemptylist(GLOB.news_network.network_channels)) - dat+="No feed channels found active...
" - else - for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
" - dat+="
Cancel" - if(11) - dat+="Nanotrasen D-Notice Handler
" - dat+="A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's" - dat+="morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed" - dat+="stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.
" - if(isemptylist(GLOB.news_network.network_channels)) - dat+="No feed channels found active...
" - else - for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
" - - dat+="
Back" - if(12) - dat+="[src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.returnAuthor(-1)] \]
" - dat+="[(src.admincaster_feed_channel.authorCensor) ? ("Undo Author censorship") : ("Censor channel Author")]
" - - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
" - else - for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - dat+="-[MESSAGE.returnBody(-1)]
\[Story by [MESSAGE.returnAuthor(-1)]\]
" - dat+="[(MESSAGE.bodyCensor) ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.authorCensor) ? ("Undo Author Censorship") : ("Censor message Author")]
" - dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]: [MESSAGE.locked ? "Unlock" : "Lock"]
" - for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments) - dat+="[comment.body] X
[comment.author] [comment.time_stamp]
" - dat+="
Back" - if(13) - dat+="[src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.returnAuthor(-1)] \]
" - dat+="Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel.
" - if(src.admincaster_feed_channel.censored) - dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
" - dat+="No further feed story additions are allowed while the D-Notice is in effect.

" - else - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
" - else - for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - dat+="-[MESSAGE.returnBody(-1)]
\[Story by [MESSAGE.returnAuthor(-1)]\]
" - dat+="
Back" - if(14) - dat+="Wanted Issue Handler:" - var/wanted_already = 0 - var/end_param = 1 - if(GLOB.news_network.wanted_issue.active) - wanted_already = 1 - end_param = 2 - if(wanted_already) - dat+="
A wanted issue is already in Feed Circulation. You can edit or cancel it below.
" - dat+="
" - dat+="Criminal Name: [src.admincaster_wanted_message.criminal]
" - dat+="Description: [src.admincaster_wanted_message.body]
" - if(wanted_already) - dat+="Wanted Issue created by:[GLOB.news_network.wanted_issue.scannedUser]
" - else - dat+="Wanted Issue will be created under prosecutor:[src.admin_signature]
" - dat+="
[(wanted_already) ? ("Edit Issue") : ("Submit")]" - if(wanted_already) - dat+="
Take down Issue" - dat+="
Cancel" - if(15) - dat+="Wanted issue for [src.admincaster_wanted_message.criminal] is now in Network Circulation.

" - dat+="
Return
" - if(16) - dat+="ERROR: Wanted Issue rejected by Network.

" - if(src.admincaster_wanted_message.criminal =="" || src.admincaster_wanted_message.criminal == "\[REDACTED\]") - dat+="•Invalid name for person wanted.
" - if(src.admincaster_wanted_message.body == "" || src.admincaster_wanted_message.body == "\[REDACTED\]") - dat+="•Invalid description.
" - dat+="
Return
" - if(17) - dat+="Wanted Issue successfully deleted from Circulation
" - dat+="
Return
" - if(18) - dat+="-- STATIONWIDE WANTED ISSUE --
\[Submitted by: [GLOB.news_network.wanted_issue.scannedUser]\]
" - dat+="Criminal: [GLOB.news_network.wanted_issue.criminal]
" - dat+="Description: [GLOB.news_network.wanted_issue.body]
" - dat+="Photo:: " - if(GLOB.news_network.wanted_issue.img) - usr << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png") - dat+="
" - else - dat+="None" - dat+="
Back
" - if(19) - dat+="Wanted issue for [src.admincaster_wanted_message.criminal] successfully edited.

" - dat+="
Return
" - else - dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" - - //to_chat(world, "Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]") - //to_chat(world, "Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]") - usr << browse(dat, "window=admincaster_main;size=400x600") - onclose(usr, "admincaster_main") - - -/datum/admins/proc/Game() - if(!check_rights(0)) - return - - var/dat = {" -
Game Panel

\n - Change Game Mode
- "} - if(GLOB.master_mode == "secret") - dat += "(Force Secret Mode)
" - - dat += {" -
- Create Object
- Quick Create Object
- Create Turf
- Create Mob
- "} - - if(marked_datum && istype(marked_datum, /atom)) - dat += "Duplicate Marked Datum
" - - usr << browse(dat, "window=admin2;size=210x200") - return - -/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge -//i.e. buttons/verbs - - -/datum/admins/proc/restart() - set category = "Server" - set name = "Reboot World" - set desc="Restarts the world immediately" - if (!usr.client.holder) - return - var/confirm = alert("Restart the game world?", "Restart", "Yes", "Cancel") - if(confirm == "Cancel") - return - if(confirm == "Yes") - SSticker.delay_end = 0 - SSblackbox.add_details("admin_verb","Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - world.Reboot("Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key].", "end_error", "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10) - -/datum/admins/proc/end_round() - set category = "Server" - set name = "End Round" - set desc = "Attempts to produce a round end report and then restart the server organically." - - if (!usr.client.holder) - return - var/confirm = alert("End the round and restart the game world?", "End Round", "Yes", "Cancel") - if(confirm == "Cancel") - return - if(confirm == "Yes") - SSticker.force_ending = 1 - SSblackbox.add_details("admin_verb","End Round") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/datum/admins/proc/announce() - set category = "Special Verbs" - set name = "Announce" - set desc="Announce your desires to the world" - if(!check_rights(0)) - return - - var/message = input("Global message to send:", "Admin Announce", null, null) as message - if(message) - if(!check_rights(R_SERVER,0)) - message = adminscrub(message,500) - to_chat(world, "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:\n \t [message]") - log_admin("Announce: [key_name(usr)] : [message]") - SSblackbox.add_details("admin_verb","Announce") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/set_admin_notice() - set category = "Special Verbs" - set name = "Set Admin Notice" - set desc ="Set an announcement that appears to everyone who joins the server. Only lasts this round" - if(!check_rights(0)) - return - - var/new_admin_notice = input(src,"Set a public notice for this round. Everyone who joins the server will see it.\n(Leaving it blank will delete the current notice):","Set Notice",GLOB.admin_notice) as message|null - if(new_admin_notice == null) - return - if(new_admin_notice == GLOB.admin_notice) - return - if(new_admin_notice == "") - message_admins("[key_name(usr)] removed the admin notice.") - log_admin("[key_name(usr)] removed the admin notice:\n[GLOB.admin_notice]") - else - message_admins("[key_name(usr)] set the admin notice.") - log_admin("[key_name(usr)] set the admin notice:\n[new_admin_notice]") - to_chat(world, "Admin Notice:\n \t [new_admin_notice]") - SSblackbox.add_details("admin_verb","Set Admin Notice") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - GLOB.admin_notice = new_admin_notice - return - -/datum/admins/proc/toggleooc() - set category = "Server" - set desc="Toggle dis bitch" - set name="Toggle OOC" - toggle_ooc() - log_admin("[key_name(usr)] toggled OOC.") - message_admins("[key_name_admin(usr)] toggled OOC.") - SSblackbox.add_details("admin_toggle","Toggle OOC|[GLOB.ooc_allowed]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleoocdead() - set category = "Server" - set desc="Toggle dis bitch" - set name="Toggle Dead OOC" - GLOB.dooc_allowed = !( GLOB.dooc_allowed ) - - log_admin("[key_name(usr)] toggled OOC.") - message_admins("[key_name_admin(usr)] toggled Dead OOC.") - SSblackbox.add_details("admin_toggle","Toggle Dead OOC|[GLOB.dooc_allowed]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/startnow() - set category = "Server" - set desc="Start the round RIGHT NOW" - set name="Start Now" - if(SSticker.current_state == GAME_STATE_PREGAME || SSticker.current_state == GAME_STATE_STARTUP) - SSticker.start_immediately = TRUE - log_admin("[usr.key] has started the game.") - var/msg = "" - if(SSticker.current_state == GAME_STATE_STARTUP) - msg = " (The server is still setting up, but the round will be \ - started as soon as possible.)" - message_admins("\ - [usr.key] has started the game.[msg]") - SSblackbox.add_details("admin_verb","Start Now") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return 1 - else - to_chat(usr, "Error: Start Now: Game has already started.") - - return 0 - -/datum/admins/proc/toggleenter() - set category = "Server" - set desc="People can't enter" - set name="Toggle Entering" - GLOB.enter_allowed = !( GLOB.enter_allowed ) - if (!( GLOB.enter_allowed )) - to_chat(world, "New players may no longer enter the game.") - else - to_chat(world, "New players may now enter the game.") - log_admin("[key_name(usr)] toggled new player game entering.") - message_admins("[key_name_admin(usr)] toggled new player game entering.") - world.update_status() - SSblackbox.add_details("admin_toggle","Toggle Entering|[GLOB.enter_allowed]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleAI() - set category = "Server" - set desc="People can't be AI" - set name="Toggle AI" - config.allow_ai = !( config.allow_ai ) - if (!( config.allow_ai )) - to_chat(world, "The AI job is no longer chooseable.") - else - to_chat(world, "The AI job is chooseable now.") - log_admin("[key_name(usr)] toggled AI allowed.") - world.update_status() - SSblackbox.add_details("admin_toggle","Toggle AI|[config.allow_ai]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleaban() - set category = "Server" - set desc="Respawn basically" - set name="Toggle Respawn" - GLOB.abandon_allowed = !( GLOB.abandon_allowed ) - if (GLOB.abandon_allowed) - to_chat(world, "You may now respawn.") - else - to_chat(world, "You may no longer respawn :(") - message_admins("[key_name_admin(usr)] toggled respawn to [GLOB.abandon_allowed ? "On" : "Off"].") - log_admin("[key_name(usr)] toggled respawn to [GLOB.abandon_allowed ? "On" : "Off"].") - world.update_status() - SSblackbox.add_details("admin_toggle","Toggle Respawn|[GLOB.abandon_allowed]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/delay() - set category = "Server" - set desc="Delay the game start" - set name="Delay pre-game" - - var/newtime = input("Set a new time in seconds. Set -1 for indefinite delay.","Set Delay",round(SSticker.GetTimeLeft()/10)) as num|null - if(SSticker.current_state > GAME_STATE_PREGAME) - return alert("Too late... The game has already started!") - if(newtime) - SSticker.SetTimeLeft(newtime * 10) - if(newtime < 0) - to_chat(world, "The game start has been delayed.") - log_admin("[key_name(usr)] delayed the round start.") - else - to_chat(world, "The game will start in [newtime] seconds.") - world << 'sound/ai/attention.ogg' - log_admin("[key_name(usr)] set the pre-game delay to [newtime] seconds.") - SSblackbox.add_details("admin_verb","Delay Game Start") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/unprison(mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Unprison" - if (M.z == ZLEVEL_CENTCOM) - M.loc = pick(GLOB.latejoin) - message_admins("[key_name_admin(usr)] has unprisoned [key_name_admin(M)]") - log_admin("[key_name(usr)] has unprisoned [key_name(M)]") - else - alert("[M.name] is not prisoned.") - SSblackbox.add_details("admin_verb","Unprison") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -////////////////////////////////////////////////////////////////////////////////////////////////ADMIN HELPER PROCS - -/* -/datum/admins/proc/get_sab_desc(var/target) - switch(target) - if(1) - return "Destroy at least 70% of the plasma canisters on the station" - if(2) - return "Destroy the AI" - if(3) - var/count = 0 - for(var/mob/living/carbon/monkey/Monkey in world) - if(Monkey.z == 1) - count++ - return "Kill all [count] of the monkeys on the station" - if(4) - return "Cut power to at least 80% of the station" - else - return "Error: Invalid sabotage target: [target]" -*/ -/datum/admins/proc/spawn_atom(object as text) - set category = "Debug" - set desc = "(atom path) Spawn an atom" - set name = "Spawn" - - if(!check_rights(R_SPAWN)) - return - - var/chosen = pick_closest_path(object) - if(!chosen) - return - if(ispath(chosen,/turf)) - var/turf/T = get_turf(usr.loc) - T.ChangeTurf(chosen) - else - var/atom/A = new chosen(usr.loc) - A.admin_spawned = TRUE - - log_admin("[key_name(usr)] spawned [chosen] at ([usr.x],[usr.y],[usr.z])") - SSblackbox.add_details("admin_verb","Spawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/datum/admins/proc/show_traitor_panel(mob/M in GLOB.mob_list) - set category = "Admin" - set desc = "Edit mobs's memory and role" - set name = "Show Traitor Panel" - - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(!M.mind) - to_chat(usr, "This mob has no mind!") - return - - M.mind.edit_memory() - SSblackbox.add_details("admin_verb","Traitor Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/datum/admins/proc/toggletintedweldhelmets() - set category = "Debug" - set desc="Reduces view range when wearing welding helmets" - set name="Toggle tinted welding helmes" - GLOB.tinted_weldhelh = !( GLOB.tinted_weldhelh ) - if (GLOB.tinted_weldhelh) - to_chat(world, "The tinted_weldhelh has been enabled!") - else - to_chat(world, "The tinted_weldhelh has been disabled!") - log_admin("[key_name(usr)] toggled tinted_weldhelh.") - message_admins("[key_name_admin(usr)] toggled tinted_weldhelh.") - SSblackbox.add_details("admin_toggle","Toggle Tinted Welding Helmets|[GLOB.tinted_weldhelh]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleguests() - set category = "Server" - set desc="Guests can't enter" - set name="Toggle guests" - GLOB.guests_allowed = !( GLOB.guests_allowed ) - if (!( GLOB.guests_allowed )) - to_chat(world, "Guests may no longer enter the game.") - else - to_chat(world, "Guests may now enter the game.") - log_admin("[key_name(usr)] toggled guests game entering [GLOB.guests_allowed?"":"dis"]allowed.") - message_admins("[key_name_admin(usr)] toggled guests game entering [GLOB.guests_allowed?"":"dis"]allowed.") - SSblackbox.add_details("admin_toggle","Toggle Guests|[GLOB.guests_allowed]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/output_ai_laws() - var/ai_number = 0 - for(var/mob/living/silicon/S in GLOB.mob_list) - ai_number++ - if(isAI(S)) - to_chat(usr, "AI [key_name(S, usr)]'s laws:") - else if(iscyborg(S)) - var/mob/living/silicon/robot/R = S - to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [R.connected_ai])":"(Independant)"]: laws:") - else if (ispAI(S)) - to_chat(usr, "pAI [key_name(S, usr)]'s laws:") - else - to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:") - - if (S.laws == null) - to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.") - else - S.laws.show_laws(usr) - if(!ai_number) - to_chat(usr, "No AIs located" ) - -/datum/admins/proc/output_all_devil_info() - var/devil_number = 0 - for(var/D in SSticker.mode.devils) - devil_number++ - to_chat(usr, "Devil #[devil_number]:

" + SSticker.mode.printdevilinfo(D)) - if(!devil_number) - to_chat(usr, "No Devils located" ) - -/datum/admins/proc/output_devil_info(mob/living/M) - if(istype(M) && M.mind && M.mind.devilinfo) - to_chat(usr, SSticker.mode.printdevilinfo(M.mind)) - else - to_chat(usr, "[M] is not a devil.") - -/datum/admins/proc/manage_free_slots() - if(!check_rights()) - return - var/dat = "Manage Free Slots" - var/count = 0 - - if(SSticker && !SSticker.mode) - alert(usr, "You cannot manage jobs before the round starts!") - return - - if(SSjob) - for(var/datum/job/job in SSjob.occupations) - count++ - var/J_title = html_encode(job.title) - var/J_opPos = html_encode(job.total_positions - (job.total_positions - job.current_positions)) - var/J_totPos = html_encode(job.total_positions) - if(job.total_positions < 0) - dat += "[J_title]: [J_opPos] (unlimited)" - else - dat += "[J_title]: [J_opPos]/[J_totPos]" - - if(job.title == "AI" || job.title == "Cyborg") - dat += " (Cannot Late Join)
" - continue - if(job.total_positions >= 0) - dat += " Add | " - if(job.total_positions > job.current_positions) - dat += "Remove | " - else - dat += "Remove | " - dat += "Unlimit" - else - dat += " Limit" - dat += "
" - - dat += "" - var/winheight = 100 + (count * 20) - winheight = min(winheight, 690) - usr << browse(dat, "window=players;size=375x[winheight]") - -// -// -//ALL DONE -//********************************************************************************************************* -//TO-DO: -// -// - -//RIP ferry snowflakes - -//Kicks all the clients currently in the lobby. The second parameter (kick_only_afk) determins if an is_afk() check is ran, or if all clients are kicked -//defaults to kicking everyone (afk + non afk clients in the lobby) -//returns a list of ckeys of the kicked clients -/proc/kick_clients_in_lobby(message, kick_only_afk = 0) - var/list/kicked_client_names = list() - for(var/client/C in GLOB.clients) - if(isnewplayer(C.mob)) - if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk - continue - if(message) - to_chat(C, message) - kicked_client_names.Add("[C.ckey]") - qdel(C) - return kicked_client_names - -//returns 1 to let the dragdrop code know we are trapping this event -//returns 0 if we don't plan to trap the event -/datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, mob/living/tomob) - - //this is the exact two check rights checks required to edit a ckey with vv. - if (!check_rights(R_VAREDIT,0) || !check_rights(R_SPAWN|R_DEBUG,0)) - return 0 - - if (!frommob.ckey) - return 0 - - var/question = "" - if (tomob.ckey) - question = "This mob already has a user ([tomob.key]) in control of it! " - question += "Are you sure you want to place [frommob.name]([frommob.key]) in control of [tomob.name]?" - - var/ask = alert(question, "Place ghost in control of mob?", "Yes", "No") - if (ask != "Yes") - return 1 - - if (!frommob || !tomob) //make sure the mobs don't go away while we waited for a response - return 1 - - tomob.ghostize(0) - - message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].") - log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].") - SSblackbox.add_details("admin_verb","Ghost Drag Control") - - tomob.ckey = frommob.ckey - qdel(frommob) - - return 1 - -/client/proc/adminGreet(logout) - if(SSticker.HasRoundStarted()) - var/string - if(logout && config && config.announce_admin_logout) - string = pick( - "Admin logout: [key_name(src)]") - else if(!logout && config && config.announce_admin_login && (prefs.toggles & ANNOUNCE_LOGIN)) - string = pick( - "Admin login: [key_name(src)]") - if(string) - message_admins("[string]") + +//////////////////////////////// +/proc/message_admins(msg) + msg = "ADMIN LOG: [msg]" + to_chat(GLOB.admins, msg) + +/proc/relay_msg_admins(msg) + msg = "RELAY: [msg]" + to_chat(GLOB.admins, msg) + + +///////////////////////////////////////////////////////////////////////////////////////////////Panels + +/datum/admins/proc/show_player_panel(mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Show Player Panel" + set desc="Edit player (respawn, ban, heal, etc)" + + if(!check_rights()) + return + + if(!isobserver(usr)) + log_game("[key_name_admin(usr)] checked the player panel while in game.") + + if(!M) + to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.") + return + + var/body = "Options for [M.key]" + body += "Options panel for [M]" + if(M.client) + body += " played by [M.client] " + body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" + + if(isnewplayer(M)) + body += " Hasn't Entered Game " + else + body += " \[Heal\] " + + if(M.client) + body += "
\[First Seen: [M.client.player_join_date]\]\[Byond account registered on: [M.client.account_join_date]\]" + + + + body += "

\[ " + body += "VV - " + body += "TP - " + body += "PM - " + body += "SM - " + body += "FLW - " + body += "LOGS\]
" + + body += "Mob type = [M.type]

" + + body += "Kick | " + body += "Ban | " + body += "Jobban | " + body += "Identity Ban | " + if(jobban_isbanned(M, "OOC")) + body+= "OOCBan | " + else + body+= "OOCBan | " + if(jobban_isbanned(M, "emote")) + body+= "EmoteBan | " + else + body+= "Emoteban | " + + body += "Notes | Messages | Watchlist | " + if(M.client) + body += "| Prison | " + body += "\ Send back to Lobby | " + var/muted = M.client.prefs.muted + body += "
Mute: " + body += "\[IC | " + body += "OOC | " + body += "PRAY | " + body += "ADMINHELP | " + body += "DEADCHAT\]" + body += "(toggle all)" + + body += "

" + body += "Jump to | " + body += "Get | " + body += "Send To" + + body += "

" + body += "Traitor panel | " + body += "Narrate to | " + body += "Subtle message | " + body += "Language Menu" + + if (M.client) + if(!isnewplayer(M)) + body += "

" + body += "Transformation:" + body += "
" + + //Human + if(ishuman(M)) + body += "Human | " + else + body += "Humanize | " + + //Monkey + if(ismonkey(M)) + body += "Monkeyized | " + else + body += "Monkeyize | " + + //Corgi + if(iscorgi(M)) + body += "Corgized | " + else + body += "Corgize | " + + //AI / Cyborg + if(isAI(M)) + body += "Is an AI " + else if(ishuman(M)) + body += "Make AI | " + body += "Make Robot | " + body += "Make Alien | " + body += "Make Slime | " + body += "Make Blob | " + + //Simple Animals + if(isanimal(M)) + body += "Re-Animalize | " + else + body += "Animalize | " + + body += "

" + body += "Rudimentary transformation:
These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.

" + body += "Observer | " + body += "\[ Alien: Drone, " + body += "Hunter, " + body += "Sentinel, " + body += "Praetorian, " + body += "Queen, " + body += "Larva \] " + body += "Human " + body += "\[ slime: Baby, " + body += "Adult \] " + body += "Monkey | " + body += "Cyborg | " + body += "Cat | " + body += "Runtime | " + body += "Corgi | " + body += "Ian | " + body += "Crab | " + body += "Coffee | " + //body += "Parrot | " + //body += "Poly | " + body += "\[ Construct: Juggernaut , " + body += "Artificer , " + body += "Wraith \] " + body += "Shade" + body += "
" + + if (M.client) + body += "

" + body += "Other actions:" + body += "
" + body += "Forcesay | " + body += "Thunderdome 1 | " + body += "Thunderdome 2 | " + body += "Thunderdome Admin | " + body += "Thunderdome Observer | " + + body += "
" + body += "" + + usr << browse(body, "window=adminplayeropts-\ref[M];size=550x515") + SSblackbox.add_details("admin_verb","Player Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/datum/admins/proc/access_news_network() //MARKER + set category = "Fun" + set name = "Access Newscaster Network" + set desc = "Allows you to view, add and edit news feeds." + + if (!istype(src,/datum/admins)) + src = usr.client.holder + if (!istype(src,/datum/admins)) + to_chat(usr, "Error: you are not an admin!") + return + var/dat + dat = text("Admin Newscaster

Admin Newscaster Unit

") + + switch(admincaster_screen) + if(0) + dat += "Welcome to the admin newscaster.
Here you can add, edit and censor every newspiece on the network." + dat += "
Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units." + dat += "
Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things!" + if(GLOB.news_network.wanted_issue.active) + dat+= "
Read Wanted Issue" + dat+= "

Create Feed Channel" + dat+= "
View Feed Channels" + dat+= "
Submit new Feed story" + dat+= "

Exit" + var/wanted_already = 0 + if(GLOB.news_network.wanted_issue.active) + wanted_already = 1 + dat+="
Feed Security functions:
" + dat+="
[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue" + dat+="
Censor Feed Stories" + dat+="
Mark Feed Channel with Nanotrasen D-Notice (disables and locks the channel)." + dat+="

The newscaster recognises you as:
[src.admin_signature]
" + if(1) + dat+= "Station Feed Channels
" + if( isemptylist(GLOB.news_network.network_channels) ) + dat+="No active channels found..." + else + for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels) + if(CHANNEL.is_admin_channel) + dat+="[CHANNEL.channel_name]
" + else + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
" + dat+="

Refresh" + dat+="
Back" + if(2) + dat+="Creating new Feed Channel..." + dat+="
Channel Name: [src.admincaster_feed_channel.channel_name]
" + dat+="Channel Author: [src.admin_signature]
" + dat+="Will Accept Public Feeds: [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]

" + dat+="
Submit

Cancel
" + if(3) + dat+="Creating new Feed Message..." + dat+="
Receiving Channel: [src.admincaster_feed_channel.channel_name]
" //MARK + dat+="Message Author: [src.admin_signature]
" + dat+="Message Body: [src.admincaster_feed_message.returnBody(-1)]
" + dat+="
Submit

Cancel
" + if(4) + dat+="Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].

" + dat+="
Return
" + if(5) + dat+="Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.

" + dat+="
Return
" + if(6) + dat+="ERROR: Could not submit Feed story to Network.

" + if(src.admincaster_feed_channel.channel_name=="") + dat+="•Invalid receiving channel name.
" + if(src.admincaster_feed_message.returnBody(-1) == "" || src.admincaster_feed_message.returnBody(-1) == "\[REDACTED\]") + dat+="•Invalid message body.
" + dat+="
Return
" + if(7) + dat+="ERROR: Could not submit Feed Channel to Network.

" + if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]") + dat+="•Invalid channel name.
" + var/check = 0 + for(var/datum/newscaster/feed_channel/FC in GLOB.news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + check = 1 + break + if(check) + dat+="•Channel name already in use.
" + dat+="
Return
" + if(9) + dat+="[admincaster_feed_channel.channel_name]: \[created by: [admincaster_feed_channel.returnAuthor(-1)]\]
" + if(src.admincaster_feed_channel.censored) + dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
" + dat+="No further feed story additions are allowed while the D-Notice is in effect.

" + else + if( isemptylist(src.admincaster_feed_channel.messages) ) + dat+="No feed messages found in channel...
" + else + var/i = 0 + for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages) + i++ + dat+="-[MESSAGE.returnBody(-1)]
" + if(MESSAGE.img) + usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png") + dat+="

" + dat+="\[Story by [MESSAGE.returnAuthor(-1)]\]
" + dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]:
" + for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments) + dat+="[comment.body]
[comment.author] [comment.time_stamp]
" + dat+="
" + dat+="

Refresh" + dat+="
Back" + if(10) + dat+="Nanotrasen Feed Censorship Tool
" + dat+="NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.
" + dat+="Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.
" + dat+="
Select Feed channel to get Stories from:
" + if(isemptylist(GLOB.news_network.network_channels)) + dat+="No feed channels found active...
" + else + for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels) + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
" + dat+="
Cancel" + if(11) + dat+="Nanotrasen D-Notice Handler
" + dat+="A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's" + dat+="morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed" + dat+="stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.
" + if(isemptylist(GLOB.news_network.network_channels)) + dat+="No feed channels found active...
" + else + for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels) + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
" + + dat+="
Back" + if(12) + dat+="[src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.returnAuthor(-1)] \]
" + dat+="[(src.admincaster_feed_channel.authorCensor) ? ("Undo Author censorship") : ("Censor channel Author")]
" + + if( isemptylist(src.admincaster_feed_channel.messages) ) + dat+="No feed messages found in channel...
" + else + for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages) + dat+="-[MESSAGE.returnBody(-1)]
\[Story by [MESSAGE.returnAuthor(-1)]\]
" + dat+="[(MESSAGE.bodyCensor) ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.authorCensor) ? ("Undo Author Censorship") : ("Censor message Author")]
" + dat+="[MESSAGE.comments.len] comment[MESSAGE.comments.len > 1 ? "s" : ""]: [MESSAGE.locked ? "Unlock" : "Lock"]
" + for(var/datum/newscaster/feed_comment/comment in MESSAGE.comments) + dat+="[comment.body] X
[comment.author] [comment.time_stamp]
" + dat+="
Back" + if(13) + dat+="[src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.returnAuthor(-1)] \]
" + dat+="Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel.
" + if(src.admincaster_feed_channel.censored) + dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
" + dat+="No further feed story additions are allowed while the D-Notice is in effect.

" + else + if( isemptylist(src.admincaster_feed_channel.messages) ) + dat+="No feed messages found in channel...
" + else + for(var/datum/newscaster/feed_message/MESSAGE in src.admincaster_feed_channel.messages) + dat+="-[MESSAGE.returnBody(-1)]
\[Story by [MESSAGE.returnAuthor(-1)]\]
" + dat+="
Back" + if(14) + dat+="Wanted Issue Handler:" + var/wanted_already = 0 + var/end_param = 1 + if(GLOB.news_network.wanted_issue.active) + wanted_already = 1 + end_param = 2 + if(wanted_already) + dat+="
A wanted issue is already in Feed Circulation. You can edit or cancel it below.
" + dat+="
" + dat+="Criminal Name: [src.admincaster_wanted_message.criminal]
" + dat+="Description: [src.admincaster_wanted_message.body]
" + if(wanted_already) + dat+="Wanted Issue created by:[GLOB.news_network.wanted_issue.scannedUser]
" + else + dat+="Wanted Issue will be created under prosecutor:[src.admin_signature]
" + dat+="
[(wanted_already) ? ("Edit Issue") : ("Submit")]" + if(wanted_already) + dat+="
Take down Issue" + dat+="
Cancel" + if(15) + dat+="Wanted issue for [src.admincaster_wanted_message.criminal] is now in Network Circulation.

" + dat+="
Return
" + if(16) + dat+="ERROR: Wanted Issue rejected by Network.

" + if(src.admincaster_wanted_message.criminal =="" || src.admincaster_wanted_message.criminal == "\[REDACTED\]") + dat+="•Invalid name for person wanted.
" + if(src.admincaster_wanted_message.body == "" || src.admincaster_wanted_message.body == "\[REDACTED\]") + dat+="•Invalid description.
" + dat+="
Return
" + if(17) + dat+="Wanted Issue successfully deleted from Circulation
" + dat+="
Return
" + if(18) + dat+="-- STATIONWIDE WANTED ISSUE --
\[Submitted by: [GLOB.news_network.wanted_issue.scannedUser]\]
" + dat+="Criminal: [GLOB.news_network.wanted_issue.criminal]
" + dat+="Description: [GLOB.news_network.wanted_issue.body]
" + dat+="Photo:: " + if(GLOB.news_network.wanted_issue.img) + usr << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png") + dat+="
" + else + dat+="None" + dat+="
Back
" + if(19) + dat+="Wanted issue for [src.admincaster_wanted_message.criminal] successfully edited.

" + dat+="
Return
" + else + dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" + + //to_chat(world, "Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]") + //to_chat(world, "Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]") + usr << browse(dat, "window=admincaster_main;size=400x600") + onclose(usr, "admincaster_main") + + +/datum/admins/proc/Game() + if(!check_rights(0)) + return + + var/dat = {" +
Game Panel

\n + Change Game Mode
+ "} + if(GLOB.master_mode == "secret") + dat += "(Force Secret Mode)
" + + dat += {" +
+ Create Object
+ Quick Create Object
+ Create Turf
+ Create Mob
+ "} + + if(marked_datum && istype(marked_datum, /atom)) + dat += "Duplicate Marked Datum
" + + usr << browse(dat, "window=admin2;size=210x200") + return + +/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge +//i.e. buttons/verbs + + +/datum/admins/proc/restart() + set category = "Server" + set name = "Reboot World" + set desc="Restarts the world immediately" + if (!usr.client.holder) + return + var/confirm = alert("Restart the game world?", "Restart", "Yes", "Cancel") + if(confirm == "Cancel") + return + if(confirm == "Yes") + SSticker.delay_end = 0 + SSblackbox.add_details("admin_verb","Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + world.Reboot("Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key].", "end_error", "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10) + +/datum/admins/proc/end_round() + set category = "Server" + set name = "End Round" + set desc = "Attempts to produce a round end report and then restart the server organically." + + if (!usr.client.holder) + return + var/confirm = alert("End the round and restart the game world?", "End Round", "Yes", "Cancel") + if(confirm == "Cancel") + return + if(confirm == "Yes") + SSticker.force_ending = 1 + SSblackbox.add_details("admin_verb","End Round") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/datum/admins/proc/announce() + set category = "Special Verbs" + set name = "Announce" + set desc="Announce your desires to the world" + if(!check_rights(0)) + return + + var/message = input("Global message to send:", "Admin Announce", null, null) as message + if(message) + if(!check_rights(R_SERVER,0)) + message = adminscrub(message,500) + to_chat(world, "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:\n \t [message]") + log_admin("Announce: [key_name(usr)] : [message]") + SSblackbox.add_details("admin_verb","Announce") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/set_admin_notice() + set category = "Special Verbs" + set name = "Set Admin Notice" + set desc ="Set an announcement that appears to everyone who joins the server. Only lasts this round" + if(!check_rights(0)) + return + + var/new_admin_notice = input(src,"Set a public notice for this round. Everyone who joins the server will see it.\n(Leaving it blank will delete the current notice):","Set Notice",GLOB.admin_notice) as message|null + if(new_admin_notice == null) + return + if(new_admin_notice == GLOB.admin_notice) + return + if(new_admin_notice == "") + message_admins("[key_name(usr)] removed the admin notice.") + log_admin("[key_name(usr)] removed the admin notice:\n[GLOB.admin_notice]") + else + message_admins("[key_name(usr)] set the admin notice.") + log_admin("[key_name(usr)] set the admin notice:\n[new_admin_notice]") + to_chat(world, "Admin Notice:\n \t [new_admin_notice]") + SSblackbox.add_details("admin_verb","Set Admin Notice") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + GLOB.admin_notice = new_admin_notice + return + +/datum/admins/proc/toggleooc() + set category = "Server" + set desc="Toggle dis bitch" + set name="Toggle OOC" + toggle_ooc() + log_admin("[key_name(usr)] toggled OOC.") + message_admins("[key_name_admin(usr)] toggled OOC.") + SSblackbox.add_details("admin_toggle","Toggle OOC|[GLOB.ooc_allowed]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleoocdead() + set category = "Server" + set desc="Toggle dis bitch" + set name="Toggle Dead OOC" + GLOB.dooc_allowed = !( GLOB.dooc_allowed ) + + log_admin("[key_name(usr)] toggled OOC.") + message_admins("[key_name_admin(usr)] toggled Dead OOC.") + SSblackbox.add_details("admin_toggle","Toggle Dead OOC|[GLOB.dooc_allowed]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/startnow() + set category = "Server" + set desc="Start the round RIGHT NOW" + set name="Start Now" + if(SSticker.current_state == GAME_STATE_PREGAME || SSticker.current_state == GAME_STATE_STARTUP) + SSticker.start_immediately = TRUE + log_admin("[usr.key] has started the game.") + var/msg = "" + if(SSticker.current_state == GAME_STATE_STARTUP) + msg = " (The server is still setting up, but the round will be \ + started as soon as possible.)" + message_admins("\ + [usr.key] has started the game.[msg]") + SSblackbox.add_details("admin_verb","Start Now") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return 1 + else + to_chat(usr, "Error: Start Now: Game has already started.") + + return 0 + +/datum/admins/proc/toggleenter() + set category = "Server" + set desc="People can't enter" + set name="Toggle Entering" + GLOB.enter_allowed = !( GLOB.enter_allowed ) + if (!( GLOB.enter_allowed )) + to_chat(world, "New players may no longer enter the game.") + else + to_chat(world, "New players may now enter the game.") + log_admin("[key_name(usr)] toggled new player game entering.") + message_admins("[key_name_admin(usr)] toggled new player game entering.") + world.update_status() + SSblackbox.add_details("admin_toggle","Toggle Entering|[GLOB.enter_allowed]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleAI() + set category = "Server" + set desc="People can't be AI" + set name="Toggle AI" + config.allow_ai = !( config.allow_ai ) + if (!( config.allow_ai )) + to_chat(world, "The AI job is no longer chooseable.") + else + to_chat(world, "The AI job is chooseable now.") + log_admin("[key_name(usr)] toggled AI allowed.") + world.update_status() + SSblackbox.add_details("admin_toggle","Toggle AI|[config.allow_ai]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleaban() + set category = "Server" + set desc="Respawn basically" + set name="Toggle Respawn" + GLOB.abandon_allowed = !( GLOB.abandon_allowed ) + if (GLOB.abandon_allowed) + to_chat(world, "You may now respawn.") + else + to_chat(world, "You may no longer respawn :(") + message_admins("[key_name_admin(usr)] toggled respawn to [GLOB.abandon_allowed ? "On" : "Off"].") + log_admin("[key_name(usr)] toggled respawn to [GLOB.abandon_allowed ? "On" : "Off"].") + world.update_status() + SSblackbox.add_details("admin_toggle","Toggle Respawn|[GLOB.abandon_allowed]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/delay() + set category = "Server" + set desc="Delay the game start" + set name="Delay pre-game" + + var/newtime = input("Set a new time in seconds. Set -1 for indefinite delay.","Set Delay",round(SSticker.GetTimeLeft()/10)) as num|null + if(SSticker.current_state > GAME_STATE_PREGAME) + return alert("Too late... The game has already started!") + if(newtime) + SSticker.SetTimeLeft(newtime * 10) + if(newtime < 0) + to_chat(world, "The game start has been delayed.") + log_admin("[key_name(usr)] delayed the round start.") + else + to_chat(world, "The game will start in [newtime] seconds.") + world << 'sound/ai/attention.ogg' + log_admin("[key_name(usr)] set the pre-game delay to [newtime] seconds.") + SSblackbox.add_details("admin_verb","Delay Game Start") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/unprison(mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Unprison" + if (M.z == ZLEVEL_CENTCOM) + M.loc = pick(GLOB.latejoin) + message_admins("[key_name_admin(usr)] has unprisoned [key_name_admin(M)]") + log_admin("[key_name(usr)] has unprisoned [key_name(M)]") + else + alert("[M.name] is not prisoned.") + SSblackbox.add_details("admin_verb","Unprison") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +////////////////////////////////////////////////////////////////////////////////////////////////ADMIN HELPER PROCS + +/* +/datum/admins/proc/get_sab_desc(var/target) + switch(target) + if(1) + return "Destroy at least 70% of the plasma canisters on the station" + if(2) + return "Destroy the AI" + if(3) + var/count = 0 + for(var/mob/living/carbon/monkey/Monkey in world) + if(Monkey.z == 1) + count++ + return "Kill all [count] of the monkeys on the station" + if(4) + return "Cut power to at least 80% of the station" + else + return "Error: Invalid sabotage target: [target]" +*/ +/datum/admins/proc/spawn_atom(object as text) + set category = "Debug" + set desc = "(atom path) Spawn an atom" + set name = "Spawn" + + if(!check_rights(R_SPAWN)) + return + + var/chosen = pick_closest_path(object) + if(!chosen) + return + if(ispath(chosen,/turf)) + var/turf/T = get_turf(usr.loc) + T.ChangeTurf(chosen) + else + var/atom/A = new chosen(usr.loc) + A.admin_spawned = TRUE + + log_admin("[key_name(usr)] spawned [chosen] at ([usr.x],[usr.y],[usr.z])") + SSblackbox.add_details("admin_verb","Spawn Atom") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/datum/admins/proc/show_traitor_panel(mob/M in GLOB.mob_list) + set category = "Admin" + set desc = "Edit mobs's memory and role" + set name = "Show Traitor Panel" + + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(!M.mind) + to_chat(usr, "This mob has no mind!") + return + + M.mind.edit_memory() + SSblackbox.add_details("admin_verb","Traitor Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/datum/admins/proc/toggletintedweldhelmets() + set category = "Debug" + set desc="Reduces view range when wearing welding helmets" + set name="Toggle tinted welding helmes" + GLOB.tinted_weldhelh = !( GLOB.tinted_weldhelh ) + if (GLOB.tinted_weldhelh) + to_chat(world, "The tinted_weldhelh has been enabled!") + else + to_chat(world, "The tinted_weldhelh has been disabled!") + log_admin("[key_name(usr)] toggled tinted_weldhelh.") + message_admins("[key_name_admin(usr)] toggled tinted_weldhelh.") + SSblackbox.add_details("admin_toggle","Toggle Tinted Welding Helmets|[GLOB.tinted_weldhelh]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleguests() + set category = "Server" + set desc="Guests can't enter" + set name="Toggle guests" + GLOB.guests_allowed = !( GLOB.guests_allowed ) + if (!( GLOB.guests_allowed )) + to_chat(world, "Guests may no longer enter the game.") + else + to_chat(world, "Guests may now enter the game.") + log_admin("[key_name(usr)] toggled guests game entering [GLOB.guests_allowed?"":"dis"]allowed.") + message_admins("[key_name_admin(usr)] toggled guests game entering [GLOB.guests_allowed?"":"dis"]allowed.") + SSblackbox.add_details("admin_toggle","Toggle Guests|[GLOB.guests_allowed]") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/output_ai_laws() + var/ai_number = 0 + for(var/mob/living/silicon/S in GLOB.mob_list) + ai_number++ + if(isAI(S)) + to_chat(usr, "AI [key_name(S, usr)]'s laws:") + else if(iscyborg(S)) + var/mob/living/silicon/robot/R = S + to_chat(usr, "CYBORG [key_name(S, usr)] [R.connected_ai?"(Slaved to: [R.connected_ai])":"(Independant)"]: laws:") + else if (ispAI(S)) + to_chat(usr, "pAI [key_name(S, usr)]'s laws:") + else + to_chat(usr, "SOMETHING SILICON [key_name(S, usr)]'s laws:") + + if (S.laws == null) + to_chat(usr, "[key_name(S, usr)]'s laws are null?? Contact a coder.") + else + S.laws.show_laws(usr) + if(!ai_number) + to_chat(usr, "No AIs located" ) + +/datum/admins/proc/output_all_devil_info() + var/devil_number = 0 + for(var/D in SSticker.mode.devils) + devil_number++ + to_chat(usr, "Devil #[devil_number]:

" + SSticker.mode.printdevilinfo(D)) + if(!devil_number) + to_chat(usr, "No Devils located" ) + +/datum/admins/proc/output_devil_info(mob/living/M) + if(istype(M) && M.mind && M.mind.devilinfo) + to_chat(usr, SSticker.mode.printdevilinfo(M.mind)) + else + to_chat(usr, "[M] is not a devil.") + +/datum/admins/proc/manage_free_slots() + if(!check_rights()) + return + var/dat = "Manage Free Slots" + var/count = 0 + + if(SSticker && !SSticker.mode) + alert(usr, "You cannot manage jobs before the round starts!") + return + + if(SSjob) + for(var/datum/job/job in SSjob.occupations) + count++ + var/J_title = html_encode(job.title) + var/J_opPos = html_encode(job.total_positions - (job.total_positions - job.current_positions)) + var/J_totPos = html_encode(job.total_positions) + if(job.total_positions < 0) + dat += "[J_title]: [J_opPos] (unlimited)" + else + dat += "[J_title]: [J_opPos]/[J_totPos]" + + if(job.title == "AI" || job.title == "Cyborg") + dat += " (Cannot Late Join)
" + continue + if(job.total_positions >= 0) + dat += " Add | " + if(job.total_positions > job.current_positions) + dat += "Remove | " + else + dat += "Remove | " + dat += "Unlimit" + else + dat += " Limit" + dat += "
" + + dat += "" + var/winheight = 100 + (count * 20) + winheight = min(winheight, 690) + usr << browse(dat, "window=players;size=375x[winheight]") + +// +// +//ALL DONE +//********************************************************************************************************* +//TO-DO: +// +// + +//RIP ferry snowflakes + +//Kicks all the clients currently in the lobby. The second parameter (kick_only_afk) determins if an is_afk() check is ran, or if all clients are kicked +//defaults to kicking everyone (afk + non afk clients in the lobby) +//returns a list of ckeys of the kicked clients +/proc/kick_clients_in_lobby(message, kick_only_afk = 0) + var/list/kicked_client_names = list() + for(var/client/C in GLOB.clients) + if(isnewplayer(C.mob)) + if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk + continue + if(message) + to_chat(C, message) + kicked_client_names.Add("[C.ckey]") + qdel(C) + return kicked_client_names + +//returns 1 to let the dragdrop code know we are trapping this event +//returns 0 if we don't plan to trap the event +/datum/admins/proc/cmd_ghost_drag(mob/dead/observer/frommob, mob/living/tomob) + + //this is the exact two check rights checks required to edit a ckey with vv. + if (!check_rights(R_VAREDIT,0) || !check_rights(R_SPAWN|R_DEBUG,0)) + return 0 + + if (!frommob.ckey) + return 0 + + var/question = "" + if (tomob.ckey) + question = "This mob already has a user ([tomob.key]) in control of it! " + question += "Are you sure you want to place [frommob.name]([frommob.key]) in control of [tomob.name]?" + + var/ask = alert(question, "Place ghost in control of mob?", "Yes", "No") + if (ask != "Yes") + return 1 + + if (!frommob || !tomob) //make sure the mobs don't go away while we waited for a response + return 1 + + tomob.ghostize(0) + + message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].") + log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].") + SSblackbox.add_details("admin_verb","Ghost Drag Control") + + tomob.ckey = frommob.ckey + qdel(frommob) + + return 1 + +/client/proc/adminGreet(logout) + if(SSticker.HasRoundStarted()) + var/string + if(logout && config && config.announce_admin_logout) + string = pick( + "Admin logout: [key_name(src)]") + else if(!logout && config && config.announce_admin_login && (prefs.toggles & ANNOUNCE_LOGIN)) + string = pick( + "Admin login: [key_name(src)]") + if(string) + message_admins("[string]") diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index 7aca1ececb..f2656bd309 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -1,21 +1,21 @@ -atom/proc/investigate_log(message, subject) - if(!message || !subject) - return - var/F = file("[GLOB.log_directory]/[subject].html") - F << "[time_stamp()] \ref[src] ([x],[y],[z]) || [src] [message]
" - - -/client/proc/investigate_show( subject in list("hrefs","notes, memos, watchlist","singulo","wires","telesci", "gravity", "records", "cargo", "supermatter", "atmos", "experimentor", "botany") ) - set name = "Investigate" - set category = "Admin" - if(!holder) - return - switch(subject) - if("notes, memos, watchlist") - browse_messages() - else - var/F = file("[GLOB.log_directory]/[subject].html") - if(!fexists(F)) - to_chat(src, "No [subject] logfile was found.") - return +atom/proc/investigate_log(message, subject) + if(!message || !subject) + return + var/F = file("[GLOB.log_directory]/[subject].html") + F << "[time_stamp()] \ref[src] ([x],[y],[z]) || [src] [message]
" + + +/client/proc/investigate_show( subject in list("hrefs","notes, memos, watchlist","singulo","wires","telesci", "gravity", "records", "cargo", "supermatter", "atmos", "experimentor", "botany") ) + set name = "Investigate" + set category = "Admin" + if(!holder) + return + switch(subject) + if("notes, memos, watchlist") + browse_messages() + else + var/F = file("[GLOB.log_directory]/[subject].html") + if(!fexists(F)) + to_chat(src, "No [subject] logfile was found.") + return src << browse(F,"window=investigate[subject];size=800x300") \ No newline at end of file diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index c1fd6de4a5..d8c6427477 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -93,10 +93,10 @@ if(href_list["time"]) timing = text2num(href_list["time"]) if(timing && istype(holder, /obj/item/device/transfer_valve)) - var/timer_message = "[ADMIN_LOOKUPFLW(usr)] activated [src] attachment on [holder]." + var/timer_message = "[ADMIN_LOOKUPFLW(usr)] activated [src] attachment on [holder]." message_admins(timer_message) GLOB.bombers += timer_message - log_game("[key_name(usr)] activated [src] attachment on [holder]") + log_game("[key_name(usr)] activated [src] attachment on [holder]") update_icon() if(href_list["repeat"]) loop = text2num(href_list["repeat"]) diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index f5168db1c3..b0967bfe43 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -16,11 +16,11 @@ armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100, fire = 40, acid = 0) -/obj/machinery/meter/Initialize(mapload) - . = ..() +/obj/machinery/meter/Initialize(mapload) + . = ..() SSair.atmos_machinery += src - if (mapload && !target) - target = locate(/obj/machinery/atmospherics/pipe) in loc + if (mapload && !target) + target = locate(/obj/machinery/atmospherics/pipe) in loc /obj/machinery/meter/Destroy() SSair.atmos_machinery -= src diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm index 63082d1988..16c786c044 100644 --- a/code/modules/atmospherics/machinery/portable/pump.dm +++ b/code/modules/atmospherics/machinery/portable/pump.dm @@ -103,9 +103,9 @@ var/plasma = air_contents.gases["plasma"] var/n2o = air_contents.gases["n2o"] if(n2o || plasma) - var/area/A = get_area(src) - message_admins("[ADMIN_LOOKUPFLW(usr)] turned on a pump that contains [n2o ? "N2O" : ""][n2o && plasma ? " & " : ""][plasma ? "Plasma" : ""] at [A][ADMIN_JMP(src)]") - log_admin("[key_name(usr)] turned on a pump that contains [n2o ? "N2O" : ""][n2o && plasma ? " & " : ""][plasma ? "Plasma" : ""] at [A][COORD(src)]") + var/area/A = get_area(src) + message_admins("[ADMIN_LOOKUPFLW(usr)] turned on a pump that contains [n2o ? "N2O" : ""][n2o && plasma ? " & " : ""][plasma ? "Plasma" : ""] at [A][ADMIN_JMP(src)]") + log_admin("[key_name(usr)] turned on a pump that contains [n2o ? "N2O" : ""][n2o && plasma ? " & " : ""][plasma ? "Plasma" : ""] at [A][COORD(src)]") . = TRUE if("direction") if(direction == PUMP_OUT) diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index c46399fa29..1c8beb2e6d 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -227,7 +227,7 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation) if(check_exile_implant(C)) say("Rejecting [AM]: Exile implant detected in contained lifeform.") return - if(AM.has_buckled_mobs()) + if(AM.has_buckled_mobs()) for(var/mob/living/carbon/C in AM.buckled_mobs) if(check_exile_implant(C)) say("Rejecting [AM]: Exile implant detected in close proximity lifeform.") diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm index 7a665b40f3..1608623080 100644 --- a/code/modules/awaymissions/mission_code/Academy.dm +++ b/code/modules/awaymissions/mission_code/Academy.dm @@ -64,18 +64,18 @@ next_check = world.time + cooldown /obj/structure/academy_wizard_spawner/proc/give_control() - set waitfor = FALSE - + set waitfor = FALSE + if(!current_wizard) return - var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", "wizard", null, be_special_flag = ROLE_WIZARD, M = current_wizard) - var/mob/dead/observer/chosen = null - - if(candidates.len) - chosen = pick(candidates) - message_admins("[key_name_admin(chosen)] was spawned as Wizard Academy Defender") - current_wizard.ghostize() // on the off chance braindead defender gets back in - current_wizard.key = chosen.key + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as Wizard Academy Defender?", "wizard", null, be_special_flag = ROLE_WIZARD, M = current_wizard) + var/mob/dead/observer/chosen = null + + if(candidates.len) + chosen = pick(candidates) + message_admins("[key_name_admin(chosen)] was spawned as Wizard Academy Defender") + current_wizard.ghostize() // on the off chance braindead defender gets back in + current_wizard.key = chosen.key /obj/structure/academy_wizard_spawner/proc/summon_wizard() var/turf/T = src.loc diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 8d5bbde4b3..0aeb922d27 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -1,1643 +1,1643 @@ - - -GLOBAL_LIST_EMPTY(preferences_datums) - - - -/datum/preferences - var/client/parent - //doohickeys for savefiles - var/path - var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used - var/max_save_slots = 10 - - //non-preference stuff - var/muted = 0 - var/last_ip - var/last_id - - //game-preferences - var/lastchangelog = "" //Saved changlog filesize to detect if there was a change - var/ooccolor = null - - //Antag preferences - var/list/be_special = list() //Special role selection - var/tmp/old_be_special = 0 //Bitflag version of be_special, used to update old savefiles and nothing more - //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were, - //autocorrected this round, not that you'd need to check that. - - - var/UI_style = "Midnight" - var/hotkeys = FALSE - var/tgui_fancy = TRUE - var/tgui_lock = TRUE - var/windowflashing = TRUE - var/toggles = TOGGLES_DEFAULT - var/chat_toggles = TOGGLES_DEFAULT_CHAT - var/ghost_form = "ghost" - var/ghost_orbit = GHOST_ORBIT_CIRCLE - var/ghost_accs = GHOST_ACCS_DEFAULT_OPTION - var/ghost_others = GHOST_OTHERS_DEFAULT_OPTION - var/ghost_hud = 1 - var/inquisitive_ghost = 1 - var/allow_midround_antag = 1 - var/preferred_map = null - - var/uses_glasses_colour = 0 - - //character preferences - var/real_name //our character's name - var/be_random_name = 0 //whether we'll have a random name every round - var/be_random_body = 0 //whether we'll have a random body every round - var/gender = MALE //gender of character (well duh) - var/age = 30 //age of character - var/underwear = "Nude" //underwear type - var/undershirt = "Nude" //undershirt type - var/socks = "Nude" //socks type - var/backbag = DBACKPACK //backpack type - var/hair_style = "Bald" //Hair type - var/hair_color = "000" //Hair color - var/facial_hair_style = "Shaved" //Face hair type - var/facial_hair_color = "000" //Facial hair color - var/skin_tone = "caucasian1" //Skin color - var/eye_color = "000" //Eye color - var/datum/species/pref_species = new /datum/species/human() //Mutant race - var/list/features = list("mcolor" = "FFF", - "mcolor2" = "FFF", - "mcolor3" = "FFF", - "tail_lizard" = "Smooth", - "tail_human" = "None", - "snout" = "Round", - "horns" = "None", - "ears" = "None", - "wings" = "None", - "frills" = "None", - "spines" = "None", - "body_markings" = "None", - "mam_body_markings" = "None", - "mam_ears" = "None", - "mam_tail" = "None", - "mam_tail_animated" = "None", - "xenodorsal" = "None", - "xenohead" = "None", - "xenotail" = "None", - "legs" = "Normal Legs", - "taur" = "None", - "exhibitionist" = FALSE, - "genitals_use_skintone" = FALSE, - "has_cock" = FALSE, - "cock_shape" = "Human", - "cock_length" = 6, - "cock_girth_ratio" = COCK_GIRTH_RATIO_DEF, - "cock_color" = "fff", - "has_sheath" = FALSE, - "sheath_color" = "fff", - "has_balls" = FALSE, - "balls_internal" = FALSE, - "balls_color" = "fff", - "balls_amount" = 2, - "balls_sack_size" = BALLS_SACK_SIZE_DEF, - "balls_size" = BALLS_SIZE_DEF, - "balls_cum_rate" = CUM_RATE, - "balls_cum_mult" = CUM_RATE_MULT, - "balls_efficiency" = CUM_EFFICIENCY, - "balls_fluid" = "semen", - "has_ovi" = FALSE, - "ovi_shape" = "knotted", - "ovi_length" = 6, - "ovi_color" = "fff", - "has_eggsack" = FALSE, - "eggsack_internal" = TRUE, - "eggsack_color" = "fff", - "eggsack_size" = BALLS_SACK_SIZE_DEF, - "eggsack_egg_color" = "fff", - "eggsack_egg_size" = EGG_GIRTH_DEF, - "has_breasts" = FALSE, - "breasts_color" = "fff", - "breasts_size" = "C", - "breasts_shape" = "Pair", - "breasts_fluid" = "milk", - "has_vag" = FALSE, - "vag_shape" = "Human", - "vag_color" = "fff", - "vag_clits" = 1, - "vag_clit_diam" = 0.25, - "vag_clit_len" = 0.25, - "has_womb" = FALSE, - "womb_cum_rate" = CUM_RATE, - "womb_cum_mult" = CUM_RATE_MULT, - "womb_efficiency" = CUM_EFFICIENCY, - "womb_fluid" = "femcum" - )//MAKE SURE TO UPDATE THE LIST IN MOBS.DM IF YOU'RE GOING TO ADD TO THIS LIST, OTHERWISE THINGS MIGHT GET FUCKEY - - var/list/custom_names = list("clown", "mime", "ai", "cyborg", "religion", "deity") - var/prefered_security_department = SEC_DEPT_RANDOM - - //Mob preview - var/icon/preview_icon = null - - //Jobs, uses bitflags - var/job_civilian_high = 0 - var/job_civilian_med = 0 - var/job_civilian_low = 0 - - var/job_medsci_high = 0 - var/job_medsci_med = 0 - var/job_medsci_low = 0 - - var/job_engsec_high = 0 - var/job_engsec_med = 0 - var/job_engsec_low = 0 - - // Want randomjob if preferences already filled - Donkie - var/joblessrole = BERANDOMJOB //defaults to 1 for fewer assistants - - // 0 = character settings, 1 = game preferences, 2 = character appearance - var/current_tab = 0 - - // OOC Metadata: - var/metadata = "" - - var/unlock_content = 0 - - var/list/ignoring = list() - - var/clientfps = 0 - - var/parallax - - var/uplink_spawn_loc = UPLINK_PDA - - var/list/menuoptions - - //citadel code - var/arousable = TRUE //Allows players to disable arousal from the character creation menu - var/flavor_text = "" - -/datum/preferences/New(client/C) - parent = C - custom_names["ai"] = pick(GLOB.ai_names) - custom_names["cyborg"] = pick(GLOB.ai_names) - custom_names["clown"] = pick(GLOB.clown_names) - custom_names["mime"] = pick(GLOB.mime_names) - if(istype(C)) - if(!IsGuestKey(C.key)) - load_path(C.ckey) - unlock_content = C.IsByondMember() - if(unlock_content) - max_save_slots = 16 - var/loaded_preferences_successfully = load_preferences() - if(loaded_preferences_successfully) - if(load_character()) - return - //we couldn't load character data so just randomize the character appearance + name - random_character() //let's create a random character then - rather than a fat, bald and naked man. - real_name = pref_species.random_name(gender,1) - if(!loaded_preferences_successfully) - save_preferences() - save_character() //let's save this new random character so it doesn't keep generating new ones. - menuoptions = list() - return - -/datum/preferences/proc/ShowChoices(mob/user) - if(!user || !user.client) - return - if(current_tab == 2) - update_preview_icon(nude=TRUE) - else - update_preview_icon(nude=FALSE) - user << browse_rsc(preview_icon, "previewicon.png") - var/dat = "
" - - dat += "Character Settings" - dat += "Character Appearance" - dat += "Game Preferences" - - if(!path) - dat += "
Please create an account to save your preferences
" - - dat += "
" - - dat += "
" - - switch(current_tab) - if (0) // Character Settings# - if(path) - var/savefile/S = new /savefile(path) - if(S) - dat += "
" - var/name - for(var/i=1, i<=max_save_slots, i++) - S.cd = "/character[i]" - S["real_name"] >> name - if(!name) - name = "Character[i]" - //if(i!=1) dat += " | " - dat += "[name] " - dat += "
" - - dat += "

Occupation Choices

" - dat += "Set Occupation Preferences
" - dat += "

Identity

" - dat += "" - - dat += "
" - if(jobban_isbanned(user, "appearance")) - dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
" - dat += "Random Name " - dat += "Always Random Name: [be_random_name ? "Yes" : "No"]
" - - dat += "Name: " - dat += "[real_name]
" - - dat += "Gender: [gender == MALE ? "Male" : "Female"]
" - dat += "Age: [age]
" - dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" - dat += "Exhibitionist:[features["exhibitionist"] == TRUE ? "Yes" : "No"]
" - dat += "Special Names:
" - dat += "Clown: [custom_names["clown"]] " - dat += "Mime:[custom_names["mime"]]
" - dat += "AI: [custom_names["ai"]] " - dat += "Cyborg: [custom_names["cyborg"]]
" - dat += "Chaplain religion: [custom_names["religion"]] " - dat += "Chaplain deity: [custom_names["deity"]]
" - - dat += "Custom job preferences:
" - dat += "Prefered security department: [prefered_security_department]
" - - dat += "
" - - dat += "
" -// dat += "Size: [character_size]
" - dat += "
" - - if (1) // Game Preferences - dat += "
" - dat += "

General Settings

" - dat += "UI Style: [UI_style]
" - dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Default"]
" - dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
" - dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"]
" - dat += "Window Flashing: [(windowflashing) ? "Yes" : "No"]
" - dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
" - dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
" - dat += "Ghost ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
" - dat += "Ghost sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
" - dat += "Ghost whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"]
" - dat += "Ghost radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "Yes" : "No"]
" - dat += "Ghost pda: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"]
" - dat += "Pull requests: [(chat_toggles & CHAT_PULLR) ? "Yes" : "No"]
" - dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Yes" : "No"]
" - if(config.allow_Metadata) - dat += "OOC Notes: Edit
" - - if(user.client) - if(user.client.holder) - dat += "Adminhelp Sound: [(toggles & SOUND_ADMINHELP)?"On":"Off"]
" - dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"On":"Off"]
" - - if(unlock_content || check_rights_for(user.client, R_ADMIN)) - dat += "OOC:     Change
" - - if(unlock_content) - dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" - dat += "Ghost Form: [ghost_form]
" - dat += "Ghost Orbit: [ghost_orbit]
" - - var/button_name = "If you see this something went wrong." - switch(ghost_accs) - if(GHOST_ACCS_FULL) - button_name = GHOST_ACCS_FULL_NAME - if(GHOST_ACCS_DIR) - button_name = GHOST_ACCS_DIR_NAME - if(GHOST_ACCS_NONE) - button_name = GHOST_ACCS_NONE_NAME - - dat += "Ghost Accessories: [button_name]
" - - switch(ghost_others) - if(GHOST_OTHERS_THEIR_SETTING) - button_name = GHOST_OTHERS_THEIR_SETTING_NAME - if(GHOST_OTHERS_DEFAULT_SPRITE) - button_name = GHOST_OTHERS_DEFAULT_SPRITE_NAME - if(GHOST_OTHERS_SIMPLE) - button_name = GHOST_OTHERS_SIMPLE_NAME - - dat += "Ghosts of Others: [button_name]
" - - if (config.maprotation) - var/p_map = preferred_map - if (!p_map) - p_map = "Default" - if (config.defaultmap) - p_map += " ([config.defaultmap.map_name])" - else - if (p_map in config.maplist) - var/datum/map_config/VM = config.maplist[p_map] - if (!VM) - p_map += " (No longer exists)" - else - p_map = VM.map_name - else - p_map += " (No longer exists)" - if(config.allow_map_voting) - dat += "Preferred Map: [p_map]
" - - dat += "FPS: [clientfps]
" - - dat += "Parallax (Fancy Space): " - switch (parallax) - if (PARALLAX_LOW) - dat += "Low" - if (PARALLAX_MED) - dat += "Medium" - if (PARALLAX_INSANE) - dat += "Insane" - if (PARALLAX_DISABLE) - dat += "Disabled" - else - dat += "High" - dat += "
" - - dat += "
" - - dat += "

Special Role Settings

" - - if(jobban_isbanned(user, "Syndicate")) - dat += "You are banned from antagonist roles." - src.be_special = list() - - - for (var/i in GLOB.special_roles) - if(jobban_isbanned(user, i)) - dat += "Be [capitalize(i)]: BANNED
" - else - var/days_remaining = null - if(config.use_age_restriction_for_jobs && ispath(GLOB.special_roles[i])) //If it's a game mode antag, check if the player meets the minimum age - var/mode_path = GLOB.special_roles[i] - var/datum/game_mode/temp_mode = new mode_path - days_remaining = temp_mode.get_remaining_days(user.client) - - if(days_remaining) - dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
" - else - dat += "Be [capitalize(i)]: [(i in be_special) ? "Yes" : "No"]
" - - dat += "
" - - //Character Appearance - if(2) - dat += "" - */ - - - dat += "
" - dat += "

" - dat += "Set Flavor Text
" - if(lentext(flavor_text) <= 40) - if(!lentext(flavor_text)) - dat += "\[...\]" - else - dat += "[flavor_text]" - else - dat += "[TextPreview(flavor_text)]...
" - if(config.mutant_races)//really don't need this check, but fuck un-tabbing all those lines - dat += "

Body

" - dat += "Gender: [gender == MALE ? "Male" : "Female"]
" - dat += "Species:[pref_species.id]
" - dat += "Random Body
" - dat += "Always Random Body: [be_random_body ? "Yes" : "No"]
" - if((MUTCOLORS in pref_species.species_traits) || (MUTCOLORS_PARTSONLY in pref_species.species_traits)) - dat += "Primary Color:     Change
" - dat += "Secondary Color:     Change
" - dat += "Tertiary Color:     Change
" - if(pref_species.use_skintones) - dat += "Skin Tone: [skin_tone]
" - dat += "Genitals Use Skintone:[features["genitals_use_skintone"] == TRUE ? "Enabled" : "Disabled"]
" - - if(HAIR in pref_species.species_traits) - dat += "Hair Style: [hair_style]
" - dat += "Hair Color:     Change
" - dat += "Facial Hair Style: [facial_hair_style]
" - dat += "Facial Hair Color:     Change
" - if(EYECOLOR in pref_species.species_traits) - dat += "Eye Color:     Change
" - if("tail_lizard" in pref_species.mutant_bodyparts) - dat += "Tail: [features["tail_lizard"]]
" - else if("mam_tail" in pref_species.mutant_bodyparts) - dat += "Tail: [features["mam_tail"]]
" - else if("tail_human" in pref_species.mutant_bodyparts) - dat += "Tail: [features["tail_human"]]
" - if("snout" in pref_species.mutant_bodyparts) - dat += "Snout: [features["snout"]]
" - if("horns" in pref_species.mutant_bodyparts) - dat += "Snout: [features["horns"]]
" - if("frills" in pref_species.mutant_bodyparts) - dat += "Frills: [features["frills"]]
" - if("spines" in pref_species.mutant_bodyparts) - dat += "Spines: [features["spines"]]
" - if("body_markings" in pref_species.mutant_bodyparts) - dat += "Body Markings: [features["body_markings"]]
" - else if("mam_body_markings" in pref_species.mutant_bodyparts) - dat += "Body Markings: [features["mam_body_markings"]]
" - if("mam_ears" in pref_species.mutant_bodyparts) - dat += "Ears: [features["mam_ears"]]
" - else if("ears" in pref_species.mutant_bodyparts) - dat += "Ears: [features["ears"]]
" - if("legs" in pref_species.mutant_bodyparts) - dat += "Legs: [features["legs"]]
" - if("taur" in pref_species.mutant_bodyparts) - dat += "Taur: [features["taur"]]
" - if("wings" in pref_species.mutant_bodyparts && GLOB.r_wings_list.len >1) - dat += "Wings: [features["wings"]]
" - if("xenohead" in pref_species.mutant_bodyparts) - dat += "Caste: [features["xenohead"]]
" - if("xenotail" in pref_species.mutant_bodyparts) - dat += "Tail: [features["xenotail"]]
" - if("xenodorsal" in pref_species.mutant_bodyparts) - dat += "Dorsal Tubes: [features["xenodorsal"]]
" - - dat += "
" - - - dat += "

Clothing & Equipment

" -//underwear will be refactored later so it fits in with other wearable equipment and isn't just an overlay -// dat += "Underwear:[underwear]
" -// dat += "Undershirt:[undershirt]
" -// dat += "Socks:[socks]
" - dat += "Backpack:[backbag]
" - dat += "Uplink Location:[uplink_spawn_loc]
" - - dat += "

Genitals

" - if(NOGENITALS in pref_species.species_traits) - dat += "Your species ([pref_species.name]) does not support genitals!
" - else - dat += "Has Penis:[features["has_cock"] == TRUE ? "Yes" : "No"]
" - if(features["has_cock"] == TRUE) - if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) - dat += "Penis Color:   (Skin tone overriding)
" - else - dat += "Penis Color:    Change
" -// dat += "
" - dat += "Penis Shape: [features["cock_shape"]]
" - dat += "Penis Length: [features["cock_length"]] inch(es)
" - dat += "Has Testicles:[features["has_balls"] == TRUE ? "Yes" : "No"]
" - if(features["has_balls"] == TRUE) - if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) - dat += "Testicles Color:   (Skin tone overriding)
" - else - dat += "Testicles Color:    Change
" - dat += "Has Vagina:[features["has_vag"] == TRUE ? "Yes" : "No"]
" - if(features["has_vag"]) - dat += "Vagina Type: [features["vag_shape"]]
" - if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) - dat += "Vagina Color:   (Skin tone overriding)
" - else - dat += "Vagina Color:    Change
" - dat += "Has Womb:[features["has_womb"] == TRUE ? "Yes" : "No"]
" - dat += "Has Breasts:[features["has_breasts"] == TRUE ? "Yes" : "No"]
" - if(features["has_breasts"]) - if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) - dat += "Color:   (Skin tone overriding)
" - else - dat += "Color:    Change
" - dat += "Cup Size:[features["breasts_size"]]
" - dat += "Breast Shape:[features["breasts_shape"]]
" - /* - dat += "

Ovipositor

" - dat += "Has Ovipositor:[features["has_ovi"] == TRUE ? "Yes" : "No"]" - if(features["has_ovi"]) - dat += "Ovi Color:    Change" - dat += "

Eggsack

" - dat += "Has Eggsack:[features["has_eggsack"] == TRUE ? "Yes" : "No"]
" - if(features["has_eggsack"] == TRUE) - dat += "Color:    Change" - dat += "Egg Color:    Change" - dat += "Egg Size:[features["eggsack_egg_size"]]\" Diameter" - - dat += "
" - dat += "
" - - if(!IsGuestKey(user.key)) - dat += "Undo " - dat += "Save Setup " - - dat += "Reset Setup" - dat += "
" - - var/datum/browser/popup = new(user, "preferences", "
Character Setup
", 640, 770) - popup.set_content(dat) - popup.open(0) - -/datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Chief Engineer"), widthPerColumn = 295, height = 620) - if(!SSjob) - return - - //limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice. - //splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice. - //widthPerColumn - Screen's width for every column. - //height - Screen's height. - - var/width = widthPerColumn - - var/HTML = "
" - if(SSjob.occupations.len <= 0) - HTML += "The job ticker is not yet finished creating jobs, please try again later" - HTML += "
Done

" // Easier to press up here. - - else - HTML += "Choose occupation chances
" - HTML += "
Left-click to raise an occupation preference, right-click to lower it.
" - HTML += "
Done

" // Easier to press up here. - HTML += "" - HTML += "
" // Table within a table for alignment, also allows you to easily add more colomns. - HTML += "" - var/index = -1 - - //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. - var/datum/job/lastJob - - for(var/datum/job/job in SSjob.occupations) - - index += 1 - if((index >= limit) || (job.title in splitJobs)) - width += widthPerColumn - if((index < limit) && (lastJob != null)) - //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with - //the last job's selection color. Creating a rather nice effect. - for(var/i = 0, i < (limit - index), i += 1) - HTML += "" - HTML += "
  
" - index = 0 - - HTML += "" - continue - if(!job.player_old_enough(user.client)) - var/available_in_days = job.available_in_days(user.client) - HTML += "[rank]" - continue - if((job_civilian_low & ASSISTANT) && (rank != "Assistant") && !jobban_isbanned(user, "Assistant")) - HTML += "[rank]" - continue - if(config.enforce_human_authority && !user.client.prefs.pref_species.qualifies_for_rank(rank, user.client.prefs.features)) - if(user.client.prefs.pref_species.id == "human") - HTML += "[rank]" - else - HTML += "[rank]" - continue - if((rank in GLOB.command_positions) || (rank == "AI"))//Bold head jobs - HTML += "[rank]" - else - HTML += "[rank]" - - HTML += "" - continue - - HTML += "[prefLevelLabel]" - HTML += "" - - for(var/i = 1, i < (limit - index), i += 1) // Finish the column so it is even - HTML += "" - - HTML += "
" - var/rank = job.title - lastJob = job - if(jobban_isbanned(user, rank)) - HTML += "[rank] BANNED
\[IN [(available_in_days)] DAYS\]
\[MUTANT\]
\[NON-HUMAN\]
" - - var/prefLevelLabel = "ERROR" - var/prefLevelColor = "pink" - var/prefUpperLevel = -1 // level to assign on left click - var/prefLowerLevel = -1 // level to assign on right click - - if(GetJobDepartment(job, 1) & job.flag) - prefLevelLabel = "High" - prefLevelColor = "slateblue" - prefUpperLevel = 4 - prefLowerLevel = 2 - else if(GetJobDepartment(job, 2) & job.flag) - prefLevelLabel = "Medium" - prefLevelColor = "green" - prefUpperLevel = 1 - prefLowerLevel = 3 - else if(GetJobDepartment(job, 3) & job.flag) - prefLevelLabel = "Low" - prefLevelColor = "orange" - prefUpperLevel = 2 - prefLowerLevel = 4 - else - prefLevelLabel = "NEVER" - prefLevelColor = "red" - prefUpperLevel = 3 - prefLowerLevel = 1 - - - HTML += "" - - if(rank == "Assistant")//Assistant is special - if(job_civilian_low & ASSISTANT) - HTML += "Yes" - else - HTML += "No" - HTML += "
  
" - HTML += "
" - - var/message = "Be an Assistant if preferences unavailable" - if(joblessrole == BERANDOMJOB) - message = "Get random job if preferences unavailable" - else if(joblessrole == RETURNTOLOBBY) - message = "Return to lobby if preferences unavailable" - HTML += "

[message]
" - HTML += "
Reset Preferences
" - - user << browse(null, "window=preferences") - var/datum/browser/popup = new(user, "mob_occupation", "
Occupation Preferences
", width, height) - popup.set_window_options("can_close=0") - popup.set_content(HTML) - popup.open(0) - return - -/datum/preferences/proc/SetJobPreferenceLevel(datum/job/job, level) - if (!job) - return 0 - - if (level == 1) // to high - // remove any other job(s) set to high - job_civilian_med |= job_civilian_high - job_engsec_med |= job_engsec_high - job_medsci_med |= job_medsci_high - job_civilian_high = 0 - job_engsec_high = 0 - job_medsci_high = 0 - - if (job.department_flag == CIVILIAN) - job_civilian_low &= ~job.flag - job_civilian_med &= ~job.flag - job_civilian_high &= ~job.flag - - switch(level) - if (1) - job_civilian_high |= job.flag - if (2) - job_civilian_med |= job.flag - if (3) - job_civilian_low |= job.flag - - return 1 - else if (job.department_flag == ENGSEC) - job_engsec_low &= ~job.flag - job_engsec_med &= ~job.flag - job_engsec_high &= ~job.flag - - switch(level) - if (1) - job_engsec_high |= job.flag - if (2) - job_engsec_med |= job.flag - if (3) - job_engsec_low |= job.flag - - return 1 - else if (job.department_flag == MEDSCI) - job_medsci_low &= ~job.flag - job_medsci_med &= ~job.flag - job_medsci_high &= ~job.flag - - switch(level) - if (1) - job_medsci_high |= job.flag - if (2) - job_medsci_med |= job.flag - if (3) - job_medsci_low |= job.flag - - return 1 - - return 0 - -/datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl) - if(!SSjob || SSjob.occupations.len <= 0) - return - var/datum/job/job = SSjob.GetJob(role) - - if(!job) - user << browse(null, "window=mob_occupation") - ShowChoices(user) - return - - if (!isnum(desiredLvl)) - to_chat(user, "UpdateJobPreference - desired level was not a number. Please notify coders!") - ShowChoices(user) - return - - if(role == "Assistant") - if(job_civilian_low & job.flag) - job_civilian_low &= ~job.flag - else - job_civilian_low |= job.flag - SetChoices(user) - return 1 - - SetJobPreferenceLevel(job, desiredLvl) - SetChoices(user) - - return 1 - - -/datum/preferences/proc/ResetJobs() - - job_civilian_high = 0 - job_civilian_med = 0 - job_civilian_low = 0 - - job_medsci_high = 0 - job_medsci_med = 0 - job_medsci_low = 0 - - job_engsec_high = 0 - job_engsec_med = 0 - job_engsec_low = 0 - - -/datum/preferences/proc/GetJobDepartment(datum/job/job, level) - if(!job || !level) - return 0 - switch(job.department_flag) - if(CIVILIAN) - switch(level) - if(1) - return job_civilian_high - if(2) - return job_civilian_med - if(3) - return job_civilian_low - if(MEDSCI) - switch(level) - if(1) - return job_medsci_high - if(2) - return job_medsci_med - if(3) - return job_medsci_low - if(ENGSEC) - switch(level) - if(1) - return job_engsec_high - if(2) - return job_engsec_med - if(3) - return job_engsec_low - return 0 - -/datum/preferences/proc/process_link(mob/user, list/href_list) - if(href_list["jobbancheck"]) - var/job = sanitizeSQL(href_list["jobbancheck"]) - var/sql_ckey = sanitizeSQL(user.ckey) - var/datum/DBQuery/query_get_jobban = SSdbcore.NewQuery("SELECT reason, bantime, duration, expiration_time, a_ckey FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[job]'") - if(!query_get_jobban.warn_execute()) - return - if(query_get_jobban.NextRow()) - var/reason = query_get_jobban.item[1] - var/bantime = query_get_jobban.item[2] - var/duration = query_get_jobban.item[3] - var/expiration_time = query_get_jobban.item[4] - var/a_ckey = query_get_jobban.item[5] - var/text - text = "You, or another user of this computer, ([user.ckey]) is banned from playing [job]. The ban reason is:
[reason]
This ban was applied by [a_ckey] on [bantime]" - if(text2num(duration) > 0) - text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)" - text += ".
" - to_chat(user, text) - return - - if(href_list["preference"] == "job") - switch(href_list["task"]) - if("close") - user << browse(null, "window=mob_occupation") - ShowChoices(user) - if("reset") - ResetJobs() - SetChoices(user) - if("random") - switch(joblessrole) - if(RETURNTOLOBBY) - if(jobban_isbanned(user, "Assistant")) - joblessrole = BERANDOMJOB - else - joblessrole = BEASSISTANT - if(BEASSISTANT) - joblessrole = BERANDOMJOB - if(BERANDOMJOB) - joblessrole = RETURNTOLOBBY - SetChoices(user) - if("setJobLevel") - UpdateJobPreference(user, href_list["text"], text2num(href_list["level"])) - else - SetChoices(user) - return 1 - - switch(href_list["task"]) - if("random") - switch(href_list["preference"]) - if("name") - real_name = pref_species.random_name(gender,1) - if("age") - age = rand(AGE_MIN, AGE_MAX) - if("hair") - hair_color = random_short_color() - if("hair_style") - hair_style = random_hair_style(gender) - if("facial") - facial_hair_color = random_short_color() - if("facial_hair_style") - facial_hair_style = random_facial_hair_style(gender) - if("underwear") - underwear = random_underwear(gender) - if("undershirt") - undershirt = random_undershirt(gender) - if("socks") - socks = random_socks() - if("eyes") - eye_color = random_eye_color() - if("s_tone") - skin_tone = random_skin_tone() - if("bag") - backbag = pick(GLOB.backbaglist) - if("all") - random_character() - - if("input") - switch(href_list["preference"]) - if("ghostform") - if(unlock_content) - var/new_form = input(user, "Thanks for supporting BYOND - Choose your ghostly form:","Thanks for supporting BYOND",null) as null|anything in GLOB.ghost_forms - if(new_form) - ghost_form = new_form - if("ghostorbit") - if(unlock_content) - var/new_orbit = input(user, "Thanks for supporting BYOND - Choose your ghostly orbit:","Thanks for supporting BYOND", null) as null|anything in GLOB.ghost_orbits - if(new_orbit) - ghost_orbit = new_orbit - - if("ghostaccs") - var/new_ghost_accs = alert("Do you want your ghost to show full accessories where possible, hide accessories but still use the directional sprites where possible, or also ignore the directions and stick to the default sprites?",,GHOST_ACCS_FULL_NAME, GHOST_ACCS_DIR_NAME, GHOST_ACCS_NONE_NAME) - switch(new_ghost_accs) - if(GHOST_ACCS_FULL_NAME) - ghost_accs = GHOST_ACCS_FULL - if(GHOST_ACCS_DIR_NAME) - ghost_accs = GHOST_ACCS_DIR - if(GHOST_ACCS_NONE_NAME) - ghost_accs = GHOST_ACCS_NONE - - if("ghostothers") - var/new_ghost_others = alert("Do you want the ghosts of others to show up as their own setting, as their default sprites or always as the default white ghost?",,GHOST_OTHERS_THEIR_SETTING_NAME, GHOST_OTHERS_DEFAULT_SPRITE_NAME, GHOST_OTHERS_SIMPLE_NAME) - switch(new_ghost_others) - if(GHOST_OTHERS_THEIR_SETTING_NAME) - ghost_others = GHOST_OTHERS_THEIR_SETTING - if(GHOST_OTHERS_DEFAULT_SPRITE_NAME) - ghost_others = GHOST_OTHERS_DEFAULT_SPRITE - if(GHOST_OTHERS_SIMPLE_NAME) - ghost_others = GHOST_OTHERS_SIMPLE - - if("name") - var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) - if(new_name) - real_name = new_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("age") - var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null - if(new_age) - age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN) - - if("flavor_text") - var/msg = input(usr,"Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!","Flavor Text",html_decode(flavor_text)) as message - if(msg != null) - msg = copytext(msg, 1, MAX_MESSAGE_LEN) - msg = html_encode(msg) - flavor_text = msg - - if("metadata") - var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , metadata) as message|null - if(new_metadata) - metadata = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN)) - - if("hair") - var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference") as null|color - if(new_hair) - hair_color = sanitize_hexcolor(new_hair) - - - if("hair_style") - var/new_hair_style - if(gender == MALE) - new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_male_list - else - new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_female_list - if(new_hair_style) - hair_style = new_hair_style - - if("next_hair_style") - if (gender == MALE) - hair_style = next_list_item(hair_style, GLOB.hair_styles_male_list) - else - hair_style = next_list_item(hair_style, GLOB.hair_styles_female_list) - - if("previous_hair_style") - if (gender == MALE) - hair_style = previous_list_item(hair_style, GLOB.hair_styles_male_list) - else - hair_style = previous_list_item(hair_style, GLOB.hair_styles_female_list) - - if("facial") - var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference") as null|color - if(new_facial) - facial_hair_color = sanitize_hexcolor(new_facial) - - if("facial_hair_style") - var/new_facial_hair_style - if(gender == MALE) - new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_male_list - else - new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_female_list - if(new_facial_hair_style) - facial_hair_style = new_facial_hair_style - - if("next_facehair_style") - if (gender == MALE) - facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_male_list) - else - facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_female_list) - - if("previous_facehair_style") - if (gender == MALE) - facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_male_list) - else - facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_female_list) - - if("underwear") - var/new_underwear - if(gender == MALE) - new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_m - else - new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_f - if(new_underwear) - underwear = new_underwear - - if("undershirt") - var/new_undershirt - if(gender == MALE) - new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in GLOB.undershirt_m - else - new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in GLOB.undershirt_f - if(new_undershirt) - undershirt = new_undershirt - - if("socks") - var/new_socks - new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in GLOB.socks_list - if(new_socks) - socks = new_socks - - if("eyes") - var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference") as color|null - if(new_eyes) - eye_color = sanitize_hexcolor(new_eyes) - - if("species") - - var/result = input(user, "Select a species", "Species Selection") as null|anything in GLOB.roundstart_species - - if(result) - var/newtype = GLOB.roundstart_species[result] - pref_species = new newtype() - //Now that we changed our species, we must verify that the mutant colour is still allowed. - var/temp_hsv = RGBtoHSV(features["mcolor"]) - if(features["mcolor"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) - features["mcolor"] = pref_species.default_color - if(features["mcolor2"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) - features["mcolor2"] = pref_species.default_color - if(features["mcolor3"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) - features["mcolor3"] = pref_species.default_color - - if("mutant_color") - var/new_mutantcolor = input(user, "Choose your character's primary alien/mutant color:", "Character Preference") as color|null - if(new_mutantcolor) - var/temp_hsv = RGBtoHSV(new_mutantcolor) - if(new_mutantcolor == "#000000") - features["mcolor"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin - features["mcolor"] = sanitize_hexcolor(new_mutantcolor) - else - to_chat(user, "Invalid color. Your color is not bright enough.") - - if("mutant_color2") - var/new_mutantcolor = input(user, "Choose your character's secondary alien/mutant color:", "Character Preference") as color|null - if(new_mutantcolor) - var/temp_hsv = RGBtoHSV(new_mutantcolor) - if(new_mutantcolor == "#000000") - features["mcolor2"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin - features["mcolor2"] = sanitize_hexcolor(new_mutantcolor) - else - to_chat(user, "Invalid color. Your color is not bright enough.") - - if("mutant_color3") - var/new_mutantcolor = input(user, "Choose your character's tertiary alien/mutant color:", "Character Preference") as color|null - if(new_mutantcolor) - var/temp_hsv = RGBtoHSV(new_mutantcolor) - if(new_mutantcolor == "#000000") - features["mcolor3"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin - features["mcolor3"] = sanitize_hexcolor(new_mutantcolor) - else - to_chat(user, "Invalid color. Your color is not bright enough.") - - if("tail_lizard") - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_lizard - if(new_tail) - features["tail_lizard"] = new_tail - if(new_tail != "None") - features["taur"] = "None" - - if("tail_human") - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_human - if(new_tail) - features["tail_human"] = new_tail - if(new_tail != "None") - features["taur"] = "None" - if("mam_tail") - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.mam_tails_list - if(new_tail) - features["mam_tail"] = new_tail - if(new_tail != "None") - features["taur"] = "None" - - if("taur") - var/new_taur - new_taur = input(user, "Choose your character's tauric body:", "Character Preference") as null|anything in GLOB.taur_list - if(new_taur) - features["taur"] = new_taur - if(new_taur != "None") - features["mam_tail"] = "None" - features["xenotail"] = "None" - -/* Doesn't exist yet. will include facial overlays to mimic 5th port species heads. - if("mam_snout") - var/new_snout - new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.mam_snouts_list - if(new_snout) - features["snout"] = new_snout -*/ - - if("snout") - var/new_snout - new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.snouts_list - if(new_snout) - features["snout"] = new_snout - - if("horns") - var/new_horns - new_horns = input(user, "Choose your character's horns:", "Character Preference") as null|anything in GLOB.horns_list - if(new_horns) - features["horns"] = new_horns - - if("mam_ears") - var/new_ears - new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.mam_ears_list - if(new_ears) - features["mam_ears"] = new_ears - - if("ears") - var/new_ears - new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.ears_list - if(new_ears) - features["ears"] = new_ears - - if("wings") - var/new_wings - new_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.r_wings_list - if(new_wings) - features["wings"] = new_wings - - if("frills") - var/new_frills - new_frills = input(user, "Choose your character's frills:", "Character Preference") as null|anything in GLOB.frills_list - if(new_frills) - features["frills"] = new_frills - - if("spines") - var/new_spines - new_spines = input(user, "Choose your character's spines:", "Character Preference") as null|anything in GLOB.spines_list - if(new_spines) - features["spines"] = new_spines - - if("body_markings") - var/new_body_markings - new_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in GLOB.body_markings_list - if(new_body_markings) - features["body_markings"] = new_body_markings - - if("mam_body_markings") - var/new_mam_body_markings - new_mam_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in GLOB.mam_body_markings_list - if(new_mam_body_markings) - features["mam_body_markings"] = new_mam_body_markings - - //Xeno Bodyparts - if("xenohead")//Head or caste type - var/new_head - new_head = input(user, "Choose your character's caste:", "Character Preference") as null|anything in GLOB.xeno_head_list - if(new_head) - features["xenohead"] = new_head - - if("xenotail")//Currently one one type, more maybe later if someone sprites them. Might include animated variants in the future. - var/new_tail - new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.xeno_tail_list - if(new_tail) - features["xenotail"] = new_tail - - if("xenodorsal") - var/new_dors - new_dors = input(user, "Choose your character's dorsal tube type:", "Character Preference") as null|anything in GLOB.xeno_dorsal_list - if(new_dors) - features["xenodorsal"] = new_dors - - if("legs") - var/new_legs - new_legs = input(user, "Choose your character's legs:", "Character Preference") as null|anything in GLOB.legs_list - if(new_legs) - features["legs"] = new_legs - - if("s_tone") - var/new_s_tone = input(user, "Choose your character's skin-tone:", "Character Preference") as null|anything in GLOB.skin_tones - if(new_s_tone) - skin_tone = new_s_tone - - if("ooccolor") - var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference") as color|null - if(new_ooccolor) - ooccolor = sanitize_ooccolor(new_ooccolor) - - if("bag") - var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in GLOB.backbaglist - if(new_backbag) - backbag = new_backbag - - if("uplink_loc") - var/new_loc = input(user, "Choose your character's traitor uplink spawn location:", "Character Preference") as null|anything in GLOB.uplink_spawn_loc_list - if(new_loc) - uplink_spawn_loc = new_loc - - if("clown_name") - var/new_clown_name = reject_bad_name( input(user, "Choose your character's clown name:", "Character Preference") as text|null ) - if(new_clown_name) - custom_names["clown"] = new_clown_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("mime_name") - var/new_mime_name = reject_bad_name( input(user, "Choose your character's mime name:", "Character Preference") as text|null ) - if(new_mime_name) - custom_names["mime"] = new_mime_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("ai_name") - var/new_ai_name = reject_bad_name( input(user, "Choose your character's AI name:", "Character Preference") as text|null, 1 ) - if(new_ai_name) - custom_names["ai"] = new_ai_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and .") - - if("cyborg_name") - var/new_cyborg_name = reject_bad_name( input(user, "Choose your character's cyborg name:", "Character Preference") as text|null, 1 ) - if(new_cyborg_name) - custom_names["cyborg"] = new_cyborg_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and .") - - if("religion_name") - var/new_religion_name = reject_bad_name( input(user, "Choose your character's religion:", "Character Preference") as text|null ) - if(new_religion_name) - custom_names["religion"] = new_religion_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("deity_name") - var/new_deity_name = reject_bad_name( input(user, "Choose your character's deity:", "Character Preference") as text|null ) - if(new_deity_name) - custom_names["deity"] = new_deity_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("sec_dept") - var/department = input(user, "Choose your prefered security department:", "Security Departments") as null|anything in GLOB.security_depts_prefs - if(department) - prefered_security_department = department - - if ("preferred_map") - var/maplist = list() - var/default = "Default" - if (config.defaultmap) - default += " ([config.defaultmap.map_name])" - for (var/M in config.maplist) - var/datum/map_config/VM = config.maplist[M] - var/friendlyname = "[VM.map_name] " - if (VM.voteweight <= 0) - friendlyname += " (disabled)" - maplist[friendlyname] = VM.map_name - maplist[default] = null - var/pickedmap = input(user, "Choose your preferred map. This will be used to help weight random map selection.", "Character Preference") as null|anything in maplist - if (pickedmap) - preferred_map = maplist[pickedmap] - - if ("clientfps") - var/version_message - if (user.client && user.client.byond_version < 511) - version_message = "\nYou need to be using byond version 511 or later to take advantage of this feature, your version of [user.client.byond_version] is too low" - if (world.byond_version < 511) - version_message += "\nThis server does not currently support client side fps. You can set now for when it does." - var/desiredfps = input(user, "Choose your desired fps.[version_message]\n(0 = synced with server tick rate (currently:[world.fps]))", "Character Preference", clientfps) as null|num - if (!isnull(desiredfps)) - clientfps = desiredfps - if (world.byond_version >= 511 && user.client && user.client.byond_version >= 511) - user.client.vars["fps"] = clientfps - if("ui") - var/pickedui = input(user, "Choose your UI style.", "Character Preference") as null|anything in list("Midnight", "Plasmafire", "Retro", "Slimecore", "Operative", "Clockwork") - if(pickedui) - UI_style = pickedui - - //citadel code - if("cock_color") - var/new_cockcolor = input(user, "Penis color:", "Character Preference") as color|null - if(new_cockcolor) - var/temp_hsv = RGBtoHSV(new_cockcolor) - if(new_cockcolor == "#000000") - features["cock_color"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) - features["cock_color"] = sanitize_hexcolor(new_cockcolor) - else - user << "Invalid color. Your color is not bright enough." - - if("cock_length") - var/new_length = input(user, "Penis length in inches:\n([COCK_SIZE_MIN]-[COCK_SIZE_MAX])", "Character Preference") as num|null - if(new_length) - features["cock_length"] = max(min( round(text2num(new_length)), COCK_SIZE_MAX),COCK_SIZE_MIN) - - if("cock_shape") - var/new_shape - new_shape = input(user, "Penis shape:", "Character Preference") as null|anything in GLOB.cock_shapes_list - if(new_shape) - features["cock_shape"] = new_shape - - if("balls_color") - var/new_ballscolor = input(user, "Testicle Color:", "Character Preference") as color|null - if(new_ballscolor) - var/temp_hsv = RGBtoHSV(new_ballscolor) - if(new_ballscolor == "#000000") - features["balls_color"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) - features["balls_color"] = sanitize_hexcolor(new_ballscolor) - else - user << "Invalid color. Your color is not bright enough." - - if("egg_size") - var/new_size - var/list/egg_sizes = list(1,2,3) - new_size = input(user, "Egg Diameter(inches):", "Egg Size") as null|anything in egg_sizes - if(new_size) - features["eggsack_egg_size"] = new_size - - if("egg_color") - var/new_egg_color = input(user, "Egg Color:", "Character Preference") as color|null - if(new_egg_color) - var/temp_hsv = RGBtoHSV(new_egg_color) - if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) - features["eggsack_egg_color"] = sanitize_hexcolor(new_egg_color) - else - user << "Invalid color. Your color is not bright enough." - if("breasts_size") - var/new_size - new_size = input(user, "Breast Size", "Character Preference") as null|anything in GLOB.breasts_size_list - if(new_size) - features["breasts_size"] = new_size - - if("breasts_shape") - var/new_shape - new_shape = input(user, "Breast Shape", "Character Preference") as null|anything in GLOB.breasts_shapes_list - if(new_shape) - features["breasts_shape"] = new_shape - - if("breasts_color") - var/new_breasts_color = input(user, "Breast Color:", "Character Preference") as color|null - if(new_breasts_color) - var/temp_hsv = RGBtoHSV(new_breasts_color) - if(new_breasts_color == "#000000") - features["breasts_color"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) - features["breasts_color"] = sanitize_hexcolor(new_breasts_color) - else - user << "Invalid color. Your color is not bright enough." - if("vag_shape") - var/new_shape - new_shape = input(user, "Vagina Type", "Character Preference") as null|anything in GLOB.vagina_shapes_list - if(new_shape) - features["vag_shape"] = new_shape - if("vag_color") - var/new_vagcolor = input(user, "Vagina color:", "Character Preference") as color|null - if(new_vagcolor) - var/temp_hsv = RGBtoHSV(new_vagcolor) - if(new_vagcolor == "#000000") - features["vag_color"] = pref_species.default_color - else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) - features["vag_color"] = sanitize_hexcolor(new_vagcolor) - else - user << "Invalid color. Your color is not bright enough." - - else - switch(href_list["preference"]) - - //citadel code - if("genital_colour") - switch(features["genitals_use_skintone"]) - if(TRUE) - features["genitals_use_skintone"] = FALSE - if(FALSE) - features["genitals_use_skintone"] = TRUE - else - features["genitals_use_skintone"] = FALSE - if("arousable") - switch(arousable) - if(TRUE) - arousable = FALSE - if(FALSE) - arousable = TRUE - else//failsafe - arousable = FALSE - if("has_cock") - switch(features["has_cock"]) - if(TRUE) - features["has_cock"] = FALSE - if(FALSE) - features["has_cock"] = TRUE - features["has_ovi"] = FALSE - features["has_eggsack"] = FALSE - else - features["has_cock"] = FALSE - features["has_ovi"] = FALSE - if("has_balls") - switch(features["has_balls"]) - if(TRUE) - features["has_balls"] = FALSE - if(FALSE) - features["has_balls"] = TRUE - features["has_eggsack"] = FALSE - else - features["has_balls"] = FALSE - features["has_eggsack"] = FALSE - - if("has_ovi") - switch(features["has_ovi"]) - if(TRUE) - features["has_ovi"] = FALSE - if(FALSE) - features["has_ovi"] = TRUE - features["has_cock"] = FALSE - features["has_balls"] = FALSE - else - features["has_ovi"] = FALSE - features["has_cock"] = FALSE - - if("has_eggsack") - switch(features["has_eggsack"]) - if(TRUE) - features["has_eggsack"] = FALSE - if(FALSE) - features["has_eggsack"] = TRUE - features["has_balls"] = FALSE - else - features["has_eggsack"] = FALSE - features["has_balls"] = FALSE - - if("balls_internal") - switch(features["balls_internal"]) - if(TRUE) - features["balls_internal"] = FALSE - if(FALSE) - features["balls_internal"] = TRUE - features["eggsack_internal"] = FALSE - else - features["balls_internal"] = FALSE - features["eggsack_internal"] = FALSE - - if("eggsack_internal") - switch(features["eggsack_internal"]) - if(TRUE) - features["eggsack_internal"] = FALSE - if(FALSE) - features["eggsack_internal"] = TRUE - features["balls_internal"] = FALSE - else - features["eggsack_internal"] = FALSE - features["balls_internal"] = FALSE - - if("has_breasts") - switch(features["has_breasts"]) - if(TRUE) - features["has_breasts"] = FALSE - if(FALSE) - features["has_breasts"] = TRUE - else - features["has_breasts"] = FALSE - if("has_vag") - switch(features["has_vag"]) - if(TRUE) - features["has_vag"] = FALSE - if(FALSE) - features["has_vag"] = TRUE - else - features["has_vag"] = FALSE - if("has_womb") - switch(features["has_womb"]) - if(TRUE) - features["has_womb"] = FALSE - if(FALSE) - features["has_womb"] = TRUE - else - features["has_womb"] = FALSE - if("exhibitionist") - switch(features["exhibitionist"]) - if(TRUE) - features["exhibitionist"] = FALSE - if(FALSE) - features["exhibitionist"] = TRUE - else - features["exhibitionist"] = FALSE - - if("publicity") - if(unlock_content) - toggles ^= MEMBER_PUBLIC - if("gender") - if(gender == MALE) - gender = FEMALE - else - gender = MALE - underwear = "Nude" - undershirt = "Nude" - socks = "Nude" - facial_hair_style = "Shaved" - hair_style = "Bald" - - if("hotkeys") - hotkeys = !hotkeys - - if("tgui_fancy") - tgui_fancy = !tgui_fancy - if("tgui_lock") - tgui_lock = !tgui_lock - if("winflash") - windowflashing = !windowflashing - if("hear_adminhelps") - toggles ^= SOUND_ADMINHELP - if("announce_login") - toggles ^= ANNOUNCE_LOGIN - - if("be_special") - var/be_special_type = href_list["be_special_type"] - if(be_special_type in be_special) - be_special -= be_special_type - else - be_special += be_special_type - - if("name") - be_random_name = !be_random_name - - if("all") - be_random_body = !be_random_body - - if("hear_midis") - toggles ^= SOUND_MIDI - - if("lobby_music") - toggles ^= SOUND_LOBBY - if((toggles & SOUND_LOBBY) && user.client) - user.client.playtitlemusic() - else - user.stop_sound_channel(CHANNEL_LOBBYMUSIC) - - if("ghost_ears") - chat_toggles ^= CHAT_GHOSTEARS - - if("ghost_sight") - chat_toggles ^= CHAT_GHOSTSIGHT - - if("ghost_whispers") - chat_toggles ^= CHAT_GHOSTWHISPER - - if("ghost_radio") - chat_toggles ^= CHAT_GHOSTRADIO - - if("ghost_pda") - chat_toggles ^= CHAT_GHOSTPDA - - if("pull_requests") - chat_toggles ^= CHAT_PULLR - - if("allow_midround_antag") - toggles ^= MIDROUND_ANTAG - - if("parallaxup") - parallax = Wrap(parallax + 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) - if (parent && parent.mob && parent.mob.hud_used) - parent.mob.hud_used.update_parallax_pref(parent.mob) - - if("parallaxdown") - parallax = Wrap(parallax - 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) - if (parent && parent.mob && parent.mob.hud_used) - parent.mob.hud_used.update_parallax_pref(parent.mob) - - if("save") - save_preferences() - save_character() - - if("load") - load_preferences() - load_character() - attempt_vr(parent.prefs_vr,"load_vore","") - - if("changeslot") - attempt_vr(parent.prefs_vr,"load_vore","") - if(!load_character(text2num(href_list["num"]))) - random_character() - real_name = random_unique_name(gender) - save_character() - - if("tab") - if (href_list["tab"]) - current_tab = text2num(href_list["tab"]) - - ShowChoices(user) - return 1 - -/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1) - if(be_random_name) - real_name = pref_species.random_name(gender) - - if(be_random_body) - random_character(gender) - - if(config.humans_need_surnames) - var/firstspace = findtext(real_name, " ") - var/name_length = length(real_name) - if(!firstspace) //we need a surname - real_name += " [pick(GLOB.last_names)]" - else if(firstspace == name_length) - real_name += "[pick(GLOB.last_names)]" - - character.real_name = real_name - character.name = character.real_name - - character.gender = gender - character.age = age - - character.eye_color = eye_color - var/obj/item/organ/eyes/organ_eyes = character.getorgan(/obj/item/organ/eyes) - if(organ_eyes) - if(!initial(organ_eyes.eye_color)) - organ_eyes.eye_color = eye_color - organ_eyes.old_eye_color = eye_color - character.hair_color = hair_color - character.facial_hair_color = facial_hair_color - - character.skin_tone = skin_tone - character.hair_style = hair_style - character.facial_hair_style = facial_hair_style - character.underwear = underwear - character.undershirt = undershirt - character.socks = socks - - character.backbag = backbag - - character.dna.features = features.Copy() - character.dna.real_name = character.real_name - var/datum/species/chosen_species - if(pref_species != /datum/species/human && config.mutant_races) - chosen_species = pref_species.type - else - chosen_species = /datum/species/human - character.set_species(chosen_species, icon_update=0) - - //citadel code - character.give_genitals() - character.flavor_text = flavor_text - character.canbearoused = arousable - - if(icon_updates) - character.update_body() - character.update_hair() - character.update_body_parts() + + +GLOBAL_LIST_EMPTY(preferences_datums) + + + +/datum/preferences + var/client/parent + //doohickeys for savefiles + var/path + var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used + var/max_save_slots = 10 + + //non-preference stuff + var/muted = 0 + var/last_ip + var/last_id + + //game-preferences + var/lastchangelog = "" //Saved changlog filesize to detect if there was a change + var/ooccolor = null + + //Antag preferences + var/list/be_special = list() //Special role selection + var/tmp/old_be_special = 0 //Bitflag version of be_special, used to update old savefiles and nothing more + //If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were, + //autocorrected this round, not that you'd need to check that. + + + var/UI_style = "Midnight" + var/hotkeys = FALSE + var/tgui_fancy = TRUE + var/tgui_lock = TRUE + var/windowflashing = TRUE + var/toggles = TOGGLES_DEFAULT + var/chat_toggles = TOGGLES_DEFAULT_CHAT + var/ghost_form = "ghost" + var/ghost_orbit = GHOST_ORBIT_CIRCLE + var/ghost_accs = GHOST_ACCS_DEFAULT_OPTION + var/ghost_others = GHOST_OTHERS_DEFAULT_OPTION + var/ghost_hud = 1 + var/inquisitive_ghost = 1 + var/allow_midround_antag = 1 + var/preferred_map = null + + var/uses_glasses_colour = 0 + + //character preferences + var/real_name //our character's name + var/be_random_name = 0 //whether we'll have a random name every round + var/be_random_body = 0 //whether we'll have a random body every round + var/gender = MALE //gender of character (well duh) + var/age = 30 //age of character + var/underwear = "Nude" //underwear type + var/undershirt = "Nude" //undershirt type + var/socks = "Nude" //socks type + var/backbag = DBACKPACK //backpack type + var/hair_style = "Bald" //Hair type + var/hair_color = "000" //Hair color + var/facial_hair_style = "Shaved" //Face hair type + var/facial_hair_color = "000" //Facial hair color + var/skin_tone = "caucasian1" //Skin color + var/eye_color = "000" //Eye color + var/datum/species/pref_species = new /datum/species/human() //Mutant race + var/list/features = list("mcolor" = "FFF", + "mcolor2" = "FFF", + "mcolor3" = "FFF", + "tail_lizard" = "Smooth", + "tail_human" = "None", + "snout" = "Round", + "horns" = "None", + "ears" = "None", + "wings" = "None", + "frills" = "None", + "spines" = "None", + "body_markings" = "None", + "mam_body_markings" = "None", + "mam_ears" = "None", + "mam_tail" = "None", + "mam_tail_animated" = "None", + "xenodorsal" = "None", + "xenohead" = "None", + "xenotail" = "None", + "legs" = "Normal Legs", + "taur" = "None", + "exhibitionist" = FALSE, + "genitals_use_skintone" = FALSE, + "has_cock" = FALSE, + "cock_shape" = "Human", + "cock_length" = 6, + "cock_girth_ratio" = COCK_GIRTH_RATIO_DEF, + "cock_color" = "fff", + "has_sheath" = FALSE, + "sheath_color" = "fff", + "has_balls" = FALSE, + "balls_internal" = FALSE, + "balls_color" = "fff", + "balls_amount" = 2, + "balls_sack_size" = BALLS_SACK_SIZE_DEF, + "balls_size" = BALLS_SIZE_DEF, + "balls_cum_rate" = CUM_RATE, + "balls_cum_mult" = CUM_RATE_MULT, + "balls_efficiency" = CUM_EFFICIENCY, + "balls_fluid" = "semen", + "has_ovi" = FALSE, + "ovi_shape" = "knotted", + "ovi_length" = 6, + "ovi_color" = "fff", + "has_eggsack" = FALSE, + "eggsack_internal" = TRUE, + "eggsack_color" = "fff", + "eggsack_size" = BALLS_SACK_SIZE_DEF, + "eggsack_egg_color" = "fff", + "eggsack_egg_size" = EGG_GIRTH_DEF, + "has_breasts" = FALSE, + "breasts_color" = "fff", + "breasts_size" = "C", + "breasts_shape" = "Pair", + "breasts_fluid" = "milk", + "has_vag" = FALSE, + "vag_shape" = "Human", + "vag_color" = "fff", + "vag_clits" = 1, + "vag_clit_diam" = 0.25, + "vag_clit_len" = 0.25, + "has_womb" = FALSE, + "womb_cum_rate" = CUM_RATE, + "womb_cum_mult" = CUM_RATE_MULT, + "womb_efficiency" = CUM_EFFICIENCY, + "womb_fluid" = "femcum" + )//MAKE SURE TO UPDATE THE LIST IN MOBS.DM IF YOU'RE GOING TO ADD TO THIS LIST, OTHERWISE THINGS MIGHT GET FUCKEY + + var/list/custom_names = list("clown", "mime", "ai", "cyborg", "religion", "deity") + var/prefered_security_department = SEC_DEPT_RANDOM + + //Mob preview + var/icon/preview_icon = null + + //Jobs, uses bitflags + var/job_civilian_high = 0 + var/job_civilian_med = 0 + var/job_civilian_low = 0 + + var/job_medsci_high = 0 + var/job_medsci_med = 0 + var/job_medsci_low = 0 + + var/job_engsec_high = 0 + var/job_engsec_med = 0 + var/job_engsec_low = 0 + + // Want randomjob if preferences already filled - Donkie + var/joblessrole = BERANDOMJOB //defaults to 1 for fewer assistants + + // 0 = character settings, 1 = game preferences, 2 = character appearance + var/current_tab = 0 + + // OOC Metadata: + var/metadata = "" + + var/unlock_content = 0 + + var/list/ignoring = list() + + var/clientfps = 0 + + var/parallax + + var/uplink_spawn_loc = UPLINK_PDA + + var/list/menuoptions + + //citadel code + var/arousable = TRUE //Allows players to disable arousal from the character creation menu + var/flavor_text = "" + +/datum/preferences/New(client/C) + parent = C + custom_names["ai"] = pick(GLOB.ai_names) + custom_names["cyborg"] = pick(GLOB.ai_names) + custom_names["clown"] = pick(GLOB.clown_names) + custom_names["mime"] = pick(GLOB.mime_names) + if(istype(C)) + if(!IsGuestKey(C.key)) + load_path(C.ckey) + unlock_content = C.IsByondMember() + if(unlock_content) + max_save_slots = 16 + var/loaded_preferences_successfully = load_preferences() + if(loaded_preferences_successfully) + if(load_character()) + return + //we couldn't load character data so just randomize the character appearance + name + random_character() //let's create a random character then - rather than a fat, bald and naked man. + real_name = pref_species.random_name(gender,1) + if(!loaded_preferences_successfully) + save_preferences() + save_character() //let's save this new random character so it doesn't keep generating new ones. + menuoptions = list() + return + +/datum/preferences/proc/ShowChoices(mob/user) + if(!user || !user.client) + return + if(current_tab == 2) + update_preview_icon(nude=TRUE) + else + update_preview_icon(nude=FALSE) + user << browse_rsc(preview_icon, "previewicon.png") + var/dat = "
" + + dat += "Character Settings" + dat += "Character Appearance" + dat += "Game Preferences" + + if(!path) + dat += "
Please create an account to save your preferences
" + + dat += "
" + + dat += "
" + + switch(current_tab) + if (0) // Character Settings# + if(path) + var/savefile/S = new /savefile(path) + if(S) + dat += "
" + var/name + for(var/i=1, i<=max_save_slots, i++) + S.cd = "/character[i]" + S["real_name"] >> name + if(!name) + name = "Character[i]" + //if(i!=1) dat += " | " + dat += "[name] " + dat += "
" + + dat += "

Occupation Choices

" + dat += "Set Occupation Preferences
" + dat += "

Identity

" + dat += "" + + dat += "
" + if(jobban_isbanned(user, "appearance")) + dat += "You are banned from using custom names and appearances. You can continue to adjust your characters, but you will be randomised once you join the game.
" + dat += "Random Name " + dat += "Always Random Name: [be_random_name ? "Yes" : "No"]
" + + dat += "Name: " + dat += "[real_name]
" + + dat += "Gender: [gender == MALE ? "Male" : "Female"]
" + dat += "Age: [age]
" + dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" + dat += "Exhibitionist:[features["exhibitionist"] == TRUE ? "Yes" : "No"]
" + dat += "Special Names:
" + dat += "Clown: [custom_names["clown"]] " + dat += "Mime:[custom_names["mime"]]
" + dat += "AI: [custom_names["ai"]] " + dat += "Cyborg: [custom_names["cyborg"]]
" + dat += "Chaplain religion: [custom_names["religion"]] " + dat += "Chaplain deity: [custom_names["deity"]]
" + + dat += "Custom job preferences:
" + dat += "Prefered security department: [prefered_security_department]
" + + dat += "
" + + dat += "
" +// dat += "Size: [character_size]
" + dat += "
" + + if (1) // Game Preferences + dat += "
" + dat += "

General Settings

" + dat += "UI Style: [UI_style]
" + dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Default"]
" + dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
" + dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"]
" + dat += "Window Flashing: [(windowflashing) ? "Yes" : "No"]
" + dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
" + dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
" + dat += "Ghost ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
" + dat += "Ghost sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
" + dat += "Ghost whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"]
" + dat += "Ghost radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "Yes" : "No"]
" + dat += "Ghost pda: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"]
" + dat += "Pull requests: [(chat_toggles & CHAT_PULLR) ? "Yes" : "No"]
" + dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Yes" : "No"]
" + if(config.allow_Metadata) + dat += "OOC Notes: Edit
" + + if(user.client) + if(user.client.holder) + dat += "Adminhelp Sound: [(toggles & SOUND_ADMINHELP)?"On":"Off"]
" + dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"On":"Off"]
" + + if(unlock_content || check_rights_for(user.client, R_ADMIN)) + dat += "OOC:     Change
" + + if(unlock_content) + dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" + dat += "Ghost Form: [ghost_form]
" + dat += "Ghost Orbit: [ghost_orbit]
" + + var/button_name = "If you see this something went wrong." + switch(ghost_accs) + if(GHOST_ACCS_FULL) + button_name = GHOST_ACCS_FULL_NAME + if(GHOST_ACCS_DIR) + button_name = GHOST_ACCS_DIR_NAME + if(GHOST_ACCS_NONE) + button_name = GHOST_ACCS_NONE_NAME + + dat += "Ghost Accessories: [button_name]
" + + switch(ghost_others) + if(GHOST_OTHERS_THEIR_SETTING) + button_name = GHOST_OTHERS_THEIR_SETTING_NAME + if(GHOST_OTHERS_DEFAULT_SPRITE) + button_name = GHOST_OTHERS_DEFAULT_SPRITE_NAME + if(GHOST_OTHERS_SIMPLE) + button_name = GHOST_OTHERS_SIMPLE_NAME + + dat += "Ghosts of Others: [button_name]
" + + if (config.maprotation) + var/p_map = preferred_map + if (!p_map) + p_map = "Default" + if (config.defaultmap) + p_map += " ([config.defaultmap.map_name])" + else + if (p_map in config.maplist) + var/datum/map_config/VM = config.maplist[p_map] + if (!VM) + p_map += " (No longer exists)" + else + p_map = VM.map_name + else + p_map += " (No longer exists)" + if(config.allow_map_voting) + dat += "Preferred Map: [p_map]
" + + dat += "FPS: [clientfps]
" + + dat += "Parallax (Fancy Space): " + switch (parallax) + if (PARALLAX_LOW) + dat += "Low" + if (PARALLAX_MED) + dat += "Medium" + if (PARALLAX_INSANE) + dat += "Insane" + if (PARALLAX_DISABLE) + dat += "Disabled" + else + dat += "High" + dat += "
" + + dat += "
" + + dat += "

Special Role Settings

" + + if(jobban_isbanned(user, "Syndicate")) + dat += "You are banned from antagonist roles." + src.be_special = list() + + + for (var/i in GLOB.special_roles) + if(jobban_isbanned(user, i)) + dat += "Be [capitalize(i)]: BANNED
" + else + var/days_remaining = null + if(config.use_age_restriction_for_jobs && ispath(GLOB.special_roles[i])) //If it's a game mode antag, check if the player meets the minimum age + var/mode_path = GLOB.special_roles[i] + var/datum/game_mode/temp_mode = new mode_path + days_remaining = temp_mode.get_remaining_days(user.client) + + if(days_remaining) + dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
" + else + dat += "Be [capitalize(i)]: [(i in be_special) ? "Yes" : "No"]
" + + dat += "
" + + //Character Appearance + if(2) + dat += "" + */ + + + dat += "
" + dat += "

" + dat += "Set Flavor Text
" + if(lentext(flavor_text) <= 40) + if(!lentext(flavor_text)) + dat += "\[...\]" + else + dat += "[flavor_text]" + else + dat += "[TextPreview(flavor_text)]...
" + if(config.mutant_races)//really don't need this check, but fuck un-tabbing all those lines + dat += "

Body

" + dat += "Gender: [gender == MALE ? "Male" : "Female"]
" + dat += "Species:[pref_species.id]
" + dat += "Random Body
" + dat += "Always Random Body: [be_random_body ? "Yes" : "No"]
" + if((MUTCOLORS in pref_species.species_traits) || (MUTCOLORS_PARTSONLY in pref_species.species_traits)) + dat += "Primary Color:     Change
" + dat += "Secondary Color:     Change
" + dat += "Tertiary Color:     Change
" + if(pref_species.use_skintones) + dat += "Skin Tone: [skin_tone]
" + dat += "Genitals Use Skintone:[features["genitals_use_skintone"] == TRUE ? "Enabled" : "Disabled"]
" + + if(HAIR in pref_species.species_traits) + dat += "Hair Style: [hair_style]
" + dat += "Hair Color:     Change
" + dat += "Facial Hair Style: [facial_hair_style]
" + dat += "Facial Hair Color:     Change
" + if(EYECOLOR in pref_species.species_traits) + dat += "Eye Color:     Change
" + if("tail_lizard" in pref_species.mutant_bodyparts) + dat += "Tail: [features["tail_lizard"]]
" + else if("mam_tail" in pref_species.mutant_bodyparts) + dat += "Tail: [features["mam_tail"]]
" + else if("tail_human" in pref_species.mutant_bodyparts) + dat += "Tail: [features["tail_human"]]
" + if("snout" in pref_species.mutant_bodyparts) + dat += "Snout: [features["snout"]]
" + if("horns" in pref_species.mutant_bodyparts) + dat += "Snout: [features["horns"]]
" + if("frills" in pref_species.mutant_bodyparts) + dat += "Frills: [features["frills"]]
" + if("spines" in pref_species.mutant_bodyparts) + dat += "Spines: [features["spines"]]
" + if("body_markings" in pref_species.mutant_bodyparts) + dat += "Body Markings: [features["body_markings"]]
" + else if("mam_body_markings" in pref_species.mutant_bodyparts) + dat += "Body Markings: [features["mam_body_markings"]]
" + if("mam_ears" in pref_species.mutant_bodyparts) + dat += "Ears: [features["mam_ears"]]
" + else if("ears" in pref_species.mutant_bodyparts) + dat += "Ears: [features["ears"]]
" + if("legs" in pref_species.mutant_bodyparts) + dat += "Legs: [features["legs"]]
" + if("taur" in pref_species.mutant_bodyparts) + dat += "Taur: [features["taur"]]
" + if("wings" in pref_species.mutant_bodyparts && GLOB.r_wings_list.len >1) + dat += "Wings: [features["wings"]]
" + if("xenohead" in pref_species.mutant_bodyparts) + dat += "Caste: [features["xenohead"]]
" + if("xenotail" in pref_species.mutant_bodyparts) + dat += "Tail: [features["xenotail"]]
" + if("xenodorsal" in pref_species.mutant_bodyparts) + dat += "Dorsal Tubes: [features["xenodorsal"]]
" + + dat += "
" + + + dat += "

Clothing & Equipment

" +//underwear will be refactored later so it fits in with other wearable equipment and isn't just an overlay +// dat += "Underwear:[underwear]
" +// dat += "Undershirt:[undershirt]
" +// dat += "Socks:[socks]
" + dat += "Backpack:[backbag]
" + dat += "Uplink Location:[uplink_spawn_loc]
" + + dat += "

Genitals

" + if(NOGENITALS in pref_species.species_traits) + dat += "Your species ([pref_species.name]) does not support genitals!
" + else + dat += "Has Penis:[features["has_cock"] == TRUE ? "Yes" : "No"]
" + if(features["has_cock"] == TRUE) + if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) + dat += "Penis Color:   (Skin tone overriding)
" + else + dat += "Penis Color:    Change
" +// dat += "
" + dat += "Penis Shape: [features["cock_shape"]]
" + dat += "Penis Length: [features["cock_length"]] inch(es)
" + dat += "Has Testicles:[features["has_balls"] == TRUE ? "Yes" : "No"]
" + if(features["has_balls"] == TRUE) + if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) + dat += "Testicles Color:   (Skin tone overriding)
" + else + dat += "Testicles Color:    Change
" + dat += "Has Vagina:[features["has_vag"] == TRUE ? "Yes" : "No"]
" + if(features["has_vag"]) + dat += "Vagina Type: [features["vag_shape"]]
" + if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) + dat += "Vagina Color:   (Skin tone overriding)
" + else + dat += "Vagina Color:    Change
" + dat += "Has Womb:[features["has_womb"] == TRUE ? "Yes" : "No"]
" + dat += "Has Breasts:[features["has_breasts"] == TRUE ? "Yes" : "No"]
" + if(features["has_breasts"]) + if(pref_species.use_skintones && features["genitals_use_skintone"] == TRUE) + dat += "Color:   (Skin tone overriding)
" + else + dat += "Color:    Change
" + dat += "Cup Size:[features["breasts_size"]]
" + dat += "Breast Shape:[features["breasts_shape"]]
" + /* + dat += "

Ovipositor

" + dat += "Has Ovipositor:[features["has_ovi"] == TRUE ? "Yes" : "No"]" + if(features["has_ovi"]) + dat += "Ovi Color:    Change" + dat += "

Eggsack

" + dat += "Has Eggsack:[features["has_eggsack"] == TRUE ? "Yes" : "No"]
" + if(features["has_eggsack"] == TRUE) + dat += "Color:    Change" + dat += "Egg Color:    Change" + dat += "Egg Size:[features["eggsack_egg_size"]]\" Diameter" + + dat += "
" + dat += "
" + + if(!IsGuestKey(user.key)) + dat += "Undo " + dat += "Save Setup " + + dat += "Reset Setup" + dat += "
" + + var/datum/browser/popup = new(user, "preferences", "
Character Setup
", 640, 770) + popup.set_content(dat) + popup.open(0) + +/datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Chief Engineer"), widthPerColumn = 295, height = 620) + if(!SSjob) + return + + //limit - The amount of jobs allowed per column. Defaults to 17 to make it look nice. + //splitJobs - Allows you split the table by job. You can make different tables for each department by including their heads. Defaults to CE to make it look nice. + //widthPerColumn - Screen's width for every column. + //height - Screen's height. + + var/width = widthPerColumn + + var/HTML = "
" + if(SSjob.occupations.len <= 0) + HTML += "The job ticker is not yet finished creating jobs, please try again later" + HTML += "
Done

" // Easier to press up here. + + else + HTML += "Choose occupation chances
" + HTML += "
Left-click to raise an occupation preference, right-click to lower it.
" + HTML += "
Done

" // Easier to press up here. + HTML += "" + HTML += "
" // Table within a table for alignment, also allows you to easily add more colomns. + HTML += "" + var/index = -1 + + //The job before the current job. I only use this to get the previous jobs color when I'm filling in blank rows. + var/datum/job/lastJob + + for(var/datum/job/job in SSjob.occupations) + + index += 1 + if((index >= limit) || (job.title in splitJobs)) + width += widthPerColumn + if((index < limit) && (lastJob != null)) + //If the cells were broken up by a job in the splitJob list then it will fill in the rest of the cells with + //the last job's selection color. Creating a rather nice effect. + for(var/i = 0, i < (limit - index), i += 1) + HTML += "" + HTML += "
  
" + index = 0 + + HTML += "" + continue + if(!job.player_old_enough(user.client)) + var/available_in_days = job.available_in_days(user.client) + HTML += "[rank]" + continue + if((job_civilian_low & ASSISTANT) && (rank != "Assistant") && !jobban_isbanned(user, "Assistant")) + HTML += "[rank]" + continue + if(config.enforce_human_authority && !user.client.prefs.pref_species.qualifies_for_rank(rank, user.client.prefs.features)) + if(user.client.prefs.pref_species.id == "human") + HTML += "[rank]" + else + HTML += "[rank]" + continue + if((rank in GLOB.command_positions) || (rank == "AI"))//Bold head jobs + HTML += "[rank]" + else + HTML += "[rank]" + + HTML += "" + continue + + HTML += "[prefLevelLabel]" + HTML += "" + + for(var/i = 1, i < (limit - index), i += 1) // Finish the column so it is even + HTML += "" + + HTML += "
" + var/rank = job.title + lastJob = job + if(jobban_isbanned(user, rank)) + HTML += "[rank] BANNED
\[IN [(available_in_days)] DAYS\]
\[MUTANT\]
\[NON-HUMAN\]
" + + var/prefLevelLabel = "ERROR" + var/prefLevelColor = "pink" + var/prefUpperLevel = -1 // level to assign on left click + var/prefLowerLevel = -1 // level to assign on right click + + if(GetJobDepartment(job, 1) & job.flag) + prefLevelLabel = "High" + prefLevelColor = "slateblue" + prefUpperLevel = 4 + prefLowerLevel = 2 + else if(GetJobDepartment(job, 2) & job.flag) + prefLevelLabel = "Medium" + prefLevelColor = "green" + prefUpperLevel = 1 + prefLowerLevel = 3 + else if(GetJobDepartment(job, 3) & job.flag) + prefLevelLabel = "Low" + prefLevelColor = "orange" + prefUpperLevel = 2 + prefLowerLevel = 4 + else + prefLevelLabel = "NEVER" + prefLevelColor = "red" + prefUpperLevel = 3 + prefLowerLevel = 1 + + + HTML += "" + + if(rank == "Assistant")//Assistant is special + if(job_civilian_low & ASSISTANT) + HTML += "Yes" + else + HTML += "No" + HTML += "
  
" + HTML += "
" + + var/message = "Be an Assistant if preferences unavailable" + if(joblessrole == BERANDOMJOB) + message = "Get random job if preferences unavailable" + else if(joblessrole == RETURNTOLOBBY) + message = "Return to lobby if preferences unavailable" + HTML += "

[message]
" + HTML += "
Reset Preferences
" + + user << browse(null, "window=preferences") + var/datum/browser/popup = new(user, "mob_occupation", "
Occupation Preferences
", width, height) + popup.set_window_options("can_close=0") + popup.set_content(HTML) + popup.open(0) + return + +/datum/preferences/proc/SetJobPreferenceLevel(datum/job/job, level) + if (!job) + return 0 + + if (level == 1) // to high + // remove any other job(s) set to high + job_civilian_med |= job_civilian_high + job_engsec_med |= job_engsec_high + job_medsci_med |= job_medsci_high + job_civilian_high = 0 + job_engsec_high = 0 + job_medsci_high = 0 + + if (job.department_flag == CIVILIAN) + job_civilian_low &= ~job.flag + job_civilian_med &= ~job.flag + job_civilian_high &= ~job.flag + + switch(level) + if (1) + job_civilian_high |= job.flag + if (2) + job_civilian_med |= job.flag + if (3) + job_civilian_low |= job.flag + + return 1 + else if (job.department_flag == ENGSEC) + job_engsec_low &= ~job.flag + job_engsec_med &= ~job.flag + job_engsec_high &= ~job.flag + + switch(level) + if (1) + job_engsec_high |= job.flag + if (2) + job_engsec_med |= job.flag + if (3) + job_engsec_low |= job.flag + + return 1 + else if (job.department_flag == MEDSCI) + job_medsci_low &= ~job.flag + job_medsci_med &= ~job.flag + job_medsci_high &= ~job.flag + + switch(level) + if (1) + job_medsci_high |= job.flag + if (2) + job_medsci_med |= job.flag + if (3) + job_medsci_low |= job.flag + + return 1 + + return 0 + +/datum/preferences/proc/UpdateJobPreference(mob/user, role, desiredLvl) + if(!SSjob || SSjob.occupations.len <= 0) + return + var/datum/job/job = SSjob.GetJob(role) + + if(!job) + user << browse(null, "window=mob_occupation") + ShowChoices(user) + return + + if (!isnum(desiredLvl)) + to_chat(user, "UpdateJobPreference - desired level was not a number. Please notify coders!") + ShowChoices(user) + return + + if(role == "Assistant") + if(job_civilian_low & job.flag) + job_civilian_low &= ~job.flag + else + job_civilian_low |= job.flag + SetChoices(user) + return 1 + + SetJobPreferenceLevel(job, desiredLvl) + SetChoices(user) + + return 1 + + +/datum/preferences/proc/ResetJobs() + + job_civilian_high = 0 + job_civilian_med = 0 + job_civilian_low = 0 + + job_medsci_high = 0 + job_medsci_med = 0 + job_medsci_low = 0 + + job_engsec_high = 0 + job_engsec_med = 0 + job_engsec_low = 0 + + +/datum/preferences/proc/GetJobDepartment(datum/job/job, level) + if(!job || !level) + return 0 + switch(job.department_flag) + if(CIVILIAN) + switch(level) + if(1) + return job_civilian_high + if(2) + return job_civilian_med + if(3) + return job_civilian_low + if(MEDSCI) + switch(level) + if(1) + return job_medsci_high + if(2) + return job_medsci_med + if(3) + return job_medsci_low + if(ENGSEC) + switch(level) + if(1) + return job_engsec_high + if(2) + return job_engsec_med + if(3) + return job_engsec_low + return 0 + +/datum/preferences/proc/process_link(mob/user, list/href_list) + if(href_list["jobbancheck"]) + var/job = sanitizeSQL(href_list["jobbancheck"]) + var/sql_ckey = sanitizeSQL(user.ckey) + var/datum/DBQuery/query_get_jobban = SSdbcore.NewQuery("SELECT reason, bantime, duration, expiration_time, a_ckey FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[job]'") + if(!query_get_jobban.warn_execute()) + return + if(query_get_jobban.NextRow()) + var/reason = query_get_jobban.item[1] + var/bantime = query_get_jobban.item[2] + var/duration = query_get_jobban.item[3] + var/expiration_time = query_get_jobban.item[4] + var/a_ckey = query_get_jobban.item[5] + var/text + text = "You, or another user of this computer, ([user.ckey]) is banned from playing [job]. The ban reason is:
[reason]
This ban was applied by [a_ckey] on [bantime]" + if(text2num(duration) > 0) + text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)" + text += ".
" + to_chat(user, text) + return + + if(href_list["preference"] == "job") + switch(href_list["task"]) + if("close") + user << browse(null, "window=mob_occupation") + ShowChoices(user) + if("reset") + ResetJobs() + SetChoices(user) + if("random") + switch(joblessrole) + if(RETURNTOLOBBY) + if(jobban_isbanned(user, "Assistant")) + joblessrole = BERANDOMJOB + else + joblessrole = BEASSISTANT + if(BEASSISTANT) + joblessrole = BERANDOMJOB + if(BERANDOMJOB) + joblessrole = RETURNTOLOBBY + SetChoices(user) + if("setJobLevel") + UpdateJobPreference(user, href_list["text"], text2num(href_list["level"])) + else + SetChoices(user) + return 1 + + switch(href_list["task"]) + if("random") + switch(href_list["preference"]) + if("name") + real_name = pref_species.random_name(gender,1) + if("age") + age = rand(AGE_MIN, AGE_MAX) + if("hair") + hair_color = random_short_color() + if("hair_style") + hair_style = random_hair_style(gender) + if("facial") + facial_hair_color = random_short_color() + if("facial_hair_style") + facial_hair_style = random_facial_hair_style(gender) + if("underwear") + underwear = random_underwear(gender) + if("undershirt") + undershirt = random_undershirt(gender) + if("socks") + socks = random_socks() + if("eyes") + eye_color = random_eye_color() + if("s_tone") + skin_tone = random_skin_tone() + if("bag") + backbag = pick(GLOB.backbaglist) + if("all") + random_character() + + if("input") + switch(href_list["preference"]) + if("ghostform") + if(unlock_content) + var/new_form = input(user, "Thanks for supporting BYOND - Choose your ghostly form:","Thanks for supporting BYOND",null) as null|anything in GLOB.ghost_forms + if(new_form) + ghost_form = new_form + if("ghostorbit") + if(unlock_content) + var/new_orbit = input(user, "Thanks for supporting BYOND - Choose your ghostly orbit:","Thanks for supporting BYOND", null) as null|anything in GLOB.ghost_orbits + if(new_orbit) + ghost_orbit = new_orbit + + if("ghostaccs") + var/new_ghost_accs = alert("Do you want your ghost to show full accessories where possible, hide accessories but still use the directional sprites where possible, or also ignore the directions and stick to the default sprites?",,GHOST_ACCS_FULL_NAME, GHOST_ACCS_DIR_NAME, GHOST_ACCS_NONE_NAME) + switch(new_ghost_accs) + if(GHOST_ACCS_FULL_NAME) + ghost_accs = GHOST_ACCS_FULL + if(GHOST_ACCS_DIR_NAME) + ghost_accs = GHOST_ACCS_DIR + if(GHOST_ACCS_NONE_NAME) + ghost_accs = GHOST_ACCS_NONE + + if("ghostothers") + var/new_ghost_others = alert("Do you want the ghosts of others to show up as their own setting, as their default sprites or always as the default white ghost?",,GHOST_OTHERS_THEIR_SETTING_NAME, GHOST_OTHERS_DEFAULT_SPRITE_NAME, GHOST_OTHERS_SIMPLE_NAME) + switch(new_ghost_others) + if(GHOST_OTHERS_THEIR_SETTING_NAME) + ghost_others = GHOST_OTHERS_THEIR_SETTING + if(GHOST_OTHERS_DEFAULT_SPRITE_NAME) + ghost_others = GHOST_OTHERS_DEFAULT_SPRITE + if(GHOST_OTHERS_SIMPLE_NAME) + ghost_others = GHOST_OTHERS_SIMPLE + + if("name") + var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) + if(new_name) + real_name = new_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + + if("age") + var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null + if(new_age) + age = max(min( round(text2num(new_age)), AGE_MAX),AGE_MIN) + + if("flavor_text") + var/msg = input(usr,"Set the flavor text in your 'examine' verb. This can also be used for OOC notes and preferences!","Flavor Text",html_decode(flavor_text)) as message + if(msg != null) + msg = copytext(msg, 1, MAX_MESSAGE_LEN) + msg = html_encode(msg) + flavor_text = msg + + if("metadata") + var/new_metadata = input(user, "Enter any information you'd like others to see, such as Roleplay-preferences:", "Game Preference" , metadata) as message|null + if(new_metadata) + metadata = sanitize(copytext(new_metadata,1,MAX_MESSAGE_LEN)) + + if("hair") + var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference") as null|color + if(new_hair) + hair_color = sanitize_hexcolor(new_hair) + + + if("hair_style") + var/new_hair_style + if(gender == MALE) + new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_male_list + else + new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_female_list + if(new_hair_style) + hair_style = new_hair_style + + if("next_hair_style") + if (gender == MALE) + hair_style = next_list_item(hair_style, GLOB.hair_styles_male_list) + else + hair_style = next_list_item(hair_style, GLOB.hair_styles_female_list) + + if("previous_hair_style") + if (gender == MALE) + hair_style = previous_list_item(hair_style, GLOB.hair_styles_male_list) + else + hair_style = previous_list_item(hair_style, GLOB.hair_styles_female_list) + + if("facial") + var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference") as null|color + if(new_facial) + facial_hair_color = sanitize_hexcolor(new_facial) + + if("facial_hair_style") + var/new_facial_hair_style + if(gender == MALE) + new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_male_list + else + new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_female_list + if(new_facial_hair_style) + facial_hair_style = new_facial_hair_style + + if("next_facehair_style") + if (gender == MALE) + facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_male_list) + else + facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_female_list) + + if("previous_facehair_style") + if (gender == MALE) + facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_male_list) + else + facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_female_list) + + if("underwear") + var/new_underwear + if(gender == MALE) + new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_m + else + new_underwear = input(user, "Choose your character's underwear:", "Character Preference") as null|anything in GLOB.underwear_f + if(new_underwear) + underwear = new_underwear + + if("undershirt") + var/new_undershirt + if(gender == MALE) + new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in GLOB.undershirt_m + else + new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference") as null|anything in GLOB.undershirt_f + if(new_undershirt) + undershirt = new_undershirt + + if("socks") + var/new_socks + new_socks = input(user, "Choose your character's socks:", "Character Preference") as null|anything in GLOB.socks_list + if(new_socks) + socks = new_socks + + if("eyes") + var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference") as color|null + if(new_eyes) + eye_color = sanitize_hexcolor(new_eyes) + + if("species") + + var/result = input(user, "Select a species", "Species Selection") as null|anything in GLOB.roundstart_species + + if(result) + var/newtype = GLOB.roundstart_species[result] + pref_species = new newtype() + //Now that we changed our species, we must verify that the mutant colour is still allowed. + var/temp_hsv = RGBtoHSV(features["mcolor"]) + if(features["mcolor"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) + features["mcolor"] = pref_species.default_color + if(features["mcolor2"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) + features["mcolor2"] = pref_species.default_color + if(features["mcolor3"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.species_traits) && ReadHSV(temp_hsv)[3] < ReadHSV("#202020")[3])) + features["mcolor3"] = pref_species.default_color + + if("mutant_color") + var/new_mutantcolor = input(user, "Choose your character's primary alien/mutant color:", "Character Preference") as color|null + if(new_mutantcolor) + var/temp_hsv = RGBtoHSV(new_mutantcolor) + if(new_mutantcolor == "#000000") + features["mcolor"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin + features["mcolor"] = sanitize_hexcolor(new_mutantcolor) + else + to_chat(user, "Invalid color. Your color is not bright enough.") + + if("mutant_color2") + var/new_mutantcolor = input(user, "Choose your character's secondary alien/mutant color:", "Character Preference") as color|null + if(new_mutantcolor) + var/temp_hsv = RGBtoHSV(new_mutantcolor) + if(new_mutantcolor == "#000000") + features["mcolor2"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin + features["mcolor2"] = sanitize_hexcolor(new_mutantcolor) + else + to_chat(user, "Invalid color. Your color is not bright enough.") + + if("mutant_color3") + var/new_mutantcolor = input(user, "Choose your character's tertiary alien/mutant color:", "Character Preference") as color|null + if(new_mutantcolor) + var/temp_hsv = RGBtoHSV(new_mutantcolor) + if(new_mutantcolor == "#000000") + features["mcolor3"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) // mutantcolors must be bright, but only if they affect the skin + features["mcolor3"] = sanitize_hexcolor(new_mutantcolor) + else + to_chat(user, "Invalid color. Your color is not bright enough.") + + if("tail_lizard") + var/new_tail + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_lizard + if(new_tail) + features["tail_lizard"] = new_tail + if(new_tail != "None") + features["taur"] = "None" + + if("tail_human") + var/new_tail + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_human + if(new_tail) + features["tail_human"] = new_tail + if(new_tail != "None") + features["taur"] = "None" + if("mam_tail") + var/new_tail + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.mam_tails_list + if(new_tail) + features["mam_tail"] = new_tail + if(new_tail != "None") + features["taur"] = "None" + + if("taur") + var/new_taur + new_taur = input(user, "Choose your character's tauric body:", "Character Preference") as null|anything in GLOB.taur_list + if(new_taur) + features["taur"] = new_taur + if(new_taur != "None") + features["mam_tail"] = "None" + features["xenotail"] = "None" + +/* Doesn't exist yet. will include facial overlays to mimic 5th port species heads. + if("mam_snout") + var/new_snout + new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.mam_snouts_list + if(new_snout) + features["snout"] = new_snout +*/ + + if("snout") + var/new_snout + new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.snouts_list + if(new_snout) + features["snout"] = new_snout + + if("horns") + var/new_horns + new_horns = input(user, "Choose your character's horns:", "Character Preference") as null|anything in GLOB.horns_list + if(new_horns) + features["horns"] = new_horns + + if("mam_ears") + var/new_ears + new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.mam_ears_list + if(new_ears) + features["mam_ears"] = new_ears + + if("ears") + var/new_ears + new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.ears_list + if(new_ears) + features["ears"] = new_ears + + if("wings") + var/new_wings + new_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.r_wings_list + if(new_wings) + features["wings"] = new_wings + + if("frills") + var/new_frills + new_frills = input(user, "Choose your character's frills:", "Character Preference") as null|anything in GLOB.frills_list + if(new_frills) + features["frills"] = new_frills + + if("spines") + var/new_spines + new_spines = input(user, "Choose your character's spines:", "Character Preference") as null|anything in GLOB.spines_list + if(new_spines) + features["spines"] = new_spines + + if("body_markings") + var/new_body_markings + new_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in GLOB.body_markings_list + if(new_body_markings) + features["body_markings"] = new_body_markings + + if("mam_body_markings") + var/new_mam_body_markings + new_mam_body_markings = input(user, "Choose your character's body markings:", "Character Preference") as null|anything in GLOB.mam_body_markings_list + if(new_mam_body_markings) + features["mam_body_markings"] = new_mam_body_markings + + //Xeno Bodyparts + if("xenohead")//Head or caste type + var/new_head + new_head = input(user, "Choose your character's caste:", "Character Preference") as null|anything in GLOB.xeno_head_list + if(new_head) + features["xenohead"] = new_head + + if("xenotail")//Currently one one type, more maybe later if someone sprites them. Might include animated variants in the future. + var/new_tail + new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.xeno_tail_list + if(new_tail) + features["xenotail"] = new_tail + + if("xenodorsal") + var/new_dors + new_dors = input(user, "Choose your character's dorsal tube type:", "Character Preference") as null|anything in GLOB.xeno_dorsal_list + if(new_dors) + features["xenodorsal"] = new_dors + + if("legs") + var/new_legs + new_legs = input(user, "Choose your character's legs:", "Character Preference") as null|anything in GLOB.legs_list + if(new_legs) + features["legs"] = new_legs + + if("s_tone") + var/new_s_tone = input(user, "Choose your character's skin-tone:", "Character Preference") as null|anything in GLOB.skin_tones + if(new_s_tone) + skin_tone = new_s_tone + + if("ooccolor") + var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference") as color|null + if(new_ooccolor) + ooccolor = sanitize_ooccolor(new_ooccolor) + + if("bag") + var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference") as null|anything in GLOB.backbaglist + if(new_backbag) + backbag = new_backbag + + if("uplink_loc") + var/new_loc = input(user, "Choose your character's traitor uplink spawn location:", "Character Preference") as null|anything in GLOB.uplink_spawn_loc_list + if(new_loc) + uplink_spawn_loc = new_loc + + if("clown_name") + var/new_clown_name = reject_bad_name( input(user, "Choose your character's clown name:", "Character Preference") as text|null ) + if(new_clown_name) + custom_names["clown"] = new_clown_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + + if("mime_name") + var/new_mime_name = reject_bad_name( input(user, "Choose your character's mime name:", "Character Preference") as text|null ) + if(new_mime_name) + custom_names["mime"] = new_mime_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + + if("ai_name") + var/new_ai_name = reject_bad_name( input(user, "Choose your character's AI name:", "Character Preference") as text|null, 1 ) + if(new_ai_name) + custom_names["ai"] = new_ai_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and .") + + if("cyborg_name") + var/new_cyborg_name = reject_bad_name( input(user, "Choose your character's cyborg name:", "Character Preference") as text|null, 1 ) + if(new_cyborg_name) + custom_names["cyborg"] = new_cyborg_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and .") + + if("religion_name") + var/new_religion_name = reject_bad_name( input(user, "Choose your character's religion:", "Character Preference") as text|null ) + if(new_religion_name) + custom_names["religion"] = new_religion_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + + if("deity_name") + var/new_deity_name = reject_bad_name( input(user, "Choose your character's deity:", "Character Preference") as text|null ) + if(new_deity_name) + custom_names["deity"] = new_deity_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + + if("sec_dept") + var/department = input(user, "Choose your prefered security department:", "Security Departments") as null|anything in GLOB.security_depts_prefs + if(department) + prefered_security_department = department + + if ("preferred_map") + var/maplist = list() + var/default = "Default" + if (config.defaultmap) + default += " ([config.defaultmap.map_name])" + for (var/M in config.maplist) + var/datum/map_config/VM = config.maplist[M] + var/friendlyname = "[VM.map_name] " + if (VM.voteweight <= 0) + friendlyname += " (disabled)" + maplist[friendlyname] = VM.map_name + maplist[default] = null + var/pickedmap = input(user, "Choose your preferred map. This will be used to help weight random map selection.", "Character Preference") as null|anything in maplist + if (pickedmap) + preferred_map = maplist[pickedmap] + + if ("clientfps") + var/version_message + if (user.client && user.client.byond_version < 511) + version_message = "\nYou need to be using byond version 511 or later to take advantage of this feature, your version of [user.client.byond_version] is too low" + if (world.byond_version < 511) + version_message += "\nThis server does not currently support client side fps. You can set now for when it does." + var/desiredfps = input(user, "Choose your desired fps.[version_message]\n(0 = synced with server tick rate (currently:[world.fps]))", "Character Preference", clientfps) as null|num + if (!isnull(desiredfps)) + clientfps = desiredfps + if (world.byond_version >= 511 && user.client && user.client.byond_version >= 511) + user.client.vars["fps"] = clientfps + if("ui") + var/pickedui = input(user, "Choose your UI style.", "Character Preference") as null|anything in list("Midnight", "Plasmafire", "Retro", "Slimecore", "Operative", "Clockwork") + if(pickedui) + UI_style = pickedui + + //citadel code + if("cock_color") + var/new_cockcolor = input(user, "Penis color:", "Character Preference") as color|null + if(new_cockcolor) + var/temp_hsv = RGBtoHSV(new_cockcolor) + if(new_cockcolor == "#000000") + features["cock_color"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) + features["cock_color"] = sanitize_hexcolor(new_cockcolor) + else + user << "Invalid color. Your color is not bright enough." + + if("cock_length") + var/new_length = input(user, "Penis length in inches:\n([COCK_SIZE_MIN]-[COCK_SIZE_MAX])", "Character Preference") as num|null + if(new_length) + features["cock_length"] = max(min( round(text2num(new_length)), COCK_SIZE_MAX),COCK_SIZE_MIN) + + if("cock_shape") + var/new_shape + new_shape = input(user, "Penis shape:", "Character Preference") as null|anything in GLOB.cock_shapes_list + if(new_shape) + features["cock_shape"] = new_shape + + if("balls_color") + var/new_ballscolor = input(user, "Testicle Color:", "Character Preference") as color|null + if(new_ballscolor) + var/temp_hsv = RGBtoHSV(new_ballscolor) + if(new_ballscolor == "#000000") + features["balls_color"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) + features["balls_color"] = sanitize_hexcolor(new_ballscolor) + else + user << "Invalid color. Your color is not bright enough." + + if("egg_size") + var/new_size + var/list/egg_sizes = list(1,2,3) + new_size = input(user, "Egg Diameter(inches):", "Egg Size") as null|anything in egg_sizes + if(new_size) + features["eggsack_egg_size"] = new_size + + if("egg_color") + var/new_egg_color = input(user, "Egg Color:", "Character Preference") as color|null + if(new_egg_color) + var/temp_hsv = RGBtoHSV(new_egg_color) + if(ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) + features["eggsack_egg_color"] = sanitize_hexcolor(new_egg_color) + else + user << "Invalid color. Your color is not bright enough." + if("breasts_size") + var/new_size + new_size = input(user, "Breast Size", "Character Preference") as null|anything in GLOB.breasts_size_list + if(new_size) + features["breasts_size"] = new_size + + if("breasts_shape") + var/new_shape + new_shape = input(user, "Breast Shape", "Character Preference") as null|anything in GLOB.breasts_shapes_list + if(new_shape) + features["breasts_shape"] = new_shape + + if("breasts_color") + var/new_breasts_color = input(user, "Breast Color:", "Character Preference") as color|null + if(new_breasts_color) + var/temp_hsv = RGBtoHSV(new_breasts_color) + if(new_breasts_color == "#000000") + features["breasts_color"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) + features["breasts_color"] = sanitize_hexcolor(new_breasts_color) + else + user << "Invalid color. Your color is not bright enough." + if("vag_shape") + var/new_shape + new_shape = input(user, "Vagina Type", "Character Preference") as null|anything in GLOB.vagina_shapes_list + if(new_shape) + features["vag_shape"] = new_shape + if("vag_color") + var/new_vagcolor = input(user, "Vagina color:", "Character Preference") as color|null + if(new_vagcolor) + var/temp_hsv = RGBtoHSV(new_vagcolor) + if(new_vagcolor == "#000000") + features["vag_color"] = pref_species.default_color + else if((MUTCOLORS_PARTSONLY in pref_species.species_traits) || ReadHSV(temp_hsv)[3] >= ReadHSV("#202020")[3]) + features["vag_color"] = sanitize_hexcolor(new_vagcolor) + else + user << "Invalid color. Your color is not bright enough." + + else + switch(href_list["preference"]) + + //citadel code + if("genital_colour") + switch(features["genitals_use_skintone"]) + if(TRUE) + features["genitals_use_skintone"] = FALSE + if(FALSE) + features["genitals_use_skintone"] = TRUE + else + features["genitals_use_skintone"] = FALSE + if("arousable") + switch(arousable) + if(TRUE) + arousable = FALSE + if(FALSE) + arousable = TRUE + else//failsafe + arousable = FALSE + if("has_cock") + switch(features["has_cock"]) + if(TRUE) + features["has_cock"] = FALSE + if(FALSE) + features["has_cock"] = TRUE + features["has_ovi"] = FALSE + features["has_eggsack"] = FALSE + else + features["has_cock"] = FALSE + features["has_ovi"] = FALSE + if("has_balls") + switch(features["has_balls"]) + if(TRUE) + features["has_balls"] = FALSE + if(FALSE) + features["has_balls"] = TRUE + features["has_eggsack"] = FALSE + else + features["has_balls"] = FALSE + features["has_eggsack"] = FALSE + + if("has_ovi") + switch(features["has_ovi"]) + if(TRUE) + features["has_ovi"] = FALSE + if(FALSE) + features["has_ovi"] = TRUE + features["has_cock"] = FALSE + features["has_balls"] = FALSE + else + features["has_ovi"] = FALSE + features["has_cock"] = FALSE + + if("has_eggsack") + switch(features["has_eggsack"]) + if(TRUE) + features["has_eggsack"] = FALSE + if(FALSE) + features["has_eggsack"] = TRUE + features["has_balls"] = FALSE + else + features["has_eggsack"] = FALSE + features["has_balls"] = FALSE + + if("balls_internal") + switch(features["balls_internal"]) + if(TRUE) + features["balls_internal"] = FALSE + if(FALSE) + features["balls_internal"] = TRUE + features["eggsack_internal"] = FALSE + else + features["balls_internal"] = FALSE + features["eggsack_internal"] = FALSE + + if("eggsack_internal") + switch(features["eggsack_internal"]) + if(TRUE) + features["eggsack_internal"] = FALSE + if(FALSE) + features["eggsack_internal"] = TRUE + features["balls_internal"] = FALSE + else + features["eggsack_internal"] = FALSE + features["balls_internal"] = FALSE + + if("has_breasts") + switch(features["has_breasts"]) + if(TRUE) + features["has_breasts"] = FALSE + if(FALSE) + features["has_breasts"] = TRUE + else + features["has_breasts"] = FALSE + if("has_vag") + switch(features["has_vag"]) + if(TRUE) + features["has_vag"] = FALSE + if(FALSE) + features["has_vag"] = TRUE + else + features["has_vag"] = FALSE + if("has_womb") + switch(features["has_womb"]) + if(TRUE) + features["has_womb"] = FALSE + if(FALSE) + features["has_womb"] = TRUE + else + features["has_womb"] = FALSE + if("exhibitionist") + switch(features["exhibitionist"]) + if(TRUE) + features["exhibitionist"] = FALSE + if(FALSE) + features["exhibitionist"] = TRUE + else + features["exhibitionist"] = FALSE + + if("publicity") + if(unlock_content) + toggles ^= MEMBER_PUBLIC + if("gender") + if(gender == MALE) + gender = FEMALE + else + gender = MALE + underwear = "Nude" + undershirt = "Nude" + socks = "Nude" + facial_hair_style = "Shaved" + hair_style = "Bald" + + if("hotkeys") + hotkeys = !hotkeys + + if("tgui_fancy") + tgui_fancy = !tgui_fancy + if("tgui_lock") + tgui_lock = !tgui_lock + if("winflash") + windowflashing = !windowflashing + if("hear_adminhelps") + toggles ^= SOUND_ADMINHELP + if("announce_login") + toggles ^= ANNOUNCE_LOGIN + + if("be_special") + var/be_special_type = href_list["be_special_type"] + if(be_special_type in be_special) + be_special -= be_special_type + else + be_special += be_special_type + + if("name") + be_random_name = !be_random_name + + if("all") + be_random_body = !be_random_body + + if("hear_midis") + toggles ^= SOUND_MIDI + + if("lobby_music") + toggles ^= SOUND_LOBBY + if((toggles & SOUND_LOBBY) && user.client) + user.client.playtitlemusic() + else + user.stop_sound_channel(CHANNEL_LOBBYMUSIC) + + if("ghost_ears") + chat_toggles ^= CHAT_GHOSTEARS + + if("ghost_sight") + chat_toggles ^= CHAT_GHOSTSIGHT + + if("ghost_whispers") + chat_toggles ^= CHAT_GHOSTWHISPER + + if("ghost_radio") + chat_toggles ^= CHAT_GHOSTRADIO + + if("ghost_pda") + chat_toggles ^= CHAT_GHOSTPDA + + if("pull_requests") + chat_toggles ^= CHAT_PULLR + + if("allow_midround_antag") + toggles ^= MIDROUND_ANTAG + + if("parallaxup") + parallax = Wrap(parallax + 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) + if (parent && parent.mob && parent.mob.hud_used) + parent.mob.hud_used.update_parallax_pref(parent.mob) + + if("parallaxdown") + parallax = Wrap(parallax - 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) + if (parent && parent.mob && parent.mob.hud_used) + parent.mob.hud_used.update_parallax_pref(parent.mob) + + if("save") + save_preferences() + save_character() + + if("load") + load_preferences() + load_character() + attempt_vr(parent.prefs_vr,"load_vore","") + + if("changeslot") + attempt_vr(parent.prefs_vr,"load_vore","") + if(!load_character(text2num(href_list["num"]))) + random_character() + real_name = random_unique_name(gender) + save_character() + + if("tab") + if (href_list["tab"]) + current_tab = text2num(href_list["tab"]) + + ShowChoices(user) + return 1 + +/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1) + if(be_random_name) + real_name = pref_species.random_name(gender) + + if(be_random_body) + random_character(gender) + + if(config.humans_need_surnames) + var/firstspace = findtext(real_name, " ") + var/name_length = length(real_name) + if(!firstspace) //we need a surname + real_name += " [pick(GLOB.last_names)]" + else if(firstspace == name_length) + real_name += "[pick(GLOB.last_names)]" + + character.real_name = real_name + character.name = character.real_name + + character.gender = gender + character.age = age + + character.eye_color = eye_color + var/obj/item/organ/eyes/organ_eyes = character.getorgan(/obj/item/organ/eyes) + if(organ_eyes) + if(!initial(organ_eyes.eye_color)) + organ_eyes.eye_color = eye_color + organ_eyes.old_eye_color = eye_color + character.hair_color = hair_color + character.facial_hair_color = facial_hair_color + + character.skin_tone = skin_tone + character.hair_style = hair_style + character.facial_hair_style = facial_hair_style + character.underwear = underwear + character.undershirt = undershirt + character.socks = socks + + character.backbag = backbag + + character.dna.features = features.Copy() + character.dna.real_name = character.real_name + var/datum/species/chosen_species + if(pref_species != /datum/species/human && config.mutant_races) + chosen_species = pref_species.type + else + chosen_species = /datum/species/human + character.set_species(chosen_species, icon_update=0) + + //citadel code + character.give_genitals() + character.flavor_text = flavor_text + character.canbearoused = arousable + + if(icon_updates) + character.update_body() + character.update_hair() + character.update_body_parts() diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 80547c4db9..ce556640e0 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -161,9 +161,9 @@ /obj/item/clothing/obj_break(damage_flag) if(!damaged_clothes) update_clothes_damaged_state(TRUE) - if(ismob(loc)) //It's not important enough to warrant a message if nobody's wearing it - var/mob/M = loc - M.visible_message("[M]'s [name] starts to fall apart!", "Your [name] starts to fall apart!") + if(ismob(loc)) //It's not important enough to warrant a message if nobody's wearing it + var/mob/M = loc + M.visible_message("[M]'s [name] starts to fall apart!", "Your [name] starts to fall apart!") /obj/item/clothing/proc/update_clothes_damaged_state(damaging = TRUE) var/index = "\ref[initial(icon)]-[initial(icon_state)]" diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm index b55d826547..67ce5b3131 100644 --- a/code/modules/clothing/head/collectable.dm +++ b/code/modules/clothing/head/collectable.dm @@ -30,7 +30,7 @@ /obj/item/clothing/head/collectable/paper name = "collectable paper hat" - desc = "What looks like an ordinary paper hat is actually a rare and valuable collector's edition paper hat. Keep away from water, fire, and Curators." + desc = "What looks like an ordinary paper hat is actually a rare and valuable collector's edition paper hat. Keep away from water, fire, and Curators." icon_state = "paper" dog_fashion = /datum/dog_fashion/head diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index 65920a06d0..e457caed32 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -158,5 +158,5 @@ name = "treasure hunter's fedora" desc = "You got red text today kid, but it doesn't mean you have to like it." icon_state = "curator" - armor = list(melee = 25, bullet = 5, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0, fire = 30, acid = 50) + armor = list(melee = 25, bullet = 5, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0, fire = 30, acid = 50) pockets = /obj/item/weapon/storage/internal/pocket/small \ No newline at end of file diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index 526a15e7f5..61d9909e69 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -182,7 +182,7 @@ item_state = "curator" blood_overlay_type = "coat" body_parts_covered = CHEST|ARMS - allowed = list(/obj/item/weapon/tank/internals, /obj/item/weapon/melee/curator_whip) + allowed = list(/obj/item/weapon/tank/internals, /obj/item/weapon/melee/curator_whip) armor = list(melee = 25, bullet = 10, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 45) cold_protection = CHEST|ARMS heat_protection = CHEST|ARMS diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm index 562f59290a..0fd86219bc 100644 --- a/code/modules/detectivework/scanner.dm +++ b/code/modules/detectivework/scanner.dm @@ -13,8 +13,8 @@ var/scanning = 0 var/list/log = list() origin_tech = "engineering=4;biotech=2;programming=5" - var/range = 8 - var/view_check = TRUE + var/range = 8 + var/view_check = TRUE /obj/item/device/detective_scanner/attack_self(mob/user) if(log.len && !scanning) @@ -45,7 +45,7 @@ log = list() scanning = 0 -/obj/item/device/detective_scanner/afterattack(atom/A, mob/user, params) +/obj/item/device/detective_scanner/afterattack(atom/A, mob/user, params) scan(A, user) return FALSE @@ -53,7 +53,7 @@ set waitfor = 0 if(!scanning) // Can remotely scan objects and mobs. - if((get_dist(A, user) > range) || (!(A in view(range, user)) && view_check) || (loc != user)) + if((get_dist(A, user) > range) || (!(A in view(range, user)) && view_check) || (loc != user)) return scanning = 1 diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm index 85923811f9..53126152ab 100644 --- a/code/modules/jobs/access.dm +++ b/code/modules/jobs/access.dm @@ -459,7 +459,7 @@ GLOBAL_VAR_CONST(access_away_generic4, 208) /proc/get_all_jobs() return list("Assistant", "Captain", "Head of Personnel", "Bartender", "Cook", "Botanist", "Quartermaster", "Cargo Technician", - "Shaft Miner", "Clown", "Mime", "Janitor", "Curator", "Lawyer", "Chaplain", "Chief Engineer", "Station Engineer", + "Shaft Miner", "Clown", "Mime", "Janitor", "Curator", "Lawyer", "Chaplain", "Chief Engineer", "Station Engineer", "Atmospheric Technician", "Chief Medical Officer", "Medical Doctor", "Chemist", "Geneticist", "Virologist", "Research Director", "Scientist", "Roboticist", "Head of Security", "Warden", "Detective", "Security Officer") diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm index 9b226e70f5..53a1fed47b 100644 --- a/code/modules/jobs/jobs.dm +++ b/code/modules/jobs/jobs.dm @@ -39,7 +39,7 @@ GLOBAL_LIST_INIT(civilian_positions, list( "Botanist", "Cook", "Janitor", - "Curator", + "Curator", "Lawyer", "Chaplain", "Clown", diff --git a/code/modules/language/language.dm.rej b/code/modules/language/language.dm.rej index c7f2ac173f..e0f9bdd14f 100644 --- a/code/modules/language/language.dm.rej +++ b/code/modules/language/language.dm.rej @@ -1,15 +1,15 @@ diff a/code/modules/language/language.dm b/code/modules/language/language.dm (rejected hunks) @@ -16,10 +16,10 @@ - // If key is null, then the language isn't real or learnable. - var/flags // Various language flags. - var/list/syllables // Used when scrambling text for a non-speaker. -- var/list/sentence_chance = 5 // Likelihood of making a new sentence after each syllable. -- var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string -+ var/sentence_chance = 5 // Likelihood of making a new sentence after each syllable. -+ var/space_chance = 55 // Likelihood of getting a space in the random scramble string - var/list/spans = list() -- var/static/list/scramble_cache = list() -+ var/list/scramble_cache = list() - var/default_priority = 0 // the language that an atom knows with the highest "default_priority" is selected by default. - - // if you are seeing someone speak popcorn language, then something is wrong. + // If key is null, then the language isn't real or learnable. + var/flags // Various language flags. + var/list/syllables // Used when scrambling text for a non-speaker. +- var/list/sentence_chance = 5 // Likelihood of making a new sentence after each syllable. +- var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string ++ var/sentence_chance = 5 // Likelihood of making a new sentence after each syllable. ++ var/space_chance = 55 // Likelihood of getting a space in the random scramble string + var/list/spans = list() +- var/static/list/scramble_cache = list() ++ var/list/scramble_cache = list() + var/default_priority = 0 // the language that an atom knows with the highest "default_priority" is selected by default. + + // if you are seeing someone speak popcorn language, then something is wrong. diff --git a/code/modules/library/lib_readme.dm b/code/modules/library/lib_readme.dm index 7b59bb5dfb..57d6046f27 100644 --- a/code/modules/library/lib_readme.dm +++ b/code/modules/library/lib_readme.dm @@ -22,7 +22,7 @@ ------------ A place for the crew to go, relax, and enjoy a good book. Aspiring authors can even self publish and, if they're lucky - convince the on-staff Curator to submit it to the Archives + convince the on-staff Curator to submit it to the Archives to be chronicled in history forever - some say even persisting through alternate dimensions. @@ -49,12 +49,12 @@ // Ideas for the future // --------------------- -// - Visitor's computer should be able to search the current in-round library inventory (that the Curator has stocked and checked in) +// - Visitor's computer should be able to search the current in-round library inventory (that the Curator has stocked and checked in) // -- Give computer other features like an Instant Messenger application, or the ability to edit, save, and print documents. // - Admin interface directly tied to the Archive DB. Right now there's no way to delete uploaded books in-game. -// -- If this gets implemented, allow Curators to "tag" or "suggest" books to be deleted. The DB ID of the tagged books gets saved to a text file (or another table in the DB maybe?). +// -- If this gets implemented, allow Curators to "tag" or "suggest" books to be deleted. The DB ID of the tagged books gets saved to a text file (or another table in the DB maybe?). // The admin interface would automatically take these IDs and SELECT them all from the DB to be displayed along with a Delete link to drop the row from the table. -// - When the game sets up and the round begins, have it automatically pick random books from the DB to populate the library with. Even if the Curator is a useless fuck there are at least a few books around. +// - When the game sets up and the round begins, have it automatically pick random books from the DB to populate the library with. Even if the Curator is a useless fuck there are at least a few books around. // - Allow books to be "hollowed out" like the Chaplain's Bible, allowing you to store one pocket-sized item inside. // - Make books/book cases burn when exposed to flame. // - Make book binder hackable. diff --git a/code/modules/mapping/ruins.dm b/code/modules/mapping/ruins.dm index d08161916c..f4824a96a2 100644 --- a/code/modules/mapping/ruins.dm +++ b/code/modules/mapping/ruins.dm @@ -20,7 +20,7 @@ if(ruins && ruins.len) ruin = ruins[pick(ruins)] else - log_world("Ruin loader had no ruins to pick from with [budget] left to spend.") + log_world("Ruin loader had no ruins to pick from with [budget] left to spend.") break // Can we afford it if(ruin.cost > budget) @@ -47,7 +47,7 @@ if(!valid) continue - log_world("Ruin \"[ruin.name]\" placed at ([T.x], [T.y], [T.z])") + log_world("Ruin \"[ruin.name]\" placed at ([T.x], [T.y], [T.z])") var/obj/effect/ruin_loader/R = new /obj/effect/ruin_loader(T) R.Load(ruins,ruin) @@ -57,7 +57,7 @@ break if(!overall_sanity) - log_world("Ruin loader gave up with [budget] left to spend.") + log_world("Ruin loader gave up with [budget] left to spend.") /obj/effect/ruin_loader diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index 8675049cee..71b1355ef5 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -1,25 +1,25 @@ -/mob/dead/observer/say(message) - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - - if (!message) - return - - log_say("Ghost/[src.key] : [message]") - - . = src.say_dead(message) - -/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) - var/atom/movable/to_follow = speaker - if(radio_freq) - var/atom/movable/virtualspeaker/V = speaker - - if(isAI(V.source)) - var/mob/living/silicon/ai/S = V.source - to_follow = S.eyeobj - else - to_follow = V.source - var/link = FOLLOW_LINK(src, to_follow) - // Recompose the message, because it's scrambled by default - message = compose_message(speaker, message_language, raw_message, radio_freq, spans) - to_chat(src, "[link] [message]") - +/mob/dead/observer/say(message) + message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + + if (!message) + return + + log_say("Ghost/[src.key] : [message]") + + . = src.say_dead(message) + +/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) + var/atom/movable/to_follow = speaker + if(radio_freq) + var/atom/movable/virtualspeaker/V = speaker + + if(isAI(V.source)) + var/mob/living/silicon/ai/S = V.source + to_follow = S.eyeobj + else + to_follow = V.source + var/link = FOLLOW_LINK(src, to_follow) + // Recompose the message, because it's scrambled by default + message = compose_message(speaker, message_language, raw_message, radio_freq, spans) + to_chat(src, "[link] [message]") + diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 6e9bcc08cc..86ea3d64e9 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -1,134 +1,134 @@ -// This is to replace the previous datum/disease/alien_embryo for slightly improved handling and maintainability -// It functions almost identically (see code/datums/diseases/alien_embryo.dm) -/obj/item/organ/body_egg/alien_embryo - name = "alien embryo" - icon = 'icons/mob/alien.dmi' - icon_state = "larva0_dead" - var/stage = 0 - var/bursting = FALSE - -/obj/item/organ/body_egg/alien_embryo/on_find(mob/living/finder) - ..() - if(stage < 4) - to_chat(finder, "It's small and weak, barely the size of a foetus.") - else - to_chat(finder, "It's grown quite large, and writhes slightly as you look at it.") - if(prob(10)) - AttemptGrow(0) - -/obj/item/organ/body_egg/alien_embryo/prepare_eat() - var/obj/S = ..() - S.reagents.add_reagent("sacid", 10) - return S - -/obj/item/organ/body_egg/alien_embryo/on_life() - switch(stage) - if(2, 3) - if(prob(2)) - owner.emote("sneeze") - if(prob(2)) - owner.emote("cough") - if(prob(2)) - to_chat(owner, "Your throat feels sore.") - if(prob(2)) - to_chat(owner, "Mucous runs down the back of your throat.") - if(4) - if(prob(2)) - owner.emote("sneeze") - if(prob(2)) - owner.emote("cough") - if(prob(4)) - to_chat(owner, "Your muscles ache.") - if(prob(20)) - owner.take_bodypart_damage(1) - if(prob(4)) - to_chat(owner, "Your stomach hurts.") - if(prob(20)) - owner.adjustToxLoss(1) - if(5) - to_chat(owner, "You feel something tearing its way out of your stomach...") - owner.adjustToxLoss(10) - -/obj/item/organ/body_egg/alien_embryo/egg_process() - if(stage < 5 && prob(3)) - stage++ - INVOKE_ASYNC(src, .proc/RefreshInfectionImage) - - if(stage == 5 && prob(50)) - for(var/datum/surgery/S in owner.surgeries) - if(S.location == "chest" && istype(S.get_surgery_step(), /datum/surgery_step/manipulate_organs)) - AttemptGrow(0) - return - AttemptGrow() - - - -/obj/item/organ/body_egg/alien_embryo/proc/AttemptGrow(gib_on_success=TRUE) - if(!owner || bursting) - return - - bursting = TRUE - - var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, null, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA) - - if(QDELETED(src) || QDELETED(owner)) - return - - if(!candidates.len || !owner) - bursting = FALSE - stage = 4 - return - - var/mob/dead/observer/ghost = pick(candidates) - - var/mutable_appearance/overlay = mutable_appearance('icons/mob/alien.dmi', "burst_lie") - owner.add_overlay(overlay) - - var/atom/xeno_loc = get_turf(owner) - var/mob/living/carbon/alien/larva/new_xeno = new(xeno_loc) - new_xeno.key = ghost.key - new_xeno << sound('sound/voice/hiss5.ogg',0,0,0,100) //To get the player's attention - new_xeno.canmove = 0 //so we don't move during the bursting animation - new_xeno.notransform = 1 - new_xeno.invisibility = INVISIBILITY_MAXIMUM - - sleep(6) - - if(QDELETED(src) || QDELETED(owner)) - return - - if(new_xeno) - new_xeno.canmove = 1 - new_xeno.notransform = 0 - new_xeno.invisibility = 0 - - if(gib_on_success) - new_xeno.visible_message("[new_xeno] bursts out of [owner] in a shower of gore!", "You exit [owner], your previous host.", "You hear organic matter ripping and tearing!") - owner.gib(TRUE) - else - new_xeno.visible_message("[new_xeno] wriggles out of [owner]!", "You exit [owner], your previous host.") - owner.adjustBruteLoss(40) - owner.cut_overlay(overlay) - qdel(src) - - -/*---------------------------------------- -Proc: AddInfectionImages(C) -Des: Adds the infection image to all aliens for this embryo -----------------------------------------*/ -/obj/item/organ/body_egg/alien_embryo/AddInfectionImages() - for(var/mob/living/carbon/alien/alien in GLOB.player_list) - if(alien.client) - var/I = image('icons/mob/alien.dmi', loc = owner, icon_state = "infected[stage]") - alien.client.images += I - -/*---------------------------------------- -Proc: RemoveInfectionImage(C) -Des: Removes all images from the mob infected by this embryo -----------------------------------------*/ -/obj/item/organ/body_egg/alien_embryo/RemoveInfectionImages() - for(var/mob/living/carbon/alien/alien in GLOB.player_list) - if(alien.client) - for(var/image/I in alien.client.images) - if(dd_hasprefix_case(I.icon_state, "infected") && I.loc == owner) - qdel(I) +// This is to replace the previous datum/disease/alien_embryo for slightly improved handling and maintainability +// It functions almost identically (see code/datums/diseases/alien_embryo.dm) +/obj/item/organ/body_egg/alien_embryo + name = "alien embryo" + icon = 'icons/mob/alien.dmi' + icon_state = "larva0_dead" + var/stage = 0 + var/bursting = FALSE + +/obj/item/organ/body_egg/alien_embryo/on_find(mob/living/finder) + ..() + if(stage < 4) + to_chat(finder, "It's small and weak, barely the size of a foetus.") + else + to_chat(finder, "It's grown quite large, and writhes slightly as you look at it.") + if(prob(10)) + AttemptGrow(0) + +/obj/item/organ/body_egg/alien_embryo/prepare_eat() + var/obj/S = ..() + S.reagents.add_reagent("sacid", 10) + return S + +/obj/item/organ/body_egg/alien_embryo/on_life() + switch(stage) + if(2, 3) + if(prob(2)) + owner.emote("sneeze") + if(prob(2)) + owner.emote("cough") + if(prob(2)) + to_chat(owner, "Your throat feels sore.") + if(prob(2)) + to_chat(owner, "Mucous runs down the back of your throat.") + if(4) + if(prob(2)) + owner.emote("sneeze") + if(prob(2)) + owner.emote("cough") + if(prob(4)) + to_chat(owner, "Your muscles ache.") + if(prob(20)) + owner.take_bodypart_damage(1) + if(prob(4)) + to_chat(owner, "Your stomach hurts.") + if(prob(20)) + owner.adjustToxLoss(1) + if(5) + to_chat(owner, "You feel something tearing its way out of your stomach...") + owner.adjustToxLoss(10) + +/obj/item/organ/body_egg/alien_embryo/egg_process() + if(stage < 5 && prob(3)) + stage++ + INVOKE_ASYNC(src, .proc/RefreshInfectionImage) + + if(stage == 5 && prob(50)) + for(var/datum/surgery/S in owner.surgeries) + if(S.location == "chest" && istype(S.get_surgery_step(), /datum/surgery_step/manipulate_organs)) + AttemptGrow(0) + return + AttemptGrow() + + + +/obj/item/organ/body_egg/alien_embryo/proc/AttemptGrow(gib_on_success=TRUE) + if(!owner || bursting) + return + + bursting = TRUE + + var/list/candidates = pollGhostCandidates("Do you want to play as an alien larva that will burst out of [owner]?", ROLE_ALIEN, null, ROLE_ALIEN, 100, POLL_IGNORE_ALIEN_LARVA) + + if(QDELETED(src) || QDELETED(owner)) + return + + if(!candidates.len || !owner) + bursting = FALSE + stage = 4 + return + + var/mob/dead/observer/ghost = pick(candidates) + + var/mutable_appearance/overlay = mutable_appearance('icons/mob/alien.dmi', "burst_lie") + owner.add_overlay(overlay) + + var/atom/xeno_loc = get_turf(owner) + var/mob/living/carbon/alien/larva/new_xeno = new(xeno_loc) + new_xeno.key = ghost.key + new_xeno << sound('sound/voice/hiss5.ogg',0,0,0,100) //To get the player's attention + new_xeno.canmove = 0 //so we don't move during the bursting animation + new_xeno.notransform = 1 + new_xeno.invisibility = INVISIBILITY_MAXIMUM + + sleep(6) + + if(QDELETED(src) || QDELETED(owner)) + return + + if(new_xeno) + new_xeno.canmove = 1 + new_xeno.notransform = 0 + new_xeno.invisibility = 0 + + if(gib_on_success) + new_xeno.visible_message("[new_xeno] bursts out of [owner] in a shower of gore!", "You exit [owner], your previous host.", "You hear organic matter ripping and tearing!") + owner.gib(TRUE) + else + new_xeno.visible_message("[new_xeno] wriggles out of [owner]!", "You exit [owner], your previous host.") + owner.adjustBruteLoss(40) + owner.cut_overlay(overlay) + qdel(src) + + +/*---------------------------------------- +Proc: AddInfectionImages(C) +Des: Adds the infection image to all aliens for this embryo +----------------------------------------*/ +/obj/item/organ/body_egg/alien_embryo/AddInfectionImages() + for(var/mob/living/carbon/alien/alien in GLOB.player_list) + if(alien.client) + var/I = image('icons/mob/alien.dmi', loc = owner, icon_state = "infected[stage]") + alien.client.images += I + +/*---------------------------------------- +Proc: RemoveInfectionImage(C) +Des: Removes all images from the mob infected by this embryo +----------------------------------------*/ +/obj/item/organ/body_egg/alien_embryo/RemoveInfectionImages() + for(var/mob/living/carbon/alien/alien in GLOB.player_list) + if(alien.client) + for(var/image/I in alien.client.images) + if(dd_hasprefix_case(I.icon_state, "infected") && I.loc == owner) + qdel(I) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index b778279f42..651d788fbd 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -171,8 +171,8 @@ dna.species.mutant_bodyparts -= "wingsopen" dna.species.mutant_bodyparts |= "wings" update_body() - if(isturf(loc)) - var/turf/T = loc - T.Entered(src) + if(isturf(loc)) + var/turf/T = loc + T.Entered(src) //Ayy lmao diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 8a62b4e058..f697215a4a 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -48,4 +48,4 @@ var/datum/personal_crafting/handcrafting can_buckle = TRUE buckle_lying = FALSE - can_ride_typecache = list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/simple_animal/parrot) + can_ride_typecache = list(/mob/living/carbon/human, /mob/living/simple_animal/slime, /mob/living/simple_animal/parrot) diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 77ac31812a..21e1a5648e 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -142,12 +142,12 @@ return not_handled //For future deeper overrides /mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE) - var/index = get_held_index_of_item(I) + var/index = get_held_index_of_item(I) . = ..() //See mob.dm for an explanation on this and some rage about people copypasting instead of calling ..() like they should. if(!. || !I) return - if(index && dna.species.mutanthands) - put_in_hand(new dna.species.mutanthands(), index) + if(index && dna.species.mutanthands) + put_in_hand(new dna.species.mutanthands(), index) if(I == wear_suit) if(s_store && invdrop) dropItemToGround(s_store, TRUE) //It makes no sense for your suit storage to stay on you if you drop your suit. diff --git a/code/modules/mob/living/carbon/human/species_types/flypeople.dm b/code/modules/mob/living/carbon/human/species_types/flypeople.dm index 3d6a10aae9..5c56a34a20 100644 --- a/code/modules/mob/living/carbon/human/species_types/flypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/flypeople.dm @@ -2,7 +2,7 @@ name = "Flyperson" id = "fly" say_mod = "buzzes" - mutanttongue = /obj/item/organ/tongue/fly + mutanttongue = /obj/item/organ/tongue/fly meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant/fly /datum/species/fly/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index e56b2221ba..65bfaf3fd5 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -1,663 +1,663 @@ -/datum/species/golem - // Animated beings of stone. They have increased defenses, and do not need to breathe. They're also slow as fuuuck. - name = "Golem" - id = "iron golem" - species_traits = list(NOBREATH,RESISTHOT,RESISTCOLD,RESISTPRESSURE,NOFIRE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER,MUTCOLORS) - speedmod = 2 - armor = 55 - siemens_coeff = 0 - punchdamagelow = 5 - punchdamagehigh = 14 - punchstunthreshold = 11 //about 40% chance to stun - no_equip = list(slot_wear_mask, slot_wear_suit, slot_gloves, slot_shoes, slot_w_uniform, slot_s_store) - nojumpsuit = 1 - sexes = 1 - damage_overlay_type = "" - meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant/golem - // To prevent golem subtypes from overwhelming the odds when random species - // changes, only the Random Golem type can be chosen - blacklisted = TRUE - dangerous_existence = TRUE - limbs_id = "golem" - fixed_mut_color = "aaa" - mutant_organs = list(/obj/item/organ/adamantine_resonator) - var/info_text = "As an Iron Golem, you don't have any special traits." - - var/prefix = "Iron" - var/list/special_names - -/datum/species/golem/random_name(gender,unique,lastname) - var/golem_surname = pick(GLOB.golem_names) - // 3% chance that our golem has a human surname, because - // cultural contamination - if(prob(3)) - golem_surname = pick(GLOB.last_names) - else if(special_names && prob(5)) - golem_surname = pick(special_names) - - var/golem_name = "[prefix] [golem_surname]" - return golem_name - -/datum/species/golem/random - name = "Random Golem" - blacklisted = FALSE - dangerous_existence = FALSE - -/datum/species/golem/random/on_species_gain(mob/living/carbon/C, datum/species/old_species) - ..() - var/list/golem_types = typesof(/datum/species/golem) - src.type - var/datum/species/golem/golem_type = pick(golem_types) - var/mob/living/carbon/human/H = C - H.set_species(golem_type) - to_chat(H, "[initial(golem_type.info_text)]") - -/datum/species/golem/adamantine - name = "Adamantine Golem" - id = "adamantine golem" - meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant/golem/adamantine - mutant_organs = list(/obj/item/organ/adamantine_resonator, /obj/item/organ/vocal_cords/adamantine) - fixed_mut_color = "4ed" - info_text = "As an Adamantine Golem, you possess special vocal cords allowing you to \"resonate\" messages to all golems." - prefix = "Adamantine" - -//Explodes on death -/datum/species/golem/plasma - name = "Plasma Golem" - id = "plasma golem" - fixed_mut_color = "a3d" - meat = /obj/item/weapon/ore/plasma - //Can burn and takes damage from heat - species_traits = list(NOBREATH,RESISTCOLD,RESISTPRESSURE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER,MUTCOLORS) - info_text = "As a Plasma Golem, you explode on death!" - burnmod = 1.5 - prefix = "Plasma" - special_names = list("Flood","Fire","Bar","Man") - -/datum/species/golem/plasma/spec_life(mob/living/carbon/human/H) - if(H.bodytemperature > 900 && H.on_fire) - explosion(get_turf(H),1,2,4,flame_range = 5) - if(H) - H.gib() - if(H.fire_stacks < 2) //flammable - H.adjust_fire_stacks(1) - ..() - -//Harder to hurt -/datum/species/golem/diamond - name = "Diamond Golem" - id = "diamond golem" - fixed_mut_color = "0ff" - armor = 70 //up from 55 - meat = /obj/item/weapon/ore/diamond - info_text = "As a Diamond Golem, you are more resistant than the average golem." - prefix = "Diamond" - special_names = list("Back") - -//Faster but softer and less armoured -/datum/species/golem/gold - name = "Gold Golem" - id = "gold golem" - fixed_mut_color = "cc0" - speedmod = 1 - armor = 25 //down from 55 - meat = /obj/item/weapon/ore/gold - info_text = "As a Gold Golem, you are faster but less resistant than the average golem." - prefix = "Golden" - -//Heavier, thus higher chance of stunning when punching -/datum/species/golem/silver - name = "Silver Golem" - id = "silver golem" - fixed_mut_color = "ddd" - punchstunthreshold = 9 //60% chance, from 40% - meat = /obj/item/weapon/ore/silver - info_text = "As a Silver Golem, your attacks are heavier and have a higher chance of stunning." - prefix = "Silver" - special_names = list("Surfer", "Chariot", "Lining") - -//Harder to stun, deals more damage, but it's even slower -/datum/species/golem/plasteel - name = "Plasteel Golem" - id = "plasteel golem" - fixed_mut_color = "bbb" - stunmod = 0.40 - punchdamagelow = 12 - punchdamagehigh = 21 - punchstunthreshold = 18 //still 40% stun chance - speedmod = 4 //pretty fucking slow - meat = /obj/item/weapon/ore/iron - info_text = "As a Plasteel Golem, you are slower, but harder to stun, and hit very hard when punching." - attack_verb = "smash" - attack_sound = 'sound/effects/meteorimpact.ogg' //hits pretty hard - prefix = "Plasteel" - -//Immune to ash storms -/datum/species/golem/titanium - name = "Titanium Golem" - id = "titanium golem" - fixed_mut_color = "fff" - meat = /obj/item/weapon/ore/titanium - info_text = "As a Titanium Golem, you are immune to ash storms, and slightly more resistant to burn damage." - burnmod = 0.9 - prefix = "Titanium" - -/datum/species/golem/titanium/on_species_gain(mob/living/carbon/C, datum/species/old_species) - . = ..() - C.weather_immunities |= "ash" - -/datum/species/golem/titanium/on_species_loss(mob/living/carbon/C) - . = ..() - C.weather_immunities -= "ash" - -//Immune to ash storms and lava -/datum/species/golem/plastitanium - name = "Plastitanium Golem" - id = "plastitanium golem" - fixed_mut_color = "888" - meat = /obj/item/weapon/ore/titanium - info_text = "As a Plastitanium Golem, you are immune to both ash storms and lava, and slightly more resistant to burn damage." - burnmod = 0.8 - prefix = "Plastitanium" - -/datum/species/golem/plastitanium/on_species_gain(mob/living/carbon/C, datum/species/old_species) - . = ..() - C.weather_immunities |= "lava" - C.weather_immunities |= "ash" - -/datum/species/golem/plastitanium/on_species_loss(mob/living/carbon/C) - . = ..() - C.weather_immunities -= "ash" - C.weather_immunities -= "lava" - -//Fast and regenerates... but can only speak like an abductor -/datum/species/golem/alloy - name = "Alien Alloy Golem" - id = "alloy golem" - fixed_mut_color = "333" - meat = /obj/item/stack/sheet/mineral/abductor - mutant_organs = list(/obj/item/organ/tongue/abductor) //abductor tongue - speedmod = 1 //faster - info_text = "As an Alloy Golem, you are made of advanced alien materials: you are faster and regenerate over time. You are, however, only able to be heard by other alloy golems." - prefix = "Alien" - special_names = list("Outsider", "Technology", "Watcher", "Stranger") //ominous and unknown - -//Regenerates because self-repairing super-advanced alien tech -/datum/species/golem/alloy/spec_life(mob/living/carbon/human/H) - if(H.stat == DEAD) - return - H.heal_overall_damage(2,2) - H.adjustToxLoss(-2) - H.adjustOxyLoss(-2) - -//Since this will usually be created from a collaboration between podpeople and free golems, wood golems are a mix between the two races -/datum/species/golem/wood - name = "Wood Golem" - id = "wood golem" - fixed_mut_color = "49311c" - meat = /obj/item/stack/sheet/mineral/wood - //Can burn and take damage from heat - species_traits = list(NOBREATH,RESISTCOLD,RESISTPRESSURE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER,MUTCOLORS) - armor = 30 - burnmod = 1.25 - heatmod = 1.5 - info_text = "As a Wooden Golem, you have plant-like traits: you take damage from extreme temperatures, can be set on fire, and have lower armor than a normal golem. You regenerate when in the light and wither in the darkness." - prefix = "Wooden" - -/datum/species/golem/wood/random_name(gender,unique,lastname) - var/plant_name = pick("Tomato", "Potato", "Broccoli", "Carrot", "Ambrosia", "Pumpkin", "Ivy", "Kudzu", "Banana", "Moss", "Flower", "Bloom", "Root", "Bark", "Glowshroom", "Petal", "Leaf", \ - "Venus", "Sprout","Cocoa", "Strawberry", "Citrus", "Oak", "Cactus", "Pepper", "Juniper") - var/golem_name = "[prefix] [plant_name]" - return golem_name - -/datum/species/golem/wood/on_species_gain(mob/living/carbon/C, datum/species/old_species) - . = ..() - C.faction |= "plants" - C.faction |= "vines" - -/datum/species/golem/wood/on_species_loss(mob/living/carbon/C) - . = ..() - C.faction -= "plants" - C.faction -= "vines" - -/datum/species/golem/wood/spec_life(mob/living/carbon/human/H) - if(H.stat == DEAD) - return - var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing - if(isturf(H.loc)) //else, there's considered to be no light - var/turf/T = H.loc - light_amount = min(1,T.get_lumcount()) - 0.5 - H.nutrition += light_amount * 10 - if(H.nutrition > NUTRITION_LEVEL_FULL) - H.nutrition = NUTRITION_LEVEL_FULL - if(light_amount > 0.2) //if there's enough light, heal - H.heal_overall_damage(1,1) - H.adjustToxLoss(-1) - H.adjustOxyLoss(-1) - - if(H.nutrition < NUTRITION_LEVEL_STARVING + 50) - H.take_overall_damage(2,0) - -/datum/species/golem/wood/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) - if(chem.id == "plantbgone") - H.adjustToxLoss(3) - H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM) - return 1 - -//Radioactive -/datum/species/golem/uranium - name = "Uranium Golem" - id = "uranium golem" - fixed_mut_color = "7f0" - meat = /obj/item/weapon/ore/uranium - info_text = "As an Uranium Golem, you emit radiation pulses every once in a while. It won't harm fellow golems, but organic lifeforms will be affected." - - var/last_event = 0 - var/active = null - prefix = "Uranium" - -/datum/species/golem/uranium/spec_life(mob/living/carbon/human/H) - if(!active) - if(world.time > last_event+30) - active = 1 - radiation_pulse(get_turf(H), 3, 3, 5, 0) - last_event = world.time - active = null - ..() - -//Immune to physical bullets and resistant to brute, but very vulnerable to burn damage. Dusts on death. -/datum/species/golem/sand - name = "Sand Golem" - id = "sand golem" - fixed_mut_color = "ffdc8f" - meat = /obj/item/weapon/ore/glass //this is sand - armor = 0 - burnmod = 3 //melts easily - brutemod = 0.25 - info_text = "As a Sand Golem, you are immune to physical bullets and take very little brute damage, but are extremely vulnerable to burn damage. You will also turn to sand when dying, preventing any form of recovery." - attack_sound = 'sound/effects/shovel_dig.ogg' - prefix = "Sand" - -/datum/species/golem/sand/spec_death(gibbed, mob/living/carbon/human/H) - H.visible_message("[H] turns into a pile of sand!") - for(var/obj/item/W in H) - H.dropItemToGround(W) - for(var/i=1, i <= rand(3,5), i++) - new /obj/item/weapon/ore/glass(get_turf(H)) - qdel(H) - -/datum/species/golem/sand/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H) - if(!(P.original == H && P.firer == H)) - if(P.flag == "bullet" || P.flag == "bomb") - playsound(H, 'sound/effects/shovel_dig.ogg', 70, 1) - H.visible_message("The [P.name] sinks harmlessly in [H]'s sandy body!", \ - "The [P.name] sinks harmlessly in [H]'s sandy body!") - return 2 - return 0 - -//Reflects lasers and resistant to burn damage, but very vulnerable to brute damage. Shatters on death. -/datum/species/golem/glass - name = "Glass Golem" - id = "glass golem" - fixed_mut_color = "5a96b4aa" //transparent body - meat = /obj/item/weapon/shard - armor = 0 - brutemod = 3 //very fragile - burnmod = 0.25 - info_text = "As a Glass Golem, you reflect lasers and energy weapons, and are very resistant to burn damage, but you are extremely vulnerable to brute damage. On death, you'll shatter beyond any hope of recovery." - attack_sound = 'sound/effects/Glassbr2.ogg' - prefix = "Glass" - -/datum/species/golem/glass/spec_death(gibbed, mob/living/carbon/human/H) - playsound(H, "shatter", 70, 1) - H.visible_message("[H] shatters!") - for(var/obj/item/W in H) - H.dropItemToGround(W) - for(var/i=1, i <= rand(3,5), i++) - new /obj/item/weapon/shard(get_turf(H)) - qdel(H) - -/datum/species/golem/glass/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H) - if(!(P.original == H && P.firer == H)) //self-shots don't reflect - if(P.flag == "laser" || P.flag == "energy") - H.visible_message("The [P.name] gets reflected by [H]'s glass skin!", \ - "The [P.name] gets reflected by [H]'s glass skin!") - if(P.starting) - var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(H) - - // redirect the projectile - P.original = locate(new_x, new_y, P.z) - P.starting = curloc - P.current = curloc - P.firer = H - P.yo = new_y - curloc.y - P.xo = new_x - curloc.x - P.Angle = null - return -1 - return 0 - -//Teleports when hit or when it wants to -/datum/species/golem/bluespace - name = "Bluespace Golem" - id = "bluespace golem" - fixed_mut_color = "33f" - meat = /obj/item/weapon/ore/bluespace_crystal - info_text = "As a Bluespace Golem, are spatially unstable: you will teleport when hit, and you can teleport manually at a long distance." - attack_verb = "bluespace punch" - attack_sound = 'sound/effects/phasein.ogg' - prefix = "Bluespace" - special_names = list("Crystal", "Polycrystal") - - var/datum/action/innate/unstable_teleport/unstable_teleport - var/teleport_cooldown = 100 - var/last_teleport = 0 - -/datum/species/golem/bluespace/proc/reactive_teleport(mob/living/carbon/human/H) - H.visible_message("[H] teleports!", "You destabilize and teleport!") - new /obj/effect/particle_effect/sparks(get_turf(H)) - playsound(get_turf(H), "sparks", 50, 1) - do_teleport(H, get_turf(H), 6, asoundin = 'sound/weapons/emitter2.ogg') - last_teleport = world.time - -/datum/species/golem/bluespace/spec_hitby(atom/movable/AM, mob/living/carbon/human/H) - ..() - var/obj/item/I - if(istype(AM, /obj/item)) - I = AM - if(I.thrownby == H) //No throwing stuff at yourself to trigger the teleport - return 0 - else - reactive_teleport(H) - -/datum/species/golem/bluespace/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) - ..() - if(world.time > last_teleport + teleport_cooldown && M != H && M.a_intent != INTENT_HELP) - reactive_teleport(H) - -/datum/species/golem/bluespace/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H) - ..() - if(world.time > last_teleport + teleport_cooldown && user != H) - reactive_teleport(H) - -/datum/species/golem/bluespace/on_hit(obj/item/projectile/P, mob/living/carbon/human/H) - ..() - if(world.time > last_teleport + teleport_cooldown) - reactive_teleport(H) - -/datum/species/golem/bluespace/on_species_gain(mob/living/carbon/C, datum/species/old_species) - ..() - if(ishuman(C)) - unstable_teleport = new - unstable_teleport.Grant(C) - -/datum/species/golem/bluespace/on_species_loss(mob/living/carbon/C) - if(unstable_teleport) - unstable_teleport.Remove(C) - ..() - -/datum/action/innate/unstable_teleport - name = "Unstable Teleport" - check_flags = AB_CHECK_CONSCIOUS - button_icon_state = "jaunt" - var/cooldown = 150 - var/last_teleport = 0 - -/datum/action/innate/unstable_teleport/IsAvailable() - if(..()) - if(world.time > last_teleport + cooldown) - return 1 - return 0 - -/datum/action/innate/unstable_teleport/Activate() - var/mob/living/carbon/human/H = owner - H.visible_message("[H] starts vibrating!", "You start charging your bluespace core...") - playsound(get_turf(H), 'sound/weapons/flash.ogg', 25, 1) - addtimer(CALLBACK(src, .proc/teleport, H), 15) - -/datum/action/innate/unstable_teleport/proc/teleport(mob/living/carbon/human/H) - H.visible_message("[H] disappears in a shower of sparks!", "You teleport!") - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread - spark_system.set_up(10, 0, src) - spark_system.attach(H) - spark_system.start() - do_teleport(H, get_turf(H), 12, asoundin = 'sound/weapons/emitter2.ogg') - last_teleport = world.time - UpdateButtonIcon() //action icon looks unavailable - sleep(cooldown + 5) - UpdateButtonIcon() //action icon looks available again - - -//honk -/datum/species/golem/bananium - name = "Bananium Golem" - id = "bananium golem" - fixed_mut_color = "ff0" - say_mod = "honks" - punchdamagelow = 0 - punchdamagehigh = 1 - punchstunthreshold = 2 //Harmless and can't stun - meat = /obj/item/weapon/ore/bananium - info_text = "As a Bananium Golem, you are made for pranking. Your body emits natural honks, and you cannot hurt people when punching them. Your skin also emits bananas when damaged." - attack_verb = "honk" - attack_sound = 'sound/items/AirHorn2.ogg' - prefix = "Bananium" - - var/last_honk = 0 - var/honkooldown = 0 - var/last_banana = 0 - var/banana_cooldown = 100 - var/active = null - -/datum/species/golem/bananium/random_name(gender,unique,lastname) - var/clown_name = pick(GLOB.clown_names) - var/golem_name = "[uppertext(clown_name)]" - return golem_name - -/datum/species/golem/bananium/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) - ..() - if(world.time > last_banana + banana_cooldown && M != H && M.a_intent != INTENT_HELP) - new/obj/item/weapon/grown/bananapeel/specialpeel(get_turf(H)) - last_banana = world.time - -/datum/species/golem/bananium/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H) - ..() - if(world.time > last_banana + banana_cooldown && user != H) - new/obj/item/weapon/grown/bananapeel/specialpeel(get_turf(H)) - last_banana = world.time - -/datum/species/golem/bananium/on_hit(obj/item/projectile/P, mob/living/carbon/human/H) - ..() - if(world.time > last_banana + banana_cooldown) - new/obj/item/weapon/grown/bananapeel/specialpeel(get_turf(H)) - last_banana = world.time - -/datum/species/golem/bananium/spec_hitby(atom/movable/AM, mob/living/carbon/human/H) - ..() - var/obj/item/I - if(istype(AM, /obj/item)) - I = AM - if(I.thrownby == H) //No throwing stuff at yourself to make bananas - return 0 - else - new/obj/item/weapon/grown/bananapeel/specialpeel(get_turf(H)) - last_banana = world.time - -/datum/species/golem/bananium/spec_life(mob/living/carbon/human/H) - if(!active) - if(world.time > last_honk + honkooldown) - active = 1 - playsound(get_turf(H), 'sound/items/bikehorn.ogg', 50, 1) - last_honk = world.time - honkooldown = rand(20, 80) - active = null - ..() - -/datum/species/golem/bananium/spec_death(gibbed, mob/living/carbon/human/H) - playsound(get_turf(H), 'sound/misc/sadtrombone.ogg', 70, 0) - -/datum/species/golem/bananium/get_spans() - return list(SPAN_CLOWN) - - -/datum/species/golem/runic - name = "Runic Golem" - id = "runic golem" - limbs_id = "cultgolem" - sexes = FALSE - info_text = "As a Runic Golem, you possess eldritch powers granted by the Elder God Nar'Sie." - species_traits = list(NOBREATH,RESISTHOT,RESISTCOLD,RESISTPRESSURE,NOFIRE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER) //no mutcolors - prefix = "Runic" - - var/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/golem/phase_shift - var/obj/effect/proc_holder/spell/targeted/abyssal_gaze/abyssal_gaze - var/obj/effect/proc_holder/spell/targeted/dominate/dominate - -/datum/species/golem/runic/random_name(gender,unique,lastname) - var/edgy_first_name = pick("Razor","Blood","Dark","Evil","Cold","Pale","Black","Silent","Chaos","Deadly") - var/edgy_last_name = pick("Edge","Night","Death","Razor","Blade","Steel","Calamity","Twilight","Shadow","Nightmare") //dammit Razor Razor - var/golem_name = "[edgy_first_name] [edgy_last_name]" - return golem_name - -/datum/species/golem/runic/on_species_gain(mob/living/carbon/C, datum/species/old_species) - . = ..() - C.faction |= "cult" - phase_shift = new - C.AddSpell(phase_shift) - abyssal_gaze = new - C.AddSpell(abyssal_gaze) - dominate = new - C.AddSpell(dominate) - -/datum/species/golem/runic/on_species_loss(mob/living/carbon/C) - . = ..() - C.faction -= "cult" - if(phase_shift) - C.RemoveSpell(phase_shift) - if(abyssal_gaze) - C.RemoveSpell(abyssal_gaze) - if(dominate) - C.RemoveSpell(dominate) - -/datum/species/golem/runic/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) - if(chem.id == "holywater") - H.adjustFireLoss(4) - H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM) - - if(chem.id == "unholywater") - H.adjustBruteLoss(-4) - H.adjustFireLoss(-4) - H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM) - -/datum/species/golem/cloth - name = "Cloth Golem" - id = "cloth golem" - limbs_id = "clothgolem" - sexes = FALSE - info_text = "As a Cloth Golem, you are able to reform yourself after death, provided your remains aren't burned or destroyed. You are, of course, very flammable." - species_traits = list(NOBREATH,RESISTCOLD,RESISTPRESSURE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER) //no mutcolors, and can burn - armor = 15 //feels no pain, but not too resistant - burnmod = 2 // don't get burned - speedmod = 1 // not as heavy as stone - punchdamagelow = 4 - punchstunthreshold = 7 - punchdamagehigh = 8 // not as heavy as stone - prefix = "Cloth" - -/datum/species/golem/cloth/random_name(gender,unique,lastname) - var/pharaoh_name = pick("Neferkare", "Hudjefa", "Khufu", "Mentuhotep", "Ahmose", "Amenhotep", "Thutmose", "Hatshepsut", "Tutankhamun", "Ramses", "Seti", \ - "Merenptah", "Djer", "Semerkhet", "Nynetjer", "Khafre", "Pepi", "Intef", "Ay") //yes, Ay was an actual pharaoh - var/golem_name = "[pharaoh_name] \Roman[rand(1,99)]" - return golem_name - -/datum/species/golem/cloth/spec_life(mob/living/carbon/human/H) - if(H.fire_stacks < 1) - H.adjust_fire_stacks(1) //always prone to burning - ..() - -/datum/species/golem/cloth/spec_death(gibbed, mob/living/carbon/human/H) - if(gibbed) - return - if(H.on_fire) - H.visible_message("[H] burns into ash!") - H.dust(just_ash = TRUE) - return - - H.visible_message("[H] falls apart into a pile of bandages!") - new /obj/structure/cloth_pile(get_turf(H), H) - ..() - -/obj/structure/cloth_pile - name = "pile of bandages" - desc = "It emits a strange aura, as if there was still life within it..." - obj_integrity = 50 - max_integrity = 50 - armor = list(melee = 90, bullet = 90, laser = 25, energy = 80, bomb = 50, bio = 100, fire = -50, acid = -50) - icon = 'icons/obj/items.dmi' - icon_state = "pile_bandages" - resistance_flags = FLAMMABLE - - var/revive_time = 900 - var/mob/living/carbon/human/cloth_golem - -/obj/structure/cloth_pile/Initialize(mapload, mob/living/carbon/human/H) - if(!QDELETED(H) && is_species(H, /datum/species/golem/cloth)) - H.unequip_everything() - H.forceMove(src) - cloth_golem = H - to_chat(cloth_golem, "You start gathering your life energy, preparing to rise again...") - addtimer(CALLBACK(src, .proc/revive), revive_time) - else - qdel(src) - -/obj/structure/cloth_pile/Destroy() - if(cloth_golem) - QDEL_NULL(cloth_golem) - return ..() - -/obj/structure/cloth_pile/burn() - visible_message("[src] burns into ash!") - new /obj/effect/decal/cleanable/ash(get_turf(src)) - ..() - -/obj/structure/cloth_pile/proc/revive() - if(QDELETED(src) || QDELETED(cloth_golem)) //QDELETED also checks for null, so if no cloth golem is set this won't runtime - return - if(cloth_golem.suiciding || cloth_golem.disabilities & NOCLONE) - QDEL_NULL(cloth_golem) - return - - invisibility = INVISIBILITY_MAXIMUM //disappear before the animation - new /obj/effect/overlay/temp/mummy_animation(get_turf(src)) - if(cloth_golem.revive(full_heal = TRUE, admin_revive = TRUE)) - cloth_golem.grab_ghost() //won't pull if it's a suicide - sleep(20) - cloth_golem.forceMove(get_turf(src)) - cloth_golem.visible_message("[src] rises and reforms into [cloth_golem]!","You reform into yourself!") - cloth_golem = null - qdel(src) - -/obj/structure/cloth_pile/attackby(obj/item/weapon/P, mob/living/carbon/human/user, params) - . = ..() - - if(resistance_flags & ON_FIRE) - return - - if(P.is_hot()) - visible_message("[src] bursts into flames!") - - fire_act() -/datum/species/golem/plastic - name = "Plastic" - id = "plastic golem" - prefix = "Plastic" - fixed_mut_color = "fff" - info_text = "As a Plastic Golem, you are capable of ventcrawling, and passing through plastic flaps." - -/datum/species/golem/plastic/on_species_gain(mob/living/carbon/C, datum/species/old_species) - . = ..() - C.ventcrawler = VENTCRAWLER_NUDE - -/datum/species/golem/plastic/on_species_loss(mob/living/carbon/C) - . = ..() - C.ventcrawler = initial(C.ventcrawler) +/datum/species/golem + // Animated beings of stone. They have increased defenses, and do not need to breathe. They're also slow as fuuuck. + name = "Golem" + id = "iron golem" + species_traits = list(NOBREATH,RESISTHOT,RESISTCOLD,RESISTPRESSURE,NOFIRE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER,MUTCOLORS) + speedmod = 2 + armor = 55 + siemens_coeff = 0 + punchdamagelow = 5 + punchdamagehigh = 14 + punchstunthreshold = 11 //about 40% chance to stun + no_equip = list(slot_wear_mask, slot_wear_suit, slot_gloves, slot_shoes, slot_w_uniform, slot_s_store) + nojumpsuit = 1 + sexes = 1 + damage_overlay_type = "" + meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant/golem + // To prevent golem subtypes from overwhelming the odds when random species + // changes, only the Random Golem type can be chosen + blacklisted = TRUE + dangerous_existence = TRUE + limbs_id = "golem" + fixed_mut_color = "aaa" + mutant_organs = list(/obj/item/organ/adamantine_resonator) + var/info_text = "As an Iron Golem, you don't have any special traits." + + var/prefix = "Iron" + var/list/special_names + +/datum/species/golem/random_name(gender,unique,lastname) + var/golem_surname = pick(GLOB.golem_names) + // 3% chance that our golem has a human surname, because + // cultural contamination + if(prob(3)) + golem_surname = pick(GLOB.last_names) + else if(special_names && prob(5)) + golem_surname = pick(special_names) + + var/golem_name = "[prefix] [golem_surname]" + return golem_name + +/datum/species/golem/random + name = "Random Golem" + blacklisted = FALSE + dangerous_existence = FALSE + +/datum/species/golem/random/on_species_gain(mob/living/carbon/C, datum/species/old_species) + ..() + var/list/golem_types = typesof(/datum/species/golem) - src.type + var/datum/species/golem/golem_type = pick(golem_types) + var/mob/living/carbon/human/H = C + H.set_species(golem_type) + to_chat(H, "[initial(golem_type.info_text)]") + +/datum/species/golem/adamantine + name = "Adamantine Golem" + id = "adamantine golem" + meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant/golem/adamantine + mutant_organs = list(/obj/item/organ/adamantine_resonator, /obj/item/organ/vocal_cords/adamantine) + fixed_mut_color = "4ed" + info_text = "As an Adamantine Golem, you possess special vocal cords allowing you to \"resonate\" messages to all golems." + prefix = "Adamantine" + +//Explodes on death +/datum/species/golem/plasma + name = "Plasma Golem" + id = "plasma golem" + fixed_mut_color = "a3d" + meat = /obj/item/weapon/ore/plasma + //Can burn and takes damage from heat + species_traits = list(NOBREATH,RESISTCOLD,RESISTPRESSURE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER,MUTCOLORS) + info_text = "As a Plasma Golem, you explode on death!" + burnmod = 1.5 + prefix = "Plasma" + special_names = list("Flood","Fire","Bar","Man") + +/datum/species/golem/plasma/spec_life(mob/living/carbon/human/H) + if(H.bodytemperature > 900 && H.on_fire) + explosion(get_turf(H),1,2,4,flame_range = 5) + if(H) + H.gib() + if(H.fire_stacks < 2) //flammable + H.adjust_fire_stacks(1) + ..() + +//Harder to hurt +/datum/species/golem/diamond + name = "Diamond Golem" + id = "diamond golem" + fixed_mut_color = "0ff" + armor = 70 //up from 55 + meat = /obj/item/weapon/ore/diamond + info_text = "As a Diamond Golem, you are more resistant than the average golem." + prefix = "Diamond" + special_names = list("Back") + +//Faster but softer and less armoured +/datum/species/golem/gold + name = "Gold Golem" + id = "gold golem" + fixed_mut_color = "cc0" + speedmod = 1 + armor = 25 //down from 55 + meat = /obj/item/weapon/ore/gold + info_text = "As a Gold Golem, you are faster but less resistant than the average golem." + prefix = "Golden" + +//Heavier, thus higher chance of stunning when punching +/datum/species/golem/silver + name = "Silver Golem" + id = "silver golem" + fixed_mut_color = "ddd" + punchstunthreshold = 9 //60% chance, from 40% + meat = /obj/item/weapon/ore/silver + info_text = "As a Silver Golem, your attacks are heavier and have a higher chance of stunning." + prefix = "Silver" + special_names = list("Surfer", "Chariot", "Lining") + +//Harder to stun, deals more damage, but it's even slower +/datum/species/golem/plasteel + name = "Plasteel Golem" + id = "plasteel golem" + fixed_mut_color = "bbb" + stunmod = 0.40 + punchdamagelow = 12 + punchdamagehigh = 21 + punchstunthreshold = 18 //still 40% stun chance + speedmod = 4 //pretty fucking slow + meat = /obj/item/weapon/ore/iron + info_text = "As a Plasteel Golem, you are slower, but harder to stun, and hit very hard when punching." + attack_verb = "smash" + attack_sound = 'sound/effects/meteorimpact.ogg' //hits pretty hard + prefix = "Plasteel" + +//Immune to ash storms +/datum/species/golem/titanium + name = "Titanium Golem" + id = "titanium golem" + fixed_mut_color = "fff" + meat = /obj/item/weapon/ore/titanium + info_text = "As a Titanium Golem, you are immune to ash storms, and slightly more resistant to burn damage." + burnmod = 0.9 + prefix = "Titanium" + +/datum/species/golem/titanium/on_species_gain(mob/living/carbon/C, datum/species/old_species) + . = ..() + C.weather_immunities |= "ash" + +/datum/species/golem/titanium/on_species_loss(mob/living/carbon/C) + . = ..() + C.weather_immunities -= "ash" + +//Immune to ash storms and lava +/datum/species/golem/plastitanium + name = "Plastitanium Golem" + id = "plastitanium golem" + fixed_mut_color = "888" + meat = /obj/item/weapon/ore/titanium + info_text = "As a Plastitanium Golem, you are immune to both ash storms and lava, and slightly more resistant to burn damage." + burnmod = 0.8 + prefix = "Plastitanium" + +/datum/species/golem/plastitanium/on_species_gain(mob/living/carbon/C, datum/species/old_species) + . = ..() + C.weather_immunities |= "lava" + C.weather_immunities |= "ash" + +/datum/species/golem/plastitanium/on_species_loss(mob/living/carbon/C) + . = ..() + C.weather_immunities -= "ash" + C.weather_immunities -= "lava" + +//Fast and regenerates... but can only speak like an abductor +/datum/species/golem/alloy + name = "Alien Alloy Golem" + id = "alloy golem" + fixed_mut_color = "333" + meat = /obj/item/stack/sheet/mineral/abductor + mutant_organs = list(/obj/item/organ/tongue/abductor) //abductor tongue + speedmod = 1 //faster + info_text = "As an Alloy Golem, you are made of advanced alien materials: you are faster and regenerate over time. You are, however, only able to be heard by other alloy golems." + prefix = "Alien" + special_names = list("Outsider", "Technology", "Watcher", "Stranger") //ominous and unknown + +//Regenerates because self-repairing super-advanced alien tech +/datum/species/golem/alloy/spec_life(mob/living/carbon/human/H) + if(H.stat == DEAD) + return + H.heal_overall_damage(2,2) + H.adjustToxLoss(-2) + H.adjustOxyLoss(-2) + +//Since this will usually be created from a collaboration between podpeople and free golems, wood golems are a mix between the two races +/datum/species/golem/wood + name = "Wood Golem" + id = "wood golem" + fixed_mut_color = "49311c" + meat = /obj/item/stack/sheet/mineral/wood + //Can burn and take damage from heat + species_traits = list(NOBREATH,RESISTCOLD,RESISTPRESSURE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER,MUTCOLORS) + armor = 30 + burnmod = 1.25 + heatmod = 1.5 + info_text = "As a Wooden Golem, you have plant-like traits: you take damage from extreme temperatures, can be set on fire, and have lower armor than a normal golem. You regenerate when in the light and wither in the darkness." + prefix = "Wooden" + +/datum/species/golem/wood/random_name(gender,unique,lastname) + var/plant_name = pick("Tomato", "Potato", "Broccoli", "Carrot", "Ambrosia", "Pumpkin", "Ivy", "Kudzu", "Banana", "Moss", "Flower", "Bloom", "Root", "Bark", "Glowshroom", "Petal", "Leaf", \ + "Venus", "Sprout","Cocoa", "Strawberry", "Citrus", "Oak", "Cactus", "Pepper", "Juniper") + var/golem_name = "[prefix] [plant_name]" + return golem_name + +/datum/species/golem/wood/on_species_gain(mob/living/carbon/C, datum/species/old_species) + . = ..() + C.faction |= "plants" + C.faction |= "vines" + +/datum/species/golem/wood/on_species_loss(mob/living/carbon/C) + . = ..() + C.faction -= "plants" + C.faction -= "vines" + +/datum/species/golem/wood/spec_life(mob/living/carbon/human/H) + if(H.stat == DEAD) + return + var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing + if(isturf(H.loc)) //else, there's considered to be no light + var/turf/T = H.loc + light_amount = min(1,T.get_lumcount()) - 0.5 + H.nutrition += light_amount * 10 + if(H.nutrition > NUTRITION_LEVEL_FULL) + H.nutrition = NUTRITION_LEVEL_FULL + if(light_amount > 0.2) //if there's enough light, heal + H.heal_overall_damage(1,1) + H.adjustToxLoss(-1) + H.adjustOxyLoss(-1) + + if(H.nutrition < NUTRITION_LEVEL_STARVING + 50) + H.take_overall_damage(2,0) + +/datum/species/golem/wood/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) + if(chem.id == "plantbgone") + H.adjustToxLoss(3) + H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM) + return 1 + +//Radioactive +/datum/species/golem/uranium + name = "Uranium Golem" + id = "uranium golem" + fixed_mut_color = "7f0" + meat = /obj/item/weapon/ore/uranium + info_text = "As an Uranium Golem, you emit radiation pulses every once in a while. It won't harm fellow golems, but organic lifeforms will be affected." + + var/last_event = 0 + var/active = null + prefix = "Uranium" + +/datum/species/golem/uranium/spec_life(mob/living/carbon/human/H) + if(!active) + if(world.time > last_event+30) + active = 1 + radiation_pulse(get_turf(H), 3, 3, 5, 0) + last_event = world.time + active = null + ..() + +//Immune to physical bullets and resistant to brute, but very vulnerable to burn damage. Dusts on death. +/datum/species/golem/sand + name = "Sand Golem" + id = "sand golem" + fixed_mut_color = "ffdc8f" + meat = /obj/item/weapon/ore/glass //this is sand + armor = 0 + burnmod = 3 //melts easily + brutemod = 0.25 + info_text = "As a Sand Golem, you are immune to physical bullets and take very little brute damage, but are extremely vulnerable to burn damage. You will also turn to sand when dying, preventing any form of recovery." + attack_sound = 'sound/effects/shovel_dig.ogg' + prefix = "Sand" + +/datum/species/golem/sand/spec_death(gibbed, mob/living/carbon/human/H) + H.visible_message("[H] turns into a pile of sand!") + for(var/obj/item/W in H) + H.dropItemToGround(W) + for(var/i=1, i <= rand(3,5), i++) + new /obj/item/weapon/ore/glass(get_turf(H)) + qdel(H) + +/datum/species/golem/sand/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H) + if(!(P.original == H && P.firer == H)) + if(P.flag == "bullet" || P.flag == "bomb") + playsound(H, 'sound/effects/shovel_dig.ogg', 70, 1) + H.visible_message("The [P.name] sinks harmlessly in [H]'s sandy body!", \ + "The [P.name] sinks harmlessly in [H]'s sandy body!") + return 2 + return 0 + +//Reflects lasers and resistant to burn damage, but very vulnerable to brute damage. Shatters on death. +/datum/species/golem/glass + name = "Glass Golem" + id = "glass golem" + fixed_mut_color = "5a96b4aa" //transparent body + meat = /obj/item/weapon/shard + armor = 0 + brutemod = 3 //very fragile + burnmod = 0.25 + info_text = "As a Glass Golem, you reflect lasers and energy weapons, and are very resistant to burn damage, but you are extremely vulnerable to brute damage. On death, you'll shatter beyond any hope of recovery." + attack_sound = 'sound/effects/Glassbr2.ogg' + prefix = "Glass" + +/datum/species/golem/glass/spec_death(gibbed, mob/living/carbon/human/H) + playsound(H, "shatter", 70, 1) + H.visible_message("[H] shatters!") + for(var/obj/item/W in H) + H.dropItemToGround(W) + for(var/i=1, i <= rand(3,5), i++) + new /obj/item/weapon/shard(get_turf(H)) + qdel(H) + +/datum/species/golem/glass/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H) + if(!(P.original == H && P.firer == H)) //self-shots don't reflect + if(P.flag == "laser" || P.flag == "energy") + H.visible_message("The [P.name] gets reflected by [H]'s glass skin!", \ + "The [P.name] gets reflected by [H]'s glass skin!") + if(P.starting) + var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(H) + + // redirect the projectile + P.original = locate(new_x, new_y, P.z) + P.starting = curloc + P.current = curloc + P.firer = H + P.yo = new_y - curloc.y + P.xo = new_x - curloc.x + P.Angle = null + return -1 + return 0 + +//Teleports when hit or when it wants to +/datum/species/golem/bluespace + name = "Bluespace Golem" + id = "bluespace golem" + fixed_mut_color = "33f" + meat = /obj/item/weapon/ore/bluespace_crystal + info_text = "As a Bluespace Golem, are spatially unstable: you will teleport when hit, and you can teleport manually at a long distance." + attack_verb = "bluespace punch" + attack_sound = 'sound/effects/phasein.ogg' + prefix = "Bluespace" + special_names = list("Crystal", "Polycrystal") + + var/datum/action/innate/unstable_teleport/unstable_teleport + var/teleport_cooldown = 100 + var/last_teleport = 0 + +/datum/species/golem/bluespace/proc/reactive_teleport(mob/living/carbon/human/H) + H.visible_message("[H] teleports!", "You destabilize and teleport!") + new /obj/effect/particle_effect/sparks(get_turf(H)) + playsound(get_turf(H), "sparks", 50, 1) + do_teleport(H, get_turf(H), 6, asoundin = 'sound/weapons/emitter2.ogg') + last_teleport = world.time + +/datum/species/golem/bluespace/spec_hitby(atom/movable/AM, mob/living/carbon/human/H) + ..() + var/obj/item/I + if(istype(AM, /obj/item)) + I = AM + if(I.thrownby == H) //No throwing stuff at yourself to trigger the teleport + return 0 + else + reactive_teleport(H) + +/datum/species/golem/bluespace/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) + ..() + if(world.time > last_teleport + teleport_cooldown && M != H && M.a_intent != INTENT_HELP) + reactive_teleport(H) + +/datum/species/golem/bluespace/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H) + ..() + if(world.time > last_teleport + teleport_cooldown && user != H) + reactive_teleport(H) + +/datum/species/golem/bluespace/on_hit(obj/item/projectile/P, mob/living/carbon/human/H) + ..() + if(world.time > last_teleport + teleport_cooldown) + reactive_teleport(H) + +/datum/species/golem/bluespace/on_species_gain(mob/living/carbon/C, datum/species/old_species) + ..() + if(ishuman(C)) + unstable_teleport = new + unstable_teleport.Grant(C) + +/datum/species/golem/bluespace/on_species_loss(mob/living/carbon/C) + if(unstable_teleport) + unstable_teleport.Remove(C) + ..() + +/datum/action/innate/unstable_teleport + name = "Unstable Teleport" + check_flags = AB_CHECK_CONSCIOUS + button_icon_state = "jaunt" + var/cooldown = 150 + var/last_teleport = 0 + +/datum/action/innate/unstable_teleport/IsAvailable() + if(..()) + if(world.time > last_teleport + cooldown) + return 1 + return 0 + +/datum/action/innate/unstable_teleport/Activate() + var/mob/living/carbon/human/H = owner + H.visible_message("[H] starts vibrating!", "You start charging your bluespace core...") + playsound(get_turf(H), 'sound/weapons/flash.ogg', 25, 1) + addtimer(CALLBACK(src, .proc/teleport, H), 15) + +/datum/action/innate/unstable_teleport/proc/teleport(mob/living/carbon/human/H) + H.visible_message("[H] disappears in a shower of sparks!", "You teleport!") + var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread + spark_system.set_up(10, 0, src) + spark_system.attach(H) + spark_system.start() + do_teleport(H, get_turf(H), 12, asoundin = 'sound/weapons/emitter2.ogg') + last_teleport = world.time + UpdateButtonIcon() //action icon looks unavailable + sleep(cooldown + 5) + UpdateButtonIcon() //action icon looks available again + + +//honk +/datum/species/golem/bananium + name = "Bananium Golem" + id = "bananium golem" + fixed_mut_color = "ff0" + say_mod = "honks" + punchdamagelow = 0 + punchdamagehigh = 1 + punchstunthreshold = 2 //Harmless and can't stun + meat = /obj/item/weapon/ore/bananium + info_text = "As a Bananium Golem, you are made for pranking. Your body emits natural honks, and you cannot hurt people when punching them. Your skin also emits bananas when damaged." + attack_verb = "honk" + attack_sound = 'sound/items/AirHorn2.ogg' + prefix = "Bananium" + + var/last_honk = 0 + var/honkooldown = 0 + var/last_banana = 0 + var/banana_cooldown = 100 + var/active = null + +/datum/species/golem/bananium/random_name(gender,unique,lastname) + var/clown_name = pick(GLOB.clown_names) + var/golem_name = "[uppertext(clown_name)]" + return golem_name + +/datum/species/golem/bananium/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style = M.martial_art) + ..() + if(world.time > last_banana + banana_cooldown && M != H && M.a_intent != INTENT_HELP) + new/obj/item/weapon/grown/bananapeel/specialpeel(get_turf(H)) + last_banana = world.time + +/datum/species/golem/bananium/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H) + ..() + if(world.time > last_banana + banana_cooldown && user != H) + new/obj/item/weapon/grown/bananapeel/specialpeel(get_turf(H)) + last_banana = world.time + +/datum/species/golem/bananium/on_hit(obj/item/projectile/P, mob/living/carbon/human/H) + ..() + if(world.time > last_banana + banana_cooldown) + new/obj/item/weapon/grown/bananapeel/specialpeel(get_turf(H)) + last_banana = world.time + +/datum/species/golem/bananium/spec_hitby(atom/movable/AM, mob/living/carbon/human/H) + ..() + var/obj/item/I + if(istype(AM, /obj/item)) + I = AM + if(I.thrownby == H) //No throwing stuff at yourself to make bananas + return 0 + else + new/obj/item/weapon/grown/bananapeel/specialpeel(get_turf(H)) + last_banana = world.time + +/datum/species/golem/bananium/spec_life(mob/living/carbon/human/H) + if(!active) + if(world.time > last_honk + honkooldown) + active = 1 + playsound(get_turf(H), 'sound/items/bikehorn.ogg', 50, 1) + last_honk = world.time + honkooldown = rand(20, 80) + active = null + ..() + +/datum/species/golem/bananium/spec_death(gibbed, mob/living/carbon/human/H) + playsound(get_turf(H), 'sound/misc/sadtrombone.ogg', 70, 0) + +/datum/species/golem/bananium/get_spans() + return list(SPAN_CLOWN) + + +/datum/species/golem/runic + name = "Runic Golem" + id = "runic golem" + limbs_id = "cultgolem" + sexes = FALSE + info_text = "As a Runic Golem, you possess eldritch powers granted by the Elder God Nar'Sie." + species_traits = list(NOBREATH,RESISTHOT,RESISTCOLD,RESISTPRESSURE,NOFIRE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER) //no mutcolors + prefix = "Runic" + + var/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/golem/phase_shift + var/obj/effect/proc_holder/spell/targeted/abyssal_gaze/abyssal_gaze + var/obj/effect/proc_holder/spell/targeted/dominate/dominate + +/datum/species/golem/runic/random_name(gender,unique,lastname) + var/edgy_first_name = pick("Razor","Blood","Dark","Evil","Cold","Pale","Black","Silent","Chaos","Deadly") + var/edgy_last_name = pick("Edge","Night","Death","Razor","Blade","Steel","Calamity","Twilight","Shadow","Nightmare") //dammit Razor Razor + var/golem_name = "[edgy_first_name] [edgy_last_name]" + return golem_name + +/datum/species/golem/runic/on_species_gain(mob/living/carbon/C, datum/species/old_species) + . = ..() + C.faction |= "cult" + phase_shift = new + C.AddSpell(phase_shift) + abyssal_gaze = new + C.AddSpell(abyssal_gaze) + dominate = new + C.AddSpell(dominate) + +/datum/species/golem/runic/on_species_loss(mob/living/carbon/C) + . = ..() + C.faction -= "cult" + if(phase_shift) + C.RemoveSpell(phase_shift) + if(abyssal_gaze) + C.RemoveSpell(abyssal_gaze) + if(dominate) + C.RemoveSpell(dominate) + +/datum/species/golem/runic/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) + if(chem.id == "holywater") + H.adjustFireLoss(4) + H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM) + + if(chem.id == "unholywater") + H.adjustBruteLoss(-4) + H.adjustFireLoss(-4) + H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM) + +/datum/species/golem/cloth + name = "Cloth Golem" + id = "cloth golem" + limbs_id = "clothgolem" + sexes = FALSE + info_text = "As a Cloth Golem, you are able to reform yourself after death, provided your remains aren't burned or destroyed. You are, of course, very flammable." + species_traits = list(NOBREATH,RESISTCOLD,RESISTPRESSURE,NOGUNS,NOBLOOD,RADIMMUNE,VIRUSIMMUNE,PIERCEIMMUNE,NODISMEMBER) //no mutcolors, and can burn + armor = 15 //feels no pain, but not too resistant + burnmod = 2 // don't get burned + speedmod = 1 // not as heavy as stone + punchdamagelow = 4 + punchstunthreshold = 7 + punchdamagehigh = 8 // not as heavy as stone + prefix = "Cloth" + +/datum/species/golem/cloth/random_name(gender,unique,lastname) + var/pharaoh_name = pick("Neferkare", "Hudjefa", "Khufu", "Mentuhotep", "Ahmose", "Amenhotep", "Thutmose", "Hatshepsut", "Tutankhamun", "Ramses", "Seti", \ + "Merenptah", "Djer", "Semerkhet", "Nynetjer", "Khafre", "Pepi", "Intef", "Ay") //yes, Ay was an actual pharaoh + var/golem_name = "[pharaoh_name] \Roman[rand(1,99)]" + return golem_name + +/datum/species/golem/cloth/spec_life(mob/living/carbon/human/H) + if(H.fire_stacks < 1) + H.adjust_fire_stacks(1) //always prone to burning + ..() + +/datum/species/golem/cloth/spec_death(gibbed, mob/living/carbon/human/H) + if(gibbed) + return + if(H.on_fire) + H.visible_message("[H] burns into ash!") + H.dust(just_ash = TRUE) + return + + H.visible_message("[H] falls apart into a pile of bandages!") + new /obj/structure/cloth_pile(get_turf(H), H) + ..() + +/obj/structure/cloth_pile + name = "pile of bandages" + desc = "It emits a strange aura, as if there was still life within it..." + obj_integrity = 50 + max_integrity = 50 + armor = list(melee = 90, bullet = 90, laser = 25, energy = 80, bomb = 50, bio = 100, fire = -50, acid = -50) + icon = 'icons/obj/items.dmi' + icon_state = "pile_bandages" + resistance_flags = FLAMMABLE + + var/revive_time = 900 + var/mob/living/carbon/human/cloth_golem + +/obj/structure/cloth_pile/Initialize(mapload, mob/living/carbon/human/H) + if(!QDELETED(H) && is_species(H, /datum/species/golem/cloth)) + H.unequip_everything() + H.forceMove(src) + cloth_golem = H + to_chat(cloth_golem, "You start gathering your life energy, preparing to rise again...") + addtimer(CALLBACK(src, .proc/revive), revive_time) + else + qdel(src) + +/obj/structure/cloth_pile/Destroy() + if(cloth_golem) + QDEL_NULL(cloth_golem) + return ..() + +/obj/structure/cloth_pile/burn() + visible_message("[src] burns into ash!") + new /obj/effect/decal/cleanable/ash(get_turf(src)) + ..() + +/obj/structure/cloth_pile/proc/revive() + if(QDELETED(src) || QDELETED(cloth_golem)) //QDELETED also checks for null, so if no cloth golem is set this won't runtime + return + if(cloth_golem.suiciding || cloth_golem.disabilities & NOCLONE) + QDEL_NULL(cloth_golem) + return + + invisibility = INVISIBILITY_MAXIMUM //disappear before the animation + new /obj/effect/overlay/temp/mummy_animation(get_turf(src)) + if(cloth_golem.revive(full_heal = TRUE, admin_revive = TRUE)) + cloth_golem.grab_ghost() //won't pull if it's a suicide + sleep(20) + cloth_golem.forceMove(get_turf(src)) + cloth_golem.visible_message("[src] rises and reforms into [cloth_golem]!","You reform into yourself!") + cloth_golem = null + qdel(src) + +/obj/structure/cloth_pile/attackby(obj/item/weapon/P, mob/living/carbon/human/user, params) + . = ..() + + if(resistance_flags & ON_FIRE) + return + + if(P.is_hot()) + visible_message("[src] bursts into flames!") + + fire_act() +/datum/species/golem/plastic + name = "Plastic" + id = "plastic golem" + prefix = "Plastic" + fixed_mut_color = "fff" + info_text = "As a Plastic Golem, you are capable of ventcrawling, and passing through plastic flaps." + +/datum/species/golem/plastic/on_species_gain(mob/living/carbon/C, datum/species/old_species) + . = ..() + C.ventcrawler = VENTCRAWLER_NUDE + +/datum/species/golem/plastic/on_species_loss(mob/living/carbon/C) + . = ..() + C.ventcrawler = initial(C.ventcrawler) diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index ca4deab288..8adc1f19f3 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -17,9 +17,9 @@ skinned_type = /obj/item/stack/sheet/animalhide/lizard exotic_bloodtype = "L" -/datum/species/lizard/after_equip_job(datum/job/J, mob/living/carbon/human/H) - H.grant_language(/datum/language/draconic) - +/datum/species/lizard/after_equip_job(datum/job/J, mob/living/carbon/human/H) + H.grant_language(/datum/language/draconic) + /datum/species/lizard/random_name(gender,unique,lastname) if(unique) return random_unique_lizard_name(gender) diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm index 6b56fa9ca4..239d901e5c 100644 --- a/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -13,11 +13,11 @@ name = "Infectious Zombie" id = "memezombies" limbs_id = "zombie" - mutanthands = /obj/item/zombie_hand + mutanthands = /obj/item/zombie_hand no_equip = list(slot_wear_mask, slot_head) armor = 20 // 120 damage to KO a zombie, which kills it speedmod = 2 - mutanteyes = /obj/item/organ/eyes/night_vision/zombie + mutanteyes = /obj/item/organ/eyes/night_vision/zombie /datum/species/zombie/infectious/spec_life(mob/living/carbon/C) . = ..() @@ -30,14 +30,14 @@ /datum/species/zombie/infectious/on_species_gain(mob/living/carbon/C, datum/species/old_species) . = ..() - // Deal with the source of this zombie corruption - // Infection organ needs to be handled separately from mutant_organs - // because it persists through species transitions + // Deal with the source of this zombie corruption + // Infection organ needs to be handled separately from mutant_organs + // because it persists through species transitions var/obj/item/organ/zombie_infection/infection infection = C.getorganslot("zombie_infection") if(!infection) - infection = new() - infection.Insert(C) + infection = new() + infection.Insert(C) // Your skin falls off diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index d908ed547d..4be1dfe851 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -49,7 +49,7 @@ timeofdeath = world.time tod = worldtime2text() var/turf/T = get_turf(src) - var/area/A = get_area(T) + var/area/A = get_area(T) if(mind && mind.name && mind.active && (!(T.flags & NO_DEATHRATTLE))) var/rendered = "[mind.name] has died at [A.name]." deadchat_broadcast(rendered, follow_target = src, turf_target = T, message_type=DEADCHAT_DEATHRATTLE) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index bb88f69781..096a4ab71e 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -6,7 +6,7 @@ verb_ask = "queries" verb_exclaim = "declares" verb_yell = "alarms" - initial_language_holder = /datum/language_holder/synthetic + initial_language_holder = /datum/language_holder/synthetic see_in_dark = 8 bubble_icon = "machine" weather_immunities = list("ash") diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index ba4e3dc68c..cdb292b3a5 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -20,7 +20,7 @@ verb_ask = "queries" verb_exclaim = "declares" verb_yell = "alarms" - initial_language_holder = /datum/language_holder/synthetic + initial_language_holder = /datum/language_holder/synthetic bubble_icon = "machine" faction = list("neutral", "silicon" , "turret") @@ -287,13 +287,13 @@ to_chat(user, "The welder must be on for this task!") else if(W.force) //if force is non-zero - do_sparks(5, TRUE, src) + do_sparks(5, TRUE, src) ..() /mob/living/simple_animal/bot/bullet_act(obj/item/projectile/Proj) if(Proj && (Proj.damage_type == BRUTE || Proj.damage_type == BURN)) if(prob(75) && Proj.damage > 0) - do_sparks(5, TRUE, src) + do_sparks(5, TRUE, src) return ..() /mob/living/simple_animal/bot/emp_act(severity) diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index c30f52aa5c..f384e70381 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -397,7 +397,7 @@ Auto Patrol: []"}, if(prob(50)) new /obj/item/bodypart/l_arm/robot(Tsec) - do_sparks(3, TRUE, src) + do_sparks(3, TRUE, src) new /obj/effect/decal/cleanable/oil(loc) ..() diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index c08f291cee..159aec35af 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -216,7 +216,7 @@ if(change) if(change > 0) if(M && stat != DEAD) - new /obj/effect/overlay/temp/heart(loc) + new /obj/effect/overlay/temp/heart(loc) emote("me", 1, "purrs!") else if(M && stat != DEAD) diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index 427d9ddaa4..c516a637e2 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -51,12 +51,12 @@ src.visible_message("[src] calms down.") if(stat == CONSCIOUS) udder.generateMilk() - eat_plants() + eat_plants() if(!pulledby) for(var/direction in shuffle(list(1,2,4,8,5,6,9,10))) var/step = get_step(src, direction) if(step) - if(locate(/obj/structure/spacevine) in step || locate(/obj/structure/glowshroom) in step) + if(locate(/obj/structure/spacevine) in step || locate(/obj/structure/glowshroom) in step) Move(step, get_dir(src, step)) /mob/living/simple_animal/hostile/retaliate/goat/Retaliate() @@ -66,22 +66,22 @@ /mob/living/simple_animal/hostile/retaliate/goat/Move() ..() if(!stat) - eat_plants() - -/mob/living/simple_animal/hostile/retaliate/goat/proc/eat_plants() - var/eaten = FALSE - var/obj/structure/spacevine/SV = locate(/obj/structure/spacevine) in loc - if(SV) - SV.eat(src) - eaten = TRUE - - var/obj/structure/glowshroom/GS = locate(/obj/structure/glowshroom) in loc - if(GS) - qdel(GS) - eaten = TRUE - - if(eaten && prob(10)) - say("Nom") + eat_plants() + +/mob/living/simple_animal/hostile/retaliate/goat/proc/eat_plants() + var/eaten = FALSE + var/obj/structure/spacevine/SV = locate(/obj/structure/spacevine) in loc + if(SV) + SV.eat(src) + eaten = TRUE + + var/obj/structure/glowshroom/GS = locate(/obj/structure/glowshroom) in loc + if(GS) + qdel(GS) + eaten = TRUE + + if(eaten && prob(10)) + say("Nom") /mob/living/simple_animal/hostile/retaliate/goat/attackby(obj/item/O, mob/user, params) if(stat == CONSCIOUS && istype(O, /obj/item/weapon/reagent_containers/glass)) @@ -311,7 +311,7 @@ name = "udder" /obj/item/udder/Initialize() - create_reagents(50) + create_reagents(50) reagents.add_reagent("milk", 20) ..() diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm index 5454f3c553..266c9b1860 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm @@ -53,5 +53,5 @@ ranged = 1 /mob/living/simple_animal/hostile/hivebot/death(gibbed) - do_sparks(3, TRUE, src) + do_sparks(3, TRUE, src) ..(1) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index 4eaa10195b..6ebac2394c 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -28,8 +28,8 @@ status_flags = CANPUSH movement_type = FLYING loot = list(/obj/item/weapon/ectoplasm) - del_on_death = TRUE - initial_language_holder = /datum/language_holder/construct + del_on_death = TRUE + initial_language_holder = /datum/language_holder/construct /mob/living/simple_animal/shade/death() deathmessage = "lets out a contented sigh as [p_their()] form unwinds." diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index de3d48f28b..ca2bcd3ba2 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -12,20 +12,20 @@ slot_flags = SLOT_BELT resistance_flags = FLAMMABLE -/obj/item/weapon/clipboard/Initialize() +/obj/item/weapon/clipboard/Initialize() update_icon() - . = ..() + . = ..() -/obj/item/weapon/clipboard/Destroy() - QDEL_NULL(haspen) - QDEL_NULL(toppaper) //let movable/Destroy handle the rest - return ..() +/obj/item/weapon/clipboard/Destroy() + QDEL_NULL(haspen) + QDEL_NULL(toppaper) //let movable/Destroy handle the rest + return ..() /obj/item/weapon/clipboard/update_icon() cut_overlays() if(toppaper) add_overlay(toppaper.icon_state) - copy_overlays(toppaper) + copy_overlays(toppaper) if(haspen) add_overlay("clipboard_pen") add_overlay("clipboard_over") @@ -119,4 +119,4 @@ //Update everything attack_self(usr) - update_icon() + update_icon() diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index b7a298d6b3..9d37173c9f 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -1,493 +1,493 @@ -/obj/machinery/power/emitter - name = "Emitter" - desc = "A heavy duty industrial laser.\nAlt-click to rotate it clockwise." - icon = 'icons/obj/singularity.dmi' - icon_state = "emitter" - var/icon_state_on = "emitter_+a" - anchored = 0 - density = 1 - req_access = list(GLOB.access_engine_equip) - - // The following 3 vars are mostly for the prototype - var/manual = FALSE - var/charge = 0 - var/atom/target = null - - use_power = 0 - idle_power_usage = 10 - active_power_usage = 300 - - var/active = 0 - var/powered = 0 - var/fire_delay = 100 - var/maximum_fire_delay = 100 - var/minimum_fire_delay = 20 - var/last_shot = 0 - var/shot_number = 0 - var/state = 0 - var/locked = 0 - - var/projectile_type = /obj/item/projectile/beam/emitter - - var/projectile_sound = 'sound/weapons/emitter.ogg' - - var/datum/effect_system/spark_spread/sparks - -/obj/machinery/power/emitter/New() - ..() - var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/emitter(null) - B.apply_default_parts(src) - RefreshParts() - wires = new /datum/wires/emitter(src) - -/obj/item/weapon/circuitboard/machine/emitter - name = "Emitter (Machine Board)" - build_path = /obj/machinery/power/emitter - origin_tech = "programming=3;powerstorage=4;engineering=4" - req_components = list( - /obj/item/weapon/stock_parts/micro_laser = 1, - /obj/item/weapon/stock_parts/manipulator = 1) - -/obj/machinery/power/emitter/RefreshParts() - var/max_firedelay = 120 - var/firedelay = 120 - var/min_firedelay = 24 - var/power_usage = 350 - for(var/obj/item/weapon/stock_parts/micro_laser/L in component_parts) - max_firedelay -= 20 * L.rating - min_firedelay -= 4 * L.rating - firedelay -= 20 * L.rating - maximum_fire_delay = max_firedelay - minimum_fire_delay = min_firedelay - fire_delay = firedelay - for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) - power_usage -= 50 * M.rating - active_power_usage = power_usage - -/obj/machinery/power/emitter/verb/rotate() - set name = "Rotate" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - if (src.anchored) - to_chat(usr, "It is fastened to the floor!") - return 0 - src.setDir(turn(src.dir, 270)) - return 1 - -/obj/machinery/power/emitter/AltClick(mob/user) - ..() - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user)) - return - else - rotate() - -/obj/machinery/power/emitter/Initialize() - . = ..() - if(state == 2 && anchored) - connect_to_network() - - sparks = new - sparks.attach(src) - sparks.set_up(5, TRUE, src) - -/obj/machinery/power/emitter/Destroy() - if(SSticker && SSticker.IsRoundInProgress()) - message_admins("Emitter deleted at ([x],[y],[z] - JMP)",0,1) - log_game("Emitter deleted at ([x],[y],[z])") - investigate_log("deleted at ([x],[y],[z]) at [get_area(src)]","singulo") - QDEL_NULL(sparks) - return ..() - -/obj/machinery/power/emitter/update_icon() - if (active && powernet && avail(active_power_usage)) - icon_state = icon_state_on - else - icon_state = initial(icon_state) - - -/obj/machinery/power/emitter/attack_hand(mob/user) - src.add_fingerprint(user) - if(state == 2) - if(!powernet) - to_chat(user, "The emitter isn't connected to a wire!") - return 1 - if(!src.locked) - if(src.active==1) - src.active = 0 - to_chat(user, "You turn off \the [src].") - message_admins("Emitter turned off by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(src)]",0,1) - log_game("Emitter turned off by [key_name(user)] in [COORD(src)]") - investigate_log("turned off by [key_name(user)] at [get_area(src)]","singulo") - else - src.active = 1 - to_chat(user, "You turn on \the [src].") - src.shot_number = 0 - src.fire_delay = maximum_fire_delay - investigate_log("turned on by [key_name(user)] at [get_area(src)]","singulo") - update_icon() - else - to_chat(user, "The controls are locked!") - else - to_chat(user, "The [src] needs to be firmly secured to the floor first!") - return 1 - -/obj/machinery/power/emitter/attack_animal(mob/living/simple_animal/M) - if(ismegafauna(M) && anchored) - state = 0 - anchored = FALSE - M.visible_message("[M] rips [src] free from its moorings!") - else - ..() - if(!anchored) - step(src, get_dir(M, src)) - - -/obj/machinery/power/emitter/emp_act(severity)//Emitters are hardened but still might have issues -// add_load(1000) -/* if((severity == 1)&&prob(1)&&prob(1)) - if(src.active) - src.active = 0 - src.use_power = 1 */ - return 1 - - -/obj/machinery/power/emitter/process() - if(stat & (BROKEN)) - return - if(src.state != 2 || (!powernet && active_power_usage)) - src.active = 0 - update_icon() - return - if(src.active == 1) - if(!active_power_usage || avail(active_power_usage)) - add_load(active_power_usage) - if(!powered) - powered = 1 - update_icon() - investigate_log("regained power and turned on at [get_area(src)]","singulo") - else - if(powered) - powered = 0 - update_icon() - investigate_log("lost power and turned off at [get_area(src)]","singulo") - log_game("Emitter lost power in ([x],[y],[z])") - return - if(charge <=80) - charge+=5 - if(!check_delay() || manual == TRUE) - return FALSE - fire_beam(target) - -/obj/machinery/power/emitter/proc/check_delay() - if((src.last_shot + src.fire_delay) <= world.time) - return TRUE - return FALSE - -/obj/machinery/power/emitter/proc/fire_beam_pulse() - if(!check_delay()) - return FALSE - if(state != 2) - return FALSE - if(avail(active_power_usage)) - add_load(active_power_usage) - fire_beam() - -/obj/machinery/power/emitter/proc/fire_beam(atom/targeted_atom, mob/user) - var/turf/targets_from = get_turf(src) - if(targeted_atom && (targeted_atom == user || targeted_atom == targets_from || targeted_atom == src)) - return - var/obj/item/projectile/P = new projectile_type(targets_from) - playsound(src.loc, projectile_sound, 50, 1) - if(prob(35)) - sparks.start() - switch(dir) - if(NORTH) - P.yo = 20 - P.xo = 0 - if(NORTHEAST) - P.yo = 20 - P.xo = 20 - if(EAST) - P.yo = 0 - P.xo = 20 - if(SOUTHEAST) - P.yo = -20 - P.xo = 20 - if(WEST) - P.yo = 0 - P.xo = -20 - if(SOUTHWEST) - P.yo = -20 - P.xo = -20 - if(NORTHWEST) - P.yo = 20 - P.xo = -20 - else // Any other - P.yo = -20 - P.xo = 0 - if(target) - P.yo = targeted_atom.y - targets_from.y - P.xo = targeted_atom.x - targets_from.x - P.current = targets_from - P.starting = targets_from - P.firer = src - P.original = targeted_atom - if(!manual) - last_shot = world.time - if(shot_number < 3) - fire_delay = 20 - shot_number ++ - else - fire_delay = rand(minimum_fire_delay,maximum_fire_delay) - shot_number = 0 - if(!target) - P.setDir(src.dir) - P.starting = loc - else - if(QDELETED(target)) - target = null - P.fire() - return P - -/obj/machinery/power/emitter/can_be_unfasten_wrench(mob/user, silent) - if(state == EM_WELDED) - if(!silent) - to_chat(user, "[src] is welded to the floor!") - return FAILED_UNFASTEN - return ..() - -/obj/machinery/power/emitter/default_unfasten_wrench(mob/user, obj/item/weapon/wrench/W, time = 20) - . = ..() - if(. == SUCCESSFUL_UNFASTEN) - if(anchored) - state = EM_SECURED - else - state = EM_UNSECURED - -/obj/machinery/power/emitter/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/weapon/wrench)) - if(active) - to_chat(user, "Turn \the [src] off first!") - return - default_unfasten_wrench(user, W, 0) - return - - if(istype(W, /obj/item/weapon/weldingtool)) - var/obj/item/weapon/weldingtool/WT = W - if(active) - to_chat(user, "Turn \the [src] off first.") - return - switch(state) - if(EM_UNSECURED) - to_chat(user, "The [src.name] needs to be wrenched to the floor!") - if(EM_SECURED) - if(WT.remove_fuel(0,user)) - playsound(loc, WT.usesound, 50, 1) - user.visible_message("[user.name] starts to weld the [name] to the floor.", \ - "You start to weld \the [src] to the floor...", \ - "You hear welding.") - if(do_after(user,20*W.toolspeed, target = src) && WT.isOn()) - state = EM_WELDED - to_chat(user, "You weld \the [src] to the floor.") - connect_to_network() - if(EM_WELDED) - if(WT.remove_fuel(0,user)) - playsound(loc, WT.usesound, 50, 1) - user.visible_message("[user.name] starts to cut the [name] free from the floor.", \ - "You start to cut \the [src] free from the floor...", \ - "You hear welding.") - if(do_after(user,20*W.toolspeed, target = src) && WT.isOn()) - state = EM_SECURED - to_chat(user, "You cut \the [src] free from the floor.") - disconnect_from_network() - return - - if(W.GetID()) - if(emagged) - to_chat(user, "The lock seems to be broken!") - return - if(allowed(user)) - if(active) - locked = !locked - to_chat(user, "You [src.locked ? "lock" : "unlock"] the controls.") - else - to_chat(user, "The controls can only be locked when \the [src] is online!") - else - to_chat(user, "Access denied.") - return - - if(is_wire_tool(W) && panel_open) - wires.interact(user) - return - - if(default_deconstruction_screwdriver(user, "emitter_open", "emitter", W)) - return - - if(exchange_parts(user, W)) - return - - if(default_pry_open(W)) - return - - if(default_deconstruction_crowbar(W)) - return - - return ..() - -/obj/machinery/power/emitter/emag_act(mob/user) - if(!emagged) - locked = 0 - emagged = 1 - if(user) - user.visible_message("[user.name] emags the [src.name].","You short out the lock.") - - -/obj/machinery/power/emitter/prototype - name = "Prototype Emitter" - icon = 'icons/obj/turrets.dmi' - icon_state = "protoemitter" - icon_state_on = "protoemitter_+a" - can_buckle = TRUE - buckle_lying = 0 - var/view_range = 12 - var/datum/action/innate/protoemitter/firing/auto - -//BUCKLE HOOKS - -/obj/machinery/power/emitter/prototype/unbuckle_mob(mob/living/buckled_mob,force = 0) - playsound(src,'sound/mecha/mechmove01.ogg', 50, 1) - manual = FALSE - for(var/obj/item/I in buckled_mob.held_items) - if(istype(I, /obj/item/weapon/turret_control)) - qdel(I) - if(istype(buckled_mob)) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 0 - if(buckled_mob.client) - buckled_mob.client.change_view(world.view) - auto.Remove(buckled_mob) - . = ..() - -/obj/machinery/power/emitter/prototype/user_buckle_mob(mob/living/M, mob/living/carbon/user) - if(user.incapacitated() || !istype(user)) - return - for(var/atom/movable/A in get_turf(src)) - if(A.density && (A != src && A != M)) - return - M.forceMove(get_turf(src)) - ..() - playsound(src,'sound/mecha/mechmove01.ogg', 50, 1) - M.pixel_y = 14 - layer = 4.1 - if(M.client) - M.client.change_view(view_range) - if(!auto) - auto = new() - auto.Grant(M, src) - -/datum/action/innate/protoemitter - check_flags = AB_CHECK_RESTRAINED | AB_CHECK_STUNNED | AB_CHECK_CONSCIOUS - var/obj/machinery/power/emitter/prototype/PE - var/mob/living/carbon/U - - -/datum/action/innate/protoemitter/Grant(mob/living/carbon/L, obj/machinery/power/emitter/prototype/proto) - PE = proto - U = L - . = ..() - -/datum/action/innate/protoemitter/firing - name = "Switch to Manual Firing" - desc = "The emitter will only fire on your command and at your designated target" - button_icon_state = "mech_zoom_on" - -/datum/action/innate/protoemitter/firing/Activate() - if(PE.manual) - playsound(PE,'sound/mecha/mechmove01.ogg', 50, 1) - PE.manual = FALSE - name = "Switch to Manual Firing" - desc = "The emitter will only fire on your command and at your designated target" - button_icon_state = "mech_zoom_on" - for(var/obj/item/I in U.held_items) - if(istype(I, /obj/item/weapon/turret_control)) - qdel(I) - UpdateButtonIcon() - return - else - playsound(PE,'sound/mecha/mechmove01.ogg', 50, 1) - name = "Switch to Automatic Firing" - desc = "Emitters will switch to periodic firing at your last target" - button_icon_state = "mech_zoom_off" - PE.manual = TRUE - for(var/V in U.held_items) - var/obj/item/I = V - if(istype(I)) - if(U.dropItemToGround(I)) - var/obj/item/weapon/turret_control/TC = new /obj/item/weapon/turret_control() - U.put_in_hands(TC) - else //Entries in the list should only ever be items or null, so if it's not an item, we can assume it's an empty hand - var/obj/item/weapon/turret_control/TC = new /obj/item/weapon/turret_control() - U.put_in_hands(TC) - UpdateButtonIcon() - - -/obj/item/weapon/turret_control - name = "turret controls" - icon_state = "offhand" - w_class = WEIGHT_CLASS_HUGE - flags = ABSTRACT | NODROP - resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF | NOBLUDGEON - var/delay = 0 - -/obj/item/weapon/turret_control/afterattack(atom/targeted_atom, mob/user) - ..() - var/obj/machinery/power/emitter/E = user.buckled - E.setDir(get_dir(E,targeted_atom)) - user.setDir(E.dir) - switch(E.dir) - if(NORTH) - E.layer = 3.9 - user.pixel_x = 0 - user.pixel_y = -14 - if(NORTHEAST) - E.layer = 3.9 - user.pixel_x = -8 - user.pixel_y = -12 - if(EAST) - E.layer = 4.1 - user.pixel_x = -14 - user.pixel_y = 0 - if(SOUTHEAST) - E.layer = 3.9 - user.pixel_x = -8 - user.pixel_y = 12 - if(SOUTH) - E.layer = 4.1 - user.pixel_x = 0 - user.pixel_y = 14 - if(SOUTHWEST) - E.layer = 3.9 - user.pixel_x = 8 - user.pixel_y = 12 - if(WEST) - E.layer = 4.1 - user.pixel_x = 14 - user.pixel_y = 0 - if(NORTHWEST) - E.layer = 3.9 - user.pixel_x = 8 - user.pixel_y = -12 - - if(E.charge >= 10 && world.time > delay) - E.charge -= 10 - E.target = targeted_atom - E.fire_beam(targeted_atom, user) - delay = world.time + 10 - else if (E.charge < 10) - playsound(get_turf(user),'sound/machines/buzz-sigh.ogg', 50, 1) +/obj/machinery/power/emitter + name = "Emitter" + desc = "A heavy duty industrial laser.\nAlt-click to rotate it clockwise." + icon = 'icons/obj/singularity.dmi' + icon_state = "emitter" + var/icon_state_on = "emitter_+a" + anchored = 0 + density = 1 + req_access = list(GLOB.access_engine_equip) + + // The following 3 vars are mostly for the prototype + var/manual = FALSE + var/charge = 0 + var/atom/target = null + + use_power = 0 + idle_power_usage = 10 + active_power_usage = 300 + + var/active = 0 + var/powered = 0 + var/fire_delay = 100 + var/maximum_fire_delay = 100 + var/minimum_fire_delay = 20 + var/last_shot = 0 + var/shot_number = 0 + var/state = 0 + var/locked = 0 + + var/projectile_type = /obj/item/projectile/beam/emitter + + var/projectile_sound = 'sound/weapons/emitter.ogg' + + var/datum/effect_system/spark_spread/sparks + +/obj/machinery/power/emitter/New() + ..() + var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/emitter(null) + B.apply_default_parts(src) + RefreshParts() + wires = new /datum/wires/emitter(src) + +/obj/item/weapon/circuitboard/machine/emitter + name = "Emitter (Machine Board)" + build_path = /obj/machinery/power/emitter + origin_tech = "programming=3;powerstorage=4;engineering=4" + req_components = list( + /obj/item/weapon/stock_parts/micro_laser = 1, + /obj/item/weapon/stock_parts/manipulator = 1) + +/obj/machinery/power/emitter/RefreshParts() + var/max_firedelay = 120 + var/firedelay = 120 + var/min_firedelay = 24 + var/power_usage = 350 + for(var/obj/item/weapon/stock_parts/micro_laser/L in component_parts) + max_firedelay -= 20 * L.rating + min_firedelay -= 4 * L.rating + firedelay -= 20 * L.rating + maximum_fire_delay = max_firedelay + minimum_fire_delay = min_firedelay + fire_delay = firedelay + for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) + power_usage -= 50 * M.rating + active_power_usage = power_usage + +/obj/machinery/power/emitter/verb/rotate() + set name = "Rotate" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + if (src.anchored) + to_chat(usr, "It is fastened to the floor!") + return 0 + src.setDir(turn(src.dir, 270)) + return 1 + +/obj/machinery/power/emitter/AltClick(mob/user) + ..() + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user)) + return + else + rotate() + +/obj/machinery/power/emitter/Initialize() + . = ..() + if(state == 2 && anchored) + connect_to_network() + + sparks = new + sparks.attach(src) + sparks.set_up(5, TRUE, src) + +/obj/machinery/power/emitter/Destroy() + if(SSticker && SSticker.IsRoundInProgress()) + message_admins("Emitter deleted at ([x],[y],[z] - JMP)",0,1) + log_game("Emitter deleted at ([x],[y],[z])") + investigate_log("deleted at ([x],[y],[z]) at [get_area(src)]","singulo") + QDEL_NULL(sparks) + return ..() + +/obj/machinery/power/emitter/update_icon() + if (active && powernet && avail(active_power_usage)) + icon_state = icon_state_on + else + icon_state = initial(icon_state) + + +/obj/machinery/power/emitter/attack_hand(mob/user) + src.add_fingerprint(user) + if(state == 2) + if(!powernet) + to_chat(user, "The emitter isn't connected to a wire!") + return 1 + if(!src.locked) + if(src.active==1) + src.active = 0 + to_chat(user, "You turn off \the [src].") + message_admins("Emitter turned off by [ADMIN_LOOKUPFLW(user)] in [ADMIN_COORDJMP(src)]",0,1) + log_game("Emitter turned off by [key_name(user)] in [COORD(src)]") + investigate_log("turned off by [key_name(user)] at [get_area(src)]","singulo") + else + src.active = 1 + to_chat(user, "You turn on \the [src].") + src.shot_number = 0 + src.fire_delay = maximum_fire_delay + investigate_log("turned on by [key_name(user)] at [get_area(src)]","singulo") + update_icon() + else + to_chat(user, "The controls are locked!") + else + to_chat(user, "The [src] needs to be firmly secured to the floor first!") + return 1 + +/obj/machinery/power/emitter/attack_animal(mob/living/simple_animal/M) + if(ismegafauna(M) && anchored) + state = 0 + anchored = FALSE + M.visible_message("[M] rips [src] free from its moorings!") + else + ..() + if(!anchored) + step(src, get_dir(M, src)) + + +/obj/machinery/power/emitter/emp_act(severity)//Emitters are hardened but still might have issues +// add_load(1000) +/* if((severity == 1)&&prob(1)&&prob(1)) + if(src.active) + src.active = 0 + src.use_power = 1 */ + return 1 + + +/obj/machinery/power/emitter/process() + if(stat & (BROKEN)) + return + if(src.state != 2 || (!powernet && active_power_usage)) + src.active = 0 + update_icon() + return + if(src.active == 1) + if(!active_power_usage || avail(active_power_usage)) + add_load(active_power_usage) + if(!powered) + powered = 1 + update_icon() + investigate_log("regained power and turned on at [get_area(src)]","singulo") + else + if(powered) + powered = 0 + update_icon() + investigate_log("lost power and turned off at [get_area(src)]","singulo") + log_game("Emitter lost power in ([x],[y],[z])") + return + if(charge <=80) + charge+=5 + if(!check_delay() || manual == TRUE) + return FALSE + fire_beam(target) + +/obj/machinery/power/emitter/proc/check_delay() + if((src.last_shot + src.fire_delay) <= world.time) + return TRUE + return FALSE + +/obj/machinery/power/emitter/proc/fire_beam_pulse() + if(!check_delay()) + return FALSE + if(state != 2) + return FALSE + if(avail(active_power_usage)) + add_load(active_power_usage) + fire_beam() + +/obj/machinery/power/emitter/proc/fire_beam(atom/targeted_atom, mob/user) + var/turf/targets_from = get_turf(src) + if(targeted_atom && (targeted_atom == user || targeted_atom == targets_from || targeted_atom == src)) + return + var/obj/item/projectile/P = new projectile_type(targets_from) + playsound(src.loc, projectile_sound, 50, 1) + if(prob(35)) + sparks.start() + switch(dir) + if(NORTH) + P.yo = 20 + P.xo = 0 + if(NORTHEAST) + P.yo = 20 + P.xo = 20 + if(EAST) + P.yo = 0 + P.xo = 20 + if(SOUTHEAST) + P.yo = -20 + P.xo = 20 + if(WEST) + P.yo = 0 + P.xo = -20 + if(SOUTHWEST) + P.yo = -20 + P.xo = -20 + if(NORTHWEST) + P.yo = 20 + P.xo = -20 + else // Any other + P.yo = -20 + P.xo = 0 + if(target) + P.yo = targeted_atom.y - targets_from.y + P.xo = targeted_atom.x - targets_from.x + P.current = targets_from + P.starting = targets_from + P.firer = src + P.original = targeted_atom + if(!manual) + last_shot = world.time + if(shot_number < 3) + fire_delay = 20 + shot_number ++ + else + fire_delay = rand(minimum_fire_delay,maximum_fire_delay) + shot_number = 0 + if(!target) + P.setDir(src.dir) + P.starting = loc + else + if(QDELETED(target)) + target = null + P.fire() + return P + +/obj/machinery/power/emitter/can_be_unfasten_wrench(mob/user, silent) + if(state == EM_WELDED) + if(!silent) + to_chat(user, "[src] is welded to the floor!") + return FAILED_UNFASTEN + return ..() + +/obj/machinery/power/emitter/default_unfasten_wrench(mob/user, obj/item/weapon/wrench/W, time = 20) + . = ..() + if(. == SUCCESSFUL_UNFASTEN) + if(anchored) + state = EM_SECURED + else + state = EM_UNSECURED + +/obj/machinery/power/emitter/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/weapon/wrench)) + if(active) + to_chat(user, "Turn \the [src] off first!") + return + default_unfasten_wrench(user, W, 0) + return + + if(istype(W, /obj/item/weapon/weldingtool)) + var/obj/item/weapon/weldingtool/WT = W + if(active) + to_chat(user, "Turn \the [src] off first.") + return + switch(state) + if(EM_UNSECURED) + to_chat(user, "The [src.name] needs to be wrenched to the floor!") + if(EM_SECURED) + if(WT.remove_fuel(0,user)) + playsound(loc, WT.usesound, 50, 1) + user.visible_message("[user.name] starts to weld the [name] to the floor.", \ + "You start to weld \the [src] to the floor...", \ + "You hear welding.") + if(do_after(user,20*W.toolspeed, target = src) && WT.isOn()) + state = EM_WELDED + to_chat(user, "You weld \the [src] to the floor.") + connect_to_network() + if(EM_WELDED) + if(WT.remove_fuel(0,user)) + playsound(loc, WT.usesound, 50, 1) + user.visible_message("[user.name] starts to cut the [name] free from the floor.", \ + "You start to cut \the [src] free from the floor...", \ + "You hear welding.") + if(do_after(user,20*W.toolspeed, target = src) && WT.isOn()) + state = EM_SECURED + to_chat(user, "You cut \the [src] free from the floor.") + disconnect_from_network() + return + + if(W.GetID()) + if(emagged) + to_chat(user, "The lock seems to be broken!") + return + if(allowed(user)) + if(active) + locked = !locked + to_chat(user, "You [src.locked ? "lock" : "unlock"] the controls.") + else + to_chat(user, "The controls can only be locked when \the [src] is online!") + else + to_chat(user, "Access denied.") + return + + if(is_wire_tool(W) && panel_open) + wires.interact(user) + return + + if(default_deconstruction_screwdriver(user, "emitter_open", "emitter", W)) + return + + if(exchange_parts(user, W)) + return + + if(default_pry_open(W)) + return + + if(default_deconstruction_crowbar(W)) + return + + return ..() + +/obj/machinery/power/emitter/emag_act(mob/user) + if(!emagged) + locked = 0 + emagged = 1 + if(user) + user.visible_message("[user.name] emags the [src.name].","You short out the lock.") + + +/obj/machinery/power/emitter/prototype + name = "Prototype Emitter" + icon = 'icons/obj/turrets.dmi' + icon_state = "protoemitter" + icon_state_on = "protoemitter_+a" + can_buckle = TRUE + buckle_lying = 0 + var/view_range = 12 + var/datum/action/innate/protoemitter/firing/auto + +//BUCKLE HOOKS + +/obj/machinery/power/emitter/prototype/unbuckle_mob(mob/living/buckled_mob,force = 0) + playsound(src,'sound/mecha/mechmove01.ogg', 50, 1) + manual = FALSE + for(var/obj/item/I in buckled_mob.held_items) + if(istype(I, /obj/item/weapon/turret_control)) + qdel(I) + if(istype(buckled_mob)) + buckled_mob.pixel_x = 0 + buckled_mob.pixel_y = 0 + if(buckled_mob.client) + buckled_mob.client.change_view(world.view) + auto.Remove(buckled_mob) + . = ..() + +/obj/machinery/power/emitter/prototype/user_buckle_mob(mob/living/M, mob/living/carbon/user) + if(user.incapacitated() || !istype(user)) + return + for(var/atom/movable/A in get_turf(src)) + if(A.density && (A != src && A != M)) + return + M.forceMove(get_turf(src)) + ..() + playsound(src,'sound/mecha/mechmove01.ogg', 50, 1) + M.pixel_y = 14 + layer = 4.1 + if(M.client) + M.client.change_view(view_range) + if(!auto) + auto = new() + auto.Grant(M, src) + +/datum/action/innate/protoemitter + check_flags = AB_CHECK_RESTRAINED | AB_CHECK_STUNNED | AB_CHECK_CONSCIOUS + var/obj/machinery/power/emitter/prototype/PE + var/mob/living/carbon/U + + +/datum/action/innate/protoemitter/Grant(mob/living/carbon/L, obj/machinery/power/emitter/prototype/proto) + PE = proto + U = L + . = ..() + +/datum/action/innate/protoemitter/firing + name = "Switch to Manual Firing" + desc = "The emitter will only fire on your command and at your designated target" + button_icon_state = "mech_zoom_on" + +/datum/action/innate/protoemitter/firing/Activate() + if(PE.manual) + playsound(PE,'sound/mecha/mechmove01.ogg', 50, 1) + PE.manual = FALSE + name = "Switch to Manual Firing" + desc = "The emitter will only fire on your command and at your designated target" + button_icon_state = "mech_zoom_on" + for(var/obj/item/I in U.held_items) + if(istype(I, /obj/item/weapon/turret_control)) + qdel(I) + UpdateButtonIcon() + return + else + playsound(PE,'sound/mecha/mechmove01.ogg', 50, 1) + name = "Switch to Automatic Firing" + desc = "Emitters will switch to periodic firing at your last target" + button_icon_state = "mech_zoom_off" + PE.manual = TRUE + for(var/V in U.held_items) + var/obj/item/I = V + if(istype(I)) + if(U.dropItemToGround(I)) + var/obj/item/weapon/turret_control/TC = new /obj/item/weapon/turret_control() + U.put_in_hands(TC) + else //Entries in the list should only ever be items or null, so if it's not an item, we can assume it's an empty hand + var/obj/item/weapon/turret_control/TC = new /obj/item/weapon/turret_control() + U.put_in_hands(TC) + UpdateButtonIcon() + + +/obj/item/weapon/turret_control + name = "turret controls" + icon_state = "offhand" + w_class = WEIGHT_CLASS_HUGE + flags = ABSTRACT | NODROP + resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF | NOBLUDGEON + var/delay = 0 + +/obj/item/weapon/turret_control/afterattack(atom/targeted_atom, mob/user) + ..() + var/obj/machinery/power/emitter/E = user.buckled + E.setDir(get_dir(E,targeted_atom)) + user.setDir(E.dir) + switch(E.dir) + if(NORTH) + E.layer = 3.9 + user.pixel_x = 0 + user.pixel_y = -14 + if(NORTHEAST) + E.layer = 3.9 + user.pixel_x = -8 + user.pixel_y = -12 + if(EAST) + E.layer = 4.1 + user.pixel_x = -14 + user.pixel_y = 0 + if(SOUTHEAST) + E.layer = 3.9 + user.pixel_x = -8 + user.pixel_y = 12 + if(SOUTH) + E.layer = 4.1 + user.pixel_x = 0 + user.pixel_y = 14 + if(SOUTHWEST) + E.layer = 3.9 + user.pixel_x = 8 + user.pixel_y = 12 + if(WEST) + E.layer = 4.1 + user.pixel_x = 14 + user.pixel_y = 0 + if(NORTHWEST) + E.layer = 3.9 + user.pixel_x = 8 + user.pixel_y = -12 + + if(E.charge >= 10 && world.time > delay) + E.charge -= 10 + E.target = targeted_atom + E.fire_beam(targeted_atom, user) + delay = world.time + 10 + else if (E.charge < 10) + playsound(get_turf(user),'sound/machines/buzz-sigh.ogg', 50, 1) diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index dc3da6db8d..136f9162cf 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -117,8 +117,8 @@ strength++ strength_change() - message_admins("PA Control Computer increased to [strength] by [ADMIN_LOOKUPFLW(usr)] in [ADMIN_COORDJMP(src)]",0,1) - log_game("PA Control Computer increased to [strength] by [key_name(usr)] in [COORD(src)]") + message_admins("PA Control Computer increased to [strength] by [ADMIN_LOOKUPFLW(usr)] in [ADMIN_COORDJMP(src)]",0,1) + log_game("PA Control Computer increased to [strength] by [key_name(usr)] in [COORD(src)]") investigate_log("increased to [strength] by [key_name(usr)]","singulo") @@ -127,8 +127,8 @@ strength-- strength_change() - message_admins("PA Control Computer decreased to [strength] by [ADMIN_LOOKUPFLW(usr)] in [ADMIN_COORDJMP(src)]",0,1) - log_game("PA Control Computer decreased to [strength] by [key_name(usr)] in [COORD(src)]") + message_admins("PA Control Computer decreased to [strength] by [ADMIN_LOOKUPFLW(usr)] in [ADMIN_COORDJMP(src)]",0,1) + log_game("PA Control Computer decreased to [strength] by [key_name(usr)] in [COORD(src)]") investigate_log("decreased to [strength] by [key_name(usr)]","singulo") diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm index b1fd49779f..b78572cfcd 100644 --- a/code/modules/projectiles/ammunition/energy.dm +++ b/code/modules/projectiles/ammunition/energy.dm @@ -191,7 +191,7 @@ fire_sound = 'sound/magic/lightningbolt.ogg' e_cost = 200 select_name = "stun" - projectile_type = /obj/item/projectile/energy/tesla/revolver + projectile_type = /obj/item/projectile/energy/tesla/revolver /obj/item/ammo_casing/energy/gravityrepulse projectile_type = /obj/item/projectile/gravityrepulse diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index 8a94c2b849..951b5aeb62 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -19,12 +19,12 @@ /obj/item/weapon/gun/energy/pulse/prize/New() . = ..() GLOB.poi_list |= src - var/msg = "A pulse rifle prize has been created at [ADMIN_COORDJMP(src)]" + var/msg = "A pulse rifle prize has been created at [ADMIN_COORDJMP(src)]" message_admins(msg) log_game(msg) - notify_ghosts("Someone won a pulse rifle as a prize!", source = src, action = NOTIFY_ORBIT) + notify_ghosts("Someone won a pulse rifle as a prize!", source = src, action = NOTIFY_ORBIT) /obj/item/weapon/gun/energy/pulse/prize/Destroy() GLOB.poi_list -= src diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 9835b1df21..0eedcd5bc8 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -179,8 +179,8 @@ return if(setAngle) Angle = setAngle - var/old_pixel_x = pixel_x - var/old_pixel_y = pixel_y + var/old_pixel_x = pixel_x + var/old_pixel_y = pixel_y if(!legacy) //new projectiles set waitfor = 0 var/next_run = world.time @@ -203,30 +203,30 @@ var/Pixel_x=round((sin(Angle)+16*sin(Angle)*2), 1) //round() is a floor operation when only one argument is supplied, we don't want that here var/Pixel_y=round((cos(Angle)+16*cos(Angle)*2), 1) - var/pixel_x_offset = old_pixel_x + Pixel_x - var/pixel_y_offset = old_pixel_y + Pixel_y + var/pixel_x_offset = old_pixel_x + Pixel_x + var/pixel_y_offset = old_pixel_y + Pixel_y var/new_x = x var/new_y = y while(pixel_x_offset > 16) pixel_x_offset -= 32 - old_pixel_x -= 32 + old_pixel_x -= 32 new_x++// x++ while(pixel_x_offset < -16) pixel_x_offset += 32 - old_pixel_x += 32 + old_pixel_x += 32 new_x-- while(pixel_y_offset > 16) pixel_y_offset -= 32 - old_pixel_y -= 32 + old_pixel_y -= 32 new_y++ while(pixel_y_offset < -16) pixel_y_offset += 32 - old_pixel_y += 32 + old_pixel_y += 32 new_y-- - - pixel_x = old_pixel_x - pixel_y = old_pixel_y + + pixel_x = old_pixel_x + pixel_y = old_pixel_y step_towards(src, locate(new_x, new_y, z)) next_run += max(world.tick_lag, speed) var/delay = next_run - world.time @@ -235,9 +235,9 @@ pixel_y = pixel_y_offset else animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (delay <= 3 ? delay - 1 : delay)), flags = ANIMATION_END_NOW) - old_pixel_x = pixel_x_offset - old_pixel_y = pixel_y_offset - + old_pixel_x = pixel_x_offset + old_pixel_y = pixel_y_offset + if(original && (original.layer>=2.75) || ismob(original)) if(loc == get_turf(original)) if(!(original in permutated)) diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm old mode 100755 new mode 100644 diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm old mode 100755 new mode 100644 diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 92e01f4824..cc38a44ffc 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -48,16 +48,16 @@ user.changeNext_move(CLICK_CD_RANGE*2) user.newtonian_move(get_dir(A, user)) var/turf/T = get_turf(src) - var/area/area = get_area(src) + var/area/area = get_area(src) if(reagents.has_reagent("sacid")) - message_admins("[ADMIN_LOOKUPFLW(user)] fired sulphuric acid from \a [src] at [area] [ADMIN_COORDJMP(T)].") - log_game("[key_name(user)] fired sulphuric acid from \a [src] at [area] ([T.x], [T.y], [T.z]).") + message_admins("[ADMIN_LOOKUPFLW(user)] fired sulphuric acid from \a [src] at [area] [ADMIN_COORDJMP(T)].") + log_game("[key_name(user)] fired sulphuric acid from \a [src] at [area] ([T.x], [T.y], [T.z]).") if(reagents.has_reagent("facid")) - message_admins("[ADMIN_LOOKUPFLW(user)] fired Fluacid from \a [src] at [area] [ADMIN_COORDJMP(T)].") - log_game("[key_name(user)] fired Fluacid from \a [src] at [area] [COORD(T)].") + message_admins("[ADMIN_LOOKUPFLW(user)] fired Fluacid from \a [src] at [area] [ADMIN_COORDJMP(T)].") + log_game("[key_name(user)] fired Fluacid from \a [src] at [area] [COORD(T)].") if(reagents.has_reagent("lube")) - message_admins("[ADMIN_LOOKUPFLW(user)] fired Space lube from \a [src] at [area] [ADMIN_COORDJMP(T)].") - log_game("[key_name(user)] fired Space lube from \a [src] at [area] [COORD(T)].") + message_admins("[ADMIN_LOOKUPFLW(user)] fired Space lube from \a [src] at [area] [ADMIN_COORDJMP(T)].") + log_game("[key_name(user)] fired Space lube from \a [src] at [area] [COORD(T)].") return diff --git a/code/modules/shuttle/ferry.dm b/code/modules/shuttle/ferry.dm index b846098fc4..8368fd58e5 100644 --- a/code/modules/shuttle/ferry.dm +++ b/code/modules/shuttle/ferry.dm @@ -30,4 +30,4 @@ return last_request = world.time to_chat(usr, "Your request has been recieved by Centcom.") - to_chat(GLOB.admins, "FERRY: [ADMIN_LOOKUPFLW(usr)] (Move Ferry) is requesting to move the transport ferry to Centcom.") + to_chat(GLOB.admins, "FERRY: [ADMIN_LOOKUPFLW(usr)] (Move Ferry) is requesting to move the transport ferry to Centcom.") diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm index 76f5b9b307..0069aa5349 100644 --- a/code/modules/spells/spell_types/wizard.dm +++ b/code/modules/spells/spell_types/wizard.dm @@ -69,24 +69,24 @@ action_icon_state = "smoke" - -/obj/effect/proc_holder/spell/targeted/smoke/lesser //Chaplain smoke book - name = "Smoke" - desc = "This spell spawns a small cloud of choking smoke at your location." - - school = "conjuration" - charge_max = 360 - clothes_req = 0 - invocation = "none" - invocation_type = "none" - range = -1 - include_user = 1 - - smoke_spread = 1 - smoke_amt = 2 - - action_icon_state = "smoke" - + +/obj/effect/proc_holder/spell/targeted/smoke/lesser //Chaplain smoke book + name = "Smoke" + desc = "This spell spawns a small cloud of choking smoke at your location." + + school = "conjuration" + charge_max = 360 + clothes_req = 0 + invocation = "none" + invocation_type = "none" + range = -1 + include_user = 1 + + smoke_spread = 1 + smoke_amt = 2 + + action_icon_state = "smoke" + /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech name = "Disable Tech" desc = "This spell disables all weapons, cameras and most other technology in range." diff --git a/icons/emoji.dmi b/icons/emoji.dmi old mode 100755 new mode 100644 diff --git a/interface/skin.dmf b/interface/skin.dmf index 65407cef0e..f48b58e34a 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -1329,7 +1329,7 @@ window "mapwindow" on-show = "" on-hide = "" style = "" - zoom-mode = "distort" + zoom-mode = "distort" window "infowindow" elem "infowindow" diff --git a/tgstation.dme b/tgstation.dme index 95c2255dfa..f643d7231a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -808,7 +808,6 @@ #include "code\game\objects\items\weapons\defib.dm" #include "code\game\objects\items\weapons\dice.dm" #include "code\game\objects\items\weapons\dna_injector.dm" -#include "code\game\objects\items\weapons\explosives.dm" #include "code\game\objects\items\weapons\extinguisher.dm" #include "code\game\objects\items\weapons\flamethrower.dm" #include "code\game\objects\items\weapons\gift.dm"