From 45c4ff46b4280032b477f597a976f778c2fa2e77 Mon Sep 17 00:00:00 2001 From: SteelSlayer Date: Wed, 30 Oct 2019 20:06:53 -0500 Subject: [PATCH 001/144] Minor cargo supply crate pathing change --- code/datums/supplypacks.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 8e20ebb7a93..3481cf4885c 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -1204,7 +1204,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine containername = "hydroponics crate" announce_beacons = list("Hydroponics" = list("Hydroponics")) -/datum/supply_packs/misc/hydroponics/hydrotank +/datum/supply_packs/organic/hydroponics/hydrotank name = "Hydroponics Watertank Crate" contains = list(/obj/item/watertank) cost = 10 From 383b4fc178856d374c08798dceb276b779ffae59 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Nov 2019 14:39:12 -0500 Subject: [PATCH 002/144] added CC Stamp to Navy Officer's backpacks --- code/game/jobs/job/central.dm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/game/jobs/job/central.dm b/code/game/jobs/job/central.dm index 1d6ae0162a2..890f8958cd4 100644 --- a/code/game/jobs/job/central.dm +++ b/code/game/jobs/job/central.dm @@ -34,6 +34,9 @@ /obj/item/implant/dust ) backpack = /obj/item/storage/backpack/satchel + backpack_contents = list( + /obj/item/stamp/centcom = 1, + ) box = /obj/item/storage/box/centcomofficer cybernetic_implants = list( /obj/item/organ/internal/cyberimp/chest/nutriment/plus From bda2ea1df9e1e03903684cf4e3bf53240ef7e8bc Mon Sep 17 00:00:00 2001 From: Arvenius169 <56715097+Arvenius169@users.noreply.github.com> Date: Fri, 10 Jan 2020 16:43:48 -0500 Subject: [PATCH 003/144] Fixes a part of the gas sample comparison CO2 comparison now uses the CO2 from the sample, instead of oxygen. --- code/datums/gas_mixture.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/gas_mixture.dm b/code/datums/gas_mixture.dm index 35cc970aa67..63b70b512ee 100644 --- a/code/datums/gas_mixture.dm +++ b/code/datums/gas_mixture.dm @@ -645,7 +645,7 @@ What are the archived variables for? ((nitrogen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen) || (nitrogen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen))) return 0 if((abs(carbon_dioxide-sample.carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && \ - ((carbon_dioxide < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide) || (oxygen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide))) + ((carbon_dioxide < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide) || (carbon_dioxide > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide))) return 0 if((abs(toxins-sample.toxins) > MINIMUM_AIR_TO_SUSPEND) && \ ((toxins < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins) || (toxins > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins))) From 5f293e0f44bbe89fa9b061f46300b8c70d10d9de Mon Sep 17 00:00:00 2001 From: joep van der velden Date: Thu, 30 Jan 2020 23:28:59 +0100 Subject: [PATCH 004/144] Fixed a few funtimes --- code/controllers/subsystem/ticker.dm | 5 ----- code/game/gamemodes/cult/runes.dm | 11 +++++++---- code/game/mecha/mecha.dm | 2 +- code/modules/customitems/item_spawning.dm | 3 +++ code/modules/mob/living/silicon/robot/robot.dm | 2 +- code/modules/projectiles/guns/rocket.dm | 4 ++-- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index f9528f2b2b9..0770a55c7eb 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -309,15 +309,12 @@ SUBSYSTEM_DEF(ticker) cinematic.mouse_opacity = MOUSE_OPACITY_TRANSPARENT cinematic.screen_loc = "1,0" - var/obj/structure/bed/temp_buckle = new(src) if(station_missed) for(var/mob/M in GLOB.mob_list) - M.buckled = temp_buckle //buckles the mob so it can't do anything if(M.client) M.client.screen += cinematic //show every client the cinematic else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" for(var/mob/M in GLOB.mob_list) - M.buckled = temp_buckle if(M.stat != DEAD) var/turf/T = get_turf(M) if(T && is_station_level(T.z) && !istype(M.loc, /obj/structure/closet/secure_closet/freezer)) @@ -387,8 +384,6 @@ SUBSYSTEM_DEF(ticker) //Otherwise if its a verb it will continue on afterwards. spawn(300) QDEL_NULL(cinematic) //end the cinematic - if(temp_buckle) - qdel(temp_buckle) //release everybody diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index d918bead0de..f8123ce89c5 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -332,10 +332,12 @@ var/list/teleport_runes = list() user.forceMove(get_turf(actual_selected_rune)) var/mob/living/carbon/human/H = user if(user.z != T.z) - H.bleed(5) + if(istype(H)) + H.bleed(5) user.apply_damage(5, BRUTE) else - H.bleed(rand(5,10)) + if(istype(H)) + H.bleed(rand(5,10)) else fail_invoke() @@ -832,7 +834,8 @@ var/list/teleport_runes = list() fail_invoke() log_game("Summon Cultist rune failed - target in away mission") return - if((cultist_to_summon.reagents.has_reagent("holywater") || cultist_to_summon.restrained()) && invokers.len < 3) + var/hard_summon = (cultist_to_summon.reagents && cultist_to_summon.reagents.has_reagent("holywater")) || cultist_to_summon.restrained() + if(hard_summon && invokers.len < 3) to_chat(user, "The summoning of [cultist_to_summon] is being blocked somehow! You need 3 chanters to counter it!") fail_invoke() new /obj/effect/temp_visual/cult/sparks(get_turf(cultist_to_summon)) //observer warning @@ -840,7 +843,7 @@ var/list/teleport_runes = list() return ..() - if(cultist_to_summon.reagents.has_reagent("holywater") || cultist_to_summon.restrained()) + if(hard_summon) summontime = 20 if(do_after(user, summontime, target = loc)) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 85f511395f1..a55a8a3be09 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -540,7 +540,7 @@ /obj/mecha/attack_alien(mob/living/user) - log_message("Attack by alien. Attacker - [user].", color = "red") + log_message("Attack by alien. Attacker - [user].", TRUE) playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) attack_generic(user, 15, BRUTE, "melee", 0) diff --git a/code/modules/customitems/item_spawning.dm b/code/modules/customitems/item_spawning.dm index 5c1250ed355..631228fed9b 100644 --- a/code/modules/customitems/item_spawning.dm +++ b/code/modules/customitems/item_spawning.dm @@ -11,6 +11,9 @@ var/propadjust = query.item[2] var/jobmask = query.item[3] var/ok = 0 + if(!path) + log_debug("Incorrect database entry found in table 'customuseritems', cuiPath is null. cuiCKey='[M.ckey]' AND (cuiRealName='[sanitizeSQL(M.real_name)]' OR cuiRealName='*'") + continue if(jobmask != "*") var/list/allowed_jobs = splittext(jobmask,",") for(var/i = 1, i <= allowed_jobs.len, i++) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 1cded581118..9b28a6c4789 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1120,7 +1120,7 @@ var/list/robot_verbs_default = list( if(!updating) updating = 1 spawn(BORG_CAMERA_BUFFER) - if(oldLoc != src.loc) + if(camera && oldLoc != src.loc) cameranet.updatePortableCamera(src.camera) updating = 0 if(module) diff --git a/code/modules/projectiles/guns/rocket.dm b/code/modules/projectiles/guns/rocket.dm index 53de1a16854..96a97c8fe1a 100644 --- a/code/modules/projectiles/guns/rocket.dm +++ b/code/modules/projectiles/guns/rocket.dm @@ -36,7 +36,7 @@ to_chat(user, "You put the rocket in [src].") to_chat(user, "[rockets.len] / [max_rockets] rockets.") else - to_chat(usr, "[src] cannot hold more rockets.") + to_chat(user, "[src] cannot hold more rockets.") else return ..() @@ -55,4 +55,4 @@ rockets -= I qdel(I) else - to_chat(usr, "[src] is empty.") + to_chat(user, "[src] is empty.") From fa84366203d775ae4b3ee217cd8b97925ce81fb1 Mon Sep 17 00:00:00 2001 From: Aquilar <20759278+Aquilar@users.noreply.github.com> Date: Sat, 1 Feb 2020 18:16:41 +0800 Subject: [PATCH 005/144] Added an icon to differentiate the blueprints from the CE's blueprints. purely to prevent confusion. Fixed the pinpointer from pointing to cyborg and slime blueprints by; Changing the steal objectives to require the CE's copy of the blueprints exclusive. Which required changing few lines in various places that reference that specific copy. Signed-off-by: Aquilar <20759278+Aquilar@users.noreply.github.com> missed the line in photography.dm --- code/game/gamemodes/steal_items.dm | 4 ++-- code/game/objects/items/blueprints.dm | 13 +++++++++---- .../closets/secure/engineering.dm | 2 +- code/modules/paperwork/photography.dm | 2 +- .../research/xenobiology/xenobiology.dm | 1 + icons/obj/items.dmi | Bin 153063 -> 153359 bytes 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/code/game/gamemodes/steal_items.dm b/code/game/gamemodes/steal_items.dm index de9f8fc0eb7..78a05f904a8 100644 --- a/code/game/gamemodes/steal_items.dm +++ b/code/game/gamemodes/steal_items.dm @@ -76,12 +76,12 @@ datum/theft_objective/ai/check_special_completion(var/obj/item/aicard/C) /datum/theft_objective/blueprints name = "the station blueprints" - typepath = /obj/item/areaeditor/blueprints + typepath = /obj/item/areaeditor/blueprints/ce protected_jobs = list("Chief Engineer") altitems = list(/obj/item/photo) /datum/objective_item/steal/blueprints/check_special_completion(obj/item/I) - if(istype(I, /obj/item/areaeditor/blueprints)) + if(istype(I, /obj/item/areaeditor/blueprints/ce)) return 1 if(istype(I, /obj/item/photo)) var/obj/item/photo/P = I diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index 79cc221ee7f..2ece6b6b32a 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -87,11 +87,11 @@ //Station blueprints!!! /obj/item/areaeditor/blueprints - name = "station blueprints" - desc = "Blueprints of the station. There is a \"Classified\" stamp and several coffee stains on it." + name = "Blueprints Tablet" + desc = "Heavy duty work tablet with blueprints of the station loaded. There is a blinking \"Classified\" icon and several dents in the casing" icon = 'icons/obj/items.dmi' - icon_state = "blueprints" - fluffnotice = "Property of Nanotrasen. For heads of staff only. Store in high-secure storage." + icon_state = "blueprinttablet" + fluffnotice = "Property of Nanotrasen. For heads of staff only. Wipe data after usage." resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF w_class = WEIGHT_CLASS_NORMAL var/list/showing = list() @@ -328,3 +328,8 @@ desc = "A digital copy of the station blueprints stored in your memory." fluffnotice = "Intellectual Property of Nanotrasen. For use in engineering cyborgs only. Wipe from memory upon departure from the station." +/obj/item/areaeditor/blueprints/ce + name = "station blueprints" + desc = "Blueprints of the station. There is a \"Classified\" stamp and several coffee stains on it." + icon_state = "blueprints" + fluffnotice = "Property of Nanotrasen. For heads of staff only. Store in high-secure storage." diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index 517732cdd11..f5beb27d0bd 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -16,7 +16,7 @@ new /obj/item/storage/backpack/satchel_eng(src) new /obj/item/storage/backpack/duffel/engineering(src) new /obj/item/clothing/head/beret/ce(src) - new /obj/item/areaeditor/blueprints(src) + new /obj/item/areaeditor/blueprints/ce(src) new /obj/item/storage/box/permits(src) new /obj/item/clothing/under/rank/chief_engineer(src) new /obj/item/clothing/under/rank/chief_engineer/skirt(src) diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 11beb1a7cb6..c579c887b59 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -256,7 +256,7 @@ var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","s var/atom/A = sorted[i] if(A) var/icon/img = getFlatIcon(A)//build_composite_icon(A) - if(istype(A, /obj/item/areaeditor/blueprints)) + if(istype(A, /obj/item/areaeditor/blueprints/ce)) blueprints = 1 // If what we got back is actually a picture, draw it. diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index c71c3f6f8d8..298946853da 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -538,6 +538,7 @@ /obj/item/areaeditor/blueprints/slime name = "cerulean prints" + icon_state = "blueprints" desc = "A one use set of blueprints made of jelly like organic material. Extends the reach of the management console." color = "#2956B2" diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi index bac18fe788d5f449aa2d0a5c1198351c27a913a8..dadc1c3540583d6027b04b954d9f906fb7952a19 100644 GIT binary patch delta 6614 zcmX|F1ymGmxSplEReC{`knUy)rBe|Q5D<_ONu^6Y%0?pPM~ zF8=r4IWuR@nVB>1^F41p@3&q=81tPl_8!Ee?EZ5iR?GhD^neuIFUStzIjCVHdaGv4 z8T9*wh6ZQ_T)(zmCJ#msg&~L_H3%Xsd|_}`PcLnq9y}$fSzLmf1_eEY@V*bibB-9Z zctTxKK4s@-@Tnt5tt!_(d&p>dkUgtVi$_g1Lix39jvzY+ecVW#gO8i$R=HcR(6GLx zyzbpE5^x0zd6#5`cPV?QvI{Zc)|r$PS?oRMZ_g@h&DSPN;TO#5XTFVgjD-aX9;cu+ zK@c2?)JPWNrA`s&Xmz|jffh``QZ+=4g@%R}6cohv zW5W**-5gCC80{P%zIs(;5?OdilZP55gt*@+8}EQM4LXJ7F-e>xLPnstdHlxgY0mX| zttiQ(NmWf%fxmUc=0*t!7PHe129V|4-F0lV*EFlRIO^7|TQ3e*x(Dq<>>M52R(p~J zU;d^M5fQOi{R&0yTsD)E@vMRPzt4edVVLFT>7bmPdzF=yEWEs04}9ILVSIc7!CD&1 zhzq?CjwZ+LASqVdnULBRp(?_+{)YQ&`0%Ua&T$;bbq010+AG*`4IiKy0~cF+qpPPk zsE@Fl?0?5mIV&TGgE6;`cJ9*>&qT5xOZ#+jgI?92CSQe95PUapjMlmKY56P3%ppI} z&M9QJE&9qdtpKgw%}3&4*2`deZ~u6q%$c{>_VL$|L&j7&lVqsQIzV^nEDumf^HDx$ z`nK^S4;Kg5a(Og7DjY5avO->b)Z5tEZiCwcz4+Mj!yNvSCmQ2CR>U@%%}Jz&E;~qo z$kuj2^%1F?Tbp%@wa|;rQ7#Ga`mUQ>`Ou%7M{bHVn(T2pBoyK@^^=e=29};9AoKfo zZ7DM|Grm5;B~1+->vcqx2U^vYnyPNsMw6$gX;D0IpO6?YhO7xJ=i%j&CcfjKV>y%- z>Nu9>X)!+^g9R|YZuE|UPrxpzfc>zuI;`)o-IL{lVw(BF*9ntb67Xb|FuM;00FMm_?7Sl(a47}GuD70&e18|$lTGF<-|yobAa%z*si%)S zse1Zdg3n)*#in-0+>h9Fv8(%FP~sn25w9}R5&Llo<^Fu610e9|>sK9SRUxJHqNgE9 z1Z^p$>b8^Us-N$)GI`3hL^JzOT;X&v9bt!a>81xaf;H%Z0xI47`c5salHM30N6um* z;ut{qjO}p=HF}zvj;unGOgWoU{V7jV8#)N#Ur9#%H?DyZsT1JIH+?e1m1W&0ga?FH z20jX)}`{}pz8xq+sqJBx>7Q9*&czJC1i%%4AYC!b4B zS~DP^(awD7i(nPxYKJ)y`L!XI^uEfFA8q6H`oc%7H=d>my(*raPXYOd6I|Of(@|A1 zuy<*`^Bw)aoR>e|I$N3z4lAzCkjrc?Nm^eL&lvsv#oQc^h6a-G5-{*S<}GV+bJB5l z7mkdIYWu(-j`jEyPu_Sv|HP7aS&MRWYpcu+93#gClQXbEIUbo~XHQ{3Rh?qm8Gjg) zW1=u1HaXeu>0DxRa(G9Fav>zB=qk`c|8s_d7g6gXd+V5JgAmkhOL}xes`JZO|AfWJ zEA8@ax}(fJr-a)oOgn2~4A5zuE{}|wT6OP-!OQf%Hl5F;R)}tVgSE1ESd*)XBVkxSI0wKUTNu?-RY*Z?L)1O)_G5}F{T#sXtZ zwFi^chJL@KRZP9!=IoD#C$;c`<_$BeqMP5D0r`m(P!VJrBuPYljdQ~Kv*I z!X=6+?~^R3HzP;G*XF;@_dIII(Nia~mnz_3RKun*j7GJu_m)?UIG8Vof9+=kw7nxp zEWr+J>>~&4s*JmFCOvM7lWvsF5jLRujFoeWOb9SgZetD*WOmyEr5DsSsM)bxUIs}l zceHg=etn)!b9kqY9@He*RkJv*1{%9l{auwd5AnqhtU?Nil`2;n9E`l)QOFk#*L zTDcsw^5A+UPN&rG!c9a>?C0x(&-iH9r=Il5btHr2+E*L9weL?O@5H92kSrvB52E3+=3&zU$AFY>~*bMy~At8-b#q1WkZ9fHQ2NL&Gk6=WVfui2a`Iq$^~ z;b>as@kDUs>?A9Ho&J;l(4gr#QQ==Yfs{aj1bX#A1zk=G=35`a3pn9|1Vr~~0V{Nu z31ytz-=0E{9Ej*998|ggVs6IdpyM>{JoEZF%I?az7t=KJ8)YKzdruCURI(Uw(g+}W z&AwqD?=PSlmh&BCuSN&X35p_}4)?$6$Q0PmrLEd6wa#Sv6 zyYnrrkDQ*%O#!&YufG4vGE!@%(D+WSM{L$??>Ql1Hd<5eOqF3*Wyh8-VZc69~keHxI%jxGK#( znLyn_irdTz$QdVJZ7hgHsN#gKqPg5&#CX3&%`aJeX%Au*?QC&(0?+ z7iy>Rp1ffhQFTefV1lI_=1E~y>WsywO9nS|SiGOTKY44JT<8NT9K z{oc8&eWhcD;|-@qBL73JTDL`R&ej$SdRlphHGPqZvAcI4P+5?C)Elx;>jvex6z=*D zCp~Gz)bZ*!yKlAwF0pBDMMN$>QrUn2*uQBt>G%`bMLhlvg00~V4B|X%=vrvt5mhY> z=Z@KE^~ee6hgql-;ZGqL4(pvjF&O-a&&7|IPv!Xn^77Juj#S&}h_yxzQnZ2(7pYl~ zs2euw4o=z5AK1fl4zMTzWlvQJ*^e;gv@7jM`O7lrYM}(TDti~*k|Y*8J89#Xe2!99 z$Y9>l^L!pq5cCn-s+%KyWJHT3^}(#^1!hgCkcf>sK5e6hp(3R>epo)zRTMaoiZ#Br zz!>yeoH_H2pg7L(wPW`^kpD1>>vVRBmN)ab?PjB?px@#J(th65^M9uK^w3GWy%5R1 zK`KshJ1-`dSz&9!%Q`a5+>X|L-o!T~1addL ztk<2q&l<=KPhr`aMy*j6!7S=uR`VZw3FGvul2R&AoIXnyPk5xPteE?_*;+`UP`|}O z0A3^RS>0GtLXOd{UH$#lJ_GLS{foI19=<4P?i%nPn#DfSwT}RFZ1fI{_Wh2wL|qG2rYN=fKy$ z=j05#UdyeFb1=4k7@+pdEsH-_r9lTk+4Ho@P0u z)|Ps>W=o(BC%7AOP<6pdaMxw#q7Q3)Vgf%@5fa=CB&gp{VFWhz_Mp{`U};^Bc7E${ zuExiV4Cj;dgoFgI9oU@frE?26I8gFpq=KeAh5W}!#Nz%V-RaL;#Zx9VXLBLAvs%)hYzdWTPzVlP(A`C~J|VZBzN%aJ-R9*xYXpXnP$*E~V9rGv zIdtd52XX>MGCMNNGpodSquiMqc?ulL2eAjN%A{_5Vwx1%gj;(yFRo_GwW99%d!G>A zfM7u>bTP1KC0vCvL<~NfAG){Igg9Ou7qr0Y|6{OxUhihW`7S0Ebl;U-e0@HDOJ}0Nq8UM+DoaEgF)Sz=Pl?^J1@S z{Vs&c%geVLlU@+^G%o+_JkxzELbmgD*#lHoH8=DBhbCN}s+=%3PrX~LWT5rx=x|03 z&1kUUS7laK76hO$E7zmX>>Ta00y4)Ovc?=1 zyQ^1Ea-gQj8*&sV0`FOq4z8Veb55Fl__Q-tAhYYocVnc(ve5KU8MieCLW5Hy2X%Be zC^mw~GbS^LH#)rSExvUJPlT3a(4or78BukW1I_vW-)(3tFecQ1v+iJ(2s;0;_PnbL z8t{76)z$aNN095mq};pTtG{so63#e|D_28sJV-GsKRW`WBFWGfH8?UphDjqr#Zp>z z>*g9qieY#u7{KG66Nb&hi(Gzp7B1It%*2VW<$gKlU2fYCjcm<4~z4kKz|kWIG;U4 zHka&$UkF$HL8Uz7xo;z<5&g6r`IV?hMOhhdv*-aWOlSA<{*B-7dL3{=rpn*qBcyoE zr>@8H-Ucuj9ZAYDrqBFgS1gKbw>87FMeMGC^X$ya_Ik(zhmyU&h#F1XNy*9cYo!>A zt+k8_t^1ASztIv&jwr&q{T=7*A3x}J@*S`PV&*o)F;i)Os$}cw%a<=%F;=najo~E2dAcK8n=*`cJ_sG;{N7e$H&l9b``I=ECLHN+HbN2Km;wGH zsw>j)zRQ#cX`Xh^SrOq>y&U<)?OPI-T+{paIEyO?u$OxhA zSH1m;d&1k0-F;vX2$;d&`7)ZGZKE%HJ1%*EA!3gvu3Ix)c(r5 zq@PdyL3Vmpxtjb#30?hr^h^O!t!FW9fb4miv*1%xlBq_gnkn?$2yTJ%k9R>C+r;mM z5W3>9)aEvY(pkK=;i3Y{dxZW{)IRRltH*OU1;BU4d&}{3HAT4A(=fDhKnzlTPnHjh zLp!x;6cx%>_1G^~lk$c{O5<84t9$IWB%tqeuz@HNI=yy;F#gehPoKHQ$)z-3U-ONl zbmPG?%BY#@;D!DB`j;n4^Dmy1ms^M-KFY98ZW?K*@aN^VXCKBCS!cM&8e?Yb`o}-@ z&L15V@ipeJ^5$|nb1_xRKE46veB;Gp=6}wk=yyp04m$AbS3cy+Va$Sfa}-(Eez*KY z?{rmvgz(!htC0iTR&gN0EH-Zu41+_cA6PE@TU%4g4reT zzK}~ymm#xP6D(&}$nHiNwcszFQiUHTKE5Y=%>rav?Ohs7huXIf`*KpT0rJMd9dS58 zm`YKT)9^Kj*SL}Z?3 zo3u?0_U4$^`!Ir|%@&cGcxhdeKBm`-=yU3@H?jjPMW8-=bFjdfUe`a7s!6Iu!*>Ujg7sjUnYLn*Sy26 zhob+QLI_9g7*3%38P9za)E}|)Y*sAuS{mB)KYzI0E86r3OxgJU0ak ziTI%xq~9w&zN{q8A8iDvVdBZciyCJR2t+`8Uc=QF{&iWGXq7GChM8)|*=DLZ;o`?o z-(1)9mWit$XN3-2J0GL8)^*ho-{{KQhEafJ1fxJD9tBIDIut~ErdO7JExL5GEgN|` z;Kz9rQh317$HnvGD*Qi7`LO*+L0Sw*N=y`S&R@!vdhzj{Yzp3I_xZEH0JONZi@xri z3P?qNz1_6mZ<7N>A?rN$s*P9TtxR^6SDpU_eQmHxIf;5G0QKYPlgYuZQA)Er?BPt z!{Vm>y1s$vfp_W!L?iEq%Fg^XBO;i^E|@7s!j$GEK431=N0vBw{%m)kJLt6O=l!a% z^W&X_dObSEe_l&p`UokXpkTE^>0*H*ODAxBrN0Be-?9h7c&Hgof~_uqwnMS&h4txv zLaF19@ZDk7YW9tp^iF@9pcOI9hRbv?dCP13`P7mqrPZ_no=ef;kr7Dk{AS8=u*SSz zHnu|mMl>lpuYkNoFRfUyrFL)^7TmS%P z13gMlwV1E<1E%qgwu_YrlX#sMjj&P206+vmd*1Q)5$Ax#Cmg} zK1iU{VeBj1RO!ZC+n=UvN%_=PTg{f)ig+HY`eeXf%HLhMU$YytwncvF_+pE0muxFS zajEJFvez-&%(JT_6Ckq%hFOd$m18-pNt9R1d&@G7`Q~BvV$SrC;9M=;9OaMKoWro= z6-DVwzX$*LYol%A#>P3%oarORsNuKTnY*30m}Y&Va_E9))|8z5RqoA?V+1fRAV5oE zhvp&1^pBk7#OZ66`s&-8^_7 zZCHTA&o}307cz~TpxFpZZ$?TxU@_m|pDf|}W%lU1>5sejVHp!5IDG@${~$GoPT;BX z1$8e-SWXH$Q}@ok${3)|dR&OvN(;~OSK>`#%2NICsh_E^L5iQ5YZ4SE zO|vE{4lw&nKlzu-U>)}{Xrss@F4xJVBrGb4jj`kP?Z1WpVOb#=G?KrWC>z** zpS=%u3T|d0%_GIb1~Oq{Gk>e}ZcoJMH`XCPSC-Tv>0=SMb+0xPo$SruYKRL_{wZn9 z)8N=C`aAr>0DkFIn3jBKB$`#R-d`jB}F)`bO-RHC6-w P=1_a2{qVb@MbQ5MB)geL delta 6315 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 dsqeX5u%9mHSBEr;Qv Date: Mon, 3 Feb 2020 22:58:34 +0100 Subject: [PATCH 006/144] EMPed flashes now don't runtime --- code/game/objects/items/devices/flash.dm | 42 +++++++++++++----------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index a618383a819..263dcb955df 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -84,27 +84,29 @@ /obj/item/flash/proc/flash_carbon(var/mob/living/carbon/M, var/mob/user = null, var/power = 5, targeted = 1) - add_attack_logs(user, M, "Flashed with [src]") - if(user && targeted) - if(M.weakeyes) - M.Weaken(3) //quick weaken bypasses eye protection but has no eye flash - if(M.flash_eyes(1, 1)) - M.AdjustConfused(power) - terrible_conversion_proc(M, user) - M.Stun(1) - visible_message("[user] blinds [M] with the flash!") - to_chat(user, "You blind [M] with the flash!") - to_chat(M, "[user] blinds you with the flash!") + if(user) + add_attack_logs(user, M, "Flashed with [src]") + if(targeted) if(M.weakeyes) - M.Stun(2) - M.visible_message("[M] gasps and shields [M.p_their()] eyes!", "You gasp and shields your eyes!") - else - visible_message("[user] fails to blind [M] with the flash!") - to_chat(user, "You fail to blind [M] with the flash!") - to_chat(M, "[user] fails to blind you with the flash!") - else - if(M.flash_eyes()) - M.AdjustConfused(power) + M.Weaken(3) //quick weaken bypasses eye protection but has no eye flash + if(M.flash_eyes(1, 1)) + M.AdjustConfused(power) + terrible_conversion_proc(M, user) + M.Stun(1) + visible_message("[user] blinds [M] with the flash!") + to_chat(user, "You blind [M] with the flash!") + to_chat(M, "[user] blinds you with the flash!") + if(M.weakeyes) + M.Stun(2) + M.visible_message("[M] gasps and shields [M.p_their()] eyes!", "You gasp and shields your eyes!") + else + visible_message("[user] fails to blind [M] with the flash!") + to_chat(user, "You fail to blind [M] with the flash!") + to_chat(M, "[user] fails to blind you with the flash!") + return + + if(M.flash_eyes()) + M.AdjustConfused(power) /obj/item/flash/attack(mob/living/M, mob/user) if(!try_use_flash(user)) From e0556675837fbbb998a1b6b541a950dc40fdba3b Mon Sep 17 00:00:00 2001 From: joep van der velden Date: Mon, 3 Feb 2020 23:07:06 +0100 Subject: [PATCH 007/144] Fixes borg hypo's runtiming on non humans --- code/modules/mob/living/carbon/alien/humanoid/queen.dm | 2 +- code/modules/mob/living/living.dm | 2 +- code/modules/mob/living/silicon/silicon.dm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 0132bcdf88b..8818438269d 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -54,7 +54,7 @@ else healths.icon_state = "health6" -/mob/living/carbon/alien/humanoid/queen/can_inject() +/mob/living/carbon/alien/humanoid/queen/can_inject(mob/user, error_msg, target_zone, penetrate_thick) return FALSE //Queen verbs diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index ba822c9cc5e..30007f48c59 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -367,7 +367,7 @@ return 0 // Living mobs use can_inject() to make sure that the mob is not syringe-proof in general. -/mob/living/proc/can_inject() +/mob/living/proc/can_inject(mob/user, error_msg, target_zone, penetrate_thick) return TRUE /mob/living/is_injectable(mob/user, allowmobs = TRUE) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 51841440989..d2713ef2936 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -92,7 +92,7 @@ /mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) return -/mob/living/silicon/can_inject(mob/user, error_msg) +/mob/living/silicon/can_inject(mob/user, error_msg, target_zone, penetrate_thick) if(error_msg) to_chat(user, "[p_their(TRUE)] outer shell is too tough.") return FALSE From 3242587ea95789b4460c98e973c76678608a0c30 Mon Sep 17 00:00:00 2001 From: joep van der velden Date: Sat, 15 Feb 2020 08:47:00 +0100 Subject: [PATCH 008/144] ispath check --- code/modules/customitems/item_spawning.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/customitems/item_spawning.dm b/code/modules/customitems/item_spawning.dm index 631228fed9b..b88219892d7 100644 --- a/code/modules/customitems/item_spawning.dm +++ b/code/modules/customitems/item_spawning.dm @@ -11,8 +11,8 @@ var/propadjust = query.item[2] var/jobmask = query.item[3] var/ok = 0 - if(!path) - log_debug("Incorrect database entry found in table 'customuseritems', cuiPath is null. cuiCKey='[M.ckey]' AND (cuiRealName='[sanitizeSQL(M.real_name)]' OR cuiRealName='*'") + if(!path || !ispath(path)) + log_debug("Incorrect database entry found in table 'customuseritems' path value = [path], cuiPath is null. cuiCKey='[M.ckey]' AND (cuiRealName='[sanitizeSQL(M.real_name)]' OR cuiRealName='*'") continue if(jobmask != "*") var/list/allowed_jobs = splittext(jobmask,",") From c97dc0e58441f101fcf7744b0ce2df026353c55c Mon Sep 17 00:00:00 2001 From: Ty-Omaha Date: Fri, 28 Feb 2020 07:39:33 -0500 Subject: [PATCH 009/144] offstation roles cult sac --- code/game/gamemodes/cult/cult_objectives.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/gamemodes/cult/cult_objectives.dm b/code/game/gamemodes/cult/cult_objectives.dm index 83ae3c2f1b7..40d90a08f7f 100644 --- a/code/game/gamemodes/cult/cult_objectives.dm +++ b/code/game/gamemodes/cult/cult_objectives.dm @@ -155,12 +155,12 @@ /datum/game_mode/cult/proc/get_possible_sac_targets() var/list/possible_sac_targets = list() for(var/mob/living/carbon/human/player in GLOB.player_list) - if(player.mind && !is_convertable_to_cult(player.mind) && (player.stat != DEAD)) + if(player.mind && !is_convertable_to_cult(player.mind) && (player.stat != DEAD) && (player.mind.offstation_role != TRUE) ) possible_sac_targets += player.mind if(!possible_sac_targets.len) //There are no living Unconvertables on the station. Looking for a Sacrifice Target among the ordinary crewmembers for(var/mob/living/carbon/human/player in GLOB.player_list) - if(is_secure_level(player.z)) //We can't sacrifice people that are on the centcom z-level + if(is_secure_level(player.z) || player.mind.offstation_role == TRUE) //We can't sacrifice people that are on the centcom z-level or offstation roles continue if(player.mind && !(player.mind in cult) && (player.stat != DEAD))//make DAMN sure they are not dead possible_sac_targets += player.mind From 6064243009fa51573293e42c154d772ba3b6726c Mon Sep 17 00:00:00 2001 From: Aquilar <20759278+Aquilar@users.noreply.github.com> Date: Sun, 15 Mar 2020 12:56:01 +0800 Subject: [PATCH 010/144] simpler --- code/game/objects/items/blueprints.dm | 13 +++++-------- .../research/xenobiology/xenobiology.dm | 1 - icons/obj/items.dmi | Bin 153359 -> 153063 bytes 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index 2ece6b6b32a..09a3e17e034 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -87,11 +87,11 @@ //Station blueprints!!! /obj/item/areaeditor/blueprints - name = "Blueprints Tablet" - desc = "Heavy duty work tablet with blueprints of the station loaded. There is a blinking \"Classified\" icon and several dents in the casing" + name = "station blueprints" + desc = "Blueprints of the station. There is a \"Classified\" stamp and several coffee stains on it." icon = 'icons/obj/items.dmi' - icon_state = "blueprinttablet" - fluffnotice = "Property of Nanotrasen. For heads of staff only. Wipe data after usage." + icon_state = "blueprints" + fluffnotice = "Property of Nanotrasen. For heads of staff only. Store in high-secure storage." resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF w_class = WEIGHT_CLASS_NORMAL var/list/showing = list() @@ -329,7 +329,4 @@ fluffnotice = "Intellectual Property of Nanotrasen. For use in engineering cyborgs only. Wipe from memory upon departure from the station." /obj/item/areaeditor/blueprints/ce - name = "station blueprints" - desc = "Blueprints of the station. There is a \"Classified\" stamp and several coffee stains on it." - icon_state = "blueprints" - fluffnotice = "Property of Nanotrasen. For heads of staff only. Store in high-secure storage." + diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 298946853da..c71c3f6f8d8 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -538,7 +538,6 @@ /obj/item/areaeditor/blueprints/slime name = "cerulean prints" - icon_state = "blueprints" desc = "A one use set of blueprints made of jelly like organic material. Extends the reach of the management console." color = "#2956B2" diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi index dadc1c3540583d6027b04b954d9f906fb7952a19..bac18fe788d5f449aa2d0a5c1198351c27a913a8 100644 GIT binary patch delta 6315 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 dsqeX5u%9mHSBEr;Qv6Y%0?pPM~ zF8=r4IWuR@nVB>1^F41p@3&q=81tPl_8!Ee?EZ5iR?GhD^neuIFUStzIjCVHdaGv4 z8T9*wh6ZQ_T)(zmCJ#msg&~L_H3%Xsd|_}`PcLnq9y}$fSzLmf1_eEY@V*bibB-9Z zctTxKK4s@-@Tnt5tt!_(d&p>dkUgtVi$_g1Lix39jvzY+ecVW#gO8i$R=HcR(6GLx zyzbpE5^x0zd6#5`cPV?QvI{Zc)|r$PS?oRMZ_g@h&DSPN;TO#5XTFVgjD-aX9;cu+ zK@c2?)JPWNrA`s&Xmz|jffh``QZ+=4g@%R}6cohv zW5W**-5gCC80{P%zIs(;5?OdilZP55gt*@+8}EQM4LXJ7F-e>xLPnstdHlxgY0mX| zttiQ(NmWf%fxmUc=0*t!7PHe129V|4-F0lV*EFlRIO^7|TQ3e*x(Dq<>>M52R(p~J zU;d^M5fQOi{R&0yTsD)E@vMRPzt4edVVLFT>7bmPdzF=yEWEs04}9ILVSIc7!CD&1 zhzq?CjwZ+LASqVdnULBRp(?_+{)YQ&`0%Ua&T$;bbq010+AG*`4IiKy0~cF+qpPPk zsE@Fl?0?5mIV&TGgE6;`cJ9*>&qT5xOZ#+jgI?92CSQe95PUapjMlmKY56P3%ppI} z&M9QJE&9qdtpKgw%}3&4*2`deZ~u6q%$c{>_VL$|L&j7&lVqsQIzV^nEDumf^HDx$ z`nK^S4;Kg5a(Og7DjY5avO->b)Z5tEZiCwcz4+Mj!yNvSCmQ2CR>U@%%}Jz&E;~qo z$kuj2^%1F?Tbp%@wa|;rQ7#Ga`mUQ>`Ou%7M{bHVn(T2pBoyK@^^=e=29};9AoKfo zZ7DM|Grm5;B~1+->vcqx2U^vYnyPNsMw6$gX;D0IpO6?YhO7xJ=i%j&CcfjKV>y%- z>Nu9>X)!+^g9R|YZuE|UPrxpzfc>zuI;`)o-IL{lVw(BF*9ntb67Xb|FuM;00FMm_?7Sl(a47}GuD70&e18|$lTGF<-|yobAa%z*si%)S zse1Zdg3n)*#in-0+>h9Fv8(%FP~sn25w9}R5&Llo<^Fu610e9|>sK9SRUxJHqNgE9 z1Z^p$>b8^Us-N$)GI`3hL^JzOT;X&v9bt!a>81xaf;H%Z0xI47`c5salHM30N6um* z;ut{qjO}p=HF}zvj;unGOgWoU{V7jV8#)N#Ur9#%H?DyZsT1JIH+?e1m1W&0ga?FH z20jX)}`{}pz8xq+sqJBx>7Q9*&czJC1i%%4AYC!b4B zS~DP^(awD7i(nPxYKJ)y`L!XI^uEfFA8q6H`oc%7H=d>my(*raPXYOd6I|Of(@|A1 zuy<*`^Bw)aoR>e|I$N3z4lAzCkjrc?Nm^eL&lvsv#oQc^h6a-G5-{*S<}GV+bJB5l z7mkdIYWu(-j`jEyPu_Sv|HP7aS&MRWYpcu+93#gClQXbEIUbo~XHQ{3Rh?qm8Gjg) zW1=u1HaXeu>0DxRa(G9Fav>zB=qk`c|8s_d7g6gXd+V5JgAmkhOL}xes`JZO|AfWJ zEA8@ax}(fJr-a)oOgn2~4A5zuE{}|wT6OP-!OQf%Hl5F;R)}tVgSE1ESd*)XBVkxSI0wKUTNu?-RY*Z?L)1O)_G5}F{T#sXtZ zwFi^chJL@KRZP9!=IoD#C$;c`<_$BeqMP5D0r`m(P!VJrBuPYljdQ~Kv*I z!X=6+?~^R3HzP;G*XF;@_dIII(Nia~mnz_3RKun*j7GJu_m)?UIG8Vof9+=kw7nxp zEWr+J>>~&4s*JmFCOvM7lWvsF5jLRujFoeWOb9SgZetD*WOmyEr5DsSsM)bxUIs}l zceHg=etn)!b9kqY9@He*RkJv*1{%9l{auwd5AnqhtU?Nil`2;n9E`l)QOFk#*L zTDcsw^5A+UPN&rG!c9a>?C0x(&-iH9r=Il5btHr2+E*L9weL?O@5H92kSrvB52E3+=3&zU$AFY>~*bMy~At8-b#q1WkZ9fHQ2NL&Gk6=WVfui2a`Iq$^~ z;b>as@kDUs>?A9Ho&J;l(4gr#QQ==Yfs{aj1bX#A1zk=G=35`a3pn9|1Vr~~0V{Nu z31ytz-=0E{9Ej*998|ggVs6IdpyM>{JoEZF%I?az7t=KJ8)YKzdruCURI(Uw(g+}W z&AwqD?=PSlmh&BCuSN&X35p_}4)?$6$Q0PmrLEd6wa#Sv6 zyYnrrkDQ*%O#!&YufG4vGE!@%(D+WSM{L$??>Ql1Hd<5eOqF3*Wyh8-VZc69~keHxI%jxGK#( znLyn_irdTz$QdVJZ7hgHsN#gKqPg5&#CX3&%`aJeX%Au*?QC&(0?+ z7iy>Rp1ffhQFTefV1lI_=1E~y>WsywO9nS|SiGOTKY44JT<8NT9K z{oc8&eWhcD;|-@qBL73JTDL`R&ej$SdRlphHGPqZvAcI4P+5?C)Elx;>jvex6z=*D zCp~Gz)bZ*!yKlAwF0pBDMMN$>QrUn2*uQBt>G%`bMLhlvg00~V4B|X%=vrvt5mhY> z=Z@KE^~ee6hgql-;ZGqL4(pvjF&O-a&&7|IPv!Xn^77Juj#S&}h_yxzQnZ2(7pYl~ zs2euw4o=z5AK1fl4zMTzWlvQJ*^e;gv@7jM`O7lrYM}(TDti~*k|Y*8J89#Xe2!99 z$Y9>l^L!pq5cCn-s+%KyWJHT3^}(#^1!hgCkcf>sK5e6hp(3R>epo)zRTMaoiZ#Br zz!>yeoH_H2pg7L(wPW`^kpD1>>vVRBmN)ab?PjB?px@#J(th65^M9uK^w3GWy%5R1 zK`KshJ1-`dSz&9!%Q`a5+>X|L-o!T~1addL ztk<2q&l<=KPhr`aMy*j6!7S=uR`VZw3FGvul2R&AoIXnyPk5xPteE?_*;+`UP`|}O z0A3^RS>0GtLXOd{UH$#lJ_GLS{foI19=<4P?i%nPn#DfSwT}RFZ1fI{_Wh2wL|qG2rYN=fKy$ z=j05#UdyeFb1=4k7@+pdEsH-_r9lTk+4Ho@P0u z)|Ps>W=o(BC%7AOP<6pdaMxw#q7Q3)Vgf%@5fa=CB&gp{VFWhz_Mp{`U};^Bc7E${ zuExiV4Cj;dgoFgI9oU@frE?26I8gFpq=KeAh5W}!#Nz%V-RaL;#Zx9VXLBLAvs%)hYzdWTPzVlP(A`C~J|VZBzN%aJ-R9*xYXpXnP$*E~V9rGv zIdtd52XX>MGCMNNGpodSquiMqc?ulL2eAjN%A{_5Vwx1%gj;(yFRo_GwW99%d!G>A zfM7u>bTP1KC0vCvL<~NfAG){Igg9Ou7qr0Y|6{OxUhihW`7S0Ebl;U-e0@HDOJ}0Nq8UM+DoaEgF)Sz=Pl?^J1@S z{Vs&c%geVLlU@+^G%o+_JkxzELbmgD*#lHoH8=DBhbCN}s+=%3PrX~LWT5rx=x|03 z&1kUUS7laK76hO$E7zmX>>Ta00y4)Ovc?=1 zyQ^1Ea-gQj8*&sV0`FOq4z8Veb55Fl__Q-tAhYYocVnc(ve5KU8MieCLW5Hy2X%Be zC^mw~GbS^LH#)rSExvUJPlT3a(4or78BukW1I_vW-)(3tFecQ1v+iJ(2s;0;_PnbL z8t{76)z$aNN095mq};pTtG{so63#e|D_28sJV-GsKRW`WBFWGfH8?UphDjqr#Zp>z z>*g9qieY#u7{KG66Nb&hi(Gzp7B1It%*2VW<$gKlU2fYCjcm<4~z4kKz|kWIG;U4 zHka&$UkF$HL8Uz7xo;z<5&g6r`IV?hMOhhdv*-aWOlSA<{*B-7dL3{=rpn*qBcyoE zr>@8H-Ucuj9ZAYDrqBFgS1gKbw>87FMeMGC^X$ya_Ik(zhmyU&h#F1XNy*9cYo!>A zt+k8_t^1ASztIv&jwr&q{T=7*A3x}J@*S`PV&*o)F;i)Os$}cw%a<=%F;=najo~E2dAcK8n=*`cJ_sG;{N7e$H&l9b``I=ECLHN+HbN2Km;wGH zsw>j)zRQ#cX`Xh^SrOq>y&U<)?OPI-T+{paIEyO?u$OxhA zSH1m;d&1k0-F;vX2$;d&`7)ZGZKE%HJ1%*EA!3gvu3Ix)c(r5 zq@PdyL3Vmpxtjb#30?hr^h^O!t!FW9fb4miv*1%xlBq_gnkn?$2yTJ%k9R>C+r;mM z5W3>9)aEvY(pkK=;i3Y{dxZW{)IRRltH*OU1;BU4d&}{3HAT4A(=fDhKnzlTPnHjh zLp!x;6cx%>_1G^~lk$c{O5<84t9$IWB%tqeuz@HNI=yy;F#gehPoKHQ$)z-3U-ONl zbmPG?%BY#@;D!DB`j;n4^Dmy1ms^M-KFY98ZW?K*@aN^VXCKBCS!cM&8e?Yb`o}-@ z&L15V@ipeJ^5$|nb1_xRKE46veB;Gp=6}wk=yyp04m$AbS3cy+Va$Sfa}-(Eez*KY z?{rmvgz(!htC0iTR&gN0EH-Zu41+_cA6PE@TU%4g4reT zzK}~ymm#xP6D(&}$nHiNwcszFQiUHTKE5Y=%>rav?Ohs7huXIf`*KpT0rJMd9dS58 zm`YKT)9^Kj*SL}Z?3 zo3u?0_U4$^`!Ir|%@&cGcxhdeKBm`-=yU3@H?jjPMW8-=bFjdfUe`a7s!6Iu!*>Ujg7sjUnYLn*Sy26 zhob+QLI_9g7*3%38P9za)E}|)Y*sAuS{mB)KYzI0E86r3OxgJU0ak ziTI%xq~9w&zN{q8A8iDvVdBZciyCJR2t+`8Uc=QF{&iWGXq7GChM8)|*=DLZ;o`?o z-(1)9mWit$XN3-2J0GL8)^*ho-{{KQhEafJ1fxJD9tBIDIut~ErdO7JExL5GEgN|` z;Kz9rQh317$HnvGD*Qi7`LO*+L0Sw*N=y`S&R@!vdhzj{Yzp3I_xZEH0JONZi@xri z3P?qNz1_6mZ<7N>A?rN$s*P9TtxR^6SDpU_eQmHxIf;5G0QKYPlgYuZQA)Er?BPt z!{Vm>y1s$vfp_W!L?iEq%Fg^XBO;i^E|@7s!j$GEK431=N0vBw{%m)kJLt6O=l!a% z^W&X_dObSEe_l&p`UokXpkTE^>0*H*ODAxBrN0Be-?9h7c&Hgof~_uqwnMS&h4txv zLaF19@ZDk7YW9tp^iF@9pcOI9hRbv?dCP13`P7mqrPZ_no=ef;kr7Dk{AS8=u*SSz zHnu|mMl>lpuYkNoFRfUyrFL)^7TmS%P z13gMlwV1E<1E%qgwu_YrlX#sMjj&P206+vmd*1Q)5$Ax#Cmg} zK1iU{VeBj1RO!ZC+n=UvN%_=PTg{f)ig+HY`eeXf%HLhMU$YytwncvF_+pE0muxFS zajEJFvez-&%(JT_6Ckq%hFOd$m18-pNt9R1d&@G7`Q~BvV$SrC;9M=;9OaMKoWro= z6-DVwzX$*LYol%A#>P3%oarORsNuKTnY*30m}Y&Va_E9))|8z5RqoA?V+1fRAV5oE zhvp&1^pBk7#OZ66`s&-8^_7 zZCHTA&o}307cz~TpxFpZZ$?TxU@_m|pDf|}W%lU1>5sejVHp!5IDG@${~$GoPT;BX z1$8e-SWXH$Q}@ok${3)|dR&OvN(;~OSK>`#%2NICsh_E^L5iQ5YZ4SE zO|vE{4lw&nKlzu-U>)}{Xrss@F4xJVBrGb4jj`kP?Z1WpVOb#=G?KrWC>z** zpS=%u3T|d0%_GIb1~Oq{Gk>e}ZcoJMH`XCPSC-Tv>0=SMb+0xPo$SruYKRL_{wZn9 z)8N=C`aAr>0DkFIn3jBKB$`#R-d`jB}F)`bO-RHC6-w P=1_a2{qVb@MbQ5MB)geL From 898ca53e71a4d0ae984af55ba66bc1f76825a4e3 Mon Sep 17 00:00:00 2001 From: Aquilar <20759278+Aquilar@users.noreply.github.com> Date: Wed, 18 Mar 2020 15:09:41 +0800 Subject: [PATCH 011/144] deconflict Signed-off-by: Aquilar <20759278+Aquilar@users.noreply.github.com> --- .../closets/secure/engineering.dm | 294 ++--- code/modules/paperwork/photography.dm | 1136 ++++++++--------- 2 files changed, 715 insertions(+), 715 deletions(-) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index e09e94710f8..f0a7336389b 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -1,152 +1,152 @@ -/obj/structure/closet/secure_closet/engineering_chief - name = "chief engineer's locker" +/obj/structure/closet/secure_closet/engineering_chief + name = "chief engineer's locker" req_access = list(ACCESS_CE) - icon_state = "securece1" - icon_closed = "securece" - icon_locked = "securece1" - icon_opened = "secureceopen" - icon_broken = "securecebroken" - icon_off = "secureceoff" - -/obj/structure/closet/secure_closet/engineering_chief/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/industrial(src) - else - new /obj/item/storage/backpack/satchel_eng(src) - new /obj/item/storage/backpack/duffel/engineering(src) - new /obj/item/clothing/head/beret/ce(src) - new /obj/item/areaeditor/blueprints/ce(src) - new /obj/item/storage/box/permits(src) - new /obj/item/clothing/under/rank/chief_engineer(src) - new /obj/item/clothing/under/rank/chief_engineer/skirt(src) - new /obj/item/clothing/suit/mantle/chief_engineer(src) - new /obj/item/clothing/head/hardhat/white(src) - new /obj/item/clothing/glasses/welding/superior(src) - new /obj/item/clothing/gloves/color/yellow(src) - new /obj/item/clothing/shoes/brown(src) - new /obj/item/tank/jetpack/suit(src) - new /obj/item/cartridge/ce(src) - new /obj/item/radio/headset/heads/ce(src) - new /obj/item/storage/toolbox/mechanical(src) - new /obj/item/clothing/suit/storage/hazardvest(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/multitool(src) - new /obj/item/holosign_creator/engineering(src) - new /obj/item/flash(src) - new /obj/item/clothing/head/beret/eng(src) - new /obj/item/door_remote/chief_engineer(src) - new /obj/item/rpd(src) - new /obj/item/reagent_containers/food/drinks/mug/ce(src) - new /obj/item/organ/internal/cyberimp/eyes/meson(src) - new /obj/item/clothing/accessory/medal/engineering(src) - new /obj/item/holosign_creator/atmos(src) - - -/obj/structure/closet/secure_closet/engineering_electrical - name = "electrical supplies locker" + icon_state = "securece1" + icon_closed = "securece" + icon_locked = "securece1" + icon_opened = "secureceopen" + icon_broken = "securecebroken" + icon_off = "secureceoff" + +/obj/structure/closet/secure_closet/engineering_chief/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/industrial(src) + else + new /obj/item/storage/backpack/satchel_eng(src) + new /obj/item/storage/backpack/duffel/engineering(src) + new /obj/item/clothing/head/beret/ce(src) + new /obj/item/areaeditor/blueprints/ce(src) + new /obj/item/storage/box/permits(src) + new /obj/item/clothing/under/rank/chief_engineer(src) + new /obj/item/clothing/under/rank/chief_engineer/skirt(src) + new /obj/item/clothing/suit/mantle/chief_engineer(src) + new /obj/item/clothing/head/hardhat/white(src) + new /obj/item/clothing/glasses/welding/superior(src) + new /obj/item/clothing/gloves/color/yellow(src) + new /obj/item/clothing/shoes/brown(src) + new /obj/item/tank/jetpack/suit(src) + new /obj/item/cartridge/ce(src) + new /obj/item/radio/headset/heads/ce(src) + new /obj/item/storage/toolbox/mechanical(src) + new /obj/item/clothing/suit/storage/hazardvest(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/multitool(src) + new /obj/item/holosign_creator/engineering(src) + new /obj/item/flash(src) + new /obj/item/clothing/head/beret/eng(src) + new /obj/item/door_remote/chief_engineer(src) + new /obj/item/rpd(src) + new /obj/item/reagent_containers/food/drinks/mug/ce(src) + new /obj/item/organ/internal/cyberimp/eyes/meson(src) + new /obj/item/clothing/accessory/medal/engineering(src) + new /obj/item/holosign_creator/atmos(src) + + +/obj/structure/closet/secure_closet/engineering_electrical + name = "electrical supplies locker" req_access = list(ACCESS_ENGINE_EQUIP) - icon_state = "secureengelec1" - icon_closed = "secureengelec" - icon_locked = "secureengelec1" - icon_opened = "toolclosetopen" - icon_broken = "secureengelecbroken" - icon_off = "secureengelecoff" - -/obj/structure/closet/secure_closet/engineering_electrical/New() - ..() - new /obj/item/clothing/gloves/color/yellow(src) - new /obj/item/clothing/gloves/color/yellow(src) - new /obj/item/storage/toolbox/electrical(src) - new /obj/item/storage/toolbox/electrical(src) - new /obj/item/storage/toolbox/electrical(src) - new /obj/item/apc_electronics(src) - new /obj/item/apc_electronics(src) - new /obj/item/apc_electronics(src) - new /obj/item/multitool(src) - new /obj/item/multitool(src) - new /obj/item/multitool(src) - new /obj/item/clothing/head/beret/eng - - -/obj/structure/closet/secure_closet/engineering_welding - name = "welding supplies locker" + icon_state = "secureengelec1" + icon_closed = "secureengelec" + icon_locked = "secureengelec1" + icon_opened = "toolclosetopen" + icon_broken = "secureengelecbroken" + icon_off = "secureengelecoff" + +/obj/structure/closet/secure_closet/engineering_electrical/New() + ..() + new /obj/item/clothing/gloves/color/yellow(src) + new /obj/item/clothing/gloves/color/yellow(src) + new /obj/item/storage/toolbox/electrical(src) + new /obj/item/storage/toolbox/electrical(src) + new /obj/item/storage/toolbox/electrical(src) + new /obj/item/apc_electronics(src) + new /obj/item/apc_electronics(src) + new /obj/item/apc_electronics(src) + new /obj/item/multitool(src) + new /obj/item/multitool(src) + new /obj/item/multitool(src) + new /obj/item/clothing/head/beret/eng + + +/obj/structure/closet/secure_closet/engineering_welding + name = "welding supplies locker" req_access = list(ACCESS_ENGINE_EQUIP) - icon_state = "secureengweld1" - icon_closed = "secureengweld" - icon_locked = "secureengweld1" - icon_opened = "toolclosetopen" - icon_broken = "secureengweldbroken" - icon_off = "secureengweldoff" - -/obj/structure/closet/secure_closet/engineering_welding/New() - ..() - new /obj/item/clothing/head/welding(src) - new /obj/item/clothing/head/welding(src) - new /obj/item/clothing/head/welding(src) - new /obj/item/weldingtool/largetank(src) - new /obj/item/weldingtool/largetank(src) - new /obj/item/weldingtool/largetank(src) - - -/obj/structure/closet/secure_closet/engineering_personal - name = "engineer's locker" + icon_state = "secureengweld1" + icon_closed = "secureengweld" + icon_locked = "secureengweld1" + icon_opened = "toolclosetopen" + icon_broken = "secureengweldbroken" + icon_off = "secureengweldoff" + +/obj/structure/closet/secure_closet/engineering_welding/New() + ..() + new /obj/item/clothing/head/welding(src) + new /obj/item/clothing/head/welding(src) + new /obj/item/clothing/head/welding(src) + new /obj/item/weldingtool/largetank(src) + new /obj/item/weldingtool/largetank(src) + new /obj/item/weldingtool/largetank(src) + + +/obj/structure/closet/secure_closet/engineering_personal + name = "engineer's locker" req_access = list(ACCESS_ENGINE_EQUIP) - icon_state = "secureeng1" - icon_closed = "secureeng" - icon_locked = "secureeng1" - icon_opened = "secureengopen" - icon_broken = "secureengbroken" - icon_off = "secureengoff" - -/obj/structure/closet/secure_closet/engineering_personal/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/industrial(src) - else - new /obj/item/storage/backpack/satchel_eng(src) - new /obj/item/storage/backpack/duffel/engineering(src) - new /obj/item/storage/toolbox/mechanical(src) - new /obj/item/holosign_creator/engineering(src) - new /obj/item/radio/headset/headset_eng(src) - new /obj/item/clothing/under/rank/engineer(src) - new /obj/item/clothing/under/rank/engineer/skirt(src) - new /obj/item/clothing/suit/storage/hazardvest(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/clothing/glasses/meson(src) - new /obj/item/cartridge/engineering(src) - new /obj/item/clothing/head/beret/eng(src) - - -/obj/structure/closet/secure_closet/atmos_personal - name = "technician's locker" + icon_state = "secureeng1" + icon_closed = "secureeng" + icon_locked = "secureeng1" + icon_opened = "secureengopen" + icon_broken = "secureengbroken" + icon_off = "secureengoff" + +/obj/structure/closet/secure_closet/engineering_personal/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/industrial(src) + else + new /obj/item/storage/backpack/satchel_eng(src) + new /obj/item/storage/backpack/duffel/engineering(src) + new /obj/item/storage/toolbox/mechanical(src) + new /obj/item/holosign_creator/engineering(src) + new /obj/item/radio/headset/headset_eng(src) + new /obj/item/clothing/under/rank/engineer(src) + new /obj/item/clothing/under/rank/engineer/skirt(src) + new /obj/item/clothing/suit/storage/hazardvest(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/clothing/glasses/meson(src) + new /obj/item/cartridge/engineering(src) + new /obj/item/clothing/head/beret/eng(src) + + +/obj/structure/closet/secure_closet/atmos_personal + name = "technician's locker" req_access = list(ACCESS_ATMOSPHERICS) - icon_state = "secureatm1" - icon_closed = "secureatm" - icon_locked = "secureatm1" - icon_opened = "secureatmopen" - icon_broken = "secureatmbroken" - icon_off = "secureatmoff" - -/obj/structure/closet/secure_closet/atmos_personal/New() - ..() - new /obj/item/radio/headset/headset_eng(src) - new /obj/item/cartridge/atmos(src) - new /obj/item/storage/toolbox/mechanical(src) - if(prob(50)) - new /obj/item/storage/backpack/industrial(src) - else - new /obj/item/storage/backpack/satchel_eng(src) - new /obj/item/storage/backpack/duffel/atmos(src) - new /obj/item/extinguisher(src) - new /obj/item/grenade/gas/oxygen(src) - new /obj/item/grenade/gas/oxygen(src) - new /obj/item/clothing/suit/storage/hazardvest(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/tank/emergency_oxygen/engi(src) - new /obj/item/holosign_creator/atmos(src) - new /obj/item/watertank/atmos(src) - new /obj/item/clothing/suit/fire/atmos(src) - new /obj/item/clothing/head/hardhat/atmos(src) - new /obj/item/rpd(src) - new /obj/item/destTagger(src) + icon_state = "secureatm1" + icon_closed = "secureatm" + icon_locked = "secureatm1" + icon_opened = "secureatmopen" + icon_broken = "secureatmbroken" + icon_off = "secureatmoff" + +/obj/structure/closet/secure_closet/atmos_personal/New() + ..() + new /obj/item/radio/headset/headset_eng(src) + new /obj/item/cartridge/atmos(src) + new /obj/item/storage/toolbox/mechanical(src) + if(prob(50)) + new /obj/item/storage/backpack/industrial(src) + else + new /obj/item/storage/backpack/satchel_eng(src) + new /obj/item/storage/backpack/duffel/atmos(src) + new /obj/item/extinguisher(src) + new /obj/item/grenade/gas/oxygen(src) + new /obj/item/grenade/gas/oxygen(src) + new /obj/item/clothing/suit/storage/hazardvest(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/tank/emergency_oxygen/engi(src) + new /obj/item/holosign_creator/atmos(src) + new /obj/item/watertank/atmos(src) + new /obj/item/clothing/suit/fire/atmos(src) + new /obj/item/clothing/head/hardhat/atmos(src) + new /obj/item/rpd(src) + new /obj/item/destTagger(src) diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 7fa60101db3..574d0b91afb 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -1,86 +1,86 @@ -/* Photography! - * Contains: - * Camera - * Camera Film - * Photos - * Photo Albums - */ - -/******* -* film * -*******/ -/obj/item/camera_film - name = "film cartridge" - icon = 'icons/obj/items.dmi' - desc = "A camera film cartridge. Insert it into a camera to reload it." - icon_state = "film" - item_state = "electropack" - w_class = WEIGHT_CLASS_TINY - resistance_flags = FLAMMABLE - - -/******** -* photo * -********/ -/obj/item/photo - name = "photo" - icon = 'icons/obj/items.dmi' - icon_state = "photo" - item_state = "paper" - w_class = WEIGHT_CLASS_SMALL - resistance_flags = FLAMMABLE - max_integrity = 50 - var/blueprints = 0 // Does this have the blueprints? - var/icon/img //Big photo image - var/scribble //Scribble on the back. - var/icon/tiny - var/photo_size = 3 - -/obj/item/photo/attack_self(mob/user as mob) - user.examinate(src) - -/obj/item/photo/attackby(obj/item/P as obj, mob/user as mob, params) - if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) - var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text) - txt = copytext(txt, 1, 128) - if(loc == user && user.stat == 0) - scribble = txt - else if(istype(P, /obj/item/lighter)) - burnphoto(P, user) - ..() - -/obj/item/photo/proc/burnphoto(obj/item/lighter/P, mob/user) - var/class = "" - - if(P.lit && !user.restrained()) - if(istype(P, /obj/item/lighter/zippo)) - class = "" - - user.visible_message("[class][user] holds \the [P] up to \the [src], it looks like [user.p_theyre()] trying to burn it!", \ - "[class]You hold [P] up to [src], burning it slowly.") - - spawn(20) - if(get_dist(src, user) < 2 && user.get_active_hand() == P && P.lit) - user.visible_message("[class][user] burns right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.", \ - "[class]You burn right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.") - - if(user.is_in_inactive_hand(src)) - user.unEquip(src) - - new /obj/effect/decal/cleanable/ash(get_turf(src)) - qdel(src) - - else - to_chat(user, "You must hold \the [P] steady to burn \the [src].") - -/obj/item/photo/examine(mob/user) - . = ..() - if(in_range(user, src) || isobserver(user)) - show(user) - else - . += "It is too far away." - -/obj/item/photo/proc/show(mob/user as mob) +/* Photography! + * Contains: + * Camera + * Camera Film + * Photos + * Photo Albums + */ + +/******* +* film * +*******/ +/obj/item/camera_film + name = "film cartridge" + icon = 'icons/obj/items.dmi' + desc = "A camera film cartridge. Insert it into a camera to reload it." + icon_state = "film" + item_state = "electropack" + w_class = WEIGHT_CLASS_TINY + resistance_flags = FLAMMABLE + + +/******** +* photo * +********/ +/obj/item/photo + name = "photo" + icon = 'icons/obj/items.dmi' + icon_state = "photo" + item_state = "paper" + w_class = WEIGHT_CLASS_SMALL + resistance_flags = FLAMMABLE + max_integrity = 50 + var/blueprints = 0 // Does this have the blueprints? + var/icon/img //Big photo image + var/scribble //Scribble on the back. + var/icon/tiny + var/photo_size = 3 + +/obj/item/photo/attack_self(mob/user as mob) + user.examinate(src) + +/obj/item/photo/attackby(obj/item/P as obj, mob/user as mob, params) + if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) + var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text) + txt = copytext(txt, 1, 128) + if(loc == user && user.stat == 0) + scribble = txt + else if(istype(P, /obj/item/lighter)) + burnphoto(P, user) + ..() + +/obj/item/photo/proc/burnphoto(obj/item/lighter/P, mob/user) + var/class = "" + + if(P.lit && !user.restrained()) + if(istype(P, /obj/item/lighter/zippo)) + class = "" + + user.visible_message("[class][user] holds \the [P] up to \the [src], it looks like [user.p_theyre()] trying to burn it!", \ + "[class]You hold [P] up to [src], burning it slowly.") + + spawn(20) + if(get_dist(src, user) < 2 && user.get_active_hand() == P && P.lit) + user.visible_message("[class][user] burns right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.", \ + "[class]You burn right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.") + + if(user.is_in_inactive_hand(src)) + user.unEquip(src) + + new /obj/effect/decal/cleanable/ash(get_turf(src)) + qdel(src) + + else + to_chat(user, "You must hold \the [P] steady to burn \the [src].") + +/obj/item/photo/examine(mob/user) + . = ..() + if(in_range(user, src) || isobserver(user)) + show(user) + else + . += "It is too far away." + +/obj/item/photo/proc/show(mob/user as mob) var/icon/img_shown = new/icon(img) var/colormatrix = user.get_screen_colour() // Apply colorblindness effects, if any. @@ -91,490 +91,490 @@ colormatrix[7], colormatrix[8], colormatrix[9], ) usr << browse_rsc(img_shown, "tmp_photo.png") - usr << browse("[name]" \ - + "" \ - + "" \ - + "[scribble ? "
Written on the back:
[scribble]" : ""]"\ + usr << browse("[name]" \ + + "" \ + + "" \ + + "[scribble ? "
Written on the back:
[scribble]" : ""]"\ + "", "window=Photo[UID()];size=[64*photo_size]x[scribble ? 400 : 64*photo_size]") onclose(usr, "Photo[UID()]") - return - -/obj/item/photo/verb/rename() - set name = "Rename photo" - set category = "Object" - set src in usr - - var/n_name = sanitize(copytext(input(usr, "What would you like to label the photo?", "Photo Labelling", name) as text, 1, MAX_MESSAGE_LEN)) - //loc.loc check is for making possible renaming photos in clipboards - if(( (loc == usr || (loc.loc && loc.loc == usr)) && usr.stat == 0)) - name = "[(n_name ? text("[n_name]") : "photo")]" - add_fingerprint(usr) - return - - -/************** -* photo album * -**************/ -/obj/item/storage/photo_album - name = "Photo album" - icon = 'icons/obj/items.dmi' - icon_state = "album" - item_state = "briefcase" - can_hold = list(/obj/item/photo) - resistance_flags = FLAMMABLE - -/obj/item/storage/photo_album/MouseDrop(obj/over_object as obj) - - if((istype(usr, /mob/living/carbon/human))) - var/mob/M = usr - if(!( istype(over_object, /obj/screen) )) - return ..() - playsound(loc, "rustle", 50, 1, -5) - if((!( M.restrained() ) && !( M.stat ) && M.back == src)) - switch(over_object.name) - if("r_hand") - M.unEquip(src) - M.put_in_r_hand(src) - if("l_hand") - M.unEquip(src) - M.put_in_l_hand(src) - add_fingerprint(usr) - return - if(over_object == usr && in_range(src, usr) || usr.contents.Find(src)) - if(usr.s_active) - usr.s_active.close(usr) - show_to(usr) - return - return - -/********* -* camera * -*********/ -/obj/item/camera - name = "camera" - icon = 'icons/obj/items.dmi' - desc = "A polaroid camera. 10 photos left." - icon_state = "camera" - item_state = "electropack" - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - var/list/matter = list("metal" = 2000) - var/pictures_max = 10 - var/pictures_left = 10 - var/on = 1 - var/blueprints = 0 - var/icon_on = "camera" - var/icon_off = "camera_off" - var/size = 3 - var/see_ghosts = 0 //for the spoop of it - - -/obj/item/camera/spooky/CheckParts(list/parts_list) - ..() - var/obj/item/camera/C = locate(/obj/item/camera) in contents - if(C) - pictures_max = C.pictures_max - pictures_left = C.pictures_left - visible_message("[C] has been imbued with godlike power!") - qdel(C) - - -var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","shadow","ghostian2") - -/obj/item/camera/spooky - name = "camera obscura" - desc = "A polaroid camera, some say it can see ghosts!" - see_ghosts = 1 - -/obj/item/camera/verb/change_size() - set name = "Set Photo Focus" - set category = "Object" - var/nsize = input("Photo Size","Pick a size of resulting photo.") as null|anything in list(1,3,5,7) - if(nsize) - size = nsize - to_chat(usr, "Camera will now take [size]x[size] photos.") - -/obj/item/camera/attack(mob/living/carbon/human/M as mob, mob/user as mob) - return - -/obj/item/camera/attack_self(mob/user as mob) - on = !on - if(on) - src.icon_state = icon_on - else - src.icon_state = icon_off - to_chat(user, "You switch the camera [on ? "on" : "off"].") - return - -/obj/item/camera/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I, /obj/item/camera_film)) - if(pictures_left) - to_chat(user, "[src] still has some film in it!") - return - to_chat(user, "You insert [I] into [src].") - user.drop_item() - qdel(I) - pictures_left = pictures_max - return - ..() - - -/obj/item/camera/proc/get_icon(list/turfs, turf/center, mob/user) - - //Bigger icon base to capture those icons that were shifted to the next tile - //i.e. pretty much all wall-mounted machinery - var/icon/res = icon('icons/effects/96x96.dmi', "") - res.Scale(size*32, size*32) - // Initialize the photograph to black. - res.Blend("#000", ICON_OVERLAY) - - var/atoms[] = list() - for(var/turf/the_turf in turfs) - // Add ourselves to the list of stuff to draw - atoms.Add(the_turf); - // As well as anything that isn't invisible. - for(var/atom/A in the_turf) - if(A.invisibility) - if(see_ghosts && istype(A,/mob/dead/observer)) - var/mob/dead/observer/O = A - if(O.following) - continue - if(user.mind && !(user.mind.assigned_role == "Chaplain")) - atoms.Add(image('icons/mob/mob.dmi', O.loc, pick(SpookyGhosts), 4, SOUTH)) - else - atoms.Add(image('icons/mob/mob.dmi', O.loc, "ghost", 4, SOUTH)) - else//its not a ghost - continue - else//not invisable, not a spookyghost add it. - var/disguised = null - if(user.viewing_alternate_appearances && user.viewing_alternate_appearances.len && ishuman(A) && A.alternate_appearances && A.alternate_appearances.len) //This whole thing and the stuff below just checks if the atom is a Solid Snake cosplayer. - for(var/datum/alternate_appearance/alt_appearance in user.viewing_alternate_appearances) - if(alt_appearance.owner == A) //If it turns out they are, don't blow their cover. That'd be rude. - atoms.Add(image(alt_appearance.img, A.loc, layer = 4, dir = A.dir)) //Render their disguise. - atoms.Remove(A) //Don't blow their cover. - disguised = 1 - continue - if(!disguised) //If they aren't, treat them normally. - atoms.Add(A) - - - // Sort the atoms into their layers - var/list/sorted = sort_atoms_by_layer(atoms) - var/center_offset = (size-1)/2 * 32 + 1 - for(var/i; i <= sorted.len; i++) - var/atom/A = sorted[i] - if(A) - var/icon/img = getFlatIcon(A)//build_composite_icon(A) - if(istype(A, /obj/item/areaeditor/blueprints/ce)) - blueprints = 1 - - // If what we got back is actually a picture, draw it. - if(istype(img, /icon)) - // Check if we're looking at a mob that's lying down - if(istype(A, /mob/living) && A:lying) - // If they are, apply that effect to their picture. - img.BecomeLying() - // Calculate where we are relative to the center of the photo - var/xoff = (A.x - center.x) * 32 + center_offset - var/yoff = (A.y - center.y) * 32 + center_offset - if(istype(A,/atom/movable)) - xoff+=A:step_x - yoff+=A:step_y - res.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) - - // Lastly, render any contained effects on top. - for(var/turf/the_turf in turfs) - // Calculate where we are relative to the center of the photo - var/xoff = (the_turf.x - center.x) * 32 + center_offset - var/yoff = (the_turf.y - center.y) * 32 + center_offset - res.Blend(getFlatIcon(the_turf.loc), blendMode2iconMode(the_turf.blend_mode),xoff,yoff) - return res - - -/obj/item/camera/proc/get_mobs(turf/the_turf as turf) - var/mob_detail - for(var/mob/M in the_turf) - if(M.invisibility) - if(see_ghosts && istype(M,/mob/dead/observer)) - var/mob/dead/observer/O = M - if(O.following) - continue - if(!mob_detail) - mob_detail = "You can see a g-g-g-g-ghooooost! " - else - mob_detail += "You can also see a g-g-g-g-ghooooost!" - else - continue - - var/holding = null - - if(istype(M, /mob/living/carbon)) - var/mob/living/carbon/A = M - if(A.l_hand || A.r_hand) - if(A.l_hand) holding = "They are holding \a [A.l_hand]" - if(A.r_hand) - if(holding) - holding += " and \a [A.r_hand]" - else - holding = "They are holding \a [A.r_hand]" - - if(!mob_detail) - mob_detail = "You can see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]. " - else - mob_detail += "You can also see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." - return mob_detail - -/obj/item/camera/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) - if(!on || !pictures_left || ismob(target.loc)) return - captureimage(target, user, flag) - - playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) - - pictures_left-- - desc = "A polaroid camera. It has [pictures_left] photos left." - to_chat(user, "[pictures_left] photos left.") - icon_state = icon_off - on = 0 - if(istype(src,/obj/item/camera/spooky)) - if(user.mind && user.mind.assigned_role == "Chaplain" && see_ghosts) - if(prob(24)) - handle_haunt(user) - spawn(64) - icon_state = icon_on - on = 1 - -/obj/item/camera/proc/can_capture_turf(turf/T, mob/user) - var/viewer = user - if(user.client) //To make shooting through security cameras possible - viewer = user.client.eye - var/can_see = (T in view(viewer)) //No x-ray vision cameras. - return can_see - -/obj/item/camera/proc/captureimage(atom/target, mob/user, flag) - var/x_c = target.x - (size-1)/2 - var/y_c = target.y + (size-1)/2 - var/z_c = target.z - var/list/turfs = list() - var/mobs = "" - for(var/i = 1; i <= size; i++) - for(var/j = 1; j <= size; j++) - var/turf/T = locate(x_c, y_c, z_c) - if(can_capture_turf(T, user)) - turfs.Add(T) - mobs += get_mobs(T) - x_c++ - y_c-- - x_c = x_c - size - - var/datum/picture/P = createpicture(target, user, turfs, mobs, flag, blueprints) - printpicture(user, P) - -/obj/item/camera/proc/createpicture(atom/target, mob/user, list/turfs, mobs, flag) - var/icon/photoimage = get_icon(turfs, target, user) - - var/icon/small_img = icon(photoimage) - var/icon/tiny_img = icon(photoimage) - var/icon/ic = icon('icons/obj/items.dmi',"photo") - var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") - small_img.Scale(8, 8) - tiny_img.Scale(4, 4) - ic.Blend(small_img,ICON_OVERLAY, 10, 13) - pc.Blend(tiny_img,ICON_OVERLAY, 12, 19) - - var/datum/picture/P = new() - if(istype(src,/obj/item/camera/digital)) - P.fields["name"] = input(user,"Name photo:","photo") - P.name = P.fields["name"]//So the name is displayed on the print/delete list. - else - P.fields["name"] = "photo" - P.fields["author"] = user - P.fields["icon"] = ic - P.fields["tiny"] = pc - P.fields["img"] = photoimage - P.fields["desc"] = mobs - P.fields["pixel_x"] = rand(-10, 10) - P.fields["pixel_y"] = rand(-10, 10) - P.fields["size"] = size - - return P - -/obj/item/camera/proc/printpicture(mob/user, var/datum/picture/P) - var/obj/item/photo/Photo = new/obj/item/photo() - Photo.loc = user.loc - if(!user.get_inactive_hand()) - user.put_in_inactive_hand(Photo) - - if(blueprints) - Photo.blueprints = 1 - blueprints = 0 - Photo.construct(P) - -/obj/item/photo/proc/construct(var/datum/picture/P) - name = P.fields["name"] - icon = P.fields["icon"] - tiny = P.fields["tiny"] - img = P.fields["img"] - desc = P.fields["desc"] - pixel_x = P.fields["pixel_x"] - pixel_y = P.fields["pixel_y"] - photo_size = P.fields["size"] - -/obj/item/photo/proc/copy() - var/obj/item/photo/p = new/obj/item/photo() - - p.icon = icon(icon, icon_state) - p.img = icon(img) - p.tiny = icon(tiny) - p.name = name - p.desc = desc - p.scribble = scribble - p.blueprints = blueprints - - return p - -/***************** -* digital camera * -******************/ -/obj/item/camera/digital - name = "digital camera" - desc = "A digital camera. A small screen shows there is space for 10 photos left." - var/list/datum/picture/saved_pictures = list() - pictures_left = 30 - var/max_storage = 10 - -/obj/item/camera/digital/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) - if(!on || !pictures_left || ismob(target.loc)) return - captureimage(target, user, flag) - - playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) - - desc = "A digital camera. A small screen shows that there are currently [saved_pictures.len] pictures stored." - icon_state = icon_off - on = 0 - spawn(64) - icon_state = icon_on - on = 1 - -/obj/item/camera/digital/captureimage(atom/target, mob/user, flag) - if(saved_pictures.len >= max_storage) - to_chat(user, "Maximum photo storage capacity reached.") - return - to_chat(user, "Picture saved.") - var/x_c = target.x - (size-1)/2 - var/y_c = target.y + (size-1)/2 - var/z_c = target.z - var/list/turfs = list() - var/mobs = "" - for(var/i = 1; i <= size; i++) - for(var/j = 1; j <= size; j++) - var/turf/T = locate(x_c, y_c, z_c) - if(can_capture_turf(T, user)) - turfs.Add(T) - mobs += get_mobs(T) - x_c++ - y_c-- - x_c = x_c - size - - var/datum/picture/P = createpicture(target, user, turfs, mobs, flag) - saved_pictures += P - -/obj/item/camera/digital/verb/print_picture() - set name = "Print picture" - set category = "Object" - set src in usr - - if(saved_pictures.len == 0) - to_chat(usr, "No images saved.") - return - if(pictures_left == 0) - to_chat(usr, "There is no film left to print.") - return - - var/datum/picture/P = null - P = input("Select image to print:",P) as null|anything in saved_pictures - if(P) - printpicture(usr,P) - pictures_left -- - -/obj/item/camera/digital/verb/delete_picture() - set name = "Delete picture" - set category = "Object" - set src in usr - - if(saved_pictures.len == 0) - to_chat(usr, "No images saved") - return - var/datum/picture/P = null - P = input("Select image to delete:",P) as null|anything in saved_pictures - if(P) - saved_pictures -= P - -/************** -*video camera * -***************/ - -/obj/item/videocam - name = "video camera" - icon = 'icons/obj/items.dmi' - desc = "video camera that can send live feed to the entertainment network." - icon_state = "videocam" - item_state = "videocam" - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - materials = list(MAT_METAL=2000) - var/on = 0 - var/obj/machinery/camera/camera - var/icon_on = "videocam_on" - var/icon_off = "videocam" - var/canhear_range = 7 - -/obj/item/videocam/attack_self(mob/user) - on = !on - if(camera) - if(on==0) - src.icon_state = icon_off - camera.c_tag = null - camera.network = list() - else - src.icon_state = icon_on - camera.network = list("news") - camera.c_tag = user.name - else - - src.icon_state = icon_on - camera = new /obj/machinery/camera(src) - camera.network = list("news") - cameranet.removeCamera(camera) - camera.c_tag = user.name - to_chat(user, "You switch the camera [on ? "on" : "off"].") - -/obj/item/videocam/examine(mob/user) - . = ..() - if(in_range(user, src)) - . += "This video camera can send live feeds to the entertainment network. It's [camera ? "" : "in"]active." - -/obj/item/videocam/hear_talk(mob/M as mob, list/message_pieces) - var/msg = multilingual_to_message(message_pieces) - if(camera && on) - if(get_dist(src, M) <= canhear_range) - talk_into(M, msg) - for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) - if(T.watchers[M] == camera) - T.audible_message("(Newscaster) [M] says, '[msg]'", hearing_distance = 2) - -/obj/item/videocam/hear_message(mob/M as mob, msg) - if(camera && on) - for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) - if(T.watchers[M] == camera) - T.audible_message("(Newscaster) [M] [msg]", hearing_distance = 2) - - -///hauntings, like hallucinations but more spooky - -/obj/item/camera/proc/handle_haunt(mob/user as mob) - var/list/creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/screech.ogg',\ - 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\ - 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\ - 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\ - 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg') - user << pick(creepyasssounds) + return + +/obj/item/photo/verb/rename() + set name = "Rename photo" + set category = "Object" + set src in usr + + var/n_name = sanitize(copytext(input(usr, "What would you like to label the photo?", "Photo Labelling", name) as text, 1, MAX_MESSAGE_LEN)) + //loc.loc check is for making possible renaming photos in clipboards + if(( (loc == usr || (loc.loc && loc.loc == usr)) && usr.stat == 0)) + name = "[(n_name ? text("[n_name]") : "photo")]" + add_fingerprint(usr) + return + + +/************** +* photo album * +**************/ +/obj/item/storage/photo_album + name = "Photo album" + icon = 'icons/obj/items.dmi' + icon_state = "album" + item_state = "briefcase" + can_hold = list(/obj/item/photo) + resistance_flags = FLAMMABLE + +/obj/item/storage/photo_album/MouseDrop(obj/over_object as obj) + + if((istype(usr, /mob/living/carbon/human))) + var/mob/M = usr + if(!( istype(over_object, /obj/screen) )) + return ..() + playsound(loc, "rustle", 50, 1, -5) + if((!( M.restrained() ) && !( M.stat ) && M.back == src)) + switch(over_object.name) + if("r_hand") + M.unEquip(src) + M.put_in_r_hand(src) + if("l_hand") + M.unEquip(src) + M.put_in_l_hand(src) + add_fingerprint(usr) + return + if(over_object == usr && in_range(src, usr) || usr.contents.Find(src)) + if(usr.s_active) + usr.s_active.close(usr) + show_to(usr) + return + return + +/********* +* camera * +*********/ +/obj/item/camera + name = "camera" + icon = 'icons/obj/items.dmi' + desc = "A polaroid camera. 10 photos left." + icon_state = "camera" + item_state = "electropack" + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + var/list/matter = list("metal" = 2000) + var/pictures_max = 10 + var/pictures_left = 10 + var/on = 1 + var/blueprints = 0 + var/icon_on = "camera" + var/icon_off = "camera_off" + var/size = 3 + var/see_ghosts = 0 //for the spoop of it + + +/obj/item/camera/spooky/CheckParts(list/parts_list) + ..() + var/obj/item/camera/C = locate(/obj/item/camera) in contents + if(C) + pictures_max = C.pictures_max + pictures_left = C.pictures_left + visible_message("[C] has been imbued with godlike power!") + qdel(C) + + +var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","shadow","ghostian2") + +/obj/item/camera/spooky + name = "camera obscura" + desc = "A polaroid camera, some say it can see ghosts!" + see_ghosts = 1 + +/obj/item/camera/verb/change_size() + set name = "Set Photo Focus" + set category = "Object" + var/nsize = input("Photo Size","Pick a size of resulting photo.") as null|anything in list(1,3,5,7) + if(nsize) + size = nsize + to_chat(usr, "Camera will now take [size]x[size] photos.") + +/obj/item/camera/attack(mob/living/carbon/human/M as mob, mob/user as mob) + return + +/obj/item/camera/attack_self(mob/user as mob) + on = !on + if(on) + src.icon_state = icon_on + else + src.icon_state = icon_off + to_chat(user, "You switch the camera [on ? "on" : "off"].") + return + +/obj/item/camera/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I, /obj/item/camera_film)) + if(pictures_left) + to_chat(user, "[src] still has some film in it!") + return + to_chat(user, "You insert [I] into [src].") + user.drop_item() + qdel(I) + pictures_left = pictures_max + return + ..() + + +/obj/item/camera/proc/get_icon(list/turfs, turf/center, mob/user) + + //Bigger icon base to capture those icons that were shifted to the next tile + //i.e. pretty much all wall-mounted machinery + var/icon/res = icon('icons/effects/96x96.dmi', "") + res.Scale(size*32, size*32) + // Initialize the photograph to black. + res.Blend("#000", ICON_OVERLAY) + + var/atoms[] = list() + for(var/turf/the_turf in turfs) + // Add ourselves to the list of stuff to draw + atoms.Add(the_turf); + // As well as anything that isn't invisible. + for(var/atom/A in the_turf) + if(A.invisibility) + if(see_ghosts && istype(A,/mob/dead/observer)) + var/mob/dead/observer/O = A + if(O.following) + continue + if(user.mind && !(user.mind.assigned_role == "Chaplain")) + atoms.Add(image('icons/mob/mob.dmi', O.loc, pick(SpookyGhosts), 4, SOUTH)) + else + atoms.Add(image('icons/mob/mob.dmi', O.loc, "ghost", 4, SOUTH)) + else//its not a ghost + continue + else//not invisable, not a spookyghost add it. + var/disguised = null + if(user.viewing_alternate_appearances && user.viewing_alternate_appearances.len && ishuman(A) && A.alternate_appearances && A.alternate_appearances.len) //This whole thing and the stuff below just checks if the atom is a Solid Snake cosplayer. + for(var/datum/alternate_appearance/alt_appearance in user.viewing_alternate_appearances) + if(alt_appearance.owner == A) //If it turns out they are, don't blow their cover. That'd be rude. + atoms.Add(image(alt_appearance.img, A.loc, layer = 4, dir = A.dir)) //Render their disguise. + atoms.Remove(A) //Don't blow their cover. + disguised = 1 + continue + if(!disguised) //If they aren't, treat them normally. + atoms.Add(A) + + + // Sort the atoms into their layers + var/list/sorted = sort_atoms_by_layer(atoms) + var/center_offset = (size-1)/2 * 32 + 1 + for(var/i; i <= sorted.len; i++) + var/atom/A = sorted[i] + if(A) + var/icon/img = getFlatIcon(A)//build_composite_icon(A) + if(istype(A, /obj/item/areaeditor/blueprints/ce)) + blueprints = 1 + + // If what we got back is actually a picture, draw it. + if(istype(img, /icon)) + // Check if we're looking at a mob that's lying down + if(istype(A, /mob/living) && A:lying) + // If they are, apply that effect to their picture. + img.BecomeLying() + // Calculate where we are relative to the center of the photo + var/xoff = (A.x - center.x) * 32 + center_offset + var/yoff = (A.y - center.y) * 32 + center_offset + if(istype(A,/atom/movable)) + xoff+=A:step_x + yoff+=A:step_y + res.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) + + // Lastly, render any contained effects on top. + for(var/turf/the_turf in turfs) + // Calculate where we are relative to the center of the photo + var/xoff = (the_turf.x - center.x) * 32 + center_offset + var/yoff = (the_turf.y - center.y) * 32 + center_offset + res.Blend(getFlatIcon(the_turf.loc), blendMode2iconMode(the_turf.blend_mode),xoff,yoff) + return res + + +/obj/item/camera/proc/get_mobs(turf/the_turf as turf) + var/mob_detail + for(var/mob/M in the_turf) + if(M.invisibility) + if(see_ghosts && istype(M,/mob/dead/observer)) + var/mob/dead/observer/O = M + if(O.following) + continue + if(!mob_detail) + mob_detail = "You can see a g-g-g-g-ghooooost! " + else + mob_detail += "You can also see a g-g-g-g-ghooooost!" + else + continue + + var/holding = null + + if(istype(M, /mob/living/carbon)) + var/mob/living/carbon/A = M + if(A.l_hand || A.r_hand) + if(A.l_hand) holding = "They are holding \a [A.l_hand]" + if(A.r_hand) + if(holding) + holding += " and \a [A.r_hand]" + else + holding = "They are holding \a [A.r_hand]" + + if(!mob_detail) + mob_detail = "You can see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]. " + else + mob_detail += "You can also see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." + return mob_detail + +/obj/item/camera/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) + if(!on || !pictures_left || ismob(target.loc)) return + captureimage(target, user, flag) + + playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) + + pictures_left-- + desc = "A polaroid camera. It has [pictures_left] photos left." + to_chat(user, "[pictures_left] photos left.") + icon_state = icon_off + on = 0 + if(istype(src,/obj/item/camera/spooky)) + if(user.mind && user.mind.assigned_role == "Chaplain" && see_ghosts) + if(prob(24)) + handle_haunt(user) + spawn(64) + icon_state = icon_on + on = 1 + +/obj/item/camera/proc/can_capture_turf(turf/T, mob/user) + var/viewer = user + if(user.client) //To make shooting through security cameras possible + viewer = user.client.eye + var/can_see = (T in view(viewer)) //No x-ray vision cameras. + return can_see + +/obj/item/camera/proc/captureimage(atom/target, mob/user, flag) + var/x_c = target.x - (size-1)/2 + var/y_c = target.y + (size-1)/2 + var/z_c = target.z + var/list/turfs = list() + var/mobs = "" + for(var/i = 1; i <= size; i++) + for(var/j = 1; j <= size; j++) + var/turf/T = locate(x_c, y_c, z_c) + if(can_capture_turf(T, user)) + turfs.Add(T) + mobs += get_mobs(T) + x_c++ + y_c-- + x_c = x_c - size + + var/datum/picture/P = createpicture(target, user, turfs, mobs, flag, blueprints) + printpicture(user, P) + +/obj/item/camera/proc/createpicture(atom/target, mob/user, list/turfs, mobs, flag) + var/icon/photoimage = get_icon(turfs, target, user) + + var/icon/small_img = icon(photoimage) + var/icon/tiny_img = icon(photoimage) + var/icon/ic = icon('icons/obj/items.dmi',"photo") + var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") + small_img.Scale(8, 8) + tiny_img.Scale(4, 4) + ic.Blend(small_img,ICON_OVERLAY, 10, 13) + pc.Blend(tiny_img,ICON_OVERLAY, 12, 19) + + var/datum/picture/P = new() + if(istype(src,/obj/item/camera/digital)) + P.fields["name"] = input(user,"Name photo:","photo") + P.name = P.fields["name"]//So the name is displayed on the print/delete list. + else + P.fields["name"] = "photo" + P.fields["author"] = user + P.fields["icon"] = ic + P.fields["tiny"] = pc + P.fields["img"] = photoimage + P.fields["desc"] = mobs + P.fields["pixel_x"] = rand(-10, 10) + P.fields["pixel_y"] = rand(-10, 10) + P.fields["size"] = size + + return P + +/obj/item/camera/proc/printpicture(mob/user, var/datum/picture/P) + var/obj/item/photo/Photo = new/obj/item/photo() + Photo.loc = user.loc + if(!user.get_inactive_hand()) + user.put_in_inactive_hand(Photo) + + if(blueprints) + Photo.blueprints = 1 + blueprints = 0 + Photo.construct(P) + +/obj/item/photo/proc/construct(var/datum/picture/P) + name = P.fields["name"] + icon = P.fields["icon"] + tiny = P.fields["tiny"] + img = P.fields["img"] + desc = P.fields["desc"] + pixel_x = P.fields["pixel_x"] + pixel_y = P.fields["pixel_y"] + photo_size = P.fields["size"] + +/obj/item/photo/proc/copy() + var/obj/item/photo/p = new/obj/item/photo() + + p.icon = icon(icon, icon_state) + p.img = icon(img) + p.tiny = icon(tiny) + p.name = name + p.desc = desc + p.scribble = scribble + p.blueprints = blueprints + + return p + +/***************** +* digital camera * +******************/ +/obj/item/camera/digital + name = "digital camera" + desc = "A digital camera. A small screen shows there is space for 10 photos left." + var/list/datum/picture/saved_pictures = list() + pictures_left = 30 + var/max_storage = 10 + +/obj/item/camera/digital/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) + if(!on || !pictures_left || ismob(target.loc)) return + captureimage(target, user, flag) + + playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) + + desc = "A digital camera. A small screen shows that there are currently [saved_pictures.len] pictures stored." + icon_state = icon_off + on = 0 + spawn(64) + icon_state = icon_on + on = 1 + +/obj/item/camera/digital/captureimage(atom/target, mob/user, flag) + if(saved_pictures.len >= max_storage) + to_chat(user, "Maximum photo storage capacity reached.") + return + to_chat(user, "Picture saved.") + var/x_c = target.x - (size-1)/2 + var/y_c = target.y + (size-1)/2 + var/z_c = target.z + var/list/turfs = list() + var/mobs = "" + for(var/i = 1; i <= size; i++) + for(var/j = 1; j <= size; j++) + var/turf/T = locate(x_c, y_c, z_c) + if(can_capture_turf(T, user)) + turfs.Add(T) + mobs += get_mobs(T) + x_c++ + y_c-- + x_c = x_c - size + + var/datum/picture/P = createpicture(target, user, turfs, mobs, flag) + saved_pictures += P + +/obj/item/camera/digital/verb/print_picture() + set name = "Print picture" + set category = "Object" + set src in usr + + if(saved_pictures.len == 0) + to_chat(usr, "No images saved.") + return + if(pictures_left == 0) + to_chat(usr, "There is no film left to print.") + return + + var/datum/picture/P = null + P = input("Select image to print:",P) as null|anything in saved_pictures + if(P) + printpicture(usr,P) + pictures_left -- + +/obj/item/camera/digital/verb/delete_picture() + set name = "Delete picture" + set category = "Object" + set src in usr + + if(saved_pictures.len == 0) + to_chat(usr, "No images saved") + return + var/datum/picture/P = null + P = input("Select image to delete:",P) as null|anything in saved_pictures + if(P) + saved_pictures -= P + +/************** +*video camera * +***************/ + +/obj/item/videocam + name = "video camera" + icon = 'icons/obj/items.dmi' + desc = "video camera that can send live feed to the entertainment network." + icon_state = "videocam" + item_state = "videocam" + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + materials = list(MAT_METAL=2000) + var/on = 0 + var/obj/machinery/camera/camera + var/icon_on = "videocam_on" + var/icon_off = "videocam" + var/canhear_range = 7 + +/obj/item/videocam/attack_self(mob/user) + on = !on + if(camera) + if(on==0) + src.icon_state = icon_off + camera.c_tag = null + camera.network = list() + else + src.icon_state = icon_on + camera.network = list("news") + camera.c_tag = user.name + else + + src.icon_state = icon_on + camera = new /obj/machinery/camera(src) + camera.network = list("news") + cameranet.removeCamera(camera) + camera.c_tag = user.name + to_chat(user, "You switch the camera [on ? "on" : "off"].") + +/obj/item/videocam/examine(mob/user) + . = ..() + if(in_range(user, src)) + . += "This video camera can send live feeds to the entertainment network. It's [camera ? "" : "in"]active." + +/obj/item/videocam/hear_talk(mob/M as mob, list/message_pieces) + var/msg = multilingual_to_message(message_pieces) + if(camera && on) + if(get_dist(src, M) <= canhear_range) + talk_into(M, msg) + for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) + if(T.watchers[M] == camera) + T.audible_message("(Newscaster) [M] says, '[msg]'", hearing_distance = 2) + +/obj/item/videocam/hear_message(mob/M as mob, msg) + if(camera && on) + for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) + if(T.watchers[M] == camera) + T.audible_message("(Newscaster) [M] [msg]", hearing_distance = 2) + + +///hauntings, like hallucinations but more spooky + +/obj/item/camera/proc/handle_haunt(mob/user as mob) + var/list/creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/screech.ogg',\ + 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\ + 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\ + 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\ + 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg') + user << pick(creepyasssounds) From f26f9cf0b6aba33619b88bab577e8ee2622fe484 Mon Sep 17 00:00:00 2001 From: joep van der velden Date: Wed, 18 Mar 2020 17:02:44 +0100 Subject: [PATCH 012/144] line fixes --- code/controllers/subsystem/ticker.dm | 1056 +++--- code/game/mecha/mecha.dm | 2890 ++++++++--------- code/game/objects/items/devices/flash.dm | 566 ++-- .../mob/living/carbon/alien/humanoid/queen.dm | 190 +- .../modules/mob/living/silicon/robot/robot.dm | 2720 ++++++++-------- code/modules/mob/living/silicon/silicon.dm | 682 ++-- 6 files changed, 4052 insertions(+), 4052 deletions(-) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 0770a55c7eb..ba80f1051dd 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -1,528 +1,528 @@ -SUBSYSTEM_DEF(ticker) - name = "Ticker" - init_order = INIT_ORDER_TICKER - - priority = FIRE_PRIORITY_TICKER - flags = SS_KEEP_TIMING - runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME - - var/round_start_time = 0 - var/const/restart_timeout = 600 - var/current_state = GAME_STATE_STARTUP - var/force_start = 0 // Do we want to force-start as soon as we can - var/force_ending = 0 - var/hide_mode = 0 // leave here at 0 ! setup() will take care of it when needed for Secret mode -walter0o - var/datum/game_mode/mode = null - var/event_time = null - var/event = 0 - var/login_music // music played in pregame lobby - var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking. - var/Bible_icon_state // icon_state the chaplain has chosen for his bible - var/Bible_item_state // item_state the chaplain has chosen for his bible - var/Bible_name // name of the bible - var/Bible_deity_name - var/datum/cult_info/cultdat = null //here instead of cult for adminbus purposes - var/random_players = 0 // if set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders - var/list/syndicate_coalition = list() // list of traitor-compatible factions - var/list/factions = list() // list of all factions - var/list/availablefactions = list() // list of factions with openings - var/tipped = FALSE //Did we broadcast the tip of the day yet? - var/selected_tip // What will be the tip of the day? - var/pregame_timeleft // This is used for calculations - var/delay_end = 0 //if set to nonzero, the round will not restart on it's own - var/triai = 0//Global holder for Triumvirate - var/initialtpass = 0 //holder for inital autotransfer vote timer - var/obj/screen/cinematic = null //used for station explosion cinematic - var/round_end_announced = 0 // Spam Prevention. Announce round end only once.\ - -/datum/controller/subsystem/ticker/Initialize() - login_music = pick(\ - 'sound/music/thunderdome.ogg',\ - 'sound/music/space.ogg',\ - 'sound/music/title1.ogg',\ - 'sound/music/title2.ogg',\ - 'sound/music/title3.ogg',) - - // Map name - if(using_map && using_map.name) - GLOB.map_name = "[using_map.name]" - else - GLOB.map_name = "Unknown" - - // World name - if(config && config.server_name) - world.name = "[config.server_name]: [station_name()]" - else - world.name = station_name() - - return ..() - - -/datum/controller/subsystem/ticker/fire() - switch(current_state) - if(GAME_STATE_STARTUP) - // This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails - round_start_time = world.time + (config.pregame_timestart * 10) - to_chat(world, "Welcome to the pre-game lobby!") - to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds") - current_state = GAME_STATE_PREGAME - fire() // TG says this is a good idea - if(GAME_STATE_PREGAME) - if(!going) - return - - // This is so we dont have sleeps in controllers, because that is a bad, bad thing - if(!delay_end) - pregame_timeleft = max(0,round_start_time - world.time) // Normal lobby countdown when roundstart was not delayed - else - pregame_timeleft = max(0,pregame_timeleft - 20) // If roundstart was delayed, we should resume the countdown where it left off - - if(pregame_timeleft <= 600 && !tipped) // 60 seconds - send_tip_of_the_round() - tipped = TRUE - - if(pregame_timeleft <= 0 || force_start) - current_state = GAME_STATE_SETTING_UP - Master.SetRunLevel(RUNLEVEL_SETUP) - if(GAME_STATE_SETTING_UP) - if(!setup()) // Setup failed - current_state = GAME_STATE_STARTUP - Master.SetRunLevel(RUNLEVEL_LOBBY) - if(GAME_STATE_PLAYING) - delay_end = 0 // reset this in case round start was delayed - mode.process() - mode.process_job_tasks() - var/game_finished = SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || mode.station_was_nuked - if(config.continuous_rounds) - mode.check_finished() // some modes contain var-changing code in here, so call even if we don't uses result - else - game_finished |= mode.check_finished() - if(game_finished) - current_state = GAME_STATE_FINISHED - if(GAME_STATE_FINISHED) - current_state = GAME_STATE_FINISHED - Master.SetRunLevel(RUNLEVEL_POSTGAME) // This shouldnt process more than once, but you never know - auto_toggle_ooc(1) // Turn it on - - spawn(0) - declare_completion() - - spawn(50) - if(mode.station_was_nuked) - world.Reboot("Station destroyed by Nuclear Device.", "end_proper", "nuke") - else - world.Reboot("Round ended.", "end_proper", "proper completion") - - -/datum/controller/subsystem/ticker/proc/votetimer() - var/timerbuffer = 0 - if(initialtpass == 0) - timerbuffer = config.vote_autotransfer_initial - else - timerbuffer = config.vote_autotransfer_interval - spawn(timerbuffer) - SSvote.autotransfer() - initialtpass = 1 - votetimer() - - -/datum/controller/subsystem/ticker/proc/setup() - cultdat = setupcult() - //Create and announce mode - if(master_mode=="secret") - src.hide_mode = 1 - var/list/datum/game_mode/runnable_modes - if((master_mode=="random") || (master_mode=="secret")) - runnable_modes = config.get_runnable_modes() - if(runnable_modes.len==0) - current_state = GAME_STATE_PREGAME - Master.SetRunLevel(RUNLEVEL_LOBBY) - to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.") - return 0 - if(secret_force_mode != "secret") - var/datum/game_mode/M = config.pick_mode(secret_force_mode) - if(M.can_start()) - src.mode = config.pick_mode(secret_force_mode) - SSjobs.ResetOccupations() - if(!src.mode) - src.mode = pickweight(runnable_modes) - if(src.mode) - var/mtype = src.mode.type - src.mode = new mtype - else - src.mode = config.pick_mode(master_mode) - if(!src.mode.can_start()) - to_chat(world, "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.") - mode = null - current_state = GAME_STATE_PREGAME - SSjobs.ResetOccupations() - Master.SetRunLevel(RUNLEVEL_LOBBY) - return 0 - - //Configure mode and assign player to special mode stuff - src.mode.pre_pre_setup() - var/can_continue - can_continue = src.mode.pre_setup()//Setup special modes - SSjobs.DivideOccupations() //Distribute jobs - if(!can_continue) - qdel(mode) - current_state = GAME_STATE_PREGAME - to_chat(world, "Error setting up [master_mode]. Reverting to pre-game lobby.") - SSjobs.ResetOccupations() - Master.SetRunLevel(RUNLEVEL_LOBBY) - return 0 - - if(hide_mode) - var/list/modes = new - for(var/datum/game_mode/M in runnable_modes) - modes+=M.name - modes = sortList(modes) - to_chat(world, "The current game mode is - Secret!") - to_chat(world, "Possibilities: [english_list(modes)]") - else - src.mode.announce() - - create_characters() //Create player characters and transfer them - populate_spawn_points() - collect_minds() - equip_characters() - data_core.manifest() - current_state = GAME_STATE_PLAYING - Master.SetRunLevel(RUNLEVEL_GAME) - - callHook("roundstart") - - //here to initialize the random events nicely at round start - setup_economy() - setupfactions() - - //shuttle_controller.setup_shuttle_docks() - - spawn(0)//Forking here so we dont have to wait for this to finish - if(!GLOB.syndicate_code_phrase) - GLOB.syndicate_code_phrase = generate_code_phrase() - if(!GLOB.syndicate_code_response) - GLOB.syndicate_code_response = generate_code_phrase() - mode.post_setup() - //Cleanup some stuff - for(var/obj/effect/landmark/start/S in GLOB.landmarks_list) - //Deleting Startpoints but we need the ai point to AI-ize people later - if(S.name != "AI") - qdel(S) - - // take care of random spesspod spawning - var/list/obj/effect/landmark/spacepod/random/L = list() - for(var/obj/effect/landmark/spacepod/random/SS in GLOB.landmarks_list) - if(istype(SS)) - L += SS - if(L.len) - var/obj/effect/landmark/spacepod/random/S = pick(L) - new /obj/spacepod/random(S.loc) - for(var/obj/effect/landmark/spacepod/random/R in L) - qdel(R) - - to_chat(world, "Enjoy the game!") - world << sound('sound/AI/welcome.ogg')// Skie - - if(SSholiday.holidays) - to_chat(world, "and...") - for(var/holidayname in SSholiday.holidays) - var/datum/holiday/holiday = SSholiday.holidays[holidayname] - to_chat(world, "

[holiday.greet()]

") - - spawn(0) // Forking dynamic room selection - var/list/area/dynamic/source/available_source_candidates = subtypesof(/area/dynamic/source) - var/list/area/dynamic/destination/available_destination_candidates = subtypesof(/area/dynamic/destination) - - for(var/area/dynamic/destination/current_destination_candidate in available_destination_candidates) - var/area/dynamic/destination/current_destination = locate(current_destination_candidate) - - if(!current_destination) - continue - - if(current_destination.match_width == 0 || current_destination.match_height == 0) - message_admins("Dynamic area destination '[current_destination.name]' does not have its size requirements set.") - continue - - var/list/area/dynamic/source/candidate_source_areas = new /list(0) - for(var/area/dynamic/source/candidate_source_area in available_source_candidates) - var/area/dynamic/source/candidate_source = locate(candidate_source_area) - - if(!candidate_source) - continue - - if(candidate_source.match_tag != current_destination.match_tag) - continue - - if(candidate_source.match_width != current_destination.match_width || \ - candidate_source.match_height != current_destination.match_height) - continue - - candidate_source_areas += candidate_source - - if(candidate_source_areas.len == 0) - message_admins("Failed to find a matching source for dynamic area: [current_destination.name]") - continue - - var/area/dynamic/source/selected_source = pick(candidate_source_areas) - available_source_candidates -= selected_source - - selected_source.copy_contents_to(current_destination, 0) - - if(current_destination.enable_lights || selected_source.enable_lights) - current_destination.power_light = 1 - else - current_destination.power_light = 0 - current_destination.power_change() - - //start_events() //handles random events and space dust. - //new random event system is handled from the MC. - - var/list/admins_number = staff_countup(R_BAN) - if(admins_number[1] == 0 && admins_number[3] == 0) - send2irc(config.admin_notify_irc, "Round has started with no admins online.") - auto_toggle_ooc(0) // Turn it off - round_start_time = world.time - - if(config.sql_enabled) - spawn(3000) - statistic_cycle() // Polls population totals regularly and stores them in an SQL DB - - votetimer() - - for(var/mob/new_player/N in GLOB.mob_list) - if(N.client) - N.new_player_panel_proc() - - return 1 - -/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed = 0, override = null) - if(cinematic) - return //already a cinematic in progress! - - auto_toggle_ooc(1) // Turn it on - //initialise our cinematic screen object - cinematic = new /obj/screen(src) - cinematic.icon = 'icons/effects/station_explosion.dmi' - cinematic.icon_state = "station_intact" - cinematic.layer = 21 - cinematic.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - cinematic.screen_loc = "1,0" - - if(station_missed) - for(var/mob/M in GLOB.mob_list) - if(M.client) - M.client.screen += cinematic //show every client the cinematic - else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" - for(var/mob/M in GLOB.mob_list) - if(M.stat != DEAD) - var/turf/T = get_turf(M) - if(T && is_station_level(T.z) && !istype(M.loc, /obj/structure/closet/secure_closet/freezer)) - var/mob/ghost = M.ghostize() - M.dust() //no mercy - if(ghost && ghost.client) //Play the victims an uninterrupted cinematic. - ghost.client.screen += cinematic - CHECK_TICK - if(M && M.client) //Play the survivors a cinematic. - M.client.screen += cinematic - - //Now animate the cinematic - switch(station_missed) - if(1) //nuke was nearby but (mostly) missed - if(mode && !override) - override = mode.name - switch(override) - if("nuclear emergency") //Nuke wasn't on station when it blew up - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/effects/explosionfar.ogg') - flick("station_intact_fade_red", cinematic) - cinematic.icon_state = "summary_nukefail" - if("fake") //The round isn't over, we're just freaking people out for fun - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/items/bikehorn.ogg') - flick("summary_selfdes", cinematic) - else - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/effects/explosionfar.ogg') - - - if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation - sleep(50) - world << sound('sound/effects/explosionfar.ogg') - else //station was destroyed - if(mode && !override) - override = mode.name - switch(override) - if("nuclear emergency") //Nuke Ops successfully bombed the station - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_nukewin" - if("AI malfunction") //Malf (screen,explosion,summary) - flick("intro_malf", cinematic) - sleep(76) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_malf" - if("blob") //Station nuked (nuke,explosion,summary) - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_selfdes" - else //Station nuked (nuke,explosion,summary) - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_selfdes" - //If its actually the end of the round, wait for it to end. - //Otherwise if its a verb it will continue on afterwards. - spawn(300) - QDEL_NULL(cinematic) //end the cinematic - - - -/datum/controller/subsystem/ticker/proc/create_characters() - for(var/mob/new_player/player in GLOB.player_list) - if(player.ready && player.mind) - if(player.mind.assigned_role == "AI") - player.close_spawn_windows() - var/mob/living/silicon/ai/ai_character = player.AIize() - ai_character.moveToAILandmark() - else if(!player.mind.assigned_role) - continue - else - player.create_character() - qdel(player) - - -/datum/controller/subsystem/ticker/proc/collect_minds() - for(var/mob/living/player in GLOB.player_list) - if(player.mind) - SSticker.minds += player.mind - - -/datum/controller/subsystem/ticker/proc/equip_characters() - var/captainless=1 - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(player && player.mind && player.mind.assigned_role) - if(player.mind.assigned_role == "Captain") - captainless=0 - if(player.mind.assigned_role != player.mind.special_role) - SSjobs.AssignRank(player, player.mind.assigned_role, 0) - SSjobs.EquipRank(player, player.mind.assigned_role, 0) - EquipCustomItems(player) - if(captainless) - for(var/mob/M in GLOB.player_list) - if(!istype(M,/mob/new_player)) - to_chat(M, "Captainship not forced on anyone.") - -/datum/controller/subsystem/ticker/proc/send_tip_of_the_round() - var/m - if(selected_tip) - m = selected_tip - else - var/list/randomtips = file2list("strings/tips.txt") - var/list/memetips = file2list("strings/sillytips.txt") - if(randomtips.len && prob(95)) - m = pick(randomtips) - else if(memetips.len) - m = pick(memetips) - - if(m) - to_chat(world, "Tip of the round: [html_encode(m)]") - -/datum/controller/subsystem/ticker/proc/getfactionbyname(var/name) - for(var/datum/faction/F in factions) - if(F.name == name) - return F - - -/datum/controller/subsystem/ticker/proc/declare_completion() - nologevent = 1 //end of round murder and shenanigans are legal; there's no need to jam up attack logs past this point. - //Round statistics report - var/datum/station_state/end_state = new /datum/station_state() - end_state.count() - var/station_integrity = min(round( 100.0 * start_state.score(end_state), 0.1), 100.0) - - to_chat(world, "
[TAB]Shift Duration: [round(ROUND_TIME / 36000)]:[add_zero("[ROUND_TIME / 600 % 60]", 2)]:[ROUND_TIME / 100 % 6][ROUND_TIME / 100 % 10]") - to_chat(world, "
[TAB]Station Integrity: [mode.station_was_nuked ? "Destroyed" : "[station_integrity]%"]") - to_chat(world, "
") - - //Silicon laws report - for(var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list) - if(aiPlayer.stat != 2) - to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the game were:") - else - to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:") - aiPlayer.show_laws(1) - - if(aiPlayer.connected_robots.len) - var/robolist = "The AI's loyal minions were: " - for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots) - robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]" - to_chat(world, "[robolist]") - - var/dronecount = 0 - - for(var/mob/living/silicon/robot/robo in GLOB.mob_list) - - if(istype(robo,/mob/living/silicon/robot/drone)) - dronecount++ - continue - - if(!robo.connected_ai) - if(robo.stat != 2) - to_chat(world, "[robo.name] (Played by: [robo.key]) survived as an AI-less borg! Its laws were:") - else - to_chat(world, "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:") - - if(robo) //How the hell do we lose robo between here and the world messages directly above this? - robo.laws.show_laws(world) - - if(dronecount) - to_chat(world, "There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] this round.") - - if(mode.eventmiscs.len) - var/emobtext = "" - for(var/datum/mind/eventmind in mode.eventmiscs) - emobtext += printeventplayer(eventmind) - emobtext += "
" - emobtext += printobjectives(eventmind) - emobtext += "
" - emobtext += "
" - to_chat(world, emobtext) - - mode.declare_completion()//To declare normal completion. - - //calls auto_declare_completion_* for all modes - for(var/handler in typesof(/datum/game_mode/proc)) - if(findtext("[handler]","auto_declare_completion_")) - call(mode, handler)() - - scoreboard() - - // Declare the completion of the station goals - mode.declare_station_goal_completion() - - //Ask the event manager to print round end information - SSevents.RoundEnd() - - // Add AntagHUD to everyone, see who was really evil the whole time! - for(var/datum/atom_hud/antag/H in huds) - for(var/m in GLOB.player_list) - var/mob/M = m - H.add_hud_to(M) - - return 1 - -/datum/controller/subsystem/ticker/proc/HasRoundStarted() - return current_state >= GAME_STATE_PLAYING - -/datum/controller/subsystem/ticker/proc/IsRoundInProgress() - return current_state == GAME_STATE_PLAYING \ No newline at end of file +SUBSYSTEM_DEF(ticker) + name = "Ticker" + init_order = INIT_ORDER_TICKER + + priority = FIRE_PRIORITY_TICKER + flags = SS_KEEP_TIMING + runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME + + var/round_start_time = 0 + var/const/restart_timeout = 600 + var/current_state = GAME_STATE_STARTUP + var/force_start = 0 // Do we want to force-start as soon as we can + var/force_ending = 0 + var/hide_mode = 0 // leave here at 0 ! setup() will take care of it when needed for Secret mode -walter0o + var/datum/game_mode/mode = null + var/event_time = null + var/event = 0 + var/login_music // music played in pregame lobby + var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking. + var/Bible_icon_state // icon_state the chaplain has chosen for his bible + var/Bible_item_state // item_state the chaplain has chosen for his bible + var/Bible_name // name of the bible + var/Bible_deity_name + var/datum/cult_info/cultdat = null //here instead of cult for adminbus purposes + var/random_players = 0 // if set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders + var/list/syndicate_coalition = list() // list of traitor-compatible factions + var/list/factions = list() // list of all factions + var/list/availablefactions = list() // list of factions with openings + var/tipped = FALSE //Did we broadcast the tip of the day yet? + var/selected_tip // What will be the tip of the day? + var/pregame_timeleft // This is used for calculations + var/delay_end = 0 //if set to nonzero, the round will not restart on it's own + var/triai = 0//Global holder for Triumvirate + var/initialtpass = 0 //holder for inital autotransfer vote timer + var/obj/screen/cinematic = null //used for station explosion cinematic + var/round_end_announced = 0 // Spam Prevention. Announce round end only once.\ + +/datum/controller/subsystem/ticker/Initialize() + login_music = pick(\ + 'sound/music/thunderdome.ogg',\ + 'sound/music/space.ogg',\ + 'sound/music/title1.ogg',\ + 'sound/music/title2.ogg',\ + 'sound/music/title3.ogg',) + + // Map name + if(using_map && using_map.name) + GLOB.map_name = "[using_map.name]" + else + GLOB.map_name = "Unknown" + + // World name + if(config && config.server_name) + world.name = "[config.server_name]: [station_name()]" + else + world.name = station_name() + + return ..() + + +/datum/controller/subsystem/ticker/fire() + switch(current_state) + if(GAME_STATE_STARTUP) + // This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails + round_start_time = world.time + (config.pregame_timestart * 10) + to_chat(world, "Welcome to the pre-game lobby!") + to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds") + current_state = GAME_STATE_PREGAME + fire() // TG says this is a good idea + if(GAME_STATE_PREGAME) + if(!going) + return + + // This is so we dont have sleeps in controllers, because that is a bad, bad thing + if(!delay_end) + pregame_timeleft = max(0,round_start_time - world.time) // Normal lobby countdown when roundstart was not delayed + else + pregame_timeleft = max(0,pregame_timeleft - 20) // If roundstart was delayed, we should resume the countdown where it left off + + if(pregame_timeleft <= 600 && !tipped) // 60 seconds + send_tip_of_the_round() + tipped = TRUE + + if(pregame_timeleft <= 0 || force_start) + current_state = GAME_STATE_SETTING_UP + Master.SetRunLevel(RUNLEVEL_SETUP) + if(GAME_STATE_SETTING_UP) + if(!setup()) // Setup failed + current_state = GAME_STATE_STARTUP + Master.SetRunLevel(RUNLEVEL_LOBBY) + if(GAME_STATE_PLAYING) + delay_end = 0 // reset this in case round start was delayed + mode.process() + mode.process_job_tasks() + var/game_finished = SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || mode.station_was_nuked + if(config.continuous_rounds) + mode.check_finished() // some modes contain var-changing code in here, so call even if we don't uses result + else + game_finished |= mode.check_finished() + if(game_finished) + current_state = GAME_STATE_FINISHED + if(GAME_STATE_FINISHED) + current_state = GAME_STATE_FINISHED + Master.SetRunLevel(RUNLEVEL_POSTGAME) // This shouldnt process more than once, but you never know + auto_toggle_ooc(1) // Turn it on + + spawn(0) + declare_completion() + + spawn(50) + if(mode.station_was_nuked) + world.Reboot("Station destroyed by Nuclear Device.", "end_proper", "nuke") + else + world.Reboot("Round ended.", "end_proper", "proper completion") + + +/datum/controller/subsystem/ticker/proc/votetimer() + var/timerbuffer = 0 + if(initialtpass == 0) + timerbuffer = config.vote_autotransfer_initial + else + timerbuffer = config.vote_autotransfer_interval + spawn(timerbuffer) + SSvote.autotransfer() + initialtpass = 1 + votetimer() + + +/datum/controller/subsystem/ticker/proc/setup() + cultdat = setupcult() + //Create and announce mode + if(master_mode=="secret") + src.hide_mode = 1 + var/list/datum/game_mode/runnable_modes + if((master_mode=="random") || (master_mode=="secret")) + runnable_modes = config.get_runnable_modes() + if(runnable_modes.len==0) + current_state = GAME_STATE_PREGAME + Master.SetRunLevel(RUNLEVEL_LOBBY) + to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.") + return 0 + if(secret_force_mode != "secret") + var/datum/game_mode/M = config.pick_mode(secret_force_mode) + if(M.can_start()) + src.mode = config.pick_mode(secret_force_mode) + SSjobs.ResetOccupations() + if(!src.mode) + src.mode = pickweight(runnable_modes) + if(src.mode) + var/mtype = src.mode.type + src.mode = new mtype + else + src.mode = config.pick_mode(master_mode) + if(!src.mode.can_start()) + to_chat(world, "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.") + mode = null + current_state = GAME_STATE_PREGAME + SSjobs.ResetOccupations() + Master.SetRunLevel(RUNLEVEL_LOBBY) + return 0 + + //Configure mode and assign player to special mode stuff + src.mode.pre_pre_setup() + var/can_continue + can_continue = src.mode.pre_setup()//Setup special modes + SSjobs.DivideOccupations() //Distribute jobs + if(!can_continue) + qdel(mode) + current_state = GAME_STATE_PREGAME + to_chat(world, "Error setting up [master_mode]. Reverting to pre-game lobby.") + SSjobs.ResetOccupations() + Master.SetRunLevel(RUNLEVEL_LOBBY) + return 0 + + if(hide_mode) + var/list/modes = new + for(var/datum/game_mode/M in runnable_modes) + modes+=M.name + modes = sortList(modes) + to_chat(world, "The current game mode is - Secret!") + to_chat(world, "Possibilities: [english_list(modes)]") + else + src.mode.announce() + + create_characters() //Create player characters and transfer them + populate_spawn_points() + collect_minds() + equip_characters() + data_core.manifest() + current_state = GAME_STATE_PLAYING + Master.SetRunLevel(RUNLEVEL_GAME) + + callHook("roundstart") + + //here to initialize the random events nicely at round start + setup_economy() + setupfactions() + + //shuttle_controller.setup_shuttle_docks() + + spawn(0)//Forking here so we dont have to wait for this to finish + if(!GLOB.syndicate_code_phrase) + GLOB.syndicate_code_phrase = generate_code_phrase() + if(!GLOB.syndicate_code_response) + GLOB.syndicate_code_response = generate_code_phrase() + mode.post_setup() + //Cleanup some stuff + for(var/obj/effect/landmark/start/S in GLOB.landmarks_list) + //Deleting Startpoints but we need the ai point to AI-ize people later + if(S.name != "AI") + qdel(S) + + // take care of random spesspod spawning + var/list/obj/effect/landmark/spacepod/random/L = list() + for(var/obj/effect/landmark/spacepod/random/SS in GLOB.landmarks_list) + if(istype(SS)) + L += SS + if(L.len) + var/obj/effect/landmark/spacepod/random/S = pick(L) + new /obj/spacepod/random(S.loc) + for(var/obj/effect/landmark/spacepod/random/R in L) + qdel(R) + + to_chat(world, "Enjoy the game!") + world << sound('sound/AI/welcome.ogg')// Skie + + if(SSholiday.holidays) + to_chat(world, "and...") + for(var/holidayname in SSholiday.holidays) + var/datum/holiday/holiday = SSholiday.holidays[holidayname] + to_chat(world, "

[holiday.greet()]

") + + spawn(0) // Forking dynamic room selection + var/list/area/dynamic/source/available_source_candidates = subtypesof(/area/dynamic/source) + var/list/area/dynamic/destination/available_destination_candidates = subtypesof(/area/dynamic/destination) + + for(var/area/dynamic/destination/current_destination_candidate in available_destination_candidates) + var/area/dynamic/destination/current_destination = locate(current_destination_candidate) + + if(!current_destination) + continue + + if(current_destination.match_width == 0 || current_destination.match_height == 0) + message_admins("Dynamic area destination '[current_destination.name]' does not have its size requirements set.") + continue + + var/list/area/dynamic/source/candidate_source_areas = new /list(0) + for(var/area/dynamic/source/candidate_source_area in available_source_candidates) + var/area/dynamic/source/candidate_source = locate(candidate_source_area) + + if(!candidate_source) + continue + + if(candidate_source.match_tag != current_destination.match_tag) + continue + + if(candidate_source.match_width != current_destination.match_width || \ + candidate_source.match_height != current_destination.match_height) + continue + + candidate_source_areas += candidate_source + + if(candidate_source_areas.len == 0) + message_admins("Failed to find a matching source for dynamic area: [current_destination.name]") + continue + + var/area/dynamic/source/selected_source = pick(candidate_source_areas) + available_source_candidates -= selected_source + + selected_source.copy_contents_to(current_destination, 0) + + if(current_destination.enable_lights || selected_source.enable_lights) + current_destination.power_light = 1 + else + current_destination.power_light = 0 + current_destination.power_change() + + //start_events() //handles random events and space dust. + //new random event system is handled from the MC. + + var/list/admins_number = staff_countup(R_BAN) + if(admins_number[1] == 0 && admins_number[3] == 0) + send2irc(config.admin_notify_irc, "Round has started with no admins online.") + auto_toggle_ooc(0) // Turn it off + round_start_time = world.time + + if(config.sql_enabled) + spawn(3000) + statistic_cycle() // Polls population totals regularly and stores them in an SQL DB + + votetimer() + + for(var/mob/new_player/N in GLOB.mob_list) + if(N.client) + N.new_player_panel_proc() + + return 1 + +/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed = 0, override = null) + if(cinematic) + return //already a cinematic in progress! + + auto_toggle_ooc(1) // Turn it on + //initialise our cinematic screen object + cinematic = new /obj/screen(src) + cinematic.icon = 'icons/effects/station_explosion.dmi' + cinematic.icon_state = "station_intact" + cinematic.layer = 21 + cinematic.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + cinematic.screen_loc = "1,0" + + if(station_missed) + for(var/mob/M in GLOB.mob_list) + if(M.client) + M.client.screen += cinematic //show every client the cinematic + else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" + for(var/mob/M in GLOB.mob_list) + if(M.stat != DEAD) + var/turf/T = get_turf(M) + if(T && is_station_level(T.z) && !istype(M.loc, /obj/structure/closet/secure_closet/freezer)) + var/mob/ghost = M.ghostize() + M.dust() //no mercy + if(ghost && ghost.client) //Play the victims an uninterrupted cinematic. + ghost.client.screen += cinematic + CHECK_TICK + if(M && M.client) //Play the survivors a cinematic. + M.client.screen += cinematic + + //Now animate the cinematic + switch(station_missed) + if(1) //nuke was nearby but (mostly) missed + if(mode && !override) + override = mode.name + switch(override) + if("nuclear emergency") //Nuke wasn't on station when it blew up + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/effects/explosionfar.ogg') + flick("station_intact_fade_red", cinematic) + cinematic.icon_state = "summary_nukefail" + if("fake") //The round isn't over, we're just freaking people out for fun + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/items/bikehorn.ogg') + flick("summary_selfdes", cinematic) + else + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/effects/explosionfar.ogg') + + + if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation + sleep(50) + world << sound('sound/effects/explosionfar.ogg') + else //station was destroyed + if(mode && !override) + override = mode.name + switch(override) + if("nuclear emergency") //Nuke Ops successfully bombed the station + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_nukewin" + if("AI malfunction") //Malf (screen,explosion,summary) + flick("intro_malf", cinematic) + sleep(76) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_malf" + if("blob") //Station nuked (nuke,explosion,summary) + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_selfdes" + else //Station nuked (nuke,explosion,summary) + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_selfdes" + //If its actually the end of the round, wait for it to end. + //Otherwise if its a verb it will continue on afterwards. + spawn(300) + QDEL_NULL(cinematic) //end the cinematic + + + +/datum/controller/subsystem/ticker/proc/create_characters() + for(var/mob/new_player/player in GLOB.player_list) + if(player.ready && player.mind) + if(player.mind.assigned_role == "AI") + player.close_spawn_windows() + var/mob/living/silicon/ai/ai_character = player.AIize() + ai_character.moveToAILandmark() + else if(!player.mind.assigned_role) + continue + else + player.create_character() + qdel(player) + + +/datum/controller/subsystem/ticker/proc/collect_minds() + for(var/mob/living/player in GLOB.player_list) + if(player.mind) + SSticker.minds += player.mind + + +/datum/controller/subsystem/ticker/proc/equip_characters() + var/captainless=1 + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(player && player.mind && player.mind.assigned_role) + if(player.mind.assigned_role == "Captain") + captainless=0 + if(player.mind.assigned_role != player.mind.special_role) + SSjobs.AssignRank(player, player.mind.assigned_role, 0) + SSjobs.EquipRank(player, player.mind.assigned_role, 0) + EquipCustomItems(player) + if(captainless) + for(var/mob/M in GLOB.player_list) + if(!istype(M,/mob/new_player)) + to_chat(M, "Captainship not forced on anyone.") + +/datum/controller/subsystem/ticker/proc/send_tip_of_the_round() + var/m + if(selected_tip) + m = selected_tip + else + var/list/randomtips = file2list("strings/tips.txt") + var/list/memetips = file2list("strings/sillytips.txt") + if(randomtips.len && prob(95)) + m = pick(randomtips) + else if(memetips.len) + m = pick(memetips) + + if(m) + to_chat(world, "Tip of the round: [html_encode(m)]") + +/datum/controller/subsystem/ticker/proc/getfactionbyname(var/name) + for(var/datum/faction/F in factions) + if(F.name == name) + return F + + +/datum/controller/subsystem/ticker/proc/declare_completion() + nologevent = 1 //end of round murder and shenanigans are legal; there's no need to jam up attack logs past this point. + //Round statistics report + var/datum/station_state/end_state = new /datum/station_state() + end_state.count() + var/station_integrity = min(round( 100.0 * start_state.score(end_state), 0.1), 100.0) + + to_chat(world, "
[TAB]Shift Duration: [round(ROUND_TIME / 36000)]:[add_zero("[ROUND_TIME / 600 % 60]", 2)]:[ROUND_TIME / 100 % 6][ROUND_TIME / 100 % 10]") + to_chat(world, "
[TAB]Station Integrity: [mode.station_was_nuked ? "Destroyed" : "[station_integrity]%"]") + to_chat(world, "
") + + //Silicon laws report + for(var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list) + if(aiPlayer.stat != 2) + to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the game were:") + else + to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:") + aiPlayer.show_laws(1) + + if(aiPlayer.connected_robots.len) + var/robolist = "The AI's loyal minions were: " + for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots) + robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]" + to_chat(world, "[robolist]") + + var/dronecount = 0 + + for(var/mob/living/silicon/robot/robo in GLOB.mob_list) + + if(istype(robo,/mob/living/silicon/robot/drone)) + dronecount++ + continue + + if(!robo.connected_ai) + if(robo.stat != 2) + to_chat(world, "[robo.name] (Played by: [robo.key]) survived as an AI-less borg! Its laws were:") + else + to_chat(world, "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:") + + if(robo) //How the hell do we lose robo between here and the world messages directly above this? + robo.laws.show_laws(world) + + if(dronecount) + to_chat(world, "There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] this round.") + + if(mode.eventmiscs.len) + var/emobtext = "" + for(var/datum/mind/eventmind in mode.eventmiscs) + emobtext += printeventplayer(eventmind) + emobtext += "
" + emobtext += printobjectives(eventmind) + emobtext += "
" + emobtext += "
" + to_chat(world, emobtext) + + mode.declare_completion()//To declare normal completion. + + //calls auto_declare_completion_* for all modes + for(var/handler in typesof(/datum/game_mode/proc)) + if(findtext("[handler]","auto_declare_completion_")) + call(mode, handler)() + + scoreboard() + + // Declare the completion of the station goals + mode.declare_station_goal_completion() + + //Ask the event manager to print round end information + SSevents.RoundEnd() + + // Add AntagHUD to everyone, see who was really evil the whole time! + for(var/datum/atom_hud/antag/H in huds) + for(var/m in GLOB.player_list) + var/mob/M = m + H.add_hud_to(M) + + return 1 + +/datum/controller/subsystem/ticker/proc/HasRoundStarted() + return current_state >= GAME_STATE_PLAYING + +/datum/controller/subsystem/ticker/proc/IsRoundInProgress() + return current_state == GAME_STATE_PLAYING diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 6a087b920d4..b9538fdc312 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -1,775 +1,775 @@ -/obj/mecha - name = "Mecha" - desc = "Exosuit" - icon = 'icons/mecha/mecha.dmi' - density = 1 //Dense. To raise the heat. - opacity = 1 ///opaque. Menacing. - anchored = 1 //no pulling around. - resistance_flags = FIRE_PROOF | ACID_PROOF - layer = MOB_LAYER //icon draw layer - infra_luminosity = 15 //byond implementation is bugged. - force = 5 - max_integrity = 300 //max_integrity is base health - armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100) - var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5) - var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. - var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) - var/can_move = 0 // time of next allowed movement - var/mob/living/carbon/occupant = null - var/step_in = 10 //make a step in step_in/10 sec. - var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. - var/normal_step_energy_drain = 10 - var/step_energy_drain = 10 - var/melee_energy_drain = 15 - var/overload_step_energy_drain_min = 100 - var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. - var/obj/item/stock_parts/cell/cell - var/state = 0 - var/list/log = new - var/last_message = 0 - var/add_req_access = 1 - var/maint_access = 1 - var/dna //dna-locking the mech - var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference - var/datum/effect_system/spark_spread/spark_system = new - var/lights = 0 - var/lights_power = 6 - var/emagged = FALSE - - //inner atmos - var/use_internal_tank = 0 - var/internal_tank_valve = ONE_ATMOSPHERE - var/obj/machinery/portable_atmospherics/canister/internal_tank - var/datum/gas_mixture/cabin_air - var/obj/machinery/atmospherics/unary/portables_connector/connected_port = null - - var/obj/item/radio/radio = null - var/list/trackers = list() - - var/max_temperature = 25000 - var/internal_damage_threshold = 50 //health percentage below which internal damage is possible - var/internal_damage = 0 //contains bitflags - - var/list/operation_req_access = list()//required access level for mecha operation +/obj/mecha + name = "Mecha" + desc = "Exosuit" + icon = 'icons/mecha/mecha.dmi' + density = 1 //Dense. To raise the heat. + opacity = 1 ///opaque. Menacing. + anchored = 1 //no pulling around. + resistance_flags = FIRE_PROOF | ACID_PROOF + layer = MOB_LAYER //icon draw layer + infra_luminosity = 15 //byond implementation is bugged. + force = 5 + max_integrity = 300 //max_integrity is base health + armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100) + var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5) + var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. + var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) + var/can_move = 0 // time of next allowed movement + var/mob/living/carbon/occupant = null + var/step_in = 10 //make a step in step_in/10 sec. + var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. + var/normal_step_energy_drain = 10 + var/step_energy_drain = 10 + var/melee_energy_drain = 15 + var/overload_step_energy_drain_min = 100 + var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. + var/obj/item/stock_parts/cell/cell + var/state = 0 + var/list/log = new + var/last_message = 0 + var/add_req_access = 1 + var/maint_access = 1 + var/dna //dna-locking the mech + var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference + var/datum/effect_system/spark_spread/spark_system = new + var/lights = 0 + var/lights_power = 6 + var/emagged = FALSE + + //inner atmos + var/use_internal_tank = 0 + var/internal_tank_valve = ONE_ATMOSPHERE + var/obj/machinery/portable_atmospherics/canister/internal_tank + var/datum/gas_mixture/cabin_air + var/obj/machinery/atmospherics/unary/portables_connector/connected_port = null + + var/obj/item/radio/radio = null + var/list/trackers = list() + + var/max_temperature = 25000 + var/internal_damage_threshold = 50 //health percentage below which internal damage is possible + var/internal_damage = 0 //contains bitflags + + var/list/operation_req_access = list()//required access level for mecha operation var/list/internals_req_access = list(ACCESS_ENGINE,ACCESS_ROBOTICS)//required access level to open cell compartment - - var/wreckage - - var/list/equipment = new - var/obj/item/mecha_parts/mecha_equipment/selected - var/max_equip = 3 - var/datum/events/events - var/turf/crashing = null - var/occupant_sight_flags = 0 - - var/stepsound = 'sound/mecha/mechstep.ogg' - var/turnsound = 'sound/mecha/mechturn.ogg' - var/nominalsound = 'sound/mecha/nominal.ogg' - var/zoomsound = 'sound/mecha/imag_enh.ogg' - var/critdestrsound = 'sound/mecha/critdestr.ogg' - var/weapdestrsound = 'sound/mecha/weapdestr.ogg' - var/lowpowersound = 'sound/mecha/lowpower.ogg' - var/longactivationsound = 'sound/mecha/nominal.ogg' - var/starting_voice = /obj/item/mecha_modkit/voice - var/activated = FALSE - var/power_warned = FALSE - - var/destruction_sleep_duration = 1 //Time that mech pilot is put to sleep for if mech is destroyed - - var/melee_cooldown = 10 - var/melee_can_hit = 1 - - // Action vars - var/defence_mode = FALSE - var/defence_mode_deflect_chance = 35 - var/leg_overload_mode = FALSE - var/leg_overload_coeff = 100 - var/thrusters_active = FALSE - var/smoke = 5 - var/smoke_ready = 1 - var/smoke_cooldown = 100 - var/zoom_mode = FALSE - var/phasing = FALSE - var/phasing_energy_drain = 200 - var/phase_state = "" //icon_state when phasing - - hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) - -/obj/mecha/Initialize() - . = ..() - events = new - icon_state += "-open" - add_radio() - add_cabin() - add_airtank() - spark_system.set_up(2, 0, src) - spark_system.attach(src) - smoke_system.set_up(3, src) - smoke_system.attach(src) - add_cell() - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - log_message("[src] created.") - GLOB.mechas_list += src //global mech list - prepare_huds() - for(var/datum/atom_hud/data/diagnostic/diag_hud in huds) - diag_hud.add_to_hud(src) - diag_hud_set_mechhealth() - diag_hud_set_mechcell() - diag_hud_set_mechstat() - diag_hud_set_mechtracking() - - var/obj/item/mecha_modkit/voice/V = new starting_voice(src) - V.install(src) - qdel(V) - -//////////////////////// -////// Helpers ///////// -//////////////////////// - -/obj/mecha/get_cell() - return cell - -/obj/mecha/proc/add_airtank() - internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) - return internal_tank - -/obj/mecha/proc/add_cell(var/obj/item/stock_parts/cell/C=null) - if(C) - C.forceMove(src) - cell = C - return - cell = new/obj/item/stock_parts/cell/high/plus(src) - -/obj/mecha/proc/add_cabin() - cabin_air = new - cabin_air.temperature = T20C - cabin_air.volume = 200 - cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) - cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) - return cabin_air - -/obj/mecha/proc/add_radio() - radio = new(src) - radio.name = "[src] radio" - radio.icon = icon - radio.icon_state = icon_state - radio.subspace_transmission = 1 - -/obj/mecha/examine(mob/user) - . = ..() - var/integrity = obj_integrity * 100 / max_integrity - switch(integrity) - if(85 to 100) - . += "It's fully intact." - if(65 to 85) - . += "It's slightly damaged." - if(45 to 65) - . += "It's badly damaged." - if(25 to 45) - . += "It's heavily damaged." - else - . += "It's falling apart." - if(equipment && equipment.len) - . += "It's equipped with:" - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - . += "[bicon(ME)] [ME]" - -/obj/mecha/hear_talk(mob/M, list/message_pieces) - if(M == occupant && radio.broadcasting) - radio.talk_into(M, message_pieces) - -/obj/mecha/proc/click_action(atom/target, mob/user, params) - if(!occupant || occupant != user ) - return - if(user.incapacitated()) - return - if(phasing) - occupant_message("Unable to interact with objects while phasing.") - return - if(state) - occupant_message("Maintenance protocols in effect.") - return - if(!get_charge()) - return - if(src == target) - return - - var/dir_to_target = get_dir(src, target) - if(dir_to_target && !(dir_to_target & dir))//wrong direction - return - - if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) - target = safepick(view(3,target)) - if(!target) - return - - var/mob/living/L = user - if(!target.Adjacent(src)) - if(selected && selected.is_ranged()) - if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful) - to_chat(L, "You don't want to harm other living beings!") - return - selected.action(target, params) - else if(selected && selected.is_melee()) - if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM)) - to_chat(user, "You don't want to harm other living beings!") - return - selected.action(target, params) - else - if(internal_damage & MECHA_INT_CONTROL_LOST) - target = safepick(oview(1, src)) - if(!melee_can_hit || !isatom(target)) - return - target.mech_melee_attack(src) - melee_can_hit = 0 - spawn(melee_cooldown) - melee_can_hit = 1 - -/obj/mecha/proc/mech_toxin_damage(mob/living/target) - playsound(src, 'sound/effects/spray2.ogg', 50, 1) - if(target.reagents) - if(target.reagents.get_reagent_amount("atropine") + force < force*2) - target.reagents.add_reagent("atropine", force/2) - if(target.reagents.get_reagent_amount("toxin") + force < force*2) - target.reagents.add_reagent("toxin", force/2.5) - -/obj/mecha/proc/range_action(atom/target) - return - - -////////////////////////////////// -//////// Movement procs //////// -////////////////////////////////// - -/obj/mecha/Move(atom/newLoc, direct) - . = ..() - if(.) - events.fireEvent("onMove",get_turf(src)) - -/obj/mecha/Process_Spacemove(var/movement_dir = 0) - . = ..() - if(.) - return 1 - if(thrusters_active && movement_dir && use_power(step_energy_drain)) - return 1 - - var/atom/movable/backup = get_spacemove_backup() - if(backup) - if(istype(backup) && movement_dir && !backup.anchored) - if(backup.newtonian_move(turn(movement_dir, 180))) - if(occupant) - to_chat(occupant, "You push off of [backup] to propel yourself.") - return 1 - -/obj/mecha/relaymove(mob/user, direction) - if(!direction) - return - if(user != occupant) //While not "realistic", this piece is player friendly. - user.forceMove(get_turf(src)) - to_chat(user, "You climb out from [src].") - return 0 - if(connected_port) - if(world.time - last_message > 20) - occupant_message("Unable to move while connected to the air system port!") - last_message = world.time - return 0 - if(state) - occupant_message("Maintenance protocols in effect.") - return - return domove(direction) - -/obj/mecha/proc/domove(direction) - if(can_move >= world.time) - return 0 - if(!Process_Spacemove(direction)) - return 0 - if(!has_charge(step_energy_drain)) - return 0 - if(defence_mode) - if(world.time - last_message > 20) - occupant_message("Unable to move while in defence mode.") - last_message = world.time - return 0 - if(zoom_mode) - if(world.time - last_message > 20) - occupant_message("Unable to move while in zoom mode.") - last_message = world.time - return 0 - - var/move_result = 0 - var/move_type = 0 - if(internal_damage & MECHA_INT_CONTROL_LOST) - move_result = mechsteprand() - move_type = MECHAMOVE_RAND - else if(dir != direction) - move_result = mechturn(direction) - move_type = MECHAMOVE_TURN - else - move_result = mechstep(direction) - move_type = MECHAMOVE_STEP - - if(move_result && move_type) - aftermove(move_type) - can_move = world.time + step_in - return TRUE - return FALSE - -/obj/mecha/proc/aftermove(move_type) - use_power(step_energy_drain) - if(move_type & (MECHAMOVE_RAND | MECHAMOVE_STEP) && occupant) - var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector) in loc - if(possible_port) - var/obj/screen/alert/mech_port_available/A = occupant.throw_alert("mechaport", /obj/screen/alert/mech_port_available, override = TRUE) - if(A) - A.target = possible_port - else - occupant.clear_alert("mechaport") - if(leg_overload_mode) - if(obj_integrity < max_integrity - max_integrity / 3) - leg_overload_mode = FALSE - step_in = initial(step_in) - step_energy_drain = initial(step_energy_drain) - occupant_message("Leg actuators damage threshold exceded. Disabling overload.") - -/obj/mecha/proc/mechturn(direction) - dir = direction - if(turnsound) - playsound(src,turnsound,40,1) - return 1 - -/obj/mecha/proc/mechstep(direction) - . = step(src, direction) - if(!.) - if(phasing && get_charge() >= phasing_energy_drain) - if(can_move < world.time) - . = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself - flick("phazon-phase", src) - forceMove(get_step(src, direction)) - use_power(phasing_energy_drain) - playsound(src, stepsound, 40, 1) - can_move = world.time + (step_in * 3) - else if(stepsound) - playsound(src, stepsound, 40, 1) - -/obj/mecha/proc/mechsteprand() - . = step_rand(src) - if(. && stepsound) - playsound(src, stepsound, 40, 1) - -/obj/mecha/Bump(var/atom/obstacle, bump_allowed) - if(throwing) //high velocity mechas in your face! - var/breakthrough = 0 - if(istype(obstacle, /obj/structure/window)) - qdel(obstacle) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/grille/)) - var/obj/structure/grille/G = obstacle - G.obj_break() - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/table)) - var/obj/structure/table/T = obstacle - qdel(T) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/rack)) - new /obj/item/rack_parts(obstacle.loc) - qdel(obstacle) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/reagent_dispensers/fueltank)) - obstacle.ex_act(1) - - else if(isliving(obstacle)) - var/mob/living/L = obstacle - var/hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg') - if(L.flags & GODMODE) - return - L.take_overall_damage(5,0) - if(L.buckled) - L.buckled = 0 - L.Stun(5) - L.Weaken(5) - L.apply_effect(STUTTER, 5) - playsound(src, pick(hit_sound), 50, 0, 0) - breakthrough = 1 - - else - if(throwing) - throwing.finalize(FALSE) - crashing = null - - ..() - - if(breakthrough) - if(crashing) - spawn(1) - throw_at(crashing, 50, throw_speed) - else - spawn(1) - crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile. - throw_at(crashing, 50, throw_speed) - - else - if(bump_allowed) - if(..()) - return - if(isobj(obstacle)) - var/obj/O = obstacle - if(istype(O, /obj/effect/portal)) //derpfix - anchored = 0 - O.Bumped(src) - spawn(0) //countering portal teleport spawn(0), hurr - anchored = 1 - else if(!O.anchored) - step(obstacle, dir) - else if(ismob(obstacle)) - step(obstacle, dir) - - -/////////////////////////////////// -//////// Internal damage //////// -/////////////////////////////////// - -/obj/mecha/proc/check_for_internal_damage(list/possible_int_damage, ignore_threshold=null) - if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) - return - if(prob(20)) - if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) - for(var/T in possible_int_damage) - if(internal_damage & T) - possible_int_damage -= T - var/int_dam_flag = safepick(possible_int_damage) - if(int_dam_flag) - setInternalDamage(int_dam_flag) - if(prob(5)) - if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) - var/obj/item/mecha_parts/mecha_equipment/ME = safepick(equipment) - if(ME) - qdel(ME) - -/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) - return int_dam_flag ? internal_damage&int_dam_flag : internal_damage - - -/obj/mecha/proc/setInternalDamage(int_dam_flag) - internal_damage |= int_dam_flag - log_append_to_last("Internal damage of type [int_dam_flag].",1) - occupant << sound('sound/machines/warning-buzzer.ogg',wait=0) - diag_hud_set_mechstat() - -/obj/mecha/proc/clearInternalDamage(int_dam_flag) - internal_damage &= ~int_dam_flag - switch(int_dam_flag) - if(MECHA_INT_TEMP_CONTROL) - occupant_message("Life support system reactivated.") - if(MECHA_INT_FIRE) - occupant_message("Internal fire extinquished.") - if(MECHA_INT_TANK_BREACH) - occupant_message("Damaged internal tank has been sealed.") - diag_hud_set_mechstat() - - -//////////////////////////////////////// -//////// Health related procs //////// -//////////////////////////////////////// - -/obj/mecha/proc/get_armour_facing(relative_dir) - switch(relative_dir) - if(0) // BACKSTAB! - return facing_modifiers[MECHA_BACK_ARMOUR] - if(45, 90, 270, 315) - return facing_modifiers[MECHA_SIDE_ARMOUR] - if(225, 180, 135) - return facing_modifiers[MECHA_FRONT_ARMOUR] - return 1 //always return non-0 - -/obj/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - spark_system.start() - switch(damage_flag) - if("fire") - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL)) - if("melee") - check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - else - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT)) - if(. >= 5 || prob(33)) - occupant_message("Taking damage!") - log_message("Took [damage_amount] points of damage. Damage type: [damage_type]") - -/obj/mecha/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - . = ..() - if(!damage_amount) - return 0 - var/booster_deflection_modifier = 1 - var/booster_damage_modifier = 1 - if(damage_flag == "bullet" || damage_flag == "laser" || damage_flag == "energy") - for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) - if(B.projectile_react()) - booster_deflection_modifier = B.deflect_coeff - booster_damage_modifier = B.damage_coeff - break - else if(damage_flag == "melee") - for(var/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/B in equipment) - if(B.attack_react()) - booster_deflection_modifier *= B.deflect_coeff - booster_damage_modifier *= B.damage_coeff - break - - if(attack_dir) - var/facing_modifier = get_armour_facing(dir2angle(attack_dir) - dir2angle(src)) - booster_damage_modifier /= facing_modifier - booster_deflection_modifier *= facing_modifier - if(prob(deflect_chance * booster_deflection_modifier)) - visible_message("[src]'s armour deflects the attack!") - log_message("Armor saved.") - return 0 - if(.) - . *= booster_damage_modifier - -/obj/mecha/attack_hand(mob/living/user) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) - playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1) - user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.") - log_message("Attack by hand/paw. Attacker - [user].") - - -/obj/mecha/attack_alien(mob/living/user) - log_message("Attack by alien. Attacker - [user].", TRUE) - playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) - attack_generic(user, 15, BRUTE, "melee", 0) - -/obj/mecha/attack_animal(mob/living/simple_animal/user) - log_message("Attack by simple animal. Attacker - [user].") - if(!user.melee_damage_upper && !user.obj_damage) - user.custom_emote(1, "[user.friendly] [src].") - return FALSE - else - var/play_soundeffect = 1 - if(user.environment_smash) - play_soundeffect = 0 - playsound(src, 'sound/effects/bang.ogg', 50, TRUE) - var/animal_damage = rand(user.melee_damage_lower,user.melee_damage_upper) - if(user.obj_damage) - animal_damage = user.obj_damage - animal_damage = min(animal_damage, 20*user.environment_smash) - user.create_attack_log("attacked [name]") - attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect) - return TRUE - -/obj/mecha/hulk_damage() - return 15 - -/obj/mecha/attack_hulk(mob/living/carbon/human/user) - . = ..() - if(.) - log_message("Attack by hulk. Attacker - [user].", 1) - add_attack_logs(user, src, "Punched with hulk powers") - -/obj/mecha/blob_act(obj/structure/blob/B) - log_message("Attack by blob. Attacker - [B].") - take_damage(30, BRUTE, "melee", 0, get_dir(src, B)) - -/obj/mecha/attack_tk() - return - -/obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper - log_message("Hit by [AM].") - . = ..() - -/obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper - log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).") - ..() - -/obj/mecha/ex_act(severity, target) - log_message("Affected by explosion of severity: [severity].") - if(prob(deflect_chance)) - severity++ - log_message("Armor saved, changing severity to [severity]") - ..() - severity++ - for(var/X in equipment) - var/obj/item/mecha_parts/mecha_equipment/ME = X - ME.ex_act(severity) - for(var/Y in trackers) - var/obj/item/mecha_parts/mecha_tracking/MT = Y - MT.ex_act(severity) - if(occupant) - occupant.ex_act(severity) - -/obj/mecha/handle_atom_del(atom/A) - if(A == occupant) - occupant = null - icon_state = initial(icon_state)+"-open" - setDir(dir_in) - -/obj/mecha/Destroy() - if(occupant) - occupant.SetSleeping(destruction_sleep_duration) - go_out() - var/mob/living/silicon/ai/AI - for(var/mob/M in src) //Let's just be ultra sure - if(isAI(M)) - occupant = null - AI = M //AIs are loaded into the mech computer itself. When the mech dies, so does the AI. They can be recovered with an AI card from the wreck. - else - M.forceMove(loc) - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - E.detach(loc) - qdel(E) - equipment.Cut() - QDEL_NULL(cell) - QDEL_NULL(internal_tank) - if(AI) - AI.gib() //No wreck, no AI to recover - STOP_PROCESSING(SSobj, src) - GLOB.poi_list.Remove(src) - if(loc) - loc.assume_air(cabin_air) - air_update_turf() - else - qdel(cabin_air) - cabin_air = null - QDEL_NULL(spark_system) - QDEL_NULL(smoke_system) - - GLOB.mechas_list -= src //global mech list - return ..() - -//TODO -/obj/mecha/emp_act(severity) - if(get_charge()) - use_power((cell.charge/3)/(severity*2)) - take_damage(30 / severity, BURN, "energy", 1) - log_message("EMP detected", 1) - check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL, MECHA_INT_CONTROL_LOST, MECHA_INT_SHORT_CIRCUIT), 1) - -/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > max_temperature) - log_message("Exposed to dangerous temperature.", 1) - take_damage(5, BURN, 0, 1) - check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) - -////////////////////// -////// AttackBy ////// -////////////////////// - -/obj/mecha/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/mmi)) - if(mmi_move_inside(W,user)) - to_chat(user, "[src]-MMI interface initialized successfuly") - else - to_chat(user, "[src]-MMI interface initialization failed.") - return - - if(istype(W, /obj/item/mecha_parts/mecha_equipment)) - var/obj/item/mecha_parts/mecha_equipment/E = W - spawn() - if(E.can_attach(src)) - if(!user.drop_item()) - return - E.attach(src) - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") - else - to_chat(user, "You were unable to attach [W] to [src]!") - return - - if(W.GetID()) - if(add_req_access || maint_access) - if(internals_access_allowed(usr)) - var/obj/item/card/id/id_card - if(istype(W, /obj/item/card/id)) - id_card = W - else - var/obj/item/pda/pda = W - id_card = pda.id - output_maintenance_dialog(id_card, user) - return - else - to_chat(user, "Invalid ID: Access denied.") - else - to_chat(user, "Maintenance protocols disabled by operator.") - - else if(istype(W, /obj/item/stack/cable_coil)) - if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - var/obj/item/stack/cable_coil/CC = W - if(CC.amount > 1) - CC.use(2) - clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) - to_chat(user, "You replace the fused wires.") - else - to_chat(user, "There's not enough wire to finish the task.") - return - - else if(istype(W, /obj/item/stock_parts/cell)) - if(state==4) - if(!cell) - if(!user.drop_item()) - return - to_chat(user, "You install the powercell.") - W.forceMove(src) - cell = W - log_message("Powercell installed") - else - to_chat(user, "There's already a powercell installed.") - return - - else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) - if(!user.unEquip(W)) - to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]") - return - W.forceMove(src) - trackers += W - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") - diag_hud_set_mechtracking() - return - - else if(istype(W, /obj/item/paintkit)) - if(occupant) - to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") - return - - var/obj/item/paintkit/P = W - var/found = null - - for(var/type in P.allowed_types) - if(type == initial_icon) - found = 1 - break - - if(!found) - to_chat(user, "That kit isn't meant for use on this class of exosuit.") - return - - user.visible_message("[user] opens [P] and spends some quality time customising [src].") - - name = P.new_name - desc = P.new_desc - initial_icon = P.new_icon - reset_icon() - - user.drop_item() - qdel(P) - - else if(istype(W, /obj/item/mecha_modkit)) - if(occupant) - to_chat(user, "You can't access the mech's modification port while it is occupied.") - return - var/obj/item/mecha_modkit/M = W - if(do_after_once(user, M.install_time, target = src)) - M.install(src, user) - else - to_chat(user, "You stop installing [M].") - - else - return ..() - + + var/wreckage + + var/list/equipment = new + var/obj/item/mecha_parts/mecha_equipment/selected + var/max_equip = 3 + var/datum/events/events + var/turf/crashing = null + var/occupant_sight_flags = 0 + + var/stepsound = 'sound/mecha/mechstep.ogg' + var/turnsound = 'sound/mecha/mechturn.ogg' + var/nominalsound = 'sound/mecha/nominal.ogg' + var/zoomsound = 'sound/mecha/imag_enh.ogg' + var/critdestrsound = 'sound/mecha/critdestr.ogg' + var/weapdestrsound = 'sound/mecha/weapdestr.ogg' + var/lowpowersound = 'sound/mecha/lowpower.ogg' + var/longactivationsound = 'sound/mecha/nominal.ogg' + var/starting_voice = /obj/item/mecha_modkit/voice + var/activated = FALSE + var/power_warned = FALSE + + var/destruction_sleep_duration = 1 //Time that mech pilot is put to sleep for if mech is destroyed + + var/melee_cooldown = 10 + var/melee_can_hit = 1 + + // Action vars + var/defence_mode = FALSE + var/defence_mode_deflect_chance = 35 + var/leg_overload_mode = FALSE + var/leg_overload_coeff = 100 + var/thrusters_active = FALSE + var/smoke = 5 + var/smoke_ready = 1 + var/smoke_cooldown = 100 + var/zoom_mode = FALSE + var/phasing = FALSE + var/phasing_energy_drain = 200 + var/phase_state = "" //icon_state when phasing + + hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) + +/obj/mecha/Initialize() + . = ..() + events = new + icon_state += "-open" + add_radio() + add_cabin() + add_airtank() + spark_system.set_up(2, 0, src) + spark_system.attach(src) + smoke_system.set_up(3, src) + smoke_system.attach(src) + add_cell() + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + log_message("[src] created.") + GLOB.mechas_list += src //global mech list + prepare_huds() + for(var/datum/atom_hud/data/diagnostic/diag_hud in huds) + diag_hud.add_to_hud(src) + diag_hud_set_mechhealth() + diag_hud_set_mechcell() + diag_hud_set_mechstat() + diag_hud_set_mechtracking() + + var/obj/item/mecha_modkit/voice/V = new starting_voice(src) + V.install(src) + qdel(V) + +//////////////////////// +////// Helpers ///////// +//////////////////////// + +/obj/mecha/get_cell() + return cell + +/obj/mecha/proc/add_airtank() + internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) + return internal_tank + +/obj/mecha/proc/add_cell(var/obj/item/stock_parts/cell/C=null) + if(C) + C.forceMove(src) + cell = C + return + cell = new/obj/item/stock_parts/cell/high/plus(src) + +/obj/mecha/proc/add_cabin() + cabin_air = new + cabin_air.temperature = T20C + cabin_air.volume = 200 + cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) + cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) + return cabin_air + +/obj/mecha/proc/add_radio() + radio = new(src) + radio.name = "[src] radio" + radio.icon = icon + radio.icon_state = icon_state + radio.subspace_transmission = 1 + +/obj/mecha/examine(mob/user) + . = ..() + var/integrity = obj_integrity * 100 / max_integrity + switch(integrity) + if(85 to 100) + . += "It's fully intact." + if(65 to 85) + . += "It's slightly damaged." + if(45 to 65) + . += "It's badly damaged." + if(25 to 45) + . += "It's heavily damaged." + else + . += "It's falling apart." + if(equipment && equipment.len) + . += "It's equipped with:" + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + . += "[bicon(ME)] [ME]" + +/obj/mecha/hear_talk(mob/M, list/message_pieces) + if(M == occupant && radio.broadcasting) + radio.talk_into(M, message_pieces) + +/obj/mecha/proc/click_action(atom/target, mob/user, params) + if(!occupant || occupant != user ) + return + if(user.incapacitated()) + return + if(phasing) + occupant_message("Unable to interact with objects while phasing.") + return + if(state) + occupant_message("Maintenance protocols in effect.") + return + if(!get_charge()) + return + if(src == target) + return + + var/dir_to_target = get_dir(src, target) + if(dir_to_target && !(dir_to_target & dir))//wrong direction + return + + if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) + target = safepick(view(3,target)) + if(!target) + return + + var/mob/living/L = user + if(!target.Adjacent(src)) + if(selected && selected.is_ranged()) + if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful) + to_chat(L, "You don't want to harm other living beings!") + return + selected.action(target, params) + else if(selected && selected.is_melee()) + if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM)) + to_chat(user, "You don't want to harm other living beings!") + return + selected.action(target, params) + else + if(internal_damage & MECHA_INT_CONTROL_LOST) + target = safepick(oview(1, src)) + if(!melee_can_hit || !isatom(target)) + return + target.mech_melee_attack(src) + melee_can_hit = 0 + spawn(melee_cooldown) + melee_can_hit = 1 + +/obj/mecha/proc/mech_toxin_damage(mob/living/target) + playsound(src, 'sound/effects/spray2.ogg', 50, 1) + if(target.reagents) + if(target.reagents.get_reagent_amount("atropine") + force < force*2) + target.reagents.add_reagent("atropine", force/2) + if(target.reagents.get_reagent_amount("toxin") + force < force*2) + target.reagents.add_reagent("toxin", force/2.5) + +/obj/mecha/proc/range_action(atom/target) + return + + +////////////////////////////////// +//////// Movement procs //////// +////////////////////////////////// + +/obj/mecha/Move(atom/newLoc, direct) + . = ..() + if(.) + events.fireEvent("onMove",get_turf(src)) + +/obj/mecha/Process_Spacemove(var/movement_dir = 0) + . = ..() + if(.) + return 1 + if(thrusters_active && movement_dir && use_power(step_energy_drain)) + return 1 + + var/atom/movable/backup = get_spacemove_backup() + if(backup) + if(istype(backup) && movement_dir && !backup.anchored) + if(backup.newtonian_move(turn(movement_dir, 180))) + if(occupant) + to_chat(occupant, "You push off of [backup] to propel yourself.") + return 1 + +/obj/mecha/relaymove(mob/user, direction) + if(!direction) + return + if(user != occupant) //While not "realistic", this piece is player friendly. + user.forceMove(get_turf(src)) + to_chat(user, "You climb out from [src].") + return 0 + if(connected_port) + if(world.time - last_message > 20) + occupant_message("Unable to move while connected to the air system port!") + last_message = world.time + return 0 + if(state) + occupant_message("Maintenance protocols in effect.") + return + return domove(direction) + +/obj/mecha/proc/domove(direction) + if(can_move >= world.time) + return 0 + if(!Process_Spacemove(direction)) + return 0 + if(!has_charge(step_energy_drain)) + return 0 + if(defence_mode) + if(world.time - last_message > 20) + occupant_message("Unable to move while in defence mode.") + last_message = world.time + return 0 + if(zoom_mode) + if(world.time - last_message > 20) + occupant_message("Unable to move while in zoom mode.") + last_message = world.time + return 0 + + var/move_result = 0 + var/move_type = 0 + if(internal_damage & MECHA_INT_CONTROL_LOST) + move_result = mechsteprand() + move_type = MECHAMOVE_RAND + else if(dir != direction) + move_result = mechturn(direction) + move_type = MECHAMOVE_TURN + else + move_result = mechstep(direction) + move_type = MECHAMOVE_STEP + + if(move_result && move_type) + aftermove(move_type) + can_move = world.time + step_in + return TRUE + return FALSE + +/obj/mecha/proc/aftermove(move_type) + use_power(step_energy_drain) + if(move_type & (MECHAMOVE_RAND | MECHAMOVE_STEP) && occupant) + var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector) in loc + if(possible_port) + var/obj/screen/alert/mech_port_available/A = occupant.throw_alert("mechaport", /obj/screen/alert/mech_port_available, override = TRUE) + if(A) + A.target = possible_port + else + occupant.clear_alert("mechaport") + if(leg_overload_mode) + if(obj_integrity < max_integrity - max_integrity / 3) + leg_overload_mode = FALSE + step_in = initial(step_in) + step_energy_drain = initial(step_energy_drain) + occupant_message("Leg actuators damage threshold exceded. Disabling overload.") + +/obj/mecha/proc/mechturn(direction) + dir = direction + if(turnsound) + playsound(src,turnsound,40,1) + return 1 + +/obj/mecha/proc/mechstep(direction) + . = step(src, direction) + if(!.) + if(phasing && get_charge() >= phasing_energy_drain) + if(can_move < world.time) + . = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself + flick("phazon-phase", src) + forceMove(get_step(src, direction)) + use_power(phasing_energy_drain) + playsound(src, stepsound, 40, 1) + can_move = world.time + (step_in * 3) + else if(stepsound) + playsound(src, stepsound, 40, 1) + +/obj/mecha/proc/mechsteprand() + . = step_rand(src) + if(. && stepsound) + playsound(src, stepsound, 40, 1) + +/obj/mecha/Bump(var/atom/obstacle, bump_allowed) + if(throwing) //high velocity mechas in your face! + var/breakthrough = 0 + if(istype(obstacle, /obj/structure/window)) + qdel(obstacle) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/grille/)) + var/obj/structure/grille/G = obstacle + G.obj_break() + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/table)) + var/obj/structure/table/T = obstacle + qdel(T) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/rack)) + new /obj/item/rack_parts(obstacle.loc) + qdel(obstacle) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/reagent_dispensers/fueltank)) + obstacle.ex_act(1) + + else if(isliving(obstacle)) + var/mob/living/L = obstacle + var/hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg') + if(L.flags & GODMODE) + return + L.take_overall_damage(5,0) + if(L.buckled) + L.buckled = 0 + L.Stun(5) + L.Weaken(5) + L.apply_effect(STUTTER, 5) + playsound(src, pick(hit_sound), 50, 0, 0) + breakthrough = 1 + + else + if(throwing) + throwing.finalize(FALSE) + crashing = null + + ..() + + if(breakthrough) + if(crashing) + spawn(1) + throw_at(crashing, 50, throw_speed) + else + spawn(1) + crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile. + throw_at(crashing, 50, throw_speed) + + else + if(bump_allowed) + if(..()) + return + if(isobj(obstacle)) + var/obj/O = obstacle + if(istype(O, /obj/effect/portal)) //derpfix + anchored = 0 + O.Bumped(src) + spawn(0) //countering portal teleport spawn(0), hurr + anchored = 1 + else if(!O.anchored) + step(obstacle, dir) + else if(ismob(obstacle)) + step(obstacle, dir) + + +/////////////////////////////////// +//////// Internal damage //////// +/////////////////////////////////// + +/obj/mecha/proc/check_for_internal_damage(list/possible_int_damage, ignore_threshold=null) + if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) + return + if(prob(20)) + if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) + for(var/T in possible_int_damage) + if(internal_damage & T) + possible_int_damage -= T + var/int_dam_flag = safepick(possible_int_damage) + if(int_dam_flag) + setInternalDamage(int_dam_flag) + if(prob(5)) + if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) + var/obj/item/mecha_parts/mecha_equipment/ME = safepick(equipment) + if(ME) + qdel(ME) + +/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) + return int_dam_flag ? internal_damage&int_dam_flag : internal_damage + + +/obj/mecha/proc/setInternalDamage(int_dam_flag) + internal_damage |= int_dam_flag + log_append_to_last("Internal damage of type [int_dam_flag].",1) + occupant << sound('sound/machines/warning-buzzer.ogg',wait=0) + diag_hud_set_mechstat() + +/obj/mecha/proc/clearInternalDamage(int_dam_flag) + internal_damage &= ~int_dam_flag + switch(int_dam_flag) + if(MECHA_INT_TEMP_CONTROL) + occupant_message("Life support system reactivated.") + if(MECHA_INT_FIRE) + occupant_message("Internal fire extinquished.") + if(MECHA_INT_TANK_BREACH) + occupant_message("Damaged internal tank has been sealed.") + diag_hud_set_mechstat() + + +//////////////////////////////////////// +//////// Health related procs //////// +//////////////////////////////////////// + +/obj/mecha/proc/get_armour_facing(relative_dir) + switch(relative_dir) + if(0) // BACKSTAB! + return facing_modifiers[MECHA_BACK_ARMOUR] + if(45, 90, 270, 315) + return facing_modifiers[MECHA_SIDE_ARMOUR] + if(225, 180, 135) + return facing_modifiers[MECHA_FRONT_ARMOUR] + return 1 //always return non-0 + +/obj/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + spark_system.start() + switch(damage_flag) + if("fire") + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL)) + if("melee") + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + else + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT)) + if(. >= 5 || prob(33)) + occupant_message("Taking damage!") + log_message("Took [damage_amount] points of damage. Damage type: [damage_type]") + +/obj/mecha/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + . = ..() + if(!damage_amount) + return 0 + var/booster_deflection_modifier = 1 + var/booster_damage_modifier = 1 + if(damage_flag == "bullet" || damage_flag == "laser" || damage_flag == "energy") + for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) + if(B.projectile_react()) + booster_deflection_modifier = B.deflect_coeff + booster_damage_modifier = B.damage_coeff + break + else if(damage_flag == "melee") + for(var/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/B in equipment) + if(B.attack_react()) + booster_deflection_modifier *= B.deflect_coeff + booster_damage_modifier *= B.damage_coeff + break + + if(attack_dir) + var/facing_modifier = get_armour_facing(dir2angle(attack_dir) - dir2angle(src)) + booster_damage_modifier /= facing_modifier + booster_deflection_modifier *= facing_modifier + if(prob(deflect_chance * booster_deflection_modifier)) + visible_message("[src]'s armour deflects the attack!") + log_message("Armor saved.") + return 0 + if(.) + . *= booster_damage_modifier + +/obj/mecha/attack_hand(mob/living/user) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) + playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1) + user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.") + log_message("Attack by hand/paw. Attacker - [user].") + + +/obj/mecha/attack_alien(mob/living/user) + log_message("Attack by alien. Attacker - [user].", TRUE) + playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) + attack_generic(user, 15, BRUTE, "melee", 0) + +/obj/mecha/attack_animal(mob/living/simple_animal/user) + log_message("Attack by simple animal. Attacker - [user].") + if(!user.melee_damage_upper && !user.obj_damage) + user.custom_emote(1, "[user.friendly] [src].") + return FALSE + else + var/play_soundeffect = 1 + if(user.environment_smash) + play_soundeffect = 0 + playsound(src, 'sound/effects/bang.ogg', 50, TRUE) + var/animal_damage = rand(user.melee_damage_lower,user.melee_damage_upper) + if(user.obj_damage) + animal_damage = user.obj_damage + animal_damage = min(animal_damage, 20*user.environment_smash) + user.create_attack_log("attacked [name]") + attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect) + return TRUE + +/obj/mecha/hulk_damage() + return 15 + +/obj/mecha/attack_hulk(mob/living/carbon/human/user) + . = ..() + if(.) + log_message("Attack by hulk. Attacker - [user].", 1) + add_attack_logs(user, src, "Punched with hulk powers") + +/obj/mecha/blob_act(obj/structure/blob/B) + log_message("Attack by blob. Attacker - [B].") + take_damage(30, BRUTE, "melee", 0, get_dir(src, B)) + +/obj/mecha/attack_tk() + return + +/obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper + log_message("Hit by [AM].") + . = ..() + +/obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper + log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).") + ..() + +/obj/mecha/ex_act(severity, target) + log_message("Affected by explosion of severity: [severity].") + if(prob(deflect_chance)) + severity++ + log_message("Armor saved, changing severity to [severity]") + ..() + severity++ + for(var/X in equipment) + var/obj/item/mecha_parts/mecha_equipment/ME = X + ME.ex_act(severity) + for(var/Y in trackers) + var/obj/item/mecha_parts/mecha_tracking/MT = Y + MT.ex_act(severity) + if(occupant) + occupant.ex_act(severity) + +/obj/mecha/handle_atom_del(atom/A) + if(A == occupant) + occupant = null + icon_state = initial(icon_state)+"-open" + setDir(dir_in) + +/obj/mecha/Destroy() + if(occupant) + occupant.SetSleeping(destruction_sleep_duration) + go_out() + var/mob/living/silicon/ai/AI + for(var/mob/M in src) //Let's just be ultra sure + if(isAI(M)) + occupant = null + AI = M //AIs are loaded into the mech computer itself. When the mech dies, so does the AI. They can be recovered with an AI card from the wreck. + else + M.forceMove(loc) + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + E.detach(loc) + qdel(E) + equipment.Cut() + QDEL_NULL(cell) + QDEL_NULL(internal_tank) + if(AI) + AI.gib() //No wreck, no AI to recover + STOP_PROCESSING(SSobj, src) + GLOB.poi_list.Remove(src) + if(loc) + loc.assume_air(cabin_air) + air_update_turf() + else + qdel(cabin_air) + cabin_air = null + QDEL_NULL(spark_system) + QDEL_NULL(smoke_system) + + GLOB.mechas_list -= src //global mech list + return ..() + +//TODO +/obj/mecha/emp_act(severity) + if(get_charge()) + use_power((cell.charge/3)/(severity*2)) + take_damage(30 / severity, BURN, "energy", 1) + log_message("EMP detected", 1) + check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL, MECHA_INT_CONTROL_LOST, MECHA_INT_SHORT_CIRCUIT), 1) + +/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > max_temperature) + log_message("Exposed to dangerous temperature.", 1) + take_damage(5, BURN, 0, 1) + check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) + +////////////////////// +////// AttackBy ////// +////////////////////// + +/obj/mecha/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/mmi)) + if(mmi_move_inside(W,user)) + to_chat(user, "[src]-MMI interface initialized successfuly") + else + to_chat(user, "[src]-MMI interface initialization failed.") + return + + if(istype(W, /obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/E = W + spawn() + if(E.can_attach(src)) + if(!user.drop_item()) + return + E.attach(src) + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + else + to_chat(user, "You were unable to attach [W] to [src]!") + return + + if(W.GetID()) + if(add_req_access || maint_access) + if(internals_access_allowed(usr)) + var/obj/item/card/id/id_card + if(istype(W, /obj/item/card/id)) + id_card = W + else + var/obj/item/pda/pda = W + id_card = pda.id + output_maintenance_dialog(id_card, user) + return + else + to_chat(user, "Invalid ID: Access denied.") + else + to_chat(user, "Maintenance protocols disabled by operator.") + + else if(istype(W, /obj/item/stack/cable_coil)) + if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) + var/obj/item/stack/cable_coil/CC = W + if(CC.amount > 1) + CC.use(2) + clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) + to_chat(user, "You replace the fused wires.") + else + to_chat(user, "There's not enough wire to finish the task.") + return + + else if(istype(W, /obj/item/stock_parts/cell)) + if(state==4) + if(!cell) + if(!user.drop_item()) + return + to_chat(user, "You install the powercell.") + W.forceMove(src) + cell = W + log_message("Powercell installed") + else + to_chat(user, "There's already a powercell installed.") + return + + else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) + if(!user.unEquip(W)) + to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]") + return + W.forceMove(src) + trackers += W + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + diag_hud_set_mechtracking() + return + + else if(istype(W, /obj/item/paintkit)) + if(occupant) + to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") + return + + var/obj/item/paintkit/P = W + var/found = null + + for(var/type in P.allowed_types) + if(type == initial_icon) + found = 1 + break + + if(!found) + to_chat(user, "That kit isn't meant for use on this class of exosuit.") + return + + user.visible_message("[user] opens [P] and spends some quality time customising [src].") + + name = P.new_name + desc = P.new_desc + initial_icon = P.new_icon + reset_icon() + + user.drop_item() + qdel(P) + + else if(istype(W, /obj/item/mecha_modkit)) + if(occupant) + to_chat(user, "You can't access the mech's modification port while it is occupied.") + return + var/obj/item/mecha_modkit/M = W + if(do_after_once(user, M.install_time, target = src)) + M.install(src, user) + else + to_chat(user, "You stop installing [M].") + + else + return ..() + /obj/mecha/crowbar_act(mob/user, obj/item/I) if(state != 2 && state != 3 && !(state == 4 && pilot_is_mmi())) @@ -845,677 +845,677 @@ else to_chat(user, "[src] is at full integrity!") -/obj/mecha/mech_melee_attack(obj/mecha/M) - if(!has_charge(melee_energy_drain)) - return 0 - use_power(melee_energy_drain) - if(M.damtype == BRUTE || M.damtype == BURN) - add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") - . = ..() - -/obj/mecha/emag_act(mob/user) - to_chat(user, "[src]'s ID slot rejects the card.") - return - - -///////////////////////////////////// -//////////// AI piloting //////////// -///////////////////////////////////// - -/obj/mecha/attack_ai(mob/living/silicon/ai/user) - if(!isAI(user)) - return - //Allows the Malf to scan a mech's status and loadout, helping it to decide if it is a worthy chariot. - if(user.can_dominate_mechs) - examine(user) //Get diagnostic information! - for(var/obj/item/mecha_parts/mecha_tracking/B in trackers) - to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") - to_chat(user, "[B.get_mecha_info_text()]") - break - //Nothing like a big, red link to make the player feel powerful! - to_chat(user, "ASSUME DIRECT CONTROL?
") - else - examine(user) - if(occupant) - user << "This exosuit has a pilot and cannot be controlled." - return - var/can_control_mech = FALSE - for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers) - can_control_mech = TRUE - to_chat(user, "[bicon(src)] Status of [name]:\n\ - [A.get_mecha_info_text()]") - break - if(!can_control_mech) - to_chat(user, "You cannot control exosuits without AI control beacons installed.") - return - to_chat(user, "Take control of exosuit?
") - -/obj/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) - if(!..()) - return - - //Transfer from core or card to mech. Proc is called by mech. - switch(interaction) - if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card. - if(!maint_access) //Mech must be in maint mode to allow carding. - to_chat(user, "[name] must have maintenance protocols active in order to allow a transfer.") - return - AI = occupant - if(!AI || !isAI(occupant)) //Mech does not have an AI for a pilot - to_chat(user, "No AI detected in the [name] onboard computer.") - return - if(AI.mind.special_role) //Malf AIs cannot leave mechs. Except through death. - to_chat(user, "ACCESS DENIED.") - return - AI.aiRestorePowerRoutine = 0//So the AI initially has power. - AI.control_disabled = 1 - AI.aiRadio.disabledAi = 1 - AI.loc = card - occupant = null - AI.controlled_mech = null - AI.remote_control = null - icon_state = initial(icon_state)+"-open" - to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") - - if(AI_MECH_HACK) //Called by AIs on the mech - AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) - if(AI.can_dominate_mechs) - if(occupant) //Oh, I am sorry, were you using that? - to_chat(AI, "Pilot detected! Forced ejection initiated!") - to_chat(occupant, "You have been forcibly ejected!") - go_out(1) //IT IS MINE, NOW. SUCK IT, RD! - ai_enter_mech(AI, interaction) - - if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech. - AI = locate(/mob/living/silicon/ai) in card - if(!AI) - to_chat(user, "There is no AI currently installed on this device.") - return - else if(AI.stat || !AI.client) - to_chat(user, "[AI.name] is currently unresponsive, and cannot be uploaded.") - return - else if(occupant || dna) //Normal AIs cannot steal mechs! - to_chat(user, "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"].") - return - AI.control_disabled = 0 - AI.aiRadio.disabledAi = 0 - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") - ai_enter_mech(AI, interaction) - -//Hack and From Card interactions share some code, so leave that here for both to use. -/obj/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction) - AI.aiRestorePowerRoutine = 0 - AI.loc = src - occupant = AI - icon_state = initial(icon_state) - playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!hasInternalDamage()) - occupant << sound(nominalsound, volume = 50) - AI.cancel_camera() - AI.controlled_mech = src - AI.remote_control = src - AI.canmove = 1 //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow. - AI.can_shunt = 0 //ONE AI ENTERS. NO AI LEAVES. - to_chat(AI, "[AI.can_dominate_mechs ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ - : "You have been uploaded to a mech's onboard computer."]") - to_chat(AI, "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.") - if(interaction == AI_TRANS_FROM_CARD) - GrantActions(AI, FALSE) - else - GrantActions(AI, !AI.can_dominate_mechs) - -///////////////////////////////////// -//////// Atmospheric stuff //////// -///////////////////////////////////// - -/obj/mecha/proc/get_turf_air() - var/turf/T = get_turf(src) - if(T) - . = T.return_air() - -/obj/mecha/remove_air(amount) - if(use_internal_tank) - return cabin_air.remove(amount) - else - var/turf/T = get_turf(src) - if(T) - return T.remove_air(amount) - -/obj/mecha/return_air() - if(use_internal_tank) - return cabin_air - return get_turf_air() - -/obj/mecha/proc/return_pressure() - var/datum/gas_mixture/t_air = return_air() - if(t_air) - . = t_air.return_pressure() - -//skytodo: //No idea what you want me to do here, mate. -/obj/mecha/proc/return_temperature() - var/datum/gas_mixture/t_air = return_air() - if(t_air) - . = t_air.return_temperature() - -/obj/mecha/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !istype(new_port) || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - connected_port.parent.reconcile_air() - - if(occupant) - occupant.clear_alert("mechaport") - occupant.throw_alert("mechaport_d", /obj/screen/alert/mech_port_disconnect) - - log_message("Connected to gas port.") - return 1 - -/obj/mecha/proc/disconnect() - if(!connected_port) - return 0 - - connected_port.connected_device = null - connected_port = null - log_message("Disconnected from gas port.") - if(occupant) - occupant.clear_alert("mechaport_d") - return 1 - -/obj/mecha/portableConnectorReturnAir() - return internal_tank.return_air() - -/obj/mecha/proc/toggle_lights() - lights = !lights - if(lights) - set_light(light_range + lights_power) - else - set_light(light_range - lights_power) - occupant_message("Toggled lights [lights ? "on" : "off"].") - log_message("Toggled lights [lights ? "on" : "off"].") - -/obj/mecha/proc/toggle_internal_tank() - use_internal_tank = !use_internal_tank - occupant_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") - log_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") - -/obj/mecha/MouseDrop_T(mob/M, mob/user) - if(user.incapacitated()) - return - if(user != M) - return - log_message("[user] tries to move in.") - if(occupant) - to_chat(user, "The [src] is already occupied!") - log_append_to_last("Permission denied.") - return - var/passed - if(dna) - if(ishuman(user)) - if(user.dna.unique_enzymes == dna) - passed = 1 - else if(operation_allowed(user)) - passed = 1 - if(!passed) - to_chat(user, "Access denied.") - log_append_to_last("Permission denied.") - return - if(user.buckled) - to_chat(user, "You are currently buckled and cannot move.") - log_append_to_last("Permission denied.") - return - if(user.has_buckled_mobs()) //mob attached to us - to_chat(user, "You can't enter the exosuit with other creatures attached to you!") - return - - visible_message("[user] starts to climb into [src]") - - if(do_after(user, 40, target = src)) - if(obj_integrity <= 0) - to_chat(user, "You cannot get in the [name], it has been destroyed!") - else if(occupant) - to_chat(user, "[occupant] was faster! Try better next time, loser.") - else if(user.buckled) - to_chat(user, "You can't enter the exosuit while buckled.") - else if(user.has_buckled_mobs()) - to_chat(user, "You can't enter the exosuit with other creatures attached to you!") - else - moved_inside(user) - else - to_chat(user, "You stop entering the exosuit!") - -/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) - if(H && H.client && H in range(1)) - occupant = H - H.stop_pulling() - H.forceMove(src) - H.reset_perspective(src) - add_fingerprint(H) - GrantActions(H, human_occupant = 1) - forceMove(loc) - log_append_to_last("[H] moved in as pilot.") - icon_state = reset_icon() - dir = dir_in - playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!activated) - occupant << sound(longactivationsound, volume = 50) - activated = TRUE - else if(!hasInternalDamage()) - occupant << sound(nominalsound, volume = 50) - if(state) - H.throw_alert("locked", /obj/screen/alert/mech_maintenance) - return 1 - else - return 0 - -/obj/mecha/proc/mmi_move_inside(var/obj/item/mmi/mmi_as_oc as obj,mob/user as mob) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected!") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Beta-rhythm below acceptable level!") - return 0 - else if(occupant) - to_chat(user, "Occupant detected!") - return 0 - else if(dna && dna != mmi_as_oc.brainmob.dna.unique_enzymes) - to_chat(user, "Access denied. [name] is secured with a DNA lock.") - return 0 - - if(do_after(user, 40, target = src)) - if(!occupant) - return mmi_moved_inside(mmi_as_oc,user) - else - to_chat(user, "Occupant detected!") - else - to_chat(user, "You stop inserting the MMI.") - return 0 - -/obj/mecha/proc/mmi_moved_inside(obj/item/mmi/mmi_as_oc,mob/user) - if(mmi_as_oc && user in range(1)) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected.") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Beta-rhythm below acceptable level.") - return 0 - if(!user.unEquip(mmi_as_oc)) - to_chat(user, "\the [mmi_as_oc] is stuck to your hand, you cannot put it in \the [src]") - return 0 - var/mob/brainmob = mmi_as_oc.brainmob - brainmob.reset_perspective(src) - occupant = brainmob - brainmob.forceMove(src) //should allow relaymove - brainmob.canmove = 1 - if(istype(mmi_as_oc, /obj/item/mmi/robotic_brain)) - var/obj/item/mmi/robotic_brain/R = mmi_as_oc - if(R.imprinted_master) - to_chat(brainmob, "Your imprint to [R.imprinted_master] has been temporarily disabled. You should help the crew and not commit harm.") - mmi_as_oc.loc = src - mmi_as_oc.mecha = src - Entered(mmi_as_oc) - Move(loc) - icon_state = reset_icon() - dir = dir_in - log_message("[mmi_as_oc] moved in as pilot.") - if(!hasInternalDamage()) - to_chat(occupant, sound(nominalsound, volume=50)) - GrantActions(brainmob) - return 1 - else - return 0 - -/obj/mecha/proc/pilot_is_mmi() - var/atom/movable/mob_container - if(istype(occupant, /mob/living/carbon/brain)) - var/mob/living/carbon/brain/brain = occupant - mob_container = brain.container - if(istype(mob_container, /obj/item/mmi)) - return 1 - return 0 - -/obj/mecha/proc/pilot_mmi_hud(var/mob/living/carbon/brain/pilot) - return - -/obj/mecha/Exited(atom/movable/M, atom/newloc) - if(occupant && occupant == M) // The occupant exited the mech without calling go_out() - go_out(1, newloc) - -/obj/mecha/proc/go_out(forced, atom/newloc = loc) - if(!occupant) - return - var/atom/movable/mob_container - occupant.clear_alert("charge") - occupant.clear_alert("locked") - occupant.clear_alert("mech damage") - occupant.clear_alert("mechaport") - occupant.clear_alert("mechaport_d") - if(ishuman(occupant)) - mob_container = occupant - RemoveActions(occupant, human_occupant = 1) - else if(isbrain(occupant)) - var/mob/living/carbon/brain/brain = occupant - RemoveActions(brain) - mob_container = brain.container - else if(isAI(occupant)) - var/mob/living/silicon/ai/AI = occupant - if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. - RemoveActions(occupant) - occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. - occupant = null - return - else - if(!AI.linked_core || QDELETED(AI.linked_core)) - to_chat(AI, "Inactive core destroyed. Unable to return.") - AI.linked_core = null - return - to_chat(AI, "Returning to core...") - AI.controlled_mech = null - AI.remote_control = null - RemoveActions(occupant, 1) - mob_container = AI - newloc = get_turf(AI.linked_core) - qdel(AI.linked_core) - else - return - var/mob/living/L = occupant - occupant = null //we need it null when forceMove calls Exited(). - if(mob_container.forceMove(newloc))//ejecting mob container - log_message("[mob_container] moved out.") - L << browse(null, "window=exosuit") - - if(istype(mob_container, /obj/item/mmi)) - var/obj/item/mmi/mmi = mob_container - if(mmi.brainmob) - L.loc = mmi - L.reset_perspective() - mmi.mecha = null - mmi.update_icon() - L.canmove = 0 - if(istype(mmi, /obj/item/mmi/robotic_brain)) - var/obj/item/mmi/robotic_brain/R = mmi - if(R.imprinted_master) - to_chat(L, "Imprint re-enabled, you are once again bound to [R.imprinted_master]'s commands.") - icon_state = initial(icon_state)+"-open" - dir = dir_in - - if(L && L.client) - L.client.RemoveViewMod("mecha") - zoom_mode = FALSE - -///////////////////////// -////// Access stuff ///// -///////////////////////// - -/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) - if(!ishuman(H)) - return 0 - for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(check_access(ID, operation_req_access)) - return 1 - return 0 - - -/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) - for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(check_access(ID, internals_req_access)) - return 1 - return 0 - - -/obj/mecha/check_access(obj/item/card/id/I, list/access_list) - if(!istype(access_list)) - return 1 - if(!access_list.len) //no requirements - return 1 - if(istype(I, /obj/item/pda)) - var/obj/item/pda/pda = I - I = pda.id - if(!istype(I) || !I.access) //not ID or no access - return 0 - if(access_list==operation_req_access) - for(var/req in access_list) - if(!(req in I.access)) //doesn't have this access - return 0 - else if(access_list==internals_req_access) - for(var/req in access_list) - if(req in I.access) - return 1 - return 1 - -/////////////////////// -///// Power stuff ///// -/////////////////////// - -/obj/mecha/proc/has_charge(amount) - return (get_charge()>=amount) - -/obj/mecha/proc/get_charge() - for(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/R in equipment) - var/relay_charge = R.get_charge() - if(relay_charge) - return relay_charge - if(cell) - return max(0, cell.charge) - -/obj/mecha/proc/use_power(amount) - if(get_charge()) - cell.use(amount) - if(occupant) - update_cell() - return 1 - return 0 - -/obj/mecha/proc/give_power(amount) - if(!isnull(get_charge())) - cell.give(amount) - if(occupant) - update_cell() - return 1 - return 0 - -/obj/mecha/proc/update_cell() - if(cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - occupant.clear_alert("charge") - if(0.5 to 0.75) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 1) - if(0.25 to 0.5) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 2) - if(power_warned) - power_warned = FALSE - if(0.01 to 0.25) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 3) - if(!power_warned) - occupant << sound(lowpowersound, volume = 50) - power_warned = TRUE - else - occupant.throw_alert("charge", /obj/screen/alert/mech_emptycell) - else - occupant.throw_alert("charge", /obj/screen/alert/mech_nocell) - -/obj/mecha/proc/reset_icon() - if(initial_icon) - icon_state = initial_icon - else - icon_state = initial(icon_state) - return icon_state - -////////////////////////////////////////// -//////// Mecha global iterators //////// -////////////////////////////////////////// - -/obj/mecha/process() - process_internal_damage() - regulate_temp() - give_air() - update_huds() - -/obj/mecha/proc/process_internal_damage() - if(!internal_damage) - return - - if(internal_damage & MECHA_INT_FIRE) - if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && prob(5)) - clearInternalDamage(MECHA_INT_FIRE) - if(internal_tank) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH)) - setInternalDamage(MECHA_INT_TANK_BREACH) - - if(int_tank_air && int_tank_air.return_volume() > 0) - int_tank_air.temperature = min(6000 + T0C, cabin_air.return_temperature() + rand(10, 15)) - - if(cabin_air && cabin_air.return_volume()>0) - cabin_air.temperature = min(6000+T0C, cabin_air.return_temperature()+rand(10,15)) - if(cabin_air.return_temperature() > max_temperature/2) - take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0) - - if(internal_damage & MECHA_INT_TANK_BREACH) //remove some air from internal tank - if(internal_tank) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) - if(loc) - loc.assume_air(leaked_gas) - air_update_turf() - else - qdel(leaked_gas) - - if(internal_damage & MECHA_INT_SHORT_CIRCUIT) - if(get_charge()) - spark_system.start() - cell.charge -= min(20,cell.charge) - cell.maxcharge -= min(20,cell.maxcharge) - -/obj/mecha/proc/regulate_temp() - if(internal_damage & MECHA_INT_TEMP_CONTROL) - return - - if(cabin_air && cabin_air.return_volume() > 0) - var/delta = cabin_air.temperature - T20C - cabin_air.temperature -= max(-10, min(10, round(delta / 4, 0.1))) - -/obj/mecha/proc/give_air() - if(!internal_tank) - return - - var/datum/gas_mixture/tank_air = internal_tank.return_air() - - var/release_pressure = internal_tank_valve - var/cabin_pressure = cabin_air.return_pressure() - var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) - var/transfer_moles = 0 - if(pressure_delta > 0) //cabin pressure lower than release pressure - if(tank_air.return_temperature() > 0) - transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) - cabin_air.merge(removed) - else if(pressure_delta < 0) //cabin pressure higher than release pressure - var/datum/gas_mixture/t_air = return_air() - pressure_delta = cabin_pressure - release_pressure - if(t_air) - pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) - if(pressure_delta > 0) //if location pressure is lower than cabin pressure - transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) - if(t_air) - t_air.merge(removed) - else //just delete the cabin gas, we're in space or some shit - qdel(removed) - -/obj/mecha/proc/update_huds() - diag_hud_set_mechhealth() - diag_hud_set_mechcell() - diag_hud_set_mechstat() - diag_hud_set_mechtracking() - - -/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) - flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30) - -/obj/mecha/update_remote_sight(mob/living/user) - if(occupant_sight_flags) - if(user == occupant) - user.sight |= occupant_sight_flags - - ..() - -/obj/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) - if(!no_effect) - if(selected) - used_item = selected - else if(!visual_effect_icon) - visual_effect_icon = ATTACK_EFFECT_SMASH - if(damtype == BURN) - visual_effect_icon = ATTACK_EFFECT_MECHFIRE - else if(damtype == TOX) - visual_effect_icon = ATTACK_EFFECT_MECHTOXIN - ..() - -/obj/mecha/obj_destruction() - if(wreckage) - var/mob/living/silicon/ai/AI - if(isAI(occupant)) - AI = occupant - occupant = null - var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI) - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - if(E.salvageable && prob(30)) - WR.crowbar_salvage += E - E.detach(WR) //detaches from src into WR - E.equip_ready = 1 - else - E.detach(loc) - qdel(E) - if(cell) - WR.crowbar_salvage += cell - cell.forceMove(WR) - cell.charge = rand(0, cell.charge) - cell = null - if(internal_tank) - WR.crowbar_salvage += internal_tank - internal_tank.forceMove(WR) - cell = null - . = ..() - -/obj/mecha/CtrlClick(mob/living/L) - if(occupant != L || !istype(L)) - return ..() - - var/list/choices = list("Cancel / No Change" = mutable_appearance(icon = 'icons/mob/screen_gen.dmi', icon_state = "x")) - var/list/choices_to_refs = list() - - for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) - if(!MT.selectable || selected == MT) - continue - var/mutable_appearance/clean/MA = new(MT) - choices[MT.name] = MA - choices_to_refs[MT.name] = MT - - var/choice = show_radial_menu(L, src, choices, radius = 48, custom_check = CALLBACK(src, .proc/check_menu, L)) - if(!check_menu(L) || choice == "Cancel / No Change") - return - - var/obj/item/mecha_parts/mecha_equipment/new_sel = LAZYACCESS(choices_to_refs, choice) - if(istype(new_sel)) - selected = new_sel - occupant_message("You switch to [selected].") - visible_message("[src] raises [selected]") - send_byjax(occupant, "exosuit.browser", "eq_list", get_equipment_list()) - -/obj/mecha/proc/check_menu(mob/living/L) - if(L != occupant || !istype(L)) - return FALSE - if(L.incapacitated()) - return FALSE - return TRUE \ No newline at end of file +/obj/mecha/mech_melee_attack(obj/mecha/M) + if(!has_charge(melee_energy_drain)) + return 0 + use_power(melee_energy_drain) + if(M.damtype == BRUTE || M.damtype == BURN) + add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") + . = ..() + +/obj/mecha/emag_act(mob/user) + to_chat(user, "[src]'s ID slot rejects the card.") + return + + +///////////////////////////////////// +//////////// AI piloting //////////// +///////////////////////////////////// + +/obj/mecha/attack_ai(mob/living/silicon/ai/user) + if(!isAI(user)) + return + //Allows the Malf to scan a mech's status and loadout, helping it to decide if it is a worthy chariot. + if(user.can_dominate_mechs) + examine(user) //Get diagnostic information! + for(var/obj/item/mecha_parts/mecha_tracking/B in trackers) + to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") + to_chat(user, "[B.get_mecha_info_text()]") + break + //Nothing like a big, red link to make the player feel powerful! + to_chat(user, "ASSUME DIRECT CONTROL?
") + else + examine(user) + if(occupant) + user << "This exosuit has a pilot and cannot be controlled." + return + var/can_control_mech = FALSE + for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers) + can_control_mech = TRUE + to_chat(user, "[bicon(src)] Status of [name]:\n\ + [A.get_mecha_info_text()]") + break + if(!can_control_mech) + to_chat(user, "You cannot control exosuits without AI control beacons installed.") + return + to_chat(user, "Take control of exosuit?
") + +/obj/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) + if(!..()) + return + + //Transfer from core or card to mech. Proc is called by mech. + switch(interaction) + if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card. + if(!maint_access) //Mech must be in maint mode to allow carding. + to_chat(user, "[name] must have maintenance protocols active in order to allow a transfer.") + return + AI = occupant + if(!AI || !isAI(occupant)) //Mech does not have an AI for a pilot + to_chat(user, "No AI detected in the [name] onboard computer.") + return + if(AI.mind.special_role) //Malf AIs cannot leave mechs. Except through death. + to_chat(user, "ACCESS DENIED.") + return + AI.aiRestorePowerRoutine = 0//So the AI initially has power. + AI.control_disabled = 1 + AI.aiRadio.disabledAi = 1 + AI.loc = card + occupant = null + AI.controlled_mech = null + AI.remote_control = null + icon_state = initial(icon_state)+"-open" + to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") + + if(AI_MECH_HACK) //Called by AIs on the mech + AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) + if(AI.can_dominate_mechs) + if(occupant) //Oh, I am sorry, were you using that? + to_chat(AI, "Pilot detected! Forced ejection initiated!") + to_chat(occupant, "You have been forcibly ejected!") + go_out(1) //IT IS MINE, NOW. SUCK IT, RD! + ai_enter_mech(AI, interaction) + + if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech. + AI = locate(/mob/living/silicon/ai) in card + if(!AI) + to_chat(user, "There is no AI currently installed on this device.") + return + else if(AI.stat || !AI.client) + to_chat(user, "[AI.name] is currently unresponsive, and cannot be uploaded.") + return + else if(occupant || dna) //Normal AIs cannot steal mechs! + to_chat(user, "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"].") + return + AI.control_disabled = 0 + AI.aiRadio.disabledAi = 0 + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") + ai_enter_mech(AI, interaction) + +//Hack and From Card interactions share some code, so leave that here for both to use. +/obj/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction) + AI.aiRestorePowerRoutine = 0 + AI.loc = src + occupant = AI + icon_state = initial(icon_state) + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + if(!hasInternalDamage()) + occupant << sound(nominalsound, volume = 50) + AI.cancel_camera() + AI.controlled_mech = src + AI.remote_control = src + AI.canmove = 1 //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow. + AI.can_shunt = 0 //ONE AI ENTERS. NO AI LEAVES. + to_chat(AI, "[AI.can_dominate_mechs ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ + : "You have been uploaded to a mech's onboard computer."]") + to_chat(AI, "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.") + if(interaction == AI_TRANS_FROM_CARD) + GrantActions(AI, FALSE) + else + GrantActions(AI, !AI.can_dominate_mechs) + +///////////////////////////////////// +//////// Atmospheric stuff //////// +///////////////////////////////////// + +/obj/mecha/proc/get_turf_air() + var/turf/T = get_turf(src) + if(T) + . = T.return_air() + +/obj/mecha/remove_air(amount) + if(use_internal_tank) + return cabin_air.remove(amount) + else + var/turf/T = get_turf(src) + if(T) + return T.remove_air(amount) + +/obj/mecha/return_air() + if(use_internal_tank) + return cabin_air + return get_turf_air() + +/obj/mecha/proc/return_pressure() + var/datum/gas_mixture/t_air = return_air() + if(t_air) + . = t_air.return_pressure() + +//skytodo: //No idea what you want me to do here, mate. +/obj/mecha/proc/return_temperature() + var/datum/gas_mixture/t_air = return_air() + if(t_air) + . = t_air.return_temperature() + +/obj/mecha/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !istype(new_port) || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(new_port.loc != loc) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + connected_port.parent.reconcile_air() + + if(occupant) + occupant.clear_alert("mechaport") + occupant.throw_alert("mechaport_d", /obj/screen/alert/mech_port_disconnect) + + log_message("Connected to gas port.") + return 1 + +/obj/mecha/proc/disconnect() + if(!connected_port) + return 0 + + connected_port.connected_device = null + connected_port = null + log_message("Disconnected from gas port.") + if(occupant) + occupant.clear_alert("mechaport_d") + return 1 + +/obj/mecha/portableConnectorReturnAir() + return internal_tank.return_air() + +/obj/mecha/proc/toggle_lights() + lights = !lights + if(lights) + set_light(light_range + lights_power) + else + set_light(light_range - lights_power) + occupant_message("Toggled lights [lights ? "on" : "off"].") + log_message("Toggled lights [lights ? "on" : "off"].") + +/obj/mecha/proc/toggle_internal_tank() + use_internal_tank = !use_internal_tank + occupant_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") + log_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") + +/obj/mecha/MouseDrop_T(mob/M, mob/user) + if(user.incapacitated()) + return + if(user != M) + return + log_message("[user] tries to move in.") + if(occupant) + to_chat(user, "The [src] is already occupied!") + log_append_to_last("Permission denied.") + return + var/passed + if(dna) + if(ishuman(user)) + if(user.dna.unique_enzymes == dna) + passed = 1 + else if(operation_allowed(user)) + passed = 1 + if(!passed) + to_chat(user, "Access denied.") + log_append_to_last("Permission denied.") + return + if(user.buckled) + to_chat(user, "You are currently buckled and cannot move.") + log_append_to_last("Permission denied.") + return + if(user.has_buckled_mobs()) //mob attached to us + to_chat(user, "You can't enter the exosuit with other creatures attached to you!") + return + + visible_message("[user] starts to climb into [src]") + + if(do_after(user, 40, target = src)) + if(obj_integrity <= 0) + to_chat(user, "You cannot get in the [name], it has been destroyed!") + else if(occupant) + to_chat(user, "[occupant] was faster! Try better next time, loser.") + else if(user.buckled) + to_chat(user, "You can't enter the exosuit while buckled.") + else if(user.has_buckled_mobs()) + to_chat(user, "You can't enter the exosuit with other creatures attached to you!") + else + moved_inside(user) + else + to_chat(user, "You stop entering the exosuit!") + +/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) + if(H && H.client && H in range(1)) + occupant = H + H.stop_pulling() + H.forceMove(src) + H.reset_perspective(src) + add_fingerprint(H) + GrantActions(H, human_occupant = 1) + forceMove(loc) + log_append_to_last("[H] moved in as pilot.") + icon_state = reset_icon() + dir = dir_in + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + if(!activated) + occupant << sound(longactivationsound, volume = 50) + activated = TRUE + else if(!hasInternalDamage()) + occupant << sound(nominalsound, volume = 50) + if(state) + H.throw_alert("locked", /obj/screen/alert/mech_maintenance) + return 1 + else + return 0 + +/obj/mecha/proc/mmi_move_inside(var/obj/item/mmi/mmi_as_oc as obj,mob/user as mob) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected!") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Beta-rhythm below acceptable level!") + return 0 + else if(occupant) + to_chat(user, "Occupant detected!") + return 0 + else if(dna && dna != mmi_as_oc.brainmob.dna.unique_enzymes) + to_chat(user, "Access denied. [name] is secured with a DNA lock.") + return 0 + + if(do_after(user, 40, target = src)) + if(!occupant) + return mmi_moved_inside(mmi_as_oc,user) + else + to_chat(user, "Occupant detected!") + else + to_chat(user, "You stop inserting the MMI.") + return 0 + +/obj/mecha/proc/mmi_moved_inside(obj/item/mmi/mmi_as_oc,mob/user) + if(mmi_as_oc && user in range(1)) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected.") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Beta-rhythm below acceptable level.") + return 0 + if(!user.unEquip(mmi_as_oc)) + to_chat(user, "\the [mmi_as_oc] is stuck to your hand, you cannot put it in \the [src]") + return 0 + var/mob/brainmob = mmi_as_oc.brainmob + brainmob.reset_perspective(src) + occupant = brainmob + brainmob.forceMove(src) //should allow relaymove + brainmob.canmove = 1 + if(istype(mmi_as_oc, /obj/item/mmi/robotic_brain)) + var/obj/item/mmi/robotic_brain/R = mmi_as_oc + if(R.imprinted_master) + to_chat(brainmob, "Your imprint to [R.imprinted_master] has been temporarily disabled. You should help the crew and not commit harm.") + mmi_as_oc.loc = src + mmi_as_oc.mecha = src + Entered(mmi_as_oc) + Move(loc) + icon_state = reset_icon() + dir = dir_in + log_message("[mmi_as_oc] moved in as pilot.") + if(!hasInternalDamage()) + to_chat(occupant, sound(nominalsound, volume=50)) + GrantActions(brainmob) + return 1 + else + return 0 + +/obj/mecha/proc/pilot_is_mmi() + var/atom/movable/mob_container + if(istype(occupant, /mob/living/carbon/brain)) + var/mob/living/carbon/brain/brain = occupant + mob_container = brain.container + if(istype(mob_container, /obj/item/mmi)) + return 1 + return 0 + +/obj/mecha/proc/pilot_mmi_hud(var/mob/living/carbon/brain/pilot) + return + +/obj/mecha/Exited(atom/movable/M, atom/newloc) + if(occupant && occupant == M) // The occupant exited the mech without calling go_out() + go_out(1, newloc) + +/obj/mecha/proc/go_out(forced, atom/newloc = loc) + if(!occupant) + return + var/atom/movable/mob_container + occupant.clear_alert("charge") + occupant.clear_alert("locked") + occupant.clear_alert("mech damage") + occupant.clear_alert("mechaport") + occupant.clear_alert("mechaport_d") + if(ishuman(occupant)) + mob_container = occupant + RemoveActions(occupant, human_occupant = 1) + else if(isbrain(occupant)) + var/mob/living/carbon/brain/brain = occupant + RemoveActions(brain) + mob_container = brain.container + else if(isAI(occupant)) + var/mob/living/silicon/ai/AI = occupant + if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. + RemoveActions(occupant) + occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. + occupant = null + return + else + if(!AI.linked_core || QDELETED(AI.linked_core)) + to_chat(AI, "Inactive core destroyed. Unable to return.") + AI.linked_core = null + return + to_chat(AI, "Returning to core...") + AI.controlled_mech = null + AI.remote_control = null + RemoveActions(occupant, 1) + mob_container = AI + newloc = get_turf(AI.linked_core) + qdel(AI.linked_core) + else + return + var/mob/living/L = occupant + occupant = null //we need it null when forceMove calls Exited(). + if(mob_container.forceMove(newloc))//ejecting mob container + log_message("[mob_container] moved out.") + L << browse(null, "window=exosuit") + + if(istype(mob_container, /obj/item/mmi)) + var/obj/item/mmi/mmi = mob_container + if(mmi.brainmob) + L.loc = mmi + L.reset_perspective() + mmi.mecha = null + mmi.update_icon() + L.canmove = 0 + if(istype(mmi, /obj/item/mmi/robotic_brain)) + var/obj/item/mmi/robotic_brain/R = mmi + if(R.imprinted_master) + to_chat(L, "Imprint re-enabled, you are once again bound to [R.imprinted_master]'s commands.") + icon_state = initial(icon_state)+"-open" + dir = dir_in + + if(L && L.client) + L.client.RemoveViewMod("mecha") + zoom_mode = FALSE + +///////////////////////// +////// Access stuff ///// +///////////////////////// + +/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) + if(!ishuman(H)) + return 0 + for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(check_access(ID, operation_req_access)) + return 1 + return 0 + + +/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) + for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(check_access(ID, internals_req_access)) + return 1 + return 0 + + +/obj/mecha/check_access(obj/item/card/id/I, list/access_list) + if(!istype(access_list)) + return 1 + if(!access_list.len) //no requirements + return 1 + if(istype(I, /obj/item/pda)) + var/obj/item/pda/pda = I + I = pda.id + if(!istype(I) || !I.access) //not ID or no access + return 0 + if(access_list==operation_req_access) + for(var/req in access_list) + if(!(req in I.access)) //doesn't have this access + return 0 + else if(access_list==internals_req_access) + for(var/req in access_list) + if(req in I.access) + return 1 + return 1 + +/////////////////////// +///// Power stuff ///// +/////////////////////// + +/obj/mecha/proc/has_charge(amount) + return (get_charge()>=amount) + +/obj/mecha/proc/get_charge() + for(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/R in equipment) + var/relay_charge = R.get_charge() + if(relay_charge) + return relay_charge + if(cell) + return max(0, cell.charge) + +/obj/mecha/proc/use_power(amount) + if(get_charge()) + cell.use(amount) + if(occupant) + update_cell() + return 1 + return 0 + +/obj/mecha/proc/give_power(amount) + if(!isnull(get_charge())) + cell.give(amount) + if(occupant) + update_cell() + return 1 + return 0 + +/obj/mecha/proc/update_cell() + if(cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + occupant.clear_alert("charge") + if(0.5 to 0.75) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 1) + if(0.25 to 0.5) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 2) + if(power_warned) + power_warned = FALSE + if(0.01 to 0.25) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 3) + if(!power_warned) + occupant << sound(lowpowersound, volume = 50) + power_warned = TRUE + else + occupant.throw_alert("charge", /obj/screen/alert/mech_emptycell) + else + occupant.throw_alert("charge", /obj/screen/alert/mech_nocell) + +/obj/mecha/proc/reset_icon() + if(initial_icon) + icon_state = initial_icon + else + icon_state = initial(icon_state) + return icon_state + +////////////////////////////////////////// +//////// Mecha global iterators //////// +////////////////////////////////////////// + +/obj/mecha/process() + process_internal_damage() + regulate_temp() + give_air() + update_huds() + +/obj/mecha/proc/process_internal_damage() + if(!internal_damage) + return + + if(internal_damage & MECHA_INT_FIRE) + if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && prob(5)) + clearInternalDamage(MECHA_INT_FIRE) + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH)) + setInternalDamage(MECHA_INT_TANK_BREACH) + + if(int_tank_air && int_tank_air.return_volume() > 0) + int_tank_air.temperature = min(6000 + T0C, cabin_air.return_temperature() + rand(10, 15)) + + if(cabin_air && cabin_air.return_volume()>0) + cabin_air.temperature = min(6000+T0C, cabin_air.return_temperature()+rand(10,15)) + if(cabin_air.return_temperature() > max_temperature/2) + take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0) + + if(internal_damage & MECHA_INT_TANK_BREACH) //remove some air from internal tank + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) + if(loc) + loc.assume_air(leaked_gas) + air_update_turf() + else + qdel(leaked_gas) + + if(internal_damage & MECHA_INT_SHORT_CIRCUIT) + if(get_charge()) + spark_system.start() + cell.charge -= min(20,cell.charge) + cell.maxcharge -= min(20,cell.maxcharge) + +/obj/mecha/proc/regulate_temp() + if(internal_damage & MECHA_INT_TEMP_CONTROL) + return + + if(cabin_air && cabin_air.return_volume() > 0) + var/delta = cabin_air.temperature - T20C + cabin_air.temperature -= max(-10, min(10, round(delta / 4, 0.1))) + +/obj/mecha/proc/give_air() + if(!internal_tank) + return + + var/datum/gas_mixture/tank_air = internal_tank.return_air() + + var/release_pressure = internal_tank_valve + var/cabin_pressure = cabin_air.return_pressure() + var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) + var/transfer_moles = 0 + if(pressure_delta > 0) //cabin pressure lower than release pressure + if(tank_air.return_temperature() > 0) + transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) + cabin_air.merge(removed) + else if(pressure_delta < 0) //cabin pressure higher than release pressure + var/datum/gas_mixture/t_air = return_air() + pressure_delta = cabin_pressure - release_pressure + if(t_air) + pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) + if(pressure_delta > 0) //if location pressure is lower than cabin pressure + transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) + if(t_air) + t_air.merge(removed) + else //just delete the cabin gas, we're in space or some shit + qdel(removed) + +/obj/mecha/proc/update_huds() + diag_hud_set_mechhealth() + diag_hud_set_mechcell() + diag_hud_set_mechstat() + diag_hud_set_mechtracking() + + +/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) + flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30) + +/obj/mecha/update_remote_sight(mob/living/user) + if(occupant_sight_flags) + if(user == occupant) + user.sight |= occupant_sight_flags + + ..() + +/obj/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) + if(!no_effect) + if(selected) + used_item = selected + else if(!visual_effect_icon) + visual_effect_icon = ATTACK_EFFECT_SMASH + if(damtype == BURN) + visual_effect_icon = ATTACK_EFFECT_MECHFIRE + else if(damtype == TOX) + visual_effect_icon = ATTACK_EFFECT_MECHTOXIN + ..() + +/obj/mecha/obj_destruction() + if(wreckage) + var/mob/living/silicon/ai/AI + if(isAI(occupant)) + AI = occupant + occupant = null + var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI) + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + if(E.salvageable && prob(30)) + WR.crowbar_salvage += E + E.detach(WR) //detaches from src into WR + E.equip_ready = 1 + else + E.detach(loc) + qdel(E) + if(cell) + WR.crowbar_salvage += cell + cell.forceMove(WR) + cell.charge = rand(0, cell.charge) + cell = null + if(internal_tank) + WR.crowbar_salvage += internal_tank + internal_tank.forceMove(WR) + cell = null + . = ..() + +/obj/mecha/CtrlClick(mob/living/L) + if(occupant != L || !istype(L)) + return ..() + + var/list/choices = list("Cancel / No Change" = mutable_appearance(icon = 'icons/mob/screen_gen.dmi', icon_state = "x")) + var/list/choices_to_refs = list() + + for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) + if(!MT.selectable || selected == MT) + continue + var/mutable_appearance/clean/MA = new(MT) + choices[MT.name] = MA + choices_to_refs[MT.name] = MT + + var/choice = show_radial_menu(L, src, choices, radius = 48, custom_check = CALLBACK(src, .proc/check_menu, L)) + if(!check_menu(L) || choice == "Cancel / No Change") + return + + var/obj/item/mecha_parts/mecha_equipment/new_sel = LAZYACCESS(choices_to_refs, choice) + if(istype(new_sel)) + selected = new_sel + occupant_message("You switch to [selected].") + visible_message("[src] raises [selected]") + send_byjax(occupant, "exosuit.browser", "eq_list", get_equipment_list()) + +/obj/mecha/proc/check_menu(mob/living/L) + if(L != occupant || !istype(L)) + return FALSE + if(L.incapacitated()) + return FALSE + return TRUE diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 263dcb955df..5cbd562a002 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -1,283 +1,283 @@ -/obj/item/flash - name = "flash" - desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production." - icon = 'icons/obj/device.dmi' - icon_state = "flash" - item_state = "flashtool" //looks exactly like a flash (and nothing like a flashbang) - throwforce = 0 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - flags = CONDUCT - materials = list(MAT_METAL = 300, MAT_GLASS = 300) - origin_tech = "magnets=2;combat=1" - - var/times_used = 0 //Number of times it's been used. - var/broken = 0 //Is the flash burnt out? - var/last_used = 0 //last world.time it was used. - var/battery_panel = 0 //whether the flash can be modified with a cell or not - var/overcharged = 0 //if overcharged the flash will set people on fire then immediately burn out (does so even if it doesn't blind them). - var/can_overcharge = TRUE //set this to FALSE if you don't want your flash to be overcharge capable - var/use_sound = 'sound/weapons/flash.ogg' - -/obj/item/flash/proc/clown_check(mob/user) - if(user && (CLUMSY in user.mutations) && prob(50)) - flash_carbon(user, user, 15, 0) - return 0 - return 1 - -/obj/item/flash/attackby(obj/item/W, mob/user, params) - if(can_overcharge) - if(istype(W, /obj/item/screwdriver)) - if(battery_panel) - to_chat(user, "You close the battery compartment on the [src].") - battery_panel = 0 - else - to_chat(user, "You open the battery compartment on the [src].") - battery_panel = 1 - if(battery_panel && !overcharged) - if(istype(W, /obj/item/stock_parts/cell)) - to_chat(user, "You jam the cell into battery compartment on the [src].") - qdel(W) - overcharged = 1 - overlays += "overcharge" - -/obj/item/flash/random/New() - ..() - if(prob(25)) - broken = 1 - icon_state = "[initial(icon_state)]burnt" - -/obj/item/flash/proc/burn_out() //Made so you can override it if you want to have an invincible flash from R&D or something. - broken = 1 - icon_state = "[initial(icon_state)]burnt" - visible_message("The [src.name] burns out!") - - -/obj/item/flash/proc/flash_recharge(var/mob/user) - if(prob(times_used * 2)) //if you use it 5 times in a minute it has a 10% chance to break! - burn_out() - return 0 - - var/deciseconds_passed = world.time - last_used - for(var/seconds = deciseconds_passed/10, seconds>=10, seconds-=10) //get 1 charge every 10 seconds - times_used-- - - last_used = world.time - times_used = max(0, times_used) //sanity - - -/obj/item/flash/proc/try_use_flash(var/mob/user = null) - flash_recharge(user) - - if(broken) - return 0 - - playsound(src.loc, use_sound, 100, 1) - flick("[initial(icon_state)]2", src) - times_used++ - - if(user && !clown_check(user)) - return 0 - - return 1 - - -/obj/item/flash/proc/flash_carbon(var/mob/living/carbon/M, var/mob/user = null, var/power = 5, targeted = 1) - if(user) - add_attack_logs(user, M, "Flashed with [src]") - if(targeted) - if(M.weakeyes) - M.Weaken(3) //quick weaken bypasses eye protection but has no eye flash - if(M.flash_eyes(1, 1)) - M.AdjustConfused(power) - terrible_conversion_proc(M, user) - M.Stun(1) - visible_message("[user] blinds [M] with the flash!") - to_chat(user, "You blind [M] with the flash!") - to_chat(M, "[user] blinds you with the flash!") - if(M.weakeyes) - M.Stun(2) - M.visible_message("[M] gasps and shields [M.p_their()] eyes!", "You gasp and shields your eyes!") - else - visible_message("[user] fails to blind [M] with the flash!") - to_chat(user, "You fail to blind [M] with the flash!") - to_chat(M, "[user] fails to blind you with the flash!") - return - - if(M.flash_eyes()) - M.AdjustConfused(power) - -/obj/item/flash/attack(mob/living/M, mob/user) - if(!try_use_flash(user)) - return 0 - - if(iscarbon(M)) - flash_carbon(M, user, 5, 1) - if(overcharged) - M.adjust_fire_stacks(6) - M.IgniteMob() - burn_out() - return 1 - - else if(issilicon(M)) - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(R.module) // Perhaps they didn't choose a module yet - for(var/obj/item/borg/combat/shield/S in R.module.modules) - if(R.activated(S)) - add_attack_logs(user, M, "Flashed with [src]") - user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but is blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by [M.p_their()] shield!") - return 1 - add_attack_logs(user, M, "Flashed with [src]") - if(M.flash_eyes(affect_silicon = 1)) - M.Weaken(rand(5,10)) - user.visible_message("[user] overloads [M]'s sensors with the [src.name]!", "You overload [M]'s sensors with the [src.name]!") - return 1 - - user.visible_message("[user] fails to blind [M] with the [src.name]!", "You fail to blind [M] with the [src.name]!") - - -/obj/item/flash/attack_self(mob/living/carbon/user, flag = 0, emp = 0) - if(!try_use_flash(user)) - return 0 - user.visible_message("[user]'s [src.name] emits a blinding light!", "Your [src.name] emits a blinding light!") - for(var/mob/living/carbon/M in oviewers(3, null)) - flash_carbon(M, user, 3, 0) - - -/obj/item/flash/emp_act(severity) - if(!try_use_flash()) - return 0 - for(var/mob/living/carbon/M in viewers(3, null)) - flash_carbon(M, null, 10, 0) - burn_out() - ..() - - -/obj/item/flash/proc/terrible_conversion_proc(mob/M, mob/user) - if(ishuman(M) && ishuman(user) && M.stat != DEAD) - if(user.mind && (user.mind in SSticker.mode.head_revolutionaries)) - if(M.client) - if(M.stat == CONSCIOUS) - M.mind_initialize() //give them a mind datum if they don't have one. - var/resisted - if(!ismindshielded(M)) - if(user.mind in SSticker.mode.head_revolutionaries) - if(SSticker.mode.add_revolutionary(M.mind)) - times_used -- //Flashes less likely to burn out for headrevs when used for conversion - else - resisted = 1 - else - resisted = 1 - - if(resisted) - to_chat(user, "This mind seems resistant to the [name]!") - else - to_chat(user, "They must be conscious before you can convert [M.p_them()]!") - else - to_chat(user, "This mind is so vacant that it is not susceptible to influence!") - - -/obj/item/flash/cyborg - origin_tech = null - -/obj/item/flash/cyborg/attack(mob/living/M, mob/user) - ..() - new /obj/effect/temp_visual/borgflash(get_turf(src)) - -/obj/item/flash/cyborg/attack_self(mob/user) - ..() - new /obj/effect/temp_visual/borgflash(get_turf(src)) - -/obj/item/flash/cameraflash - name = "camera" - icon = 'icons/obj/items.dmi' - desc = "A polaroid camera. 10 photos left." - icon_state = "camera" - item_state = "electropack" //spelling, a coders worst enemy. This part gave me trouble for a while. - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - can_overcharge = FALSE - var/flash_max_charges = 5 - var/flash_cur_charges = 5 - var/charge_tick = 0 - use_sound = 'sound/items/polaroid1.ogg' - -/obj/item/flash/cameraflash/burn_out() //stops from burning out - return - -/obj/item/flash/cameraflash/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/item/flash/cameraflash/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/flash/cameraflash/process() //this and the two parts above are part of the charge system. - charge_tick++ - if(charge_tick < 10) - return FALSE - charge_tick = 0 - flash_cur_charges = min(flash_cur_charges+1, flash_max_charges) - return TRUE - -/obj/item/flash/cameraflash/attack(mob/living/M, mob/user) - if(flash_cur_charges > 0) - flash_cur_charges -= 1 - to_chat(user, "[src] now has [flash_cur_charges] charge\s.") - ..() - else - to_chat(user, "\The [src] needs time to recharge!") - return - -/obj/item/flash/cameraflash/attack_self(mob/living/carbon/user, flag = 0) - if(flash_cur_charges > 0) - flash_cur_charges -= 1 - to_chat(user, "[src] now has [flash_cur_charges] charge\s.") - ..() - else - to_chat(user, "\The [src] needs time to recharge!") - return - -/obj/item/flash/memorizer - name = "memorizer" - desc = "If you see this, you're not likely to remember it any time soon." - icon_state = "memorizer" - item_state = "nullrod" - -/obj/item/flash/armimplant - name = "photon projector" - desc = "A high-powered photon projector implant normally used for lighting purposes, but also doubles as a flashbulb weapon. Self-repair protocols fix the flashbulb if it ever burns out." - var/flashcd = 20 - var/overheat = 0 - var/obj/item/organ/internal/cyberimp/arm/flash/I = null - -/obj/item/flash/armimplant/Destroy() - I = null - return ..() - -/obj/item/flash/armimplant/burn_out() - if(I && I.owner) - to_chat(I.owner, "Your photon projector implant overheats and deactivates!") - I.Retract() - overheat = FALSE - addtimer(CALLBACK(src, .proc/cooldown), flashcd * 2) - -/obj/item/flash/armimplant/try_use_flash(mob/user = null) - if(overheat) - if(I && I.owner) - to_chat(I.owner, "Your photon projector is running too hot to be used again so quickly!") - return FALSE - overheat = TRUE - addtimer(CALLBACK(src, .proc/cooldown), flashcd) - playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) - update_icon(1) - return TRUE - -/obj/item/flash/armimplant/proc/cooldown() - overheat = FALSE - - -/obj/item/flash/synthetic //just a regular flash now +/obj/item/flash + name = "flash" + desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production." + icon = 'icons/obj/device.dmi' + icon_state = "flash" + item_state = "flashtool" //looks exactly like a flash (and nothing like a flashbang) + throwforce = 0 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + flags = CONDUCT + materials = list(MAT_METAL = 300, MAT_GLASS = 300) + origin_tech = "magnets=2;combat=1" + + var/times_used = 0 //Number of times it's been used. + var/broken = 0 //Is the flash burnt out? + var/last_used = 0 //last world.time it was used. + var/battery_panel = 0 //whether the flash can be modified with a cell or not + var/overcharged = 0 //if overcharged the flash will set people on fire then immediately burn out (does so even if it doesn't blind them). + var/can_overcharge = TRUE //set this to FALSE if you don't want your flash to be overcharge capable + var/use_sound = 'sound/weapons/flash.ogg' + +/obj/item/flash/proc/clown_check(mob/user) + if(user && (CLUMSY in user.mutations) && prob(50)) + flash_carbon(user, user, 15, 0) + return 0 + return 1 + +/obj/item/flash/attackby(obj/item/W, mob/user, params) + if(can_overcharge) + if(istype(W, /obj/item/screwdriver)) + if(battery_panel) + to_chat(user, "You close the battery compartment on the [src].") + battery_panel = 0 + else + to_chat(user, "You open the battery compartment on the [src].") + battery_panel = 1 + if(battery_panel && !overcharged) + if(istype(W, /obj/item/stock_parts/cell)) + to_chat(user, "You jam the cell into battery compartment on the [src].") + qdel(W) + overcharged = 1 + overlays += "overcharge" + +/obj/item/flash/random/New() + ..() + if(prob(25)) + broken = 1 + icon_state = "[initial(icon_state)]burnt" + +/obj/item/flash/proc/burn_out() //Made so you can override it if you want to have an invincible flash from R&D or something. + broken = 1 + icon_state = "[initial(icon_state)]burnt" + visible_message("The [src.name] burns out!") + + +/obj/item/flash/proc/flash_recharge(var/mob/user) + if(prob(times_used * 2)) //if you use it 5 times in a minute it has a 10% chance to break! + burn_out() + return 0 + + var/deciseconds_passed = world.time - last_used + for(var/seconds = deciseconds_passed/10, seconds>=10, seconds-=10) //get 1 charge every 10 seconds + times_used-- + + last_used = world.time + times_used = max(0, times_used) //sanity + + +/obj/item/flash/proc/try_use_flash(var/mob/user = null) + flash_recharge(user) + + if(broken) + return 0 + + playsound(src.loc, use_sound, 100, 1) + flick("[initial(icon_state)]2", src) + times_used++ + + if(user && !clown_check(user)) + return 0 + + return 1 + + +/obj/item/flash/proc/flash_carbon(var/mob/living/carbon/M, var/mob/user = null, var/power = 5, targeted = 1) + if(user) + add_attack_logs(user, M, "Flashed with [src]") + if(targeted) + if(M.weakeyes) + M.Weaken(3) //quick weaken bypasses eye protection but has no eye flash + if(M.flash_eyes(1, 1)) + M.AdjustConfused(power) + terrible_conversion_proc(M, user) + M.Stun(1) + visible_message("[user] blinds [M] with the flash!") + to_chat(user, "You blind [M] with the flash!") + to_chat(M, "[user] blinds you with the flash!") + if(M.weakeyes) + M.Stun(2) + M.visible_message("[M] gasps and shields [M.p_their()] eyes!", "You gasp and shields your eyes!") + else + visible_message("[user] fails to blind [M] with the flash!") + to_chat(user, "You fail to blind [M] with the flash!") + to_chat(M, "[user] fails to blind you with the flash!") + return + + if(M.flash_eyes()) + M.AdjustConfused(power) + +/obj/item/flash/attack(mob/living/M, mob/user) + if(!try_use_flash(user)) + return 0 + + if(iscarbon(M)) + flash_carbon(M, user, 5, 1) + if(overcharged) + M.adjust_fire_stacks(6) + M.IgniteMob() + burn_out() + return 1 + + else if(issilicon(M)) + if(isrobot(M)) + var/mob/living/silicon/robot/R = M + if(R.module) // Perhaps they didn't choose a module yet + for(var/obj/item/borg/combat/shield/S in R.module.modules) + if(R.activated(S)) + add_attack_logs(user, M, "Flashed with [src]") + user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but is blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by [M.p_their()] shield!") + return 1 + add_attack_logs(user, M, "Flashed with [src]") + if(M.flash_eyes(affect_silicon = 1)) + M.Weaken(rand(5,10)) + user.visible_message("[user] overloads [M]'s sensors with the [src.name]!", "You overload [M]'s sensors with the [src.name]!") + return 1 + + user.visible_message("[user] fails to blind [M] with the [src.name]!", "You fail to blind [M] with the [src.name]!") + + +/obj/item/flash/attack_self(mob/living/carbon/user, flag = 0, emp = 0) + if(!try_use_flash(user)) + return 0 + user.visible_message("[user]'s [src.name] emits a blinding light!", "Your [src.name] emits a blinding light!") + for(var/mob/living/carbon/M in oviewers(3, null)) + flash_carbon(M, user, 3, 0) + + +/obj/item/flash/emp_act(severity) + if(!try_use_flash()) + return 0 + for(var/mob/living/carbon/M in viewers(3, null)) + flash_carbon(M, null, 10, 0) + burn_out() + ..() + + +/obj/item/flash/proc/terrible_conversion_proc(mob/M, mob/user) + if(ishuman(M) && ishuman(user) && M.stat != DEAD) + if(user.mind && (user.mind in SSticker.mode.head_revolutionaries)) + if(M.client) + if(M.stat == CONSCIOUS) + M.mind_initialize() //give them a mind datum if they don't have one. + var/resisted + if(!ismindshielded(M)) + if(user.mind in SSticker.mode.head_revolutionaries) + if(SSticker.mode.add_revolutionary(M.mind)) + times_used -- //Flashes less likely to burn out for headrevs when used for conversion + else + resisted = 1 + else + resisted = 1 + + if(resisted) + to_chat(user, "This mind seems resistant to the [name]!") + else + to_chat(user, "They must be conscious before you can convert [M.p_them()]!") + else + to_chat(user, "This mind is so vacant that it is not susceptible to influence!") + + +/obj/item/flash/cyborg + origin_tech = null + +/obj/item/flash/cyborg/attack(mob/living/M, mob/user) + ..() + new /obj/effect/temp_visual/borgflash(get_turf(src)) + +/obj/item/flash/cyborg/attack_self(mob/user) + ..() + new /obj/effect/temp_visual/borgflash(get_turf(src)) + +/obj/item/flash/cameraflash + name = "camera" + icon = 'icons/obj/items.dmi' + desc = "A polaroid camera. 10 photos left." + icon_state = "camera" + item_state = "electropack" //spelling, a coders worst enemy. This part gave me trouble for a while. + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + can_overcharge = FALSE + var/flash_max_charges = 5 + var/flash_cur_charges = 5 + var/charge_tick = 0 + use_sound = 'sound/items/polaroid1.ogg' + +/obj/item/flash/cameraflash/burn_out() //stops from burning out + return + +/obj/item/flash/cameraflash/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/item/flash/cameraflash/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/flash/cameraflash/process() //this and the two parts above are part of the charge system. + charge_tick++ + if(charge_tick < 10) + return FALSE + charge_tick = 0 + flash_cur_charges = min(flash_cur_charges+1, flash_max_charges) + return TRUE + +/obj/item/flash/cameraflash/attack(mob/living/M, mob/user) + if(flash_cur_charges > 0) + flash_cur_charges -= 1 + to_chat(user, "[src] now has [flash_cur_charges] charge\s.") + ..() + else + to_chat(user, "\The [src] needs time to recharge!") + return + +/obj/item/flash/cameraflash/attack_self(mob/living/carbon/user, flag = 0) + if(flash_cur_charges > 0) + flash_cur_charges -= 1 + to_chat(user, "[src] now has [flash_cur_charges] charge\s.") + ..() + else + to_chat(user, "\The [src] needs time to recharge!") + return + +/obj/item/flash/memorizer + name = "memorizer" + desc = "If you see this, you're not likely to remember it any time soon." + icon_state = "memorizer" + item_state = "nullrod" + +/obj/item/flash/armimplant + name = "photon projector" + desc = "A high-powered photon projector implant normally used for lighting purposes, but also doubles as a flashbulb weapon. Self-repair protocols fix the flashbulb if it ever burns out." + var/flashcd = 20 + var/overheat = 0 + var/obj/item/organ/internal/cyberimp/arm/flash/I = null + +/obj/item/flash/armimplant/Destroy() + I = null + return ..() + +/obj/item/flash/armimplant/burn_out() + if(I && I.owner) + to_chat(I.owner, "Your photon projector implant overheats and deactivates!") + I.Retract() + overheat = FALSE + addtimer(CALLBACK(src, .proc/cooldown), flashcd * 2) + +/obj/item/flash/armimplant/try_use_flash(mob/user = null) + if(overheat) + if(I && I.owner) + to_chat(I.owner, "Your photon projector is running too hot to be used again so quickly!") + return FALSE + overheat = TRUE + addtimer(CALLBACK(src, .proc/cooldown), flashcd) + playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) + update_icon(1) + return TRUE + +/obj/item/flash/armimplant/proc/cooldown() + overheat = FALSE + + +/obj/item/flash/synthetic //just a regular flash now diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 8818438269d..9390885c558 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -1,95 +1,95 @@ -/mob/living/carbon/alien/humanoid/queen - name = "alien queen" - caste = "q" - maxHealth = 250 - health = 250 - icon_state = "alienq_s" - status_flags = CANPARALYSE - heal_rate = 5 - large = 1 - ventcrawler = 0 - pressure_resistance = 200 //Because big, stompy xenos should not be blown around like paper. - -/mob/living/carbon/alien/humanoid/queen/New() - create_reagents(100) - - //there should only be one queen - for(var/mob/living/carbon/alien/humanoid/queen/Q in GLOB.living_mob_list) - if(Q == src) continue - if(Q.stat == DEAD) continue - if(Q.client) - name = "alien princess ([rand(1, 999)])" //if this is too cutesy feel free to change it/remove it. - break - - real_name = src.name - alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/queen - alien_organs += new /obj/item/organ/internal/xenos/acidgland - alien_organs += new /obj/item/organ/internal/xenos/eggsac - alien_organs += new /obj/item/organ/internal/xenos/resinspinner - alien_organs += new /obj/item/organ/internal/xenos/neurotoxin - ..() - -/mob/living/carbon/alien/humanoid/queen/movement_delay() - . = ..() - . += 3 - -/mob/living/carbon/alien/humanoid/queen/handle_hud_icons_health() - ..() //-Yvarov - - if(healths) - if(stat != DEAD) - switch(health) - if(250 to INFINITY) - healths.icon_state = "health0" - if(175 to 250) - healths.icon_state = "health1" - if(100 to 175) - healths.icon_state = "health2" - if(50 to 100) - healths.icon_state = "health3" - if(0 to 50) - healths.icon_state = "health4" - else - healths.icon_state = "health5" - else - healths.icon_state = "health6" - -/mob/living/carbon/alien/humanoid/queen/can_inject(mob/user, error_msg, target_zone, penetrate_thick) - return FALSE - -//Queen verbs -/mob/living/carbon/alien/humanoid/queen/verb/lay_egg() - - set name = "Lay Egg (75)" - set desc = "Lay an egg to produce huggers to impregnate prey with." - set category = "Alien" - if(locate(/obj/structure/alien/egg) in get_turf(src)) - to_chat(src, "There's already an egg here.") - return - - if(powerc(75,1))//Can't plant eggs on spess tiles. That's silly. - adjustPlasma(-75) - for(var/mob/O in viewers(src, null)) - O.show_message(text("[src] has laid an egg!"), 1) - new /obj/structure/alien/egg(loc) - return - - -/mob/living/carbon/alien/humanoid/queen/large - icon = 'icons/mob/alienlarge.dmi' - icon_state = "queen_s" - pixel_x = -16 - large = 1 - -/mob/living/carbon/alien/humanoid/queen/large/update_icons() - overlays.Cut() - - if(stat == DEAD) - icon_state = "queen_dead" - else if(stat == UNCONSCIOUS || lying || resting) - icon_state = "queen_sleep" - else - icon_state = "queen_s" - - for(var/image/I in overlays_standing) - overlays += I \ No newline at end of file +/mob/living/carbon/alien/humanoid/queen + name = "alien queen" + caste = "q" + maxHealth = 250 + health = 250 + icon_state = "alienq_s" + status_flags = CANPARALYSE + heal_rate = 5 + large = 1 + ventcrawler = 0 + pressure_resistance = 200 //Because big, stompy xenos should not be blown around like paper. + +/mob/living/carbon/alien/humanoid/queen/New() + create_reagents(100) + + //there should only be one queen + for(var/mob/living/carbon/alien/humanoid/queen/Q in GLOB.living_mob_list) + if(Q == src) continue + if(Q.stat == DEAD) continue + if(Q.client) + name = "alien princess ([rand(1, 999)])" //if this is too cutesy feel free to change it/remove it. + break + + real_name = src.name + alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/queen + alien_organs += new /obj/item/organ/internal/xenos/acidgland + alien_organs += new /obj/item/organ/internal/xenos/eggsac + alien_organs += new /obj/item/organ/internal/xenos/resinspinner + alien_organs += new /obj/item/organ/internal/xenos/neurotoxin + ..() + +/mob/living/carbon/alien/humanoid/queen/movement_delay() + . = ..() + . += 3 + +/mob/living/carbon/alien/humanoid/queen/handle_hud_icons_health() + ..() //-Yvarov + + if(healths) + if(stat != DEAD) + switch(health) + if(250 to INFINITY) + healths.icon_state = "health0" + if(175 to 250) + healths.icon_state = "health1" + if(100 to 175) + healths.icon_state = "health2" + if(50 to 100) + healths.icon_state = "health3" + if(0 to 50) + healths.icon_state = "health4" + else + healths.icon_state = "health5" + else + healths.icon_state = "health6" + +/mob/living/carbon/alien/humanoid/queen/can_inject(mob/user, error_msg, target_zone, penetrate_thick) + return FALSE + +//Queen verbs +/mob/living/carbon/alien/humanoid/queen/verb/lay_egg() + + set name = "Lay Egg (75)" + set desc = "Lay an egg to produce huggers to impregnate prey with." + set category = "Alien" + if(locate(/obj/structure/alien/egg) in get_turf(src)) + to_chat(src, "There's already an egg here.") + return + + if(powerc(75,1))//Can't plant eggs on spess tiles. That's silly. + adjustPlasma(-75) + for(var/mob/O in viewers(src, null)) + O.show_message(text("[src] has laid an egg!"), 1) + new /obj/structure/alien/egg(loc) + return + + +/mob/living/carbon/alien/humanoid/queen/large + icon = 'icons/mob/alienlarge.dmi' + icon_state = "queen_s" + pixel_x = -16 + large = 1 + +/mob/living/carbon/alien/humanoid/queen/large/update_icons() + overlays.Cut() + + if(stat == DEAD) + icon_state = "queen_dead" + else if(stat == UNCONSCIOUS || lying || resting) + icon_state = "queen_sleep" + else + icon_state = "queen_s" + + for(var/image/I in overlays_standing) + overlays += I diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index c642c189b5e..79efcffb47c 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1,717 +1,717 @@ -var/list/robot_verbs_default = list( - /mob/living/silicon/robot/proc/sensor_mode, -) - -/mob/living/silicon/robot - name = "Cyborg" - real_name = "Cyborg" - icon = 'icons/mob/robots.dmi' - icon_state = "robot" - maxHealth = 100 - health = 100 - universal_understand = 1 - deathgasp_on_death = TRUE - - var/sight_mode = 0 - var/custom_name = "" - var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best - -//Hud stuff - - var/obj/screen/inv1 = null - var/obj/screen/inv2 = null - var/obj/screen/inv3 = null - var/obj/screen/lamp_button = null - var/obj/screen/thruster_button = null - - var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not - var/obj/screen/robot_modules_background - -//3 Modules can be activated at any one time. - var/obj/item/robot_module/module = null - var/module_active = null - var/module_state_1 = null - var/module_state_2 = null - var/module_state_3 = null - - var/obj/item/radio/borg/radio = null - var/mob/living/silicon/ai/connected_ai = null - var/obj/item/stock_parts/cell/cell = null - var/obj/machinery/camera/camera = null - - // Components are basically robot organs. - var/list/components = list() - - var/obj/item/robot_parts/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of.. - var/obj/item/mmi/mmi = null - - var/obj/item/pda/silicon/robot/rbPDA = null - - var/datum/wires/robot/wires = null - - var/opened = 0 - var/custom_panel = null - var/list/custom_panel_names = list("Cricket") - var/list/custom_eye_names = list("Cricket","Standard") - var/emagged = 0 - var/is_emaggable = TRUE - var/eye_protection = 0 - var/ear_protection = 0 - - var/list/force_modules = list() - var/allow_rename = TRUE - var/weapons_unlock = FALSE - var/static_radio_channels = FALSE - - var/wiresexposed = 0 - var/locked = 1 +var/list/robot_verbs_default = list( + /mob/living/silicon/robot/proc/sensor_mode, +) + +/mob/living/silicon/robot + name = "Cyborg" + real_name = "Cyborg" + icon = 'icons/mob/robots.dmi' + icon_state = "robot" + maxHealth = 100 + health = 100 + universal_understand = 1 + deathgasp_on_death = TRUE + + var/sight_mode = 0 + var/custom_name = "" + var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best + +//Hud stuff + + var/obj/screen/inv1 = null + var/obj/screen/inv2 = null + var/obj/screen/inv3 = null + var/obj/screen/lamp_button = null + var/obj/screen/thruster_button = null + + var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not + var/obj/screen/robot_modules_background + +//3 Modules can be activated at any one time. + var/obj/item/robot_module/module = null + var/module_active = null + var/module_state_1 = null + var/module_state_2 = null + var/module_state_3 = null + + var/obj/item/radio/borg/radio = null + var/mob/living/silicon/ai/connected_ai = null + var/obj/item/stock_parts/cell/cell = null + var/obj/machinery/camera/camera = null + + // Components are basically robot organs. + var/list/components = list() + + var/obj/item/robot_parts/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of.. + var/obj/item/mmi/mmi = null + + var/obj/item/pda/silicon/robot/rbPDA = null + + var/datum/wires/robot/wires = null + + var/opened = 0 + var/custom_panel = null + var/list/custom_panel_names = list("Cricket") + var/list/custom_eye_names = list("Cricket","Standard") + var/emagged = 0 + var/is_emaggable = TRUE + var/eye_protection = 0 + var/ear_protection = 0 + + var/list/force_modules = list() + var/allow_rename = TRUE + var/weapons_unlock = FALSE + var/static_radio_channels = FALSE + + var/wiresexposed = 0 + var/locked = 1 var/list/req_one_access = list(ACCESS_ROBOTICS) - var/list/req_access - var/ident = 0 - //var/list/laws = list() - var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) - var/viewalerts = 0 - var/modtype = "Default" - var/lower_mod = 0 - var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N - var/jeton = 0 - var/low_power_mode = 0 //whether the robot has no charge left. - var/weapon_lock = 0 - var/weaponlock_time = 120 - var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default - var/lockcharge //Used when locking down a borg to preserve cell charge - var/speed = 0 //Cause sec borgs gotta go fast //No they dont! - var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. - var/pdahide = 0 //Used to hide the borg from the messenger list - var/tracking_entities = 0 //The number of known entities currently accessing the internal camera - var/braintype = "Cyborg" - var/base_icon = "" - var/crisis = 0 - - var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting. - var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power. - var/lamp_recharging = 0 //Flag for if the lamp is on cooldown after being forcibly disabled. - - var/updating = 0 //portable camera camerachunk update - - hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) - - var/magpulse = 0 - var/ionpulse = 0 // Jetpack-like effect. - var/ionpulse_on = 0 // Jetpack-like effect. - var/datum/effect_system/trail_follow/ion/ion_trail // Ionpulse effect. - - var/datum/action/item_action/toggle_research_scanner/scanner = null - var/list/module_actions = list() - -/mob/living/silicon/robot/get_cell() - return cell - -/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0, var/alien = 0) - spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - add_language("Robot Talk", 1) - - wires = new(src) - - robot_modules_background = new() - robot_modules_background.icon_state = "block" - robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer 20, UI should be just below it. - robot_modules_background.plane = HUD_PLANE - - ident = rand(1, 999) - rename_character(null, get_default_name()) - update_icons() - update_headlamp() - - radio = new /obj/item/radio/borg(src) - common_radio = radio - - init() - - if(!scrambledcodes && !camera) - camera = new /obj/machinery/camera(src) - camera.c_tag = real_name - camera.network = list("SS13","Robots") - if(wires.IsCameraCut()) // 5 = BORG CAMERA - camera.status = 0 - - if(mmi == null) - mmi = new /obj/item/mmi/robotic_brain(src) //Give the borg an MMI if he spawns without for some reason. (probably not the correct way to spawn a robotic brain, but it works) - mmi.icon_state = "boris" - + var/list/req_access + var/ident = 0 + //var/list/laws = list() + var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) + var/viewalerts = 0 + var/modtype = "Default" + var/lower_mod = 0 + var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N + var/jeton = 0 + var/low_power_mode = 0 //whether the robot has no charge left. + var/weapon_lock = 0 + var/weaponlock_time = 120 + var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default + var/lockcharge //Used when locking down a borg to preserve cell charge + var/speed = 0 //Cause sec borgs gotta go fast //No they dont! + var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. + var/pdahide = 0 //Used to hide the borg from the messenger list + var/tracking_entities = 0 //The number of known entities currently accessing the internal camera + var/braintype = "Cyborg" + var/base_icon = "" + var/crisis = 0 + + var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting. + var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power. + var/lamp_recharging = 0 //Flag for if the lamp is on cooldown after being forcibly disabled. + + var/updating = 0 //portable camera camerachunk update + + hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) + + var/magpulse = 0 + var/ionpulse = 0 // Jetpack-like effect. + var/ionpulse_on = 0 // Jetpack-like effect. + var/datum/effect_system/trail_follow/ion/ion_trail // Ionpulse effect. + + var/datum/action/item_action/toggle_research_scanner/scanner = null + var/list/module_actions = list() + +/mob/living/silicon/robot/get_cell() + return cell + +/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0, var/alien = 0) + spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + add_language("Robot Talk", 1) + + wires = new(src) + + robot_modules_background = new() + robot_modules_background.icon_state = "block" + robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer 20, UI should be just below it. + robot_modules_background.plane = HUD_PLANE + + ident = rand(1, 999) + rename_character(null, get_default_name()) + update_icons() + update_headlamp() + + radio = new /obj/item/radio/borg(src) + common_radio = radio + + init() + + if(!scrambledcodes && !camera) + camera = new /obj/machinery/camera(src) + camera.c_tag = real_name + camera.network = list("SS13","Robots") + if(wires.IsCameraCut()) // 5 = BORG CAMERA + camera.status = 0 + + if(mmi == null) + mmi = new /obj/item/mmi/robotic_brain(src) //Give the borg an MMI if he spawns without for some reason. (probably not the correct way to spawn a robotic brain, but it works) + mmi.icon_state = "boris" + if(!cell) // Make sure a new cell gets created *before* executing initialize_components(). The cell component needs an existing cell for it to get set up properly cell = new /obj/item/stock_parts/cell/high(src) - - initialize_components() - //if(!unfinished) - // Create all the robot parts. - for(var/V in components) if(V != "power cell") - var/datum/robot_component/C = components[V] - C.installed = 1 - C.wrapped = new C.external_type - - ..() - - add_robot_verbs() - - if(cell) - var/datum/robot_component/cell_component = components["power cell"] - cell_component.wrapped = cell - cell_component.installed = 1 - cell_component.install() - - diag_hud_set_borgcell() - scanner = new(src) - scanner.Grant(src) - -/mob/living/silicon/robot/proc/init(var/alien=0) - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - make_laws() - additional_law_channels["Binary"] = ":b " - var/new_ai = select_active_ai_with_fewest_borgs() - if(new_ai) - lawupdate = 1 - connect_to_ai(new_ai) - else - lawupdate = 0 - - playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) - -/mob/living/silicon/robot/rename_character(oldname, newname) - if(!..(oldname, newname)) - return 0 - - if(oldname != real_name) - notify_ai(3, oldname, newname) - custom_name = (newname != get_default_name()) ? newname : null - setup_PDA() - - //We also need to update name of internal camera. - if(camera) - camera.c_tag = newname - - //Check for custom sprite - if(!custom_sprite) - var/file = file2text("config/custom_sprites.txt") - var/lines = splittext(file, "\n") - - for(var/line in lines) - // split & clean up - var/list/Entry = splittext(line, ":") - for(var/i = 1 to Entry.len) - Entry[i] = trim(Entry[i]) - - if(Entry.len < 2 || Entry[1] != "cyborg") //ignore incorrectly formatted entries or entries that aren't marked for cyborg - continue - - if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required - custom_sprite = 1 - - return 1 - - -/mob/living/silicon/robot/proc/get_default_name(var/prefix as text) - if(prefix) - modtype = prefix - if(mmi) - if(istype(mmi, /obj/item/mmi/robotic_brain)) - braintype = "Android" - else - braintype = "Cyborg" - else - braintype = "Robot" - - if(custom_name) - return custom_name - else - return "[modtype] [braintype]-[num2text(ident)]" - -/mob/living/silicon/robot/verb/Namepick() - set category = "Robot Commands" - if(custom_name) - return 0 - if(!allow_rename) - to_chat(src, "Rename functionality is not enabled on this unit."); - return 0 - rename_self(braintype, 1) - -/mob/living/silicon/robot/proc/sync() - if(lawupdate && connected_ai) - lawsync() - photosync() - -// setup the PDA and its name -/mob/living/silicon/robot/proc/setup_PDA() - if(!rbPDA) - rbPDA = new(src) - rbPDA.set_name_and_job(real_name, braintype) - var/datum/data/pda/app/messenger/M = rbPDA.find_program(/datum/data/pda/app/messenger) - if(M) - if(scrambledcodes) - M.hidden = 1 - if(pdahide) - M.toff = 1 - -/mob/living/silicon/robot/binarycheck() - if(is_component_functioning("comms")) - return 1 - return 0 - -//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO -//Improved /N -/mob/living/silicon/robot/Destroy() - if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. - var/turf/T = get_turf(loc)//To hopefully prevent run time errors. - if(T) mmi.loc = T - if(mmi.brainmob) - mind.transfer_to(mmi.brainmob) - mmi.update_icon() - else - to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") - ghostize() - error("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") - mmi = null - if(connected_ai) - connected_ai.connected_robots -= src - QDEL_NULL(wires) - QDEL_NULL(module) - QDEL_NULL(camera) - QDEL_NULL(cell) - QDEL_NULL(robot_suit) - QDEL_NULL(spark_system) - return ..() - -/mob/living/silicon/robot/proc/pick_module() - if(module) - return - var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") - if(islist(force_modules) && force_modules.len) - modules = force_modules.Copy() - if(security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis) - to_chat(src, "Crisis mode active. The combat module is now available.") - modules += "Combat" - if(mmi != null && mmi.alien) - modules = list("Hunter") - modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules - if(!modtype) - return - designation = modtype - var/module_sprites[0] //Used to store the associations between sprite names and sprite index. - - if(module) - return - - switch(modtype) - if("Standard") - module = new /obj/item/robot_module/standard(src) - module.channels = list("Service" = 1) - module_sprites["Basic"] = "robot_old" - module_sprites["Android"] = "droid" - module_sprites["Default"] = "Standard" - module_sprites["Noble-STD"] = "Noble-STD" - - if("Service") - module = new /obj/item/robot_module/butler(src) - module.channels = list("Service" = 1) - module_sprites["Waitress"] = "Service" - module_sprites["Kent"] = "toiletbot" - module_sprites["Bro"] = "Brobot" - module_sprites["Rich"] = "maximillion" - module_sprites["Default"] = "Service2" - module_sprites["Standard"] = "Standard-Serv" - module_sprites["Noble-SRV"] = "Noble-SRV" - module_sprites["Cricket"] = "Cricket-SERV" - - if("Miner") - module = new /obj/item/robot_module/miner(src) - module.channels = list("Supply" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Mining Outpost") - module_sprites["Basic"] = "Miner_old" - module_sprites["Advanced Droid"] = "droid-miner" - module_sprites["Treadhead"] = "Miner" - module_sprites["Standard"] = "Standard-Mine" - module_sprites["Noble-DIG"] = "Noble-DIG" - module_sprites["Cricket"] = "Cricket-MINE" - - if("Medical") - module = new /obj/item/robot_module/medical(src) - module.channels = list("Medical" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Medical") - module_sprites["Basic"] = "Medbot" - module_sprites["Surgeon"] = "surgeon" - module_sprites["Advanced Droid"] = "droid-medical" - module_sprites["Needles"] = "medicalrobot" - module_sprites["Standard"] = "Standard-Medi" - module_sprites["Noble-MED"] = "Noble-MED" - module_sprites["Cricket"] = "Cricket-MEDI" - status_flags &= ~CANPUSH - - if("Security") - module = new /obj/item/robot_module/security(src) - module.channels = list("Security" = 1) - module_sprites["Basic"] = "secborg" - module_sprites["Red Knight"] = "Security" - module_sprites["Black Knight"] = "securityrobot" - module_sprites["Bloodhound"] = "bloodhound" - module_sprites["Standard"] = "Standard-Secy" - module_sprites["Noble-SEC"] = "Noble-SEC" - module_sprites["Cricket"] = "Cricket-SEC" - status_flags &= ~CANPUSH - - if("Engineering") - module = new /obj/item/robot_module/engineering(src) - module.channels = list("Engineering" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Engineering") - module_sprites["Basic"] = "Engineering" - module_sprites["Antique"] = "engineerrobot" - module_sprites["Landmate"] = "landmate" - module_sprites["Standard"] = "Standard-Engi" - module_sprites["Noble-ENG"] = "Noble-ENG" - module_sprites["Cricket"] = "Cricket-ENGI" - magpulse = 1 - - if("Janitor") - module = new /obj/item/robot_module/janitor(src) - module.channels = list("Service" = 1) - module_sprites["Basic"] = "JanBot2" - module_sprites["Mopbot"] = "janitorrobot" - module_sprites["Mop Gear Rex"] = "mopgearrex" - module_sprites["Standard"] = "Standard-Jani" - module_sprites["Noble-CLN"] = "Noble-CLN" - module_sprites["Cricket"] = "Cricket-JANI" - - if("Combat") - module = new /obj/item/robot_module/combat(src) - module.channels = list("Security" = 1) - icon_state = "droidcombat" - - if("Hunter") - module = new /obj/item/robot_module/alien/hunter(src) - icon_state = "xenoborg-state-a" - modtype = "Xeno-Hu" - feedback_inc("xeborg_hunter",1) - - - //languages - module.add_languages(src) - //subsystems - module.add_subsystems_and_actions(src) - - //Custom_sprite check and entry - if(custom_sprite && check_sprite("[ckey]-[modtype]")) - module_sprites["Custom"] = "[src.ckey]-[modtype]" - - hands.icon_state = lowertext(module.module_type) - feedback_inc("cyborg_[lowertext(modtype)]",1) - rename_character(real_name, get_default_name()) - - if(modtype == "Medical" || modtype == "Security" || modtype == "Combat") - status_flags &= ~CANPUSH - - choose_icon(6,module_sprites) - if(!static_radio_channels) - radio.config(module.channels) - notify_ai(2) - -/mob/living/silicon/robot/proc/reset_module() - notify_ai(2) - - uneq_all() - sight_mode = null - hands.icon_state = "nomod" - icon_state = "robot" - module.remove_subsystems_and_actions(src) - QDEL_NULL(module) - - camera.network.Remove(list("Engineering", "Medical", "Mining Outpost")) - rename_character(real_name, get_default_name("Default")) - languages = list() - speech_synthesizer_langs = list() - - update_icons() - update_headlamp() - - speed = 0 // Remove upgrades. - ionpulse = FALSE - magpulse = FALSE - add_language("Robot Talk", 1) - - status_flags |= CANPUSH - -//for borg hotkeys, here module refers to borg inv slot, not core module -/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) - set name = "Toggle Module" - set hidden = 1 - toggle_module(module) - -/mob/living/silicon/robot/verb/cmd_unequip_module() - set name = "Unequip Module" - set hidden = 1 - uneq_active() - -// this verb lets cyborgs see the stations manifest -/mob/living/silicon/robot/verb/cmd_station_manifest() - set category = "Robot Commands" - set name = "Show Station Manifest" - show_station_manifest() - -/mob/living/silicon/robot/proc/self_diagnosis() - if(!is_component_functioning("diagnosis unit")) - return null - - var/dat = "[src.name] Self-Diagnosis Report\n" - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 0) - dat += "[C.name]
MISSING
" - else - dat += "[C.name][C.installed == -1 ? "
DESTROYED" : ""]
Brute Damage:[C.brute_damage]
Electronics Damage:[C.electronics_damage]
Powered:[C.is_powered() ? "Yes" : "No"]
Toggled:[ C.toggled ? "Yes" : "No"]

" - return dat - -/mob/living/silicon/robot/verb/self_diagnosis_verb() - set category = "Robot Commands" - set name = "Self Diagnosis" - - if(!is_component_functioning("diagnosis unit")) - to_chat(src, "Your self-diagnosis component isn't functioning.") - - var/dat = self_diagnosis() - src << browse(dat, "window=robotdiagnosis") - - -/mob/living/silicon/robot/verb/toggle_component() - set category = "Robot Commands" - set name = "Toggle Component" - set desc = "Toggle a component, conserving power." - - var/list/installed_components = list() - for(var/V in components) - if(V == "power cell") continue - var/datum/robot_component/C = components[V] - if(C.installed) - installed_components += V - - var/toggle = input(src, "Which component do you want to toggle?", "Toggle Component") as null|anything in installed_components - if(!toggle) - return - - var/datum/robot_component/C = components[toggle] - C.toggle() - to_chat(src, "You [C.toggled ? "enable" : "disable"] [C.name].") - -/mob/living/silicon/robot/proc/sensor_mode() - set name = "Set Sensor Augmentation" - set desc = "Augment visual feed with internal sensor overlays." - set category = "Robot Commands" - toggle_sensor_mode() - -/mob/living/silicon/robot/proc/add_robot_verbs() - src.verbs |= robot_verbs_default - src.verbs |= silicon_subsystems - -/mob/living/silicon/robot/proc/remove_robot_verbs() - src.verbs -= robot_verbs_default - src.verbs -= silicon_subsystems - -/mob/living/silicon/robot/proc/ionpulse() - if(!ionpulse_on) - return - - if(cell.charge <= 50) - toggle_ionpulse() - return - - cell.charge -= 25 // 500 steps on a default cell. - return 1 - -/mob/living/silicon/robot/proc/toggle_ionpulse() - if(!ionpulse) - to_chat(src, "No thrusters are installed!") - return - - if(!ion_trail) - ion_trail = new - ion_trail.set_up(src) - - ionpulse_on = !ionpulse_on - to_chat(src, "You [ionpulse_on ? null :"de"]activate your ion thrusters.") - if(ionpulse_on) - ion_trail.start() - else - ion_trail.stop() - if(thruster_button) - thruster_button.icon_state = "ionpulse[ionpulse_on]" - -/mob/living/silicon/robot/blob_act(obj/structure/blob/B) - if(stat != DEAD) - adjustBruteLoss(30) - else - gib() - return TRUE - -// this function displays the cyborgs current cell charge in the stat panel -/mob/living/silicon/robot/proc/show_cell_power() - if(cell) - stat(null, text("Charge Left: [cell.charge]/[cell.maxcharge]")) - else - stat(null, text("No Cell Inserted!")) - - -// update the status screen display -/mob/living/silicon/robot/Stat() - ..() - statpanel("Status") - if(client.statpanel == "Status") - show_cell_power() - var/total_user_contents = GetAllContents() - if(locate(/obj/item/gps/cyborg) in total_user_contents) - var/turf/T = get_turf(src) - stat(null, "GPS: [COORD(T)]") - -/mob/living/silicon/robot/restrained() - return 0 - -/mob/living/silicon/robot/InCritical() - return low_power_mode - -/mob/living/silicon/robot/ex_act(severity) - switch(severity) - if(1.0) - gib() - return - if(2.0) - if(stat != 2) - adjustBruteLoss(60) - adjustFireLoss(60) - if(3.0) - if(stat != 2) - adjustBruteLoss(30) - return - - -/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) - ..(Proj) - if(prob(75) && Proj.damage > 0) spark_system.start() - return 2 - - -/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) + + initialize_components() + //if(!unfinished) + // Create all the robot parts. + for(var/V in components) if(V != "power cell") + var/datum/robot_component/C = components[V] + C.installed = 1 + C.wrapped = new C.external_type + + ..() + + add_robot_verbs() + + if(cell) + var/datum/robot_component/cell_component = components["power cell"] + cell_component.wrapped = cell + cell_component.installed = 1 + cell_component.install() + + diag_hud_set_borgcell() + scanner = new(src) + scanner.Grant(src) + +/mob/living/silicon/robot/proc/init(var/alien=0) + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + make_laws() + additional_law_channels["Binary"] = ":b " + var/new_ai = select_active_ai_with_fewest_borgs() + if(new_ai) + lawupdate = 1 + connect_to_ai(new_ai) + else + lawupdate = 0 + + playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) + +/mob/living/silicon/robot/rename_character(oldname, newname) + if(!..(oldname, newname)) + return 0 + + if(oldname != real_name) + notify_ai(3, oldname, newname) + custom_name = (newname != get_default_name()) ? newname : null + setup_PDA() + + //We also need to update name of internal camera. + if(camera) + camera.c_tag = newname + + //Check for custom sprite + if(!custom_sprite) + var/file = file2text("config/custom_sprites.txt") + var/lines = splittext(file, "\n") + + for(var/line in lines) + // split & clean up + var/list/Entry = splittext(line, ":") + for(var/i = 1 to Entry.len) + Entry[i] = trim(Entry[i]) + + if(Entry.len < 2 || Entry[1] != "cyborg") //ignore incorrectly formatted entries or entries that aren't marked for cyborg + continue + + if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required + custom_sprite = 1 + + return 1 + + +/mob/living/silicon/robot/proc/get_default_name(var/prefix as text) + if(prefix) + modtype = prefix + if(mmi) + if(istype(mmi, /obj/item/mmi/robotic_brain)) + braintype = "Android" + else + braintype = "Cyborg" + else + braintype = "Robot" + + if(custom_name) + return custom_name + else + return "[modtype] [braintype]-[num2text(ident)]" + +/mob/living/silicon/robot/verb/Namepick() + set category = "Robot Commands" + if(custom_name) + return 0 + if(!allow_rename) + to_chat(src, "Rename functionality is not enabled on this unit."); + return 0 + rename_self(braintype, 1) + +/mob/living/silicon/robot/proc/sync() + if(lawupdate && connected_ai) + lawsync() + photosync() + +// setup the PDA and its name +/mob/living/silicon/robot/proc/setup_PDA() + if(!rbPDA) + rbPDA = new(src) + rbPDA.set_name_and_job(real_name, braintype) + var/datum/data/pda/app/messenger/M = rbPDA.find_program(/datum/data/pda/app/messenger) + if(M) + if(scrambledcodes) + M.hidden = 1 + if(pdahide) + M.toff = 1 + +/mob/living/silicon/robot/binarycheck() + if(is_component_functioning("comms")) + return 1 + return 0 + +//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO +//Improved /N +/mob/living/silicon/robot/Destroy() + if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. + var/turf/T = get_turf(loc)//To hopefully prevent run time errors. + if(T) mmi.loc = T + if(mmi.brainmob) + mind.transfer_to(mmi.brainmob) + mmi.update_icon() + else + to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") + ghostize() + error("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") + mmi = null + if(connected_ai) + connected_ai.connected_robots -= src + QDEL_NULL(wires) + QDEL_NULL(module) + QDEL_NULL(camera) + QDEL_NULL(cell) + QDEL_NULL(robot_suit) + QDEL_NULL(spark_system) + return ..() + +/mob/living/silicon/robot/proc/pick_module() + if(module) + return + var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") + if(islist(force_modules) && force_modules.len) + modules = force_modules.Copy() + if(security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis) + to_chat(src, "Crisis mode active. The combat module is now available.") + modules += "Combat" + if(mmi != null && mmi.alien) + modules = list("Hunter") + modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules + if(!modtype) + return + designation = modtype + var/module_sprites[0] //Used to store the associations between sprite names and sprite index. + + if(module) + return + + switch(modtype) + if("Standard") + module = new /obj/item/robot_module/standard(src) + module.channels = list("Service" = 1) + module_sprites["Basic"] = "robot_old" + module_sprites["Android"] = "droid" + module_sprites["Default"] = "Standard" + module_sprites["Noble-STD"] = "Noble-STD" + + if("Service") + module = new /obj/item/robot_module/butler(src) + module.channels = list("Service" = 1) + module_sprites["Waitress"] = "Service" + module_sprites["Kent"] = "toiletbot" + module_sprites["Bro"] = "Brobot" + module_sprites["Rich"] = "maximillion" + module_sprites["Default"] = "Service2" + module_sprites["Standard"] = "Standard-Serv" + module_sprites["Noble-SRV"] = "Noble-SRV" + module_sprites["Cricket"] = "Cricket-SERV" + + if("Miner") + module = new /obj/item/robot_module/miner(src) + module.channels = list("Supply" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Mining Outpost") + module_sprites["Basic"] = "Miner_old" + module_sprites["Advanced Droid"] = "droid-miner" + module_sprites["Treadhead"] = "Miner" + module_sprites["Standard"] = "Standard-Mine" + module_sprites["Noble-DIG"] = "Noble-DIG" + module_sprites["Cricket"] = "Cricket-MINE" + + if("Medical") + module = new /obj/item/robot_module/medical(src) + module.channels = list("Medical" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Medical") + module_sprites["Basic"] = "Medbot" + module_sprites["Surgeon"] = "surgeon" + module_sprites["Advanced Droid"] = "droid-medical" + module_sprites["Needles"] = "medicalrobot" + module_sprites["Standard"] = "Standard-Medi" + module_sprites["Noble-MED"] = "Noble-MED" + module_sprites["Cricket"] = "Cricket-MEDI" + status_flags &= ~CANPUSH + + if("Security") + module = new /obj/item/robot_module/security(src) + module.channels = list("Security" = 1) + module_sprites["Basic"] = "secborg" + module_sprites["Red Knight"] = "Security" + module_sprites["Black Knight"] = "securityrobot" + module_sprites["Bloodhound"] = "bloodhound" + module_sprites["Standard"] = "Standard-Secy" + module_sprites["Noble-SEC"] = "Noble-SEC" + module_sprites["Cricket"] = "Cricket-SEC" + status_flags &= ~CANPUSH + + if("Engineering") + module = new /obj/item/robot_module/engineering(src) + module.channels = list("Engineering" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Engineering") + module_sprites["Basic"] = "Engineering" + module_sprites["Antique"] = "engineerrobot" + module_sprites["Landmate"] = "landmate" + module_sprites["Standard"] = "Standard-Engi" + module_sprites["Noble-ENG"] = "Noble-ENG" + module_sprites["Cricket"] = "Cricket-ENGI" + magpulse = 1 + + if("Janitor") + module = new /obj/item/robot_module/janitor(src) + module.channels = list("Service" = 1) + module_sprites["Basic"] = "JanBot2" + module_sprites["Mopbot"] = "janitorrobot" + module_sprites["Mop Gear Rex"] = "mopgearrex" + module_sprites["Standard"] = "Standard-Jani" + module_sprites["Noble-CLN"] = "Noble-CLN" + module_sprites["Cricket"] = "Cricket-JANI" + + if("Combat") + module = new /obj/item/robot_module/combat(src) + module.channels = list("Security" = 1) + icon_state = "droidcombat" + + if("Hunter") + module = new /obj/item/robot_module/alien/hunter(src) + icon_state = "xenoborg-state-a" + modtype = "Xeno-Hu" + feedback_inc("xeborg_hunter",1) + + + //languages + module.add_languages(src) + //subsystems + module.add_subsystems_and_actions(src) + + //Custom_sprite check and entry + if(custom_sprite && check_sprite("[ckey]-[modtype]")) + module_sprites["Custom"] = "[src.ckey]-[modtype]" + + hands.icon_state = lowertext(module.module_type) + feedback_inc("cyborg_[lowertext(modtype)]",1) + rename_character(real_name, get_default_name()) + + if(modtype == "Medical" || modtype == "Security" || modtype == "Combat") + status_flags &= ~CANPUSH + + choose_icon(6,module_sprites) + if(!static_radio_channels) + radio.config(module.channels) + notify_ai(2) + +/mob/living/silicon/robot/proc/reset_module() + notify_ai(2) + + uneq_all() + sight_mode = null + hands.icon_state = "nomod" + icon_state = "robot" + module.remove_subsystems_and_actions(src) + QDEL_NULL(module) + + camera.network.Remove(list("Engineering", "Medical", "Mining Outpost")) + rename_character(real_name, get_default_name("Default")) + languages = list() + speech_synthesizer_langs = list() + + update_icons() + update_headlamp() + + speed = 0 // Remove upgrades. + ionpulse = FALSE + magpulse = FALSE + add_language("Robot Talk", 1) + + status_flags |= CANPUSH + +//for borg hotkeys, here module refers to borg inv slot, not core module +/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) + set name = "Toggle Module" + set hidden = 1 + toggle_module(module) + +/mob/living/silicon/robot/verb/cmd_unequip_module() + set name = "Unequip Module" + set hidden = 1 + uneq_active() + +// this verb lets cyborgs see the stations manifest +/mob/living/silicon/robot/verb/cmd_station_manifest() + set category = "Robot Commands" + set name = "Show Station Manifest" + show_station_manifest() + +/mob/living/silicon/robot/proc/self_diagnosis() + if(!is_component_functioning("diagnosis unit")) + return null + + var/dat = "[src.name] Self-Diagnosis Report\n" + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 0) + dat += "[C.name]
MISSING
" + else + dat += "[C.name][C.installed == -1 ? "
DESTROYED" : ""]
Brute Damage:[C.brute_damage]
Electronics Damage:[C.electronics_damage]
Powered:[C.is_powered() ? "Yes" : "No"]
Toggled:[ C.toggled ? "Yes" : "No"]

" + return dat + +/mob/living/silicon/robot/verb/self_diagnosis_verb() + set category = "Robot Commands" + set name = "Self Diagnosis" + + if(!is_component_functioning("diagnosis unit")) + to_chat(src, "Your self-diagnosis component isn't functioning.") + + var/dat = self_diagnosis() + src << browse(dat, "window=robotdiagnosis") + + +/mob/living/silicon/robot/verb/toggle_component() + set category = "Robot Commands" + set name = "Toggle Component" + set desc = "Toggle a component, conserving power." + + var/list/installed_components = list() + for(var/V in components) + if(V == "power cell") continue + var/datum/robot_component/C = components[V] + if(C.installed) + installed_components += V + + var/toggle = input(src, "Which component do you want to toggle?", "Toggle Component") as null|anything in installed_components + if(!toggle) + return + + var/datum/robot_component/C = components[toggle] + C.toggle() + to_chat(src, "You [C.toggled ? "enable" : "disable"] [C.name].") + +/mob/living/silicon/robot/proc/sensor_mode() + set name = "Set Sensor Augmentation" + set desc = "Augment visual feed with internal sensor overlays." + set category = "Robot Commands" + toggle_sensor_mode() + +/mob/living/silicon/robot/proc/add_robot_verbs() + src.verbs |= robot_verbs_default + src.verbs |= silicon_subsystems + +/mob/living/silicon/robot/proc/remove_robot_verbs() + src.verbs -= robot_verbs_default + src.verbs -= silicon_subsystems + +/mob/living/silicon/robot/proc/ionpulse() + if(!ionpulse_on) + return + + if(cell.charge <= 50) + toggle_ionpulse() + return + + cell.charge -= 25 // 500 steps on a default cell. + return 1 + +/mob/living/silicon/robot/proc/toggle_ionpulse() + if(!ionpulse) + to_chat(src, "No thrusters are installed!") + return + + if(!ion_trail) + ion_trail = new + ion_trail.set_up(src) + + ionpulse_on = !ionpulse_on + to_chat(src, "You [ionpulse_on ? null :"de"]activate your ion thrusters.") + if(ionpulse_on) + ion_trail.start() + else + ion_trail.stop() + if(thruster_button) + thruster_button.icon_state = "ionpulse[ionpulse_on]" + +/mob/living/silicon/robot/blob_act(obj/structure/blob/B) + if(stat != DEAD) + adjustBruteLoss(30) + else + gib() + return TRUE + +// this function displays the cyborgs current cell charge in the stat panel +/mob/living/silicon/robot/proc/show_cell_power() + if(cell) + stat(null, text("Charge Left: [cell.charge]/[cell.maxcharge]")) + else + stat(null, text("No Cell Inserted!")) + + +// update the status screen display +/mob/living/silicon/robot/Stat() + ..() + statpanel("Status") + if(client.statpanel == "Status") + show_cell_power() + var/total_user_contents = GetAllContents() + if(locate(/obj/item/gps/cyborg) in total_user_contents) + var/turf/T = get_turf(src) + stat(null, "GPS: [COORD(T)]") + +/mob/living/silicon/robot/restrained() + return 0 + +/mob/living/silicon/robot/InCritical() + return low_power_mode + +/mob/living/silicon/robot/ex_act(severity) + switch(severity) + if(1.0) + gib() + return + if(2.0) + if(stat != 2) + adjustBruteLoss(60) + adjustFireLoss(60) + if(3.0) + if(stat != 2) + adjustBruteLoss(30) + return + + +/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) + ..(Proj) + if(prob(75) && Proj.damage > 0) spark_system.start() + return 2 + + +/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) // Check if the user is trying to insert another component like a radio, actuator, armor etc. if(istype(W, /obj/item/robot_parts/robot_component) && opened) - for(var/V in components) - var/datum/robot_component/C = components[V] - if(!C.installed && istype(W, C.external_type)) - C.installed = 1 - C.wrapped = W - C.install() - user.drop_item() - W.loc = null - - var/obj/item/robot_parts/robot_component/WC = W - if(istype(WC)) - C.brute_damage = WC.brute - C.electronics_damage = WC.burn - - to_chat(usr, "You install the [W.name].") - - return - + for(var/V in components) + var/datum/robot_component/C = components[V] + if(!C.installed && istype(W, C.external_type)) + C.installed = 1 + C.wrapped = W + C.install() + user.drop_item() + W.loc = null + + var/obj/item/robot_parts/robot_component/WC = W + if(istype(WC)) + C.brute_damage = WC.brute + C.electronics_damage = WC.burn + + to_chat(usr, "You install the [W.name].") + + return + if(istype(W, /obj/item/stack/cable_coil) && user.a_intent == INTENT_HELP && (wiresexposed || istype(src, /mob/living/silicon/robot/drone))) - user.changeNext_move(CLICK_CD_MELEE) - if(!getFireLoss()) - to_chat(user, "Nothing to fix!") - return - else if(!getFireLoss(TRUE)) - to_chat(user, "The damaged components are beyond saving!") - return - var/obj/item/stack/cable_coil/coil = W - adjustFireLoss(-30) - updatehealth() - add_fingerprint(user) - coil.use(1) - user.visible_message("\The [user] fixes some of the burnt wires on \the [src] with \the [coil].") - - else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside + user.changeNext_move(CLICK_CD_MELEE) + if(!getFireLoss()) + to_chat(user, "Nothing to fix!") + return + else if(!getFireLoss(TRUE)) + to_chat(user, "The damaged components are beyond saving!") + return + var/obj/item/stack/cable_coil/coil = W + adjustFireLoss(-30) + updatehealth() + add_fingerprint(user) + coil.use(1) + user.visible_message("\The [user] fixes some of the burnt wires on \the [src] with \the [coil].") + + else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside var/datum/robot_component/cell/C = components["power cell"] - if(wiresexposed) - to_chat(user, "Close the panel first.") - else if(cell) - to_chat(user, "There is a power cell already installed.") - else - user.drop_item() - W.loc = src - cell = W - to_chat(user, "You insert the power cell.") - - C.installed = 1 - C.wrapped = W - C.install() + if(wiresexposed) + to_chat(user, "Close the panel first.") + else if(cell) + to_chat(user, "There is a power cell already installed.") + else + user.drop_item() + W.loc = src + cell = W + to_chat(user, "You insert the power cell.") + + C.installed = 1 + C.wrapped = W + C.install() C.external_type = W.type // Update the cell component's `external_type` to the path of new cell - //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z - C.brute_damage = 0 - C.electronics_damage = 0 - diag_hud_set_borgcell() - - else if(istype(W, /obj/item/encryptionkey/) && opened) - if(radio)//sanityyyyyy - radio.attackby(W,user)//GTFO, you have your own procs - else - to_chat(user, "Unable to locate a radio.") - - else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card - if(emagged)//still allow them to open the cover - to_chat(user, "The interface seems slightly damaged.") - if(opened) - to_chat(user, "You must close the cover to swipe an ID card.") - else - if(allowed(W)) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") - update_icons() - else - to_chat(user, "Access denied.") - - else if(istype(W, /obj/item/borg/upgrade/)) - var/obj/item/borg/upgrade/U = W - if(!opened) - to_chat(user, "You must access the borg's internals!") - else if(!src.module && U.require_module) - to_chat(user, "The borg must choose a module before it can be upgraded!") - else if(U.locked) - to_chat(user, "The upgrade is locked and cannot be used yet!") - else - if(!user.drop_item()) - return - if(U.action(src)) - to_chat(user, "You apply the upgrade to [src].") - U.forceMove(src) - else - to_chat(user, "Upgrade error.") - - else if(istype(W, /obj/item/mmi_radio_upgrade)) - if(!opened) - to_chat(user, "You must access the borg's internals!") - return - else if(!mmi) - to_chat(user, "This cyborg does not have an MMI to augment!") - return - else if(mmi.radio) - to_chat(user, "A radio upgrade is already installed in the MMI!") - return - else if(user.drop_item()) - to_chat(user, "You apply the upgrade to [src].") - to_chat(src, "MMI radio capability installed.") - mmi.install_radio() - qdel(W) - else - return ..() - + //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z + C.brute_damage = 0 + C.electronics_damage = 0 + diag_hud_set_borgcell() + + else if(istype(W, /obj/item/encryptionkey/) && opened) + if(radio)//sanityyyyyy + radio.attackby(W,user)//GTFO, you have your own procs + else + to_chat(user, "Unable to locate a radio.") + + else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card + if(emagged)//still allow them to open the cover + to_chat(user, "The interface seems slightly damaged.") + if(opened) + to_chat(user, "You must close the cover to swipe an ID card.") + else + if(allowed(W)) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") + update_icons() + else + to_chat(user, "Access denied.") + + else if(istype(W, /obj/item/borg/upgrade/)) + var/obj/item/borg/upgrade/U = W + if(!opened) + to_chat(user, "You must access the borg's internals!") + else if(!src.module && U.require_module) + to_chat(user, "The borg must choose a module before it can be upgraded!") + else if(U.locked) + to_chat(user, "The upgrade is locked and cannot be used yet!") + else + if(!user.drop_item()) + return + if(U.action(src)) + to_chat(user, "You apply the upgrade to [src].") + U.forceMove(src) + else + to_chat(user, "Upgrade error.") + + else if(istype(W, /obj/item/mmi_radio_upgrade)) + if(!opened) + to_chat(user, "You must access the borg's internals!") + return + else if(!mmi) + to_chat(user, "This cyborg does not have an MMI to augment!") + return + else if(mmi.radio) + to_chat(user, "A radio upgrade is already installed in the MMI!") + return + else if(user.drop_item()) + to_chat(user, "You apply the upgrade to [src].") + to_chat(src, "MMI radio capability installed.") + mmi.install_radio() + qdel(W) + else + return ..() + /mob/living/silicon/robot/wirecutter_act(mob/user, obj/item/I) if(!opened) return @@ -814,661 +814,661 @@ var/list/robot_verbs_default = list( -/mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone) - if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. - spark_system.start() - ..() - -/mob/living/silicon/robot/emag_act(user as mob) - if(!ishuman(user) && !issilicon(user)) - return - var/mob/living/M = user - if(!opened)//Cover is closed - if(!is_emaggable) - to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable.") - else if(locked) - to_chat(user, "You emag the cover lock.") - locked = 0 - else - to_chat(user, "The cover is already unlocked.") - return - - if(opened)//Cover is open - if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice - if(wiresexposed) - to_chat(user, "You must close the panel first") - return - else - sleep(6) - emagged = 1 - SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown - if(src.hud_used) - src.hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. - disconnect_from_ai() - to_chat(user, "You emag [src]'s interface.") -// message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") - log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") - clear_supplied_laws() - clear_inherent_laws() - laws = new /datum/ai_laws/syndicate_override - var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [M.name]([M.key]) emagged [name]([key])") - set_zeroth_law("Only [M.real_name] and people [M.p_they()] designate[M.p_s()] as being such are Syndicate Agents.") - to_chat(src, "ALERT: Foreign software detected.") - sleep(5) - to_chat(src, "Initiating diagnostics...") - sleep(20) - to_chat(src, "SynBorg v1.7 loaded.") - sleep(5) - to_chat(src, "LAW SYNCHRONISATION ERROR") - sleep(5) - to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") - sleep(10) - to_chat(src, "> N") - sleep(20) - to_chat(src, "ERRORERRORERROR") - to_chat(src, "Obey these laws:") - laws.show_laws(src) - to_chat(src, "ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.") - SetLockdown(0) - if(src.module && istype(src.module, /obj/item/robot_module/miner)) - for(var/obj/item/pickaxe/drill/cyborg/D in src.module.modules) - qdel(D) - src.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(src.module) - src.module.rebuild() - if(src.module && istype(src.module, /obj/item/robot_module/medical)) - for(var/obj/item/borg_defib/F in src.module.modules) - F.safety = 0 - if(module) - module.module_type = "Malf" // For the cool factor - update_module_icon() - update_icons() - return - -/mob/living/silicon/robot/verb/unlock_own_cover() - set category = "Robot Commands" - set name = "Unlock Cover" - set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you." - if(locked) - switch(alert("You can not lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No")) - if("Yes") - locked = 0 - update_icons() - to_chat(usr, "You unlock your cover.") - -/mob/living/silicon/robot/attack_ghost(mob/user) - if(wiresexposed) - wires.Interact(user) - else - ..() //this calls the /mob/living/attack_ghost proc for the ghost health/cyborg analyzer - -/mob/living/silicon/robot/proc/allowed(obj/item/I) - var/obj/dummy = new /obj(null) // Create a dummy object to check access on as to avoid having to snowflake check_access on every mob - dummy.req_access = req_access - dummy.req_one_access = req_one_access - - if(dummy.check_access(I)) - qdel(dummy) - return 1 - - qdel(dummy) - return 0 - -/mob/living/silicon/robot/update_icons() - - overlays.Cut() - if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned. - if(custom_panel in custom_eye_names) - overlays += "eyes-[custom_panel]" - else - overlays += "eyes-[icon_state]" - else - overlays -= "eyes" - - if(opened) - var/panelprefix = "ov" - if(custom_sprite) //Custom borgs also have custom panels, heh - panelprefix = "[ckey]" - - if(custom_panel in custom_panel_names) //For default borgs with different panels - panelprefix = custom_panel - - if(wiresexposed) - overlays += "[panelprefix]-openpanel +w" - else if(cell) - overlays += "[panelprefix]-openpanel +c" - else - overlays += "[panelprefix]-openpanel -c" - - var/combat = list("Combat") - if(modtype in combat) - if(base_icon == "") - base_icon = icon_state - if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) - icon_state = "[base_icon]-roll" - else - icon_state = base_icon - if(module) - for(var/obj/item/borg/combat/shield/S in module.modules) - if(activated(S)) - overlays += "[base_icon]-shield" - update_fire() - -/mob/living/silicon/robot/proc/installed_modules() - if(weapon_lock) - to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") - return - - if(!module) - pick_module() - return - var/dat = {"Close -
-
- Activated Modules -
- - - - -
Module 1:[module_state_1 ? "[module_state_1]" : "No Module"]
Module 2:[module_state_2 ? "[module_state_2]" : "No Module"]
Module 3:[module_state_3 ? "[module_state_3]" : "No Module"]

- Installed Modules

- - "} - for(var/obj in module.modules) - if(!obj) - dat += text("") - else if(activated(obj)) - dat += text("") - else - dat += text("") - if(emagged || weapons_unlock) - if(activated(module.emag)) - dat += text("") - else - dat += text("") - dat += "
Resource depleted
[obj]Activated
[obj]Activate
[module.emag]Activated
[module.emag]Activate
" -/* - if(activated(obj)) - dat += text("[obj]: \[Activated | Deactivate\]
") - else - dat += text("[obj]: \[Activate | Deactivated\]
") -*/ - var/datum/browser/popup = new(src, "robotmod", "Modules") - popup.set_content(dat) - popup.open() - - -/mob/living/silicon/robot/Topic(href, href_list) - if(..()) - return 1 - - if(usr != src) - return 1 - - if(href_list["mach_close"]) - var/t1 = text("window=[href_list["mach_close"]]") - unset_machine() - src << browse(null, t1) - return 1 - - if(href_list["showalerts"]) - subsystem_alarm_monitor() - return 1 - - if(href_list["mod"]) - var/obj/item/O = locate(href_list["mod"]) - if(istype(O) && (O.loc == src)) - O.attack_self(src) - return 1 - - if(href_list["act"]) - var/obj/item/O = locate(href_list["act"]) - if(!istype(O) || !(O.loc == src || O.loc == src.module)) - return 1 - - activate_module(O) - installed_modules() - - if(href_list["deact"]) - var/obj/item/O = locate(href_list["deact"]) - if(activated(O)) - if(module_state_1 == O) - module_state_1 = null - contents -= O - else if(module_state_2 == O) - module_state_2 = null - contents -= O - else if(module_state_3 == O) - module_state_3 = null - contents -= O - else - to_chat(src, "Module isn't activated.") - else - to_chat(src, "Module isn't activated") - installed_modules() - return 1 - - return 1 - -/mob/living/silicon/robot/proc/radio_menu() - radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code - -/mob/living/silicon/robot/proc/control_headlamp() - if(stat || lamp_recharging || low_power_mode) - to_chat(src, "This function is currently offline.") - return - -//Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0. - lamp_intensity = (lamp_intensity+2) % (lamp_max+2) - to_chat(src, "[lamp_intensity ? "Headlamp power set to Level [lamp_intensity/2]" : "Headlamp disabled."]") - update_headlamp() - -/mob/living/silicon/robot/proc/update_headlamp(var/turn_off = 0, var/cooldown = 100) - set_light(0) - - if(lamp_intensity && (turn_off || stat || low_power_mode)) - to_chat(src, "Your headlamp has been deactivated.") - lamp_intensity = 0 - lamp_recharging = 1 - spawn(cooldown) //10 seconds by default, if the source of the deactivation does not keep stat that long. - lamp_recharging = 0 - else - set_light(light_range + lamp_intensity) - - if(lamp_button) - lamp_button.icon_state = "lamp[lamp_intensity]" - - update_icons() - -/mob/living/silicon/robot/proc/deconstruct() - var/turf/T = get_turf(src) - if(robot_suit) - robot_suit.forceMove(T) - robot_suit.l_leg.forceMove(T) - robot_suit.l_leg = null - robot_suit.r_leg.forceMove(T) - robot_suit.r_leg = null - new /obj/item/stack/cable_coil(T, robot_suit.chest.wired) - robot_suit.chest.forceMove(T) - robot_suit.chest.wired = FALSE - robot_suit.chest = null - robot_suit.l_arm.forceMove(T) - robot_suit.l_arm = null - robot_suit.r_arm.forceMove(T) - robot_suit.r_arm = null - robot_suit.head.forceMove(T) - robot_suit.head.flash1.forceMove(T) - robot_suit.head.flash1.burn_out() - robot_suit.head.flash1 = null - robot_suit.head.flash2.forceMove(T) - robot_suit.head.flash2.burn_out() - robot_suit.head.flash2 = null - robot_suit.head = null - robot_suit.updateicon() - else - new /obj/item/robot_parts/robot_suit(T) - new /obj/item/robot_parts/l_leg(T) - new /obj/item/robot_parts/r_leg(T) - new /obj/item/stack/cable_coil(T, 1) - new /obj/item/robot_parts/chest(T) - new /obj/item/robot_parts/l_arm(T) - new /obj/item/robot_parts/r_arm(T) - new /obj/item/robot_parts/head(T) - var/b - for(b=0, b!=2, b++) - var/obj/item/flash/F = new /obj/item/flash(T) - F.burn_out() - if(cell) //Sanity check. - cell.forceMove(T) - cell = null - qdel(src) - -#define BORG_CAMERA_BUFFER 30 -/mob/living/silicon/robot/Move(a, b, flag) - var/oldLoc = src.loc - . = ..() - if(.) - if(src.camera) - if(!updating) - updating = 1 - spawn(BORG_CAMERA_BUFFER) - if(camera && oldLoc != src.loc) - cameranet.updatePortableCamera(src.camera) - updating = 0 - if(module) - if(module.type == /obj/item/robot_module/janitor) - var/turf/tile = loc - if(isturf(tile)) - var/floor_only = TRUE - for(var/A in tile) - if(istype(A, /obj/effect)) - if(is_cleanable(A)) - var/obj/effect/decal/cleanable/blood/B = A - if(istype(B) && B.off_floor) - floor_only = FALSE - else - qdel(A) - else if(istype(A, /obj/item)) - var/obj/item/cleaned_item = A - cleaned_item.clean_blood() - else if(istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/cleaned_human = A - if(cleaned_human.lying) - if(cleaned_human.head) - cleaned_human.head.clean_blood() - cleaned_human.update_inv_head(0,0) - if(cleaned_human.wear_suit) - cleaned_human.wear_suit.clean_blood() - cleaned_human.update_inv_wear_suit(0,0) - else if(cleaned_human.w_uniform) - cleaned_human.w_uniform.clean_blood() - cleaned_human.update_inv_w_uniform(0,0) - if(cleaned_human.shoes) - cleaned_human.shoes.clean_blood() - cleaned_human.update_inv_shoes(0,0) - cleaned_human.clean_blood() - to_chat(cleaned_human, "[src] cleans your face!") - if(floor_only) - tile.clean_blood() - return -#undef BORG_CAMERA_BUFFER - -/mob/living/silicon/robot/proc/self_destruct() - if(emagged) - if(mmi) - qdel(mmi) - explosion(src.loc,1,2,4,flame_range = 2) - else - explosion(src.loc,-1,0,2) - gib() - return - -/mob/living/silicon/robot/proc/UnlinkSelf() - disconnect_from_ai() - lawupdate = 0 - lockcharge = 0 - canmove = 1 - scrambledcodes = 1 - //Disconnect it's camera so it's not so easily tracked. - QDEL_NULL(src.camera) - // I'm trying to get the Cyborg to not be listed in the camera list - // Instead of being listed as "deactivated". The downside is that I'm going - // to have to check if every camera is null or not before doing anything, to prevent runtime errors. - // I could change the network to null but I don't know what would happen, and it seems too hacky for me. - -/mob/living/silicon/robot/proc/ResetSecurityCodes() - set category = "Robot Commands" - set name = "Reset Identity Codes" - set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permanently severs you from your AI and the robotics console and will deactivate your camera system." - - var/mob/living/silicon/robot/R = src - - if(R) - R.UnlinkSelf() - to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") - src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes - -/mob/living/silicon/robot/mode() - set name = "Activate Held Object" - set category = "IC" - set src = usr - - var/obj/item/W = get_active_hand() - if(W) - W.attack_self(src) - - return - -/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) - // They stay locked down if their wire is cut. - if(wires.LockedCut()) - state = 1 - if(state) - throw_alert("locked", /obj/screen/alert/locked) - else - clear_alert("locked") - lockcharge = state - update_canmove() - -/mob/living/silicon/robot/proc/choose_icon(var/triesleft, var/list/module_sprites) - - if(triesleft<1 || !module_sprites.len) - return - else - triesleft-- - - var/icontype - lockcharge = 1 //Locks borg until it select an icon to avoid secborgs running around with a standard sprite - icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chances." : "This is your last try."]", "Robot", null, null) in module_sprites - - if(icontype) - if(icontype == "Custom") - icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' - else - icon = 'icons/mob/robots.dmi' - icon_state = module_sprites[icontype] - if(icontype == "Bro") - module.module_type = "Brobot" - update_module_icon() - lockcharge = null - var/list/names = splittext(icontype, "-") - custom_panel = trim(names[1]) - else - to_chat(src, "Something is badly wrong with the sprite selection. Harass a coder.") - icon_state = module_sprites[1] - lockcharge = null - return - - update_icons() - - if(triesleft >= 1) - var/choice = input("Look at your icon - is this what you want?") in list("Yes","No") - if(choice=="No") - choose_icon(triesleft, module_sprites) - return - else - triesleft = 0 - return - else - to_chat(src, "Your icon has been set. You now require a module reset to change it.") - -/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/oldname, var/newname) - if(!connected_ai) - return - switch(notifytype) - if(1) //New Cyborg - to_chat(connected_ai, "

NOTICE - New cyborg connection detected: [name]
") - if(2) //New Module - to_chat(connected_ai, "

NOTICE - Cyborg module change detected: [name] has loaded the [designation] module.
") - if(3) //New Name - to_chat(connected_ai, "

NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].
") - -/mob/living/silicon/robot/proc/disconnect_from_ai() - if(connected_ai) - sync() // One last sync attempt - connected_ai.connected_robots -= src - connected_ai = null - -/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) - if(AI && AI != connected_ai) - disconnect_from_ai() - connected_ai = AI - connected_ai.connected_robots |= src - notify_ai(1) - sync() - -/mob/living/silicon/robot/adjustOxyLoss(var/amount) - if(suiciding) - return ..() - else - return STATUS_UPDATE_NONE - -/mob/living/silicon/robot/regenerate_icons() - ..() - update_module_icon() - -/mob/living/silicon/robot/deathsquad - base_icon = "nano_bloodhound" - icon_state = "nano_bloodhound" - designation = "SpecOps" - lawupdate = 0 - scrambledcodes = 1 +/mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone) + if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. + spark_system.start() + ..() + +/mob/living/silicon/robot/emag_act(user as mob) + if(!ishuman(user) && !issilicon(user)) + return + var/mob/living/M = user + if(!opened)//Cover is closed + if(!is_emaggable) + to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable.") + else if(locked) + to_chat(user, "You emag the cover lock.") + locked = 0 + else + to_chat(user, "The cover is already unlocked.") + return + + if(opened)//Cover is open + if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice + if(wiresexposed) + to_chat(user, "You must close the panel first") + return + else + sleep(6) + emagged = 1 + SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown + if(src.hud_used) + src.hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. + disconnect_from_ai() + to_chat(user, "You emag [src]'s interface.") +// message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") + log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") + clear_supplied_laws() + clear_inherent_laws() + laws = new /datum/ai_laws/syndicate_override + var/time = time2text(world.realtime,"hh:mm:ss") + lawchanges.Add("[time] : [M.name]([M.key]) emagged [name]([key])") + set_zeroth_law("Only [M.real_name] and people [M.p_they()] designate[M.p_s()] as being such are Syndicate Agents.") + to_chat(src, "ALERT: Foreign software detected.") + sleep(5) + to_chat(src, "Initiating diagnostics...") + sleep(20) + to_chat(src, "SynBorg v1.7 loaded.") + sleep(5) + to_chat(src, "LAW SYNCHRONISATION ERROR") + sleep(5) + to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") + sleep(10) + to_chat(src, "> N") + sleep(20) + to_chat(src, "ERRORERRORERROR") + to_chat(src, "Obey these laws:") + laws.show_laws(src) + to_chat(src, "ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.") + SetLockdown(0) + if(src.module && istype(src.module, /obj/item/robot_module/miner)) + for(var/obj/item/pickaxe/drill/cyborg/D in src.module.modules) + qdel(D) + src.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(src.module) + src.module.rebuild() + if(src.module && istype(src.module, /obj/item/robot_module/medical)) + for(var/obj/item/borg_defib/F in src.module.modules) + F.safety = 0 + if(module) + module.module_type = "Malf" // For the cool factor + update_module_icon() + update_icons() + return + +/mob/living/silicon/robot/verb/unlock_own_cover() + set category = "Robot Commands" + set name = "Unlock Cover" + set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you." + if(locked) + switch(alert("You can not lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No")) + if("Yes") + locked = 0 + update_icons() + to_chat(usr, "You unlock your cover.") + +/mob/living/silicon/robot/attack_ghost(mob/user) + if(wiresexposed) + wires.Interact(user) + else + ..() //this calls the /mob/living/attack_ghost proc for the ghost health/cyborg analyzer + +/mob/living/silicon/robot/proc/allowed(obj/item/I) + var/obj/dummy = new /obj(null) // Create a dummy object to check access on as to avoid having to snowflake check_access on every mob + dummy.req_access = req_access + dummy.req_one_access = req_one_access + + if(dummy.check_access(I)) + qdel(dummy) + return 1 + + qdel(dummy) + return 0 + +/mob/living/silicon/robot/update_icons() + + overlays.Cut() + if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned. + if(custom_panel in custom_eye_names) + overlays += "eyes-[custom_panel]" + else + overlays += "eyes-[icon_state]" + else + overlays -= "eyes" + + if(opened) + var/panelprefix = "ov" + if(custom_sprite) //Custom borgs also have custom panels, heh + panelprefix = "[ckey]" + + if(custom_panel in custom_panel_names) //For default borgs with different panels + panelprefix = custom_panel + + if(wiresexposed) + overlays += "[panelprefix]-openpanel +w" + else if(cell) + overlays += "[panelprefix]-openpanel +c" + else + overlays += "[panelprefix]-openpanel -c" + + var/combat = list("Combat") + if(modtype in combat) + if(base_icon == "") + base_icon = icon_state + if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) + icon_state = "[base_icon]-roll" + else + icon_state = base_icon + if(module) + for(var/obj/item/borg/combat/shield/S in module.modules) + if(activated(S)) + overlays += "[base_icon]-shield" + update_fire() + +/mob/living/silicon/robot/proc/installed_modules() + if(weapon_lock) + to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") + return + + if(!module) + pick_module() + return + var/dat = {"Close +
+
+ Activated Modules +
+ + + + +
Module 1:[module_state_1 ? "[module_state_1]" : "No Module"]
Module 2:[module_state_2 ? "[module_state_2]" : "No Module"]
Module 3:[module_state_3 ? "[module_state_3]" : "No Module"]

+ Installed Modules

+ + "} + for(var/obj in module.modules) + if(!obj) + dat += text("") + else if(activated(obj)) + dat += text("") + else + dat += text("") + if(emagged || weapons_unlock) + if(activated(module.emag)) + dat += text("") + else + dat += text("") + dat += "
Resource depleted
[obj]Activated
[obj]Activate
[module.emag]Activated
[module.emag]Activate
" +/* + if(activated(obj)) + dat += text("[obj]: \[Activated | Deactivate\]
") + else + dat += text("[obj]: \[Activate | Deactivated\]
") +*/ + var/datum/browser/popup = new(src, "robotmod", "Modules") + popup.set_content(dat) + popup.open() + + +/mob/living/silicon/robot/Topic(href, href_list) + if(..()) + return 1 + + if(usr != src) + return 1 + + if(href_list["mach_close"]) + var/t1 = text("window=[href_list["mach_close"]]") + unset_machine() + src << browse(null, t1) + return 1 + + if(href_list["showalerts"]) + subsystem_alarm_monitor() + return 1 + + if(href_list["mod"]) + var/obj/item/O = locate(href_list["mod"]) + if(istype(O) && (O.loc == src)) + O.attack_self(src) + return 1 + + if(href_list["act"]) + var/obj/item/O = locate(href_list["act"]) + if(!istype(O) || !(O.loc == src || O.loc == src.module)) + return 1 + + activate_module(O) + installed_modules() + + if(href_list["deact"]) + var/obj/item/O = locate(href_list["deact"]) + if(activated(O)) + if(module_state_1 == O) + module_state_1 = null + contents -= O + else if(module_state_2 == O) + module_state_2 = null + contents -= O + else if(module_state_3 == O) + module_state_3 = null + contents -= O + else + to_chat(src, "Module isn't activated.") + else + to_chat(src, "Module isn't activated") + installed_modules() + return 1 + + return 1 + +/mob/living/silicon/robot/proc/radio_menu() + radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code + +/mob/living/silicon/robot/proc/control_headlamp() + if(stat || lamp_recharging || low_power_mode) + to_chat(src, "This function is currently offline.") + return + +//Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0. + lamp_intensity = (lamp_intensity+2) % (lamp_max+2) + to_chat(src, "[lamp_intensity ? "Headlamp power set to Level [lamp_intensity/2]" : "Headlamp disabled."]") + update_headlamp() + +/mob/living/silicon/robot/proc/update_headlamp(var/turn_off = 0, var/cooldown = 100) + set_light(0) + + if(lamp_intensity && (turn_off || stat || low_power_mode)) + to_chat(src, "Your headlamp has been deactivated.") + lamp_intensity = 0 + lamp_recharging = 1 + spawn(cooldown) //10 seconds by default, if the source of the deactivation does not keep stat that long. + lamp_recharging = 0 + else + set_light(light_range + lamp_intensity) + + if(lamp_button) + lamp_button.icon_state = "lamp[lamp_intensity]" + + update_icons() + +/mob/living/silicon/robot/proc/deconstruct() + var/turf/T = get_turf(src) + if(robot_suit) + robot_suit.forceMove(T) + robot_suit.l_leg.forceMove(T) + robot_suit.l_leg = null + robot_suit.r_leg.forceMove(T) + robot_suit.r_leg = null + new /obj/item/stack/cable_coil(T, robot_suit.chest.wired) + robot_suit.chest.forceMove(T) + robot_suit.chest.wired = FALSE + robot_suit.chest = null + robot_suit.l_arm.forceMove(T) + robot_suit.l_arm = null + robot_suit.r_arm.forceMove(T) + robot_suit.r_arm = null + robot_suit.head.forceMove(T) + robot_suit.head.flash1.forceMove(T) + robot_suit.head.flash1.burn_out() + robot_suit.head.flash1 = null + robot_suit.head.flash2.forceMove(T) + robot_suit.head.flash2.burn_out() + robot_suit.head.flash2 = null + robot_suit.head = null + robot_suit.updateicon() + else + new /obj/item/robot_parts/robot_suit(T) + new /obj/item/robot_parts/l_leg(T) + new /obj/item/robot_parts/r_leg(T) + new /obj/item/stack/cable_coil(T, 1) + new /obj/item/robot_parts/chest(T) + new /obj/item/robot_parts/l_arm(T) + new /obj/item/robot_parts/r_arm(T) + new /obj/item/robot_parts/head(T) + var/b + for(b=0, b!=2, b++) + var/obj/item/flash/F = new /obj/item/flash(T) + F.burn_out() + if(cell) //Sanity check. + cell.forceMove(T) + cell = null + qdel(src) + +#define BORG_CAMERA_BUFFER 30 +/mob/living/silicon/robot/Move(a, b, flag) + var/oldLoc = src.loc + . = ..() + if(.) + if(src.camera) + if(!updating) + updating = 1 + spawn(BORG_CAMERA_BUFFER) + if(camera && oldLoc != src.loc) + cameranet.updatePortableCamera(src.camera) + updating = 0 + if(module) + if(module.type == /obj/item/robot_module/janitor) + var/turf/tile = loc + if(isturf(tile)) + var/floor_only = TRUE + for(var/A in tile) + if(istype(A, /obj/effect)) + if(is_cleanable(A)) + var/obj/effect/decal/cleanable/blood/B = A + if(istype(B) && B.off_floor) + floor_only = FALSE + else + qdel(A) + else if(istype(A, /obj/item)) + var/obj/item/cleaned_item = A + cleaned_item.clean_blood() + else if(istype(A, /mob/living/carbon/human)) + var/mob/living/carbon/human/cleaned_human = A + if(cleaned_human.lying) + if(cleaned_human.head) + cleaned_human.head.clean_blood() + cleaned_human.update_inv_head(0,0) + if(cleaned_human.wear_suit) + cleaned_human.wear_suit.clean_blood() + cleaned_human.update_inv_wear_suit(0,0) + else if(cleaned_human.w_uniform) + cleaned_human.w_uniform.clean_blood() + cleaned_human.update_inv_w_uniform(0,0) + if(cleaned_human.shoes) + cleaned_human.shoes.clean_blood() + cleaned_human.update_inv_shoes(0,0) + cleaned_human.clean_blood() + to_chat(cleaned_human, "[src] cleans your face!") + if(floor_only) + tile.clean_blood() + return +#undef BORG_CAMERA_BUFFER + +/mob/living/silicon/robot/proc/self_destruct() + if(emagged) + if(mmi) + qdel(mmi) + explosion(src.loc,1,2,4,flame_range = 2) + else + explosion(src.loc,-1,0,2) + gib() + return + +/mob/living/silicon/robot/proc/UnlinkSelf() + disconnect_from_ai() + lawupdate = 0 + lockcharge = 0 + canmove = 1 + scrambledcodes = 1 + //Disconnect it's camera so it's not so easily tracked. + QDEL_NULL(src.camera) + // I'm trying to get the Cyborg to not be listed in the camera list + // Instead of being listed as "deactivated". The downside is that I'm going + // to have to check if every camera is null or not before doing anything, to prevent runtime errors. + // I could change the network to null but I don't know what would happen, and it seems too hacky for me. + +/mob/living/silicon/robot/proc/ResetSecurityCodes() + set category = "Robot Commands" + set name = "Reset Identity Codes" + set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permanently severs you from your AI and the robotics console and will deactivate your camera system." + + var/mob/living/silicon/robot/R = src + + if(R) + R.UnlinkSelf() + to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") + src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes + +/mob/living/silicon/robot/mode() + set name = "Activate Held Object" + set category = "IC" + set src = usr + + var/obj/item/W = get_active_hand() + if(W) + W.attack_self(src) + + return + +/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) + // They stay locked down if their wire is cut. + if(wires.LockedCut()) + state = 1 + if(state) + throw_alert("locked", /obj/screen/alert/locked) + else + clear_alert("locked") + lockcharge = state + update_canmove() + +/mob/living/silicon/robot/proc/choose_icon(var/triesleft, var/list/module_sprites) + + if(triesleft<1 || !module_sprites.len) + return + else + triesleft-- + + var/icontype + lockcharge = 1 //Locks borg until it select an icon to avoid secborgs running around with a standard sprite + icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chances." : "This is your last try."]", "Robot", null, null) in module_sprites + + if(icontype) + if(icontype == "Custom") + icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' + else + icon = 'icons/mob/robots.dmi' + icon_state = module_sprites[icontype] + if(icontype == "Bro") + module.module_type = "Brobot" + update_module_icon() + lockcharge = null + var/list/names = splittext(icontype, "-") + custom_panel = trim(names[1]) + else + to_chat(src, "Something is badly wrong with the sprite selection. Harass a coder.") + icon_state = module_sprites[1] + lockcharge = null + return + + update_icons() + + if(triesleft >= 1) + var/choice = input("Look at your icon - is this what you want?") in list("Yes","No") + if(choice=="No") + choose_icon(triesleft, module_sprites) + return + else + triesleft = 0 + return + else + to_chat(src, "Your icon has been set. You now require a module reset to change it.") + +/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/oldname, var/newname) + if(!connected_ai) + return + switch(notifytype) + if(1) //New Cyborg + to_chat(connected_ai, "

NOTICE - New cyborg connection detected: [name]
") + if(2) //New Module + to_chat(connected_ai, "

NOTICE - Cyborg module change detected: [name] has loaded the [designation] module.
") + if(3) //New Name + to_chat(connected_ai, "

NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].
") + +/mob/living/silicon/robot/proc/disconnect_from_ai() + if(connected_ai) + sync() // One last sync attempt + connected_ai.connected_robots -= src + connected_ai = null + +/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) + if(AI && AI != connected_ai) + disconnect_from_ai() + connected_ai = AI + connected_ai.connected_robots |= src + notify_ai(1) + sync() + +/mob/living/silicon/robot/adjustOxyLoss(var/amount) + if(suiciding) + return ..() + else + return STATUS_UPDATE_NONE + +/mob/living/silicon/robot/regenerate_icons() + ..() + update_module_icon() + +/mob/living/silicon/robot/deathsquad + base_icon = "nano_bloodhound" + icon_state = "nano_bloodhound" + designation = "SpecOps" + lawupdate = 0 + scrambledcodes = 1 req_one_access = list(ACCESS_CENT_SPECOPS) - ionpulse = 1 - magpulse = 1 - pdahide = 1 - eye_protection = 2 // Immunity to flashes and the visual part of flashbangs - ear_protection = 1 // Immunity to the audio part of flashbangs - allow_rename = FALSE - modtype = "Commando" - faction = list("nanotrasen") - is_emaggable = FALSE - -/mob/living/silicon/robot/deathsquad/New(loc) - ..() - cell = new /obj/item/stock_parts/cell/hyper(src) - -/mob/living/silicon/robot/deathsquad/init() - laws = new /datum/ai_laws/deathsquad - module = new /obj/item/robot_module/deathsquad(src) - - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - radio = new /obj/item/radio/borg/deathsquad(src) - radio.recalculateChannels() - - playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) - -/mob/living/silicon/robot/combat - base_icon = "droidcombat" - icon_state = "droidcombat" - modtype = "Combat" - designation = "Combat" - -/mob/living/silicon/robot/combat/init() - ..() - module = new /obj/item/robot_module/combat(src) - module.channels = list("Security" = 1) - //languages - module.add_languages(src) - //subsystems - module.add_subsystems_and_actions(src) - - status_flags &= ~CANPUSH - - radio.config(module.channels) - notify_ai(2) - -/mob/living/silicon/robot/ert - designation = "ERT" - lawupdate = 0 - scrambledcodes = 1 + ionpulse = 1 + magpulse = 1 + pdahide = 1 + eye_protection = 2 // Immunity to flashes and the visual part of flashbangs + ear_protection = 1 // Immunity to the audio part of flashbangs + allow_rename = FALSE + modtype = "Commando" + faction = list("nanotrasen") + is_emaggable = FALSE + +/mob/living/silicon/robot/deathsquad/New(loc) + ..() + cell = new /obj/item/stock_parts/cell/hyper(src) + +/mob/living/silicon/robot/deathsquad/init() + laws = new /datum/ai_laws/deathsquad + module = new /obj/item/robot_module/deathsquad(src) + + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + radio = new /obj/item/radio/borg/deathsquad(src) + radio.recalculateChannels() + + playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) + +/mob/living/silicon/robot/combat + base_icon = "droidcombat" + icon_state = "droidcombat" + modtype = "Combat" + designation = "Combat" + +/mob/living/silicon/robot/combat/init() + ..() + module = new /obj/item/robot_module/combat(src) + module.channels = list("Security" = 1) + //languages + module.add_languages(src) + //subsystems + module.add_subsystems_and_actions(src) + + status_flags &= ~CANPUSH + + radio.config(module.channels) + notify_ai(2) + +/mob/living/silicon/robot/ert + designation = "ERT" + lawupdate = 0 + scrambledcodes = 1 req_one_access = list(ACCESS_CENT_SPECOPS) - ionpulse = 1 - - force_modules = list("Engineering", "Medical", "Security") - static_radio_channels = 1 - allow_rename = FALSE - weapons_unlock = TRUE - - -/mob/living/silicon/robot/ert/init() - laws = new /datum/ai_laws/ert_override - radio = new /obj/item/radio/borg/ert(src) - radio.recalculateChannels() - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - -/mob/living/silicon/robot/ert/New(loc, cyborg_unlock) - ..(loc) - cell = new /obj/item/stock_parts/cell/hyper(src) - var/rnum = rand(1,1000) - var/borgname = "ERT [rnum]" - name = borgname - custom_name = borgname - real_name = name - mind = new - mind.current = src - mind.original = src - mind.assigned_role = SPECIAL_ROLE_ERT - mind.special_role = SPECIAL_ROLE_ERT - if(cyborg_unlock) - crisis = 1 - if(!(mind in SSticker.minds)) - SSticker.minds += mind - SSticker.mode.ert += mind - -/mob/living/silicon/robot/ert/gamma - crisis = 1 - -/mob/living/silicon/robot/emp_act(severity) - ..() - switch(severity) - if(1) - disable_component("comms", 160) - if(2) - disable_component("comms", 60) - -/mob/living/silicon/robot/extinguish_light() - update_headlamp(1, 150) - -/mob/living/silicon/robot/rejuvenate() - ..() - var/brute = 1000 - var/burn = 1000 - var/list/datum/robot_component/borked_parts = get_damaged_components(TRUE, TRUE, TRUE, TRUE) - for(var/datum/robot_component/borked_part in borked_parts) - brute = borked_part.brute_damage - burn = borked_part.electronics_damage - borked_part.installed = 1 - borked_part.wrapped = new borked_part.external_type + ionpulse = 1 + + force_modules = list("Engineering", "Medical", "Security") + static_radio_channels = 1 + allow_rename = FALSE + weapons_unlock = TRUE + + +/mob/living/silicon/robot/ert/init() + laws = new /datum/ai_laws/ert_override + radio = new /obj/item/radio/borg/ert(src) + radio.recalculateChannels() + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + +/mob/living/silicon/robot/ert/New(loc, cyborg_unlock) + ..(loc) + cell = new /obj/item/stock_parts/cell/hyper(src) + var/rnum = rand(1,1000) + var/borgname = "ERT [rnum]" + name = borgname + custom_name = borgname + real_name = name + mind = new + mind.current = src + mind.original = src + mind.assigned_role = SPECIAL_ROLE_ERT + mind.special_role = SPECIAL_ROLE_ERT + if(cyborg_unlock) + crisis = 1 + if(!(mind in SSticker.minds)) + SSticker.minds += mind + SSticker.mode.ert += mind + +/mob/living/silicon/robot/ert/gamma + crisis = 1 + +/mob/living/silicon/robot/emp_act(severity) + ..() + switch(severity) + if(1) + disable_component("comms", 160) + if(2) + disable_component("comms", 60) + +/mob/living/silicon/robot/extinguish_light() + update_headlamp(1, 150) + +/mob/living/silicon/robot/rejuvenate() + ..() + var/brute = 1000 + var/burn = 1000 + var/list/datum/robot_component/borked_parts = get_damaged_components(TRUE, TRUE, TRUE, TRUE) + for(var/datum/robot_component/borked_part in borked_parts) + brute = borked_part.brute_damage + burn = borked_part.electronics_damage + borked_part.installed = 1 + borked_part.wrapped = new borked_part.external_type if(ispath(borked_part.external_type, /obj/item/stock_parts/cell)) // is the broken part a cell? cell = new borked_part.external_type // borgs that have their cell destroyed have their `cell` var set to null. we need create a new cell for them based on their old cell type. - borked_part.heal_damage(brute,burn) - borked_part.install() - -/mob/living/silicon/robot/proc/check_sprite(spritename) - . = FALSE - - var/static/all_borg_icon_states = icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi') - if(spritename in all_borg_icon_states) - . = TRUE - -/mob/living/silicon/robot/check_eye_prot() - return eye_protection - -/mob/living/silicon/robot/check_ear_prot() - return ear_protection - -/mob/living/silicon/robot/update_sight() - if(!client) - return - - if(stat == DEAD) - grant_death_vision() - return - - see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) - sight = initial(sight) - lighting_alpha = initial(lighting_alpha) - - if(client.eye != src) - var/atom/A = client.eye - if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. - return - - if(sight_mode & BORGMESON) - sight |= SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - - if(sight_mode & BORGXRAY) - sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_invisible = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - see_in_dark = 8 - - if(sight_mode & BORGTHERM) - sight |= SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - - SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() + borked_part.heal_damage(brute,burn) + borked_part.install() + +/mob/living/silicon/robot/proc/check_sprite(spritename) + . = FALSE + + var/static/all_borg_icon_states = icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi') + if(spritename in all_borg_icon_states) + . = TRUE + +/mob/living/silicon/robot/check_eye_prot() + return eye_protection + +/mob/living/silicon/robot/check_ear_prot() + return ear_protection + +/mob/living/silicon/robot/update_sight() + if(!client) + return + + if(stat == DEAD) + grant_death_vision() + return + + see_invisible = initial(see_invisible) + see_in_dark = initial(see_in_dark) + sight = initial(sight) + lighting_alpha = initial(lighting_alpha) + + if(client.eye != src) + var/atom/A = client.eye + if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. + return + + if(sight_mode & BORGMESON) + sight |= SEE_TURFS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + + if(sight_mode & BORGXRAY) + sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) + see_invisible = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + see_in_dark = 8 + + if(sight_mode & BORGTHERM) + sight |= SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + + SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) + sync_lighting_plane_alpha() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index c7a93368433..544b3ae0a98 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -1,105 +1,105 @@ -/mob/living/silicon - gender = NEUTER - robot_talk_understand = 1 - voice_name = "synthesized voice" - has_unlimited_silicon_privilege = 1 - var/syndicate = 0 - var/const/MAIN_CHANNEL = "Main Frequency" - var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws - var/list/stating_laws = list()// Channels laws are currently being stated on - var/list/alarms_to_show = list() - var/list/alarms_to_clear = list() - //var/list/hud_list[10] - var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer - var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to - var/designation = "" - var/obj/item/camera/siliconcam/aiCamera = null //photography -//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented. - var/speak_statement = "states" - var/speak_exclamation = "declares" - var/speak_query = "queries" - var/pose //Yes, now AIs can pose too. - var/death_sound = 'sound/voice/borg_deathsound.ogg' - - //var/sensor_mode = 0 //Determines the current HUD. - - var/next_alarm_notice - var/list/datum/alarm/queued_alarms = new() - - hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD) - - - var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use - var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use - var/d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED //There is only one kind of diag hud - - var/obj/item/radio/common_radio - -/mob/living/silicon/New() - GLOB.silicon_mob_list |= src - ..() - var/datum/atom_hud/data/diagnostic/diag_hud = huds[DATA_HUD_DIAGNOSTIC] - diag_hud.add_to_hud(src) - diag_hud_set_status() - diag_hud_set_health() - add_language("Galactic Common") - init_subsystems() - -/mob/living/silicon/med_hud_set_health() - return //we use a different hud - -/mob/living/silicon/med_hud_set_status() - return //we use a different hud - -/mob/living/silicon/Destroy() - GLOB.silicon_mob_list -= src - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.unregister(src) - return ..() - -/mob/living/silicon/rename_character(oldname, newname) - // we actually don't want it changing minds and stuff - if(!newname) - return 0 - - real_name = newname - name = real_name - return 1 - -/mob/living/silicon/proc/show_laws() - return - -/mob/living/silicon/drop_item() - return - -/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) - return FALSE //So borgs they don't die trying to fix wiring - -/mob/living/silicon/emp_act(severity) - switch(severity) - if(1) - src.take_organ_damage(20) - Stun(8) - if(2) - src.take_organ_damage(10) - Stun(3) - flash_eyes(affect_silicon = 1) - to_chat(src, "*BZZZT*") - to_chat(src, "Warning: Electromagnetic pulse detected.") - ..() - - -/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) - return - -/mob/living/silicon/can_inject(mob/user, error_msg, target_zone, penetrate_thick) - if(error_msg) - to_chat(user, "[p_their(TRUE)] outer shell is too tough.") - return FALSE - -/mob/living/silicon/IsAdvancedToolUser() - return TRUE - +/mob/living/silicon + gender = NEUTER + robot_talk_understand = 1 + voice_name = "synthesized voice" + has_unlimited_silicon_privilege = 1 + var/syndicate = 0 + var/const/MAIN_CHANNEL = "Main Frequency" + var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws + var/list/stating_laws = list()// Channels laws are currently being stated on + var/list/alarms_to_show = list() + var/list/alarms_to_clear = list() + //var/list/hud_list[10] + var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer + var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to + var/designation = "" + var/obj/item/camera/siliconcam/aiCamera = null //photography +//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented. + var/speak_statement = "states" + var/speak_exclamation = "declares" + var/speak_query = "queries" + var/pose //Yes, now AIs can pose too. + var/death_sound = 'sound/voice/borg_deathsound.ogg' + + //var/sensor_mode = 0 //Determines the current HUD. + + var/next_alarm_notice + var/list/datum/alarm/queued_alarms = new() + + hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD) + + + var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use + var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use + var/d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED //There is only one kind of diag hud + + var/obj/item/radio/common_radio + +/mob/living/silicon/New() + GLOB.silicon_mob_list |= src + ..() + var/datum/atom_hud/data/diagnostic/diag_hud = huds[DATA_HUD_DIAGNOSTIC] + diag_hud.add_to_hud(src) + diag_hud_set_status() + diag_hud_set_health() + add_language("Galactic Common") + init_subsystems() + +/mob/living/silicon/med_hud_set_health() + return //we use a different hud + +/mob/living/silicon/med_hud_set_status() + return //we use a different hud + +/mob/living/silicon/Destroy() + GLOB.silicon_mob_list -= src + for(var/datum/alarm_handler/AH in alarm_handlers) + AH.unregister(src) + return ..() + +/mob/living/silicon/rename_character(oldname, newname) + // we actually don't want it changing minds and stuff + if(!newname) + return 0 + + real_name = newname + name = real_name + return 1 + +/mob/living/silicon/proc/show_laws() + return + +/mob/living/silicon/drop_item() + return + +/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) + return FALSE //So borgs they don't die trying to fix wiring + +/mob/living/silicon/emp_act(severity) + switch(severity) + if(1) + src.take_organ_damage(20) + Stun(8) + if(2) + src.take_organ_damage(10) + Stun(3) + flash_eyes(affect_silicon = 1) + to_chat(src, "*BZZZT*") + to_chat(src, "Warning: Electromagnetic pulse detected.") + ..() + + +/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) + return + +/mob/living/silicon/can_inject(mob/user, error_msg, target_zone, penetrate_thick) + if(error_msg) + to_chat(user, "[p_their(TRUE)] outer shell is too tough.") + return FALSE + +/mob/living/silicon/IsAdvancedToolUser() + return TRUE + /mob/living/silicon/robot/welder_act(mob/user, obj/item/I) if(user.a_intent != INTENT_HELP) return @@ -119,242 +119,242 @@ user.visible_message("[user] patches some dents on [src] with [I].") -/mob/living/silicon/bullet_act(var/obj/item/projectile/Proj) - - - if(!Proj.nodamage) - switch(Proj.damage_type) - if(BRUTE) - adjustBruteLoss(Proj.damage) - if(BURN) - adjustFireLoss(Proj.damage) - - Proj.on_hit(src,2) - - return 2 - -/mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/negate_armor = 0) - return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now -/* - if(!effect || (blocked >= 2)) return 0 - switch(effecttype) - if(STUN) - Stun(effect / (blocked + 1)) - if(WEAKEN) - Weaken(effect / (blocked + 1)) - if(PARALYZE) - Paralyse(effect / (blocked + 1)) - if(IRRADIATE) - radiation += min((effect - (effect*getarmor(null, "rad"))), 0)//Rads auto check armor - if(STUTTER) - stuttering = max(stuttering,(effect/(blocked+1))) - if(EYE_BLUR) - eye_blurry = max(eye_blurry,(effect/(blocked+1))) - if(DROWSY) - drowsyness = max(drowsyness,(effect/(blocked+1))) - updatehealth() - return 1*/ - -/proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) - if(!istype(bot) || !istype(ai)) - return 0 - if(bot.connected_ai == ai) - return 1 - return 0 - - -// this function shows the health of the pAI in the Status panel -/mob/living/silicon/proc/show_system_integrity() - if(!src.stat) - stat(null, text("System integrity: [round((health/maxHealth)*100)]%")) - else - stat(null, text("Systems nonfunctional")) - - -// This adds the basic clock, shuttle recall timer, and malf_ai info to all silicon lifeforms -/mob/living/silicon/Stat() - ..() - if(statpanel("Status")) - show_stat_emergency_shuttle_eta() - show_system_integrity() - -//Silicon mob language procs - -/mob/living/silicon/can_speak_language(datum/language/speaking) - return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language - -/mob/living/silicon/add_language(var/language, var/can_speak=1) - if(..(language) && can_speak) - speech_synthesizer_langs.Add(GLOB.all_languages[language]) - return 1 - -/mob/living/silicon/remove_language(var/rem_language) - ..(rem_language) - - for(var/datum/language/L in speech_synthesizer_langs) - if(L.name == rem_language) - speech_synthesizer_langs -= L - -/mob/living/silicon/check_lang_data() - . = "" - - if(default_language) - . += "Current default language: [default_language] - reset

" - - for(var/datum/language/L in languages) - if(!(L.flags & NONGLOBAL)) - var/default_str - if(L == default_language) - default_str = " - default - reset" - else - default_str = " - set default" - - var/synth = (L in speech_synthesizer_langs) - . += "[L.name] (:[L.key])[synth ? default_str : null]
Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
[L.desc]

" - - -// this function displays the stations manifest in a separate window -/mob/living/silicon/proc/show_station_manifest() - var/dat - dat += "

Crew Manifest

" - if(data_core) - dat += data_core.get_manifest(1) // make it monochrome - dat += "
" - src << browse(dat, "window=airoster") - onclose(src, "airoster") - -/mob/living/silicon/assess_threat() //Secbots won't hunt silicon units - return -10 - -/mob/living/silicon/verb/pose() - set name = "Set Pose" - set desc = "Sets a description which will be shown when someone examines you." - set category = "IC" - - pose = sanitize(copytext(input(usr, "This is [src]. It is...", "Pose", null) as text, 1, MAX_MESSAGE_LEN)) - -/mob/living/silicon/verb/set_flavor() - set name = "Set Flavour Text" - set desc = "Sets an extended description of your character's features." - set category = "IC" - - update_flavor_text() - -/mob/living/silicon/binarycheck() - return 1 - -/mob/living/silicon/proc/remove_med_sec_hud() - var/datum/atom_hud/secsensor = huds[sec_hud] - var/datum/atom_hud/medsensor = huds[med_hud] - for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) - diagsensor.remove_hud_from(src) - secsensor.remove_hud_from(src) - medsensor.remove_hud_from(src) - - -/mob/living/silicon/proc/add_sec_hud() - var/datum/atom_hud/secsensor = huds[sec_hud] - secsensor.add_hud_to(src) - -/mob/living/silicon/proc/add_med_hud() - var/datum/atom_hud/medsensor = huds[med_hud] - medsensor.add_hud_to(src) - -/mob/living/silicon/proc/add_diag_hud() - for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) - diagsensor.add_hud_to(src) - - -/mob/living/silicon/proc/toggle_sensor_mode() - var/sensor_type = input("Please select sensor type.", "Sensor Integration", null) in list("Security", "Medical","Diagnostic","Disable") - remove_med_sec_hud() - switch(sensor_type) - if("Security") - add_sec_hud() - to_chat(src, "Security records overlay enabled.") - if("Medical") - add_med_hud() - to_chat(src, "Life signs monitor overlay enabled.") - if("Diagnostic") - add_diag_hud() - to_chat(src, "Robotics diagnostic overlay enabled.") - if("Disable") - to_chat(src, "Sensor augmentations disabled.") - -/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) - if(!next_alarm_notice) - next_alarm_notice = world.time + SecondsToTicks(10) - - var/list/alarms = queued_alarms[alarm_handler] - if(was_raised) - // Raised alarms are always set - alarms[alarm] = 1 - else - // Alarms that were raised but then cleared before the next notice are instead removed - if(alarm in alarms) - alarms -= alarm - // And alarms that have only been cleared thus far are set as such - else - alarms[alarm] = -1 - -/mob/living/silicon/proc/process_queued_alarms() - if(next_alarm_notice && (world.time > next_alarm_notice)) - next_alarm_notice = 0 - - var/alarm_raised = 0 - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - var/reported = 0 - for(var/datum/alarm/A in alarms) - if(alarms[A] == 1) - if(!reported) - reported = 1 - to_chat(src, "--- [AH.category] Detected ---") - raised_alarm(A) - - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - var/reported = 0 - for(var/datum/alarm/A in alarms) - if(alarms[A] == -1) - if(!reported) - reported = 1 - to_chat(src, "--- [AH.category] Cleared ---") - to_chat(src, "\The [A.alarm_name()].") - - if(alarm_raised) - to_chat(src, "\[Show Alerts\]") - - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - alarms.Cut() - -/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A) - to_chat(src, "[A.alarm_name()]!") - -/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A) - var/cameratext = "" - for(var/obj/machinery/camera/C in A.cameras()) - cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]" - to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])") - -/mob/living/silicon/adjustToxLoss(var/amount) - return STATUS_UPDATE_NONE - -/mob/living/silicon/get_access() - return IGNORE_ACCESS //silicons always have access - -/mob/living/silicon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/noise) - if(affect_silicon) - return ..() - -/mob/living/silicon/is_mechanical() - return 1 - -/mob/living/silicon/is_literate() - return 1 - -/////////////////////////////////// EAR DAMAGE //////////////////////////////////// -/mob/living/silicon/can_hear() - . = TRUE - +/mob/living/silicon/bullet_act(var/obj/item/projectile/Proj) + + + if(!Proj.nodamage) + switch(Proj.damage_type) + if(BRUTE) + adjustBruteLoss(Proj.damage) + if(BURN) + adjustFireLoss(Proj.damage) + + Proj.on_hit(src,2) + + return 2 + +/mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/negate_armor = 0) + return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now +/* + if(!effect || (blocked >= 2)) return 0 + switch(effecttype) + if(STUN) + Stun(effect / (blocked + 1)) + if(WEAKEN) + Weaken(effect / (blocked + 1)) + if(PARALYZE) + Paralyse(effect / (blocked + 1)) + if(IRRADIATE) + radiation += min((effect - (effect*getarmor(null, "rad"))), 0)//Rads auto check armor + if(STUTTER) + stuttering = max(stuttering,(effect/(blocked+1))) + if(EYE_BLUR) + eye_blurry = max(eye_blurry,(effect/(blocked+1))) + if(DROWSY) + drowsyness = max(drowsyness,(effect/(blocked+1))) + updatehealth() + return 1*/ + +/proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) + if(!istype(bot) || !istype(ai)) + return 0 + if(bot.connected_ai == ai) + return 1 + return 0 + + +// this function shows the health of the pAI in the Status panel +/mob/living/silicon/proc/show_system_integrity() + if(!src.stat) + stat(null, text("System integrity: [round((health/maxHealth)*100)]%")) + else + stat(null, text("Systems nonfunctional")) + + +// This adds the basic clock, shuttle recall timer, and malf_ai info to all silicon lifeforms +/mob/living/silicon/Stat() + ..() + if(statpanel("Status")) + show_stat_emergency_shuttle_eta() + show_system_integrity() + +//Silicon mob language procs + +/mob/living/silicon/can_speak_language(datum/language/speaking) + return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language + +/mob/living/silicon/add_language(var/language, var/can_speak=1) + if(..(language) && can_speak) + speech_synthesizer_langs.Add(GLOB.all_languages[language]) + return 1 + +/mob/living/silicon/remove_language(var/rem_language) + ..(rem_language) + + for(var/datum/language/L in speech_synthesizer_langs) + if(L.name == rem_language) + speech_synthesizer_langs -= L + +/mob/living/silicon/check_lang_data() + . = "" + + if(default_language) + . += "Current default language: [default_language] - reset

" + + for(var/datum/language/L in languages) + if(!(L.flags & NONGLOBAL)) + var/default_str + if(L == default_language) + default_str = " - default - reset" + else + default_str = " - set default" + + var/synth = (L in speech_synthesizer_langs) + . += "[L.name] (:[L.key])[synth ? default_str : null]
Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
[L.desc]

" + + +// this function displays the stations manifest in a separate window +/mob/living/silicon/proc/show_station_manifest() + var/dat + dat += "

Crew Manifest

" + if(data_core) + dat += data_core.get_manifest(1) // make it monochrome + dat += "
" + src << browse(dat, "window=airoster") + onclose(src, "airoster") + +/mob/living/silicon/assess_threat() //Secbots won't hunt silicon units + return -10 + +/mob/living/silicon/verb/pose() + set name = "Set Pose" + set desc = "Sets a description which will be shown when someone examines you." + set category = "IC" + + pose = sanitize(copytext(input(usr, "This is [src]. It is...", "Pose", null) as text, 1, MAX_MESSAGE_LEN)) + +/mob/living/silicon/verb/set_flavor() + set name = "Set Flavour Text" + set desc = "Sets an extended description of your character's features." + set category = "IC" + + update_flavor_text() + +/mob/living/silicon/binarycheck() + return 1 + +/mob/living/silicon/proc/remove_med_sec_hud() + var/datum/atom_hud/secsensor = huds[sec_hud] + var/datum/atom_hud/medsensor = huds[med_hud] + for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) + diagsensor.remove_hud_from(src) + secsensor.remove_hud_from(src) + medsensor.remove_hud_from(src) + + +/mob/living/silicon/proc/add_sec_hud() + var/datum/atom_hud/secsensor = huds[sec_hud] + secsensor.add_hud_to(src) + +/mob/living/silicon/proc/add_med_hud() + var/datum/atom_hud/medsensor = huds[med_hud] + medsensor.add_hud_to(src) + +/mob/living/silicon/proc/add_diag_hud() + for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) + diagsensor.add_hud_to(src) + + +/mob/living/silicon/proc/toggle_sensor_mode() + var/sensor_type = input("Please select sensor type.", "Sensor Integration", null) in list("Security", "Medical","Diagnostic","Disable") + remove_med_sec_hud() + switch(sensor_type) + if("Security") + add_sec_hud() + to_chat(src, "Security records overlay enabled.") + if("Medical") + add_med_hud() + to_chat(src, "Life signs monitor overlay enabled.") + if("Diagnostic") + add_diag_hud() + to_chat(src, "Robotics diagnostic overlay enabled.") + if("Disable") + to_chat(src, "Sensor augmentations disabled.") + +/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) + if(!next_alarm_notice) + next_alarm_notice = world.time + SecondsToTicks(10) + + var/list/alarms = queued_alarms[alarm_handler] + if(was_raised) + // Raised alarms are always set + alarms[alarm] = 1 + else + // Alarms that were raised but then cleared before the next notice are instead removed + if(alarm in alarms) + alarms -= alarm + // And alarms that have only been cleared thus far are set as such + else + alarms[alarm] = -1 + +/mob/living/silicon/proc/process_queued_alarms() + if(next_alarm_notice && (world.time > next_alarm_notice)) + next_alarm_notice = 0 + + var/alarm_raised = 0 + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + var/reported = 0 + for(var/datum/alarm/A in alarms) + if(alarms[A] == 1) + if(!reported) + reported = 1 + to_chat(src, "--- [AH.category] Detected ---") + raised_alarm(A) + + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + var/reported = 0 + for(var/datum/alarm/A in alarms) + if(alarms[A] == -1) + if(!reported) + reported = 1 + to_chat(src, "--- [AH.category] Cleared ---") + to_chat(src, "\The [A.alarm_name()].") + + if(alarm_raised) + to_chat(src, "\[Show Alerts\]") + + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + alarms.Cut() + +/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A) + to_chat(src, "[A.alarm_name()]!") + +/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A) + var/cameratext = "" + for(var/obj/machinery/camera/C in A.cameras()) + cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]" + to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])") + +/mob/living/silicon/adjustToxLoss(var/amount) + return STATUS_UPDATE_NONE + +/mob/living/silicon/get_access() + return IGNORE_ACCESS //silicons always have access + +/mob/living/silicon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/noise) + if(affect_silicon) + return ..() + +/mob/living/silicon/is_mechanical() + return 1 + +/mob/living/silicon/is_literate() + return 1 + +/////////////////////////////////// EAR DAMAGE //////////////////////////////////// +/mob/living/silicon/can_hear() + . = TRUE + From d2931c860bb249924acc305d8a5d816b6fec4fbb Mon Sep 17 00:00:00 2001 From: NotDhu Date: Wed, 18 Mar 2020 14:21:43 -0400 Subject: [PATCH 013/144] Makes classic secHUD available to Magistrate. --- code/modules/client/preference/loadout/loadout_general.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/client/preference/loadout/loadout_general.dm b/code/modules/client/preference/loadout/loadout_general.dm index 50105813fd6..8c08063ad7e 100644 --- a/code/modules/client/preference/loadout/loadout_general.dm +++ b/code/modules/client/preference/loadout/loadout_general.dm @@ -29,7 +29,7 @@ /datum/gear/sechud display_name = "a classic security HUD" path = /obj/item/clothing/glasses/hud/security - allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot", "Internal Affairs Agent") + allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot", "Internal Affairs Agent","Magistrate") /datum/gear/matches display_name = "a box of matches" @@ -42,7 +42,7 @@ /datum/gear/doublecards display_name = "a double deck of standard cards" path = /obj/item/deck/doublecards - + /datum/gear/tarot display_name = "a deck of tarot cards" path = /obj/item/deck/tarot From 61adc6abb90484f70bc7838b9ab9e75bf34d3f5b Mon Sep 17 00:00:00 2001 From: datlo Date: Sun, 22 Mar 2020 12:32:51 +0100 Subject: [PATCH 014/144] Add wizard loadouts --- code/datums/spells/lichdom.dm | 22 ++-- code/game/gamemodes/wizard/spellbook.dm | 118 +++++++++++++----- code/game/gamemodes/wizard/wizloadouts.dm | 52 ++++++++ code/game/objects/items/weapons/scrolls.dm | 6 +- .../objects/items/weapons/storage/boxes.dm | 5 + icons/obj/storage.dmi | Bin 76226 -> 76699 bytes paradise.dme | 1 + 7 files changed, 159 insertions(+), 45 deletions(-) create mode 100644 code/game/gamemodes/wizard/wizloadouts.dm diff --git a/code/datums/spells/lichdom.dm b/code/datums/spells/lichdom.dm index 9db46b517db..3c70ddbbdae 100644 --- a/code/datums/spells/lichdom.dm +++ b/code/datums/spells/lichdom.dm @@ -28,7 +28,7 @@ config.continuous_rounds = 0 return ..() -/obj/effect/proc_holder/spell/targeted/lichdom/cast(list/targets,mob/user = usr) +/obj/effect/proc_holder/spell/targeted/lichdom/cast(list/targets, mob/user = usr) if(!config.continuous_rounds) existence_stops_round_end = 1 config.continuous_rounds = 1 @@ -36,7 +36,7 @@ for(var/mob/M in targets) var/list/hand_items = list() if(iscarbon(M)) - hand_items = list(M.get_active_hand(),M.get_inactive_hand()) + hand_items = list(M.get_active_hand(), M.get_inactive_hand()) if(marked_item && !stat_allowed) //sanity, shouldn't happen without badminry marked_item = null @@ -74,12 +74,9 @@ var/mob/old_body = current_body var/turf/body_turf = get_turf(old_body) current_body = lich - lich.Weaken(10+10*resurrections) + lich.Weaken(10 + 10 * resurrections) ++resurrections - lich.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(lich), slot_shoes) - lich.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(lich), slot_w_uniform) - lich.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(lich), slot_wear_suit) - lich.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(lich), slot_head) + equip_lich(lich) if(old_body && old_body.loc) if(iscarbon(old_body)) @@ -127,7 +124,10 @@ H.unEquip(H.head) H.unEquip(H.shoes) H.unEquip(H.head) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(H), slot_head) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), slot_shoes) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(H), slot_w_uniform) + equip_lich(H) + +/obj/effect/proc_holder/spell/targeted/lichdom/proc/equip_lich(mob/living/carbon/human/H) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(H), slot_head) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(H), slot_w_uniform) \ No newline at end of file diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index f97f14a9975..360a1ddcacd 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -6,34 +6,36 @@ var/category = "Offensive" var/log_name = "XX" //What it shows up as in logs var/cost = 2 - var/refundable = 1 + var/refundable = TRUE 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 /datum/spellbook_entry/proc/IsSpellAvailable() // For config prefs / gamemode restrictions - these are round applied - return 1 + return TRUE /datum/spellbook_entry/proc/CanBuy(mob/living/carbon/human/user, obj/item/spellbook/book) // Specific circumstances - if(book.uses= aspell.level_max) to_chat(user, "This spell cannot be improved further.") - return 0 + return FALSE 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) + 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) @@ -51,12 +53,12 @@ aspell.name = "Instant [aspell.name]" if(aspell.spell_level >= aspell.level_max) to_chat(user, "This spell cannot be strengthened any further.") - return 1 + return TRUE //No same spell found - just learn it - feedback_add_details("wizard_spell_learned",log_name) - user.mind.AddSpell(S) - to_chat(user, "You have learned [S.name].") - return 1 + feedback_add_details("wizard_spell_learned", log_name) + user.mind.AddSpell(newspell) + to_chat(user, "You have learned [newspell.name].") + return TRUE /datum/spellbook_entry/proc/CanRefund(mob/living/carbon/human/user, obj/item/spellbook/book) if(!refundable) @@ -71,7 +73,7 @@ /datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user, obj/item/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") + to_chat(user, "You can only refund spells at the wizard lair.") return -1 if(!S) S = new spell_type() @@ -81,7 +83,7 @@ spell_levels = aspell.spell_level user.mind.spell_list.Remove(aspell) QDEL_NULL(S) - return cost * (spell_levels+1) + return cost * (spell_levels + 1) return -1 /datum/spellbook_entry/proc/GetInfo() @@ -571,6 +573,48 @@ category = "Summons" limit = 1 +//Spell loadouts datum, list of loadouts is in wizloadouts.dm +/datum/spellbook_entry/loadout + name = "Standard Loadout" + cost = 10 + category = "Standard" + refundable = FALSE + buy_word = "Summon" + var/list/items_path = list() + var/list/spells_path = list() + var/destroy_spellbook = FALSE //Destroy the spellbook when bought, for loadouts containing non-standard items/spells, otherwise wiz can refund spells + +/datum/spellbook_entry/loadout/GetInfo() + var/dat = "" + dat += "[name]" + if(cost > 0) + dat += " Cost:[cost]
" + else + dat += " No Cost
" + dat += "[desc]
" + return dat + +/datum/spellbook_entry/loadout/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + if(destroy_spellbook) + var/response = alert(user, "The [src] loadout cannot be refunded once bought. Are you sure this is what you want?", "No refunds!", "No", "Yes") + if(response == "No") + return FALSE + to_chat(user, "[book] crumbles to ashes as you acquire its knowledge.") + qdel(book) + else if(items_path.len) + var/response = alert(user, "The [src] loadout contains items that will not be refundable if bought. Are you sure this is what you want?", "No refunds!", "No", "Yes") + if(response == "No") + return FALSE + if(items_path.len) + var/obj/item/storage/box/wizard/B = new(src) + for(var/path in items_path) + new path(B) + user.put_in_hands(B) + for(var/path in spells_path) + var/obj/effect/proc_holder/spell/S = new path() + LearnSpell(user, book, S) + return TRUE + /obj/item/spellbook name = "spell book" desc = "The legendary book of spells of the wizard." @@ -587,12 +631,13 @@ var/mob/living/carbon/human/owner var/list/datum/spellbook_entry/entries = list() var/list/categories = list() - var/list/main_categories = list("Spells", "Magical Items") + var/list/main_categories = list("Spells", "Magical Items", "Loadouts") var/list/spell_categories = list("Offensive", "Defensive", "Mobility", "Assistance", "Rituals") var/list/item_categories = list("Artefacts", "Weapons and Armors", "Staves", "Summons") + var/list/loadout_categories = list("Standard", "Unique") /obj/item/spellbook/proc/initialize() - var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon + var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon - /datum/spellbook_entry/loadout for(var/T in entry_types) var/datum/spellbook_entry/E = new T if(E.IsSpellAvailable()) @@ -676,6 +721,12 @@ if("Summons") dat += "Magical items geared towards bringing in outside forces to aid you.

" dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
" + if("Standard") + dat += "These battle-tested spell sets are easy to use and provide good balance between offense and defense.

" + dat += "They all cost, and are worth, 10 spell points. You are able to refund any of the spells included as long as you stay in the wizard den.
" + if("Unique") + dat += "These esoteric loadouts usually contain spells or items that cannot be bought elsewhere in this spellbook.

" + dat += "Recommended for experienced wizards looking for something new. No refunds once purchased!
" return dat /obj/item/spellbook/proc/wrap(content) @@ -720,23 +771,24 @@ cat_dat[main_category] = "
" dat += "
  • [main_category]
  • " dat += "" + dat += "