From 6dc9e56a446ba6d0e185256e9afc4197eabdec52 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 16 Oct 2018 17:27:35 -0400 Subject: [PATCH 001/174] Shelter Capsule Content Tweaks - Adds some more synth batteries to both capsules - Removed the Adv first aid kit from the table - Adds a Basic/Adv/O2/Fire/Toxin kit to luxury storage - Removes the violin due to being a source of grief - Changes the bag of dice to a bag of gaming dice - Luxury capsule now has twice the amount of MREs The main reasons for this PR: - Removes shelter capsules as a source of violins, which have been involved in a few... incidents - Equals the amount of batteries to the amount of liquid foods in the basic capsule, so that synth crew are not required to push for the luxury capsule and its recharger - Adds a toxin kit to shelter capsules, since toxins are a bigger issue in the phoron-based atmosphere of V2 - Provides a health analyzer to the shelter capsule, the omission of which was very strange. Rest of the kits were added just for completion sake. All of the ones included be obtained from cargo for a single ten point crate, so the effects on balance will be minimal. --- code/modules/mining/shelter_atoms.dm | 28 ++++++++++++++++++++++------ maps/submaps/shelters/shelter_2.dmm | 5 +---- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/code/modules/mining/shelter_atoms.dm b/code/modules/mining/shelter_atoms.dm index 25af7b1394..aa5bbed628 100644 --- a/code/modules/mining/shelter_atoms.dm +++ b/code/modules/mining/shelter_atoms.dm @@ -211,6 +211,7 @@ pixel_y = -4 max_n_of_items = 10 var/empty = FALSE + var/luxury = FALSE /obj/machinery/smartfridge/survival_pod/initialize(mapload) . = ..() @@ -219,15 +220,27 @@ for(var/i in 1 to 5) var/obj/item/weapon/reagent_containers/food/snacks/liquidfood/W = new(src) stock(W) - for(var/i in 1 to 2) + for(var/i in 1 to 5) var/obj/item/device/fbp_backup_cell/W = new(src) stock(W) - if(prob(50)) - var/obj/item/weapon/storage/pill_bottle/dice/D = new(src) + for(var/i in 1 to 1) + var/obj/item/weapon/storage/pill_bottle/dice_nerd/D = new(src) stock(D) - else - var/obj/item/device/instrument/violin/V = new(src) - stock(V) + if(luxury) + for(var/i in 1 to 5) + var/obj/item/weapon/reagent_containers/food/snacks/liquidfood/W = new(src) + stock(W) + for(var/i in 1 to 1) + var/obj/item/weapon/storage/firstaid/regular/B = new(src) + stock(B) + var/obj/item/weapon/storage/firstaid/fire/F = new(src) + stock(F) + var/obj/item/weapon/storage/firstaid/toxin/T = new(src) + stock(T) + var/obj/item/weapon/storage/firstaid/o2/O = new(src) + stock(O) + var/obj/item/weapon/storage/firstaid/adv/A = new(src) + stock(A) /obj/machinery/smartfridge/survival_pod/accept_check(obj/item/O) return isitem(O) @@ -237,6 +250,9 @@ desc = "A heated storage unit. This one's seen better days." empty = TRUE +/obj/machinery/smartfridge/survival_pod/luxury + luxury = TRUE + //Fans /obj/structure/fans icon = 'icons/obj/survival_pod.dmi' diff --git a/maps/submaps/shelters/shelter_2.dmm b/maps/submaps/shelters/shelter_2.dmm index f06fb90420..a4ce468480 100644 --- a/maps/submaps/shelters/shelter_2.dmm +++ b/maps/submaps/shelters/shelter_2.dmm @@ -31,7 +31,7 @@ /turf/simulated/shuttle/floor/voidcraft, /area/survivalpod) "f" = ( -/obj/machinery/smartfridge/survival_pod, +/obj/machinery/smartfridge/survival_pod/luxury, /turf/simulated/shuttle/floor/voidcraft, /area/survivalpod) "g" = ( @@ -87,9 +87,6 @@ /area/survivalpod) "p" = ( /obj/structure/table/survival_pod, -/obj/item/weapon/storage/firstaid/adv{ - pixel_x = 4 - }, /turf/simulated/shuttle/floor/voidcraft, /area/survivalpod) "q" = ( From 3a510e97790e732fbf098c452664cd8279b1d384 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 16 Oct 2018 22:19:46 -0400 Subject: [PATCH 002/174] Tweaks contents a bit more --- code/modules/mining/shelter_atoms.dm | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/code/modules/mining/shelter_atoms.dm b/code/modules/mining/shelter_atoms.dm index aa5bbed628..ea73c19875 100644 --- a/code/modules/mining/shelter_atoms.dm +++ b/code/modules/mining/shelter_atoms.dm @@ -223,24 +223,18 @@ for(var/i in 1 to 5) var/obj/item/device/fbp_backup_cell/W = new(src) stock(W) - for(var/i in 1 to 1) - var/obj/item/weapon/storage/pill_bottle/dice_nerd/D = new(src) - stock(D) + var/obj/item/weapon/storage/pill_bottle/dice_nerd/D = new(src) + stock(D) if(luxury) for(var/i in 1 to 5) var/obj/item/weapon/reagent_containers/food/snacks/liquidfood/W = new(src) stock(W) - for(var/i in 1 to 1) - var/obj/item/weapon/storage/firstaid/regular/B = new(src) - stock(B) - var/obj/item/weapon/storage/firstaid/fire/F = new(src) - stock(F) - var/obj/item/weapon/storage/firstaid/toxin/T = new(src) - stock(T) - var/obj/item/weapon/storage/firstaid/o2/O = new(src) - stock(O) - var/obj/item/weapon/storage/firstaid/adv/A = new(src) - stock(A) + var/obj/item/device/healthanalyzer/B = new(src) + stock(B) + var/obj/item/weapon/storage/firstaid/adv/A = new(src) + stock(A) + var/obj/item/weapon/storage/pill_bottle/antitox/F = new(src) + stock(F) /obj/machinery/smartfridge/survival_pod/accept_check(obj/item/O) return isitem(O) From 4ffe580832513a5073e86536e27f96686da01e70 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 23 Oct 2018 02:48:46 -0400 Subject: [PATCH 003/174] More Expedition Tweaks The goal of this PR is mainly to move our exploration away from the Polaris roots and refine the various expedition roles for Virgo, and a lot of cosmetic tweaks. - Renames Search and Rescue to Field Medic - Cosmetic changes for ID Card graphics for expedition crew and Field Medic PDA - Moves the holdout phasers from the expedition weapons locker to the job equipment lockers of the roles intended to use them (Pilots/FMs) - Removes Psychologist, Morgue, and Explorer access from Field Medics - Removes Pilot access from Explorers - Adds Pilot access to Field Medics - Restricts the expedition shuttle to Pilot access only - Adds a medic version of the explorer suit for Field Medics, identical stats, but has a medic arm-band attached. Note: Might be replaced with a reskinned explorer suit down the road. - Adds liquidfood rations for SARs and Pilots - Adds winter boots for Pilots --- code/game/jobs/jobs.dm | 2 +- code/modules/clothing/suits/hooded_vr.dm | 3 ++ maps/southern_cross/job/outfits.dm | 11 +++--- maps/southern_cross/job/outfits_vr.dm | 2 +- maps/southern_cross/southern_cross_jobs.dm | 38 +++++++++++++------ maps/southern_cross/southern_cross_jobs_vr.dm | 10 ++++- .../southern_cross/structures/closets/misc.dm | 15 ++++++-- maps/tether/tether_shuttles.dm | 2 +- maps/tether/tether_things.dm | 2 - vorestation.dme | 1 + 10 files changed, 58 insertions(+), 28 deletions(-) create mode 100644 code/modules/clothing/suits/hooded_vr.dm diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index 395b46e934..cd698067f5 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -73,7 +73,7 @@ var/list/medical_positions = list( "Geneticist", "Psychiatrist", "Chemist", - "Search and Rescue", // VOREStation Edit - Moved SAR from planetary -> medical + "Field Medic", // VOREStation Edit - Moved SAR from planetary -> medical "Paramedic" ) diff --git a/code/modules/clothing/suits/hooded_vr.dm b/code/modules/clothing/suits/hooded_vr.dm new file mode 100644 index 0000000000..ef13979bdc --- /dev/null +++ b/code/modules/clothing/suits/hooded_vr.dm @@ -0,0 +1,3 @@ +// Field Medic Suit - Someone who can sprite should probably reskin this +/obj/item/clothing/suit/storage/hooded/explorer/medic + starting_accessories = list(/obj/item/clothing/accessory/armband/med/cross) diff --git a/maps/southern_cross/job/outfits.dm b/maps/southern_cross/job/outfits.dm index a32b784c0d..bb9bc7aef8 100644 --- a/maps/southern_cross/job/outfits.dm +++ b/maps/southern_cross/job/outfits.dm @@ -14,7 +14,7 @@ Keep outfits simple. Spawn with basic uniforms and minimal gear. Gear instead go id_slot = slot_wear_id pda_slot = slot_l_store pda_type = /obj/item/device/pda/cargo // Brown looks more rugged - id_type = /obj/item/weapon/card/id/civilian/explorer + id_type = /obj/item/weapon/card/id/explorer/explorer //VOREStation Edit id_pda_assignment = "Explorer" flags = OUTFIT_HAS_BACKPACK|OUTFIT_COMPREHENSIVE_SURVIVAL backpack_contents = list(/obj/item/clothing/accessory/permit/gun/planetside = 1) @@ -47,19 +47,20 @@ Keep outfits simple. Spawn with basic uniforms and minimal gear. Gear instead go id_slot = slot_wear_id pda_slot = slot_belt pda_type = /obj/item/device/pda/cargo // Brown looks more rugged - id_type = /obj/item/weapon/card/id/civilian/pilot + id_type = /obj/item/weapon/card/id/explorer/pilot //VOREStation Edit id_pda_assignment = "Pilot" flags = OUTFIT_HAS_BACKPACK|OUTFIT_COMPREHENSIVE_SURVIVAL /decl/hierarchy/outfit/job/medical/sar - name = OUTFIT_JOB_NAME("Search and Rescue") + name = OUTFIT_JOB_NAME("Field Medic") //VOREStation Edit uniform = /obj/item/clothing/under/utility/blue - suit = /obj/item/clothing/suit/storage/hooded/wintercoat/medical/sar + //suit = /obj/item/clothing/suit/storage/hooded/wintercoat/medical/sar //VOREStation Edit shoes = /obj/item/clothing/shoes/boots/winter/explorer l_ear = /obj/item/device/radio/headset/sar l_hand = /obj/item/weapon/storage/firstaid/regular belt = /obj/item/weapon/storage/belt/medical/emt pda_slot = slot_l_store + pda_type = /obj/item/device/pda/viro //VOREStation Add id_type = /obj/item/weapon/card/id/medical/sar - id_pda_assignment = "Search and Rescue" + id_pda_assignment = "Field Medic" //VOREStation Edit flags = OUTFIT_HAS_BACKPACK|OUTFIT_EXTENDED_SURVIVAL|OUTFIT_COMPREHENSIVE_SURVIVAL diff --git a/maps/southern_cross/job/outfits_vr.dm b/maps/southern_cross/job/outfits_vr.dm index 51b3c3670e..902a0658cc 100644 --- a/maps/southern_cross/job/outfits_vr.dm +++ b/maps/southern_cross/job/outfits_vr.dm @@ -6,7 +6,7 @@ id_slot = slot_wear_id pda_slot = slot_l_store pda_type = /obj/item/device/pda/pathfinder - id_type = /obj/item/weapon/card/id/science/head/pathfinder + id_type = /obj/item/weapon/card/id/explorer/head/pathfinder id_pda_assignment = "Pathfinder" flags = OUTFIT_HAS_BACKPACK|OUTFIT_EXTENDED_SURVIVAL|OUTFIT_COMPREHENSIVE_SURVIVAL backpack_contents = list(/obj/item/clothing/accessory/permit/gun/planetside = 1) diff --git a/maps/southern_cross/southern_cross_jobs.dm b/maps/southern_cross/southern_cross_jobs.dm index df8aae6e98..452b52c724 100644 --- a/maps/southern_cross/southern_cross_jobs.dm +++ b/maps/southern_cross/southern_cross_jobs.dm @@ -21,16 +21,30 @@ var/const/access_explorer = 43 //SC IDs /obj/item/weapon/card/id/medical/sar - assignment = "Search and Rescue" - rank = "Search and Rescue" + //VOREStation Block Edit - Start + assignment = "Field Medic" + rank = "Field Medic" + icon_state = "cyan" + primary_color = rgb(47,189,189) + secondary_color = rgb(127,223,223) + //VOREStation Block Edit - End job_access_type = /datum/job/sar -/obj/item/weapon/card/id/civilian/pilot +//VOREStation Block Edit - Start +/obj/item/weapon/card/id/explorer + name = "identification card" + desc = "A card issued to station exploration staff." + icon_state = "green" + primary_color = rgb(47,189,0) + secondary_color = rgb(127,223,95) +//VOREstation Block Edit - End + +/obj/item/weapon/card/id/explorer/pilot assignment = "Pilot" rank = "Pilot" job_access_type = /datum/job/pilot -/obj/item/weapon/card/id/civilian/explorer +/obj/item/weapon/card/id/explorer/explorer assignment = "Explorer" rank = "Explorer" job_access_type = /datum/job/explorer @@ -79,7 +93,7 @@ var/const/access_explorer = 43 spawn_positions = 2 supervisors = "the pathfinder and the head of personnel" //VOREStation Edit selection_color = "#515151" - idtype = /obj/item/weapon/card/id/civilian/pilot + idtype = /obj/item/weapon/card/id/explorer/pilot //VOREStation Edit economic_modifier = 5 //VOREStation Edit access = list(access_pilot) //VOREStation Edit minimal_access = list(access_pilot) //VOREStation Edit @@ -95,10 +109,10 @@ var/const/access_explorer = 43 spawn_positions = 2 //VOREStation Edit supervisors = "the pathfinder and the research director" //VOREStation Edit selection_color = "#633D63" //VOREStation Edit - idtype = /obj/item/weapon/card/id/civilian/explorer + idtype = /obj/item/weapon/card/id/explorer/explorer //VOREStation Edit economic_modifier = 6 //VOREStation Edit - access = list(access_pilot, access_explorer, access_research) //VOREStation Edit - minimal_access = list(access_pilot, access_explorer, access_research) //VOREStation Edit + access = list(access_explorer, access_research) //VOREStation Edit + minimal_access = list(access_explorer, access_research) //VOREStation Edit outfit_type = /decl/hierarchy/outfit/job/explorer2 /* alt_titles = list( @@ -107,7 +121,7 @@ var/const/access_explorer = 43 */ /datum/job/sar - title = "Search and Rescue" + title = "Field Medic" //VOREStation Edit flag = SAR department = "Medical" department_flag = MEDSCI @@ -116,8 +130,8 @@ var/const/access_explorer = 43 spawn_positions = 2 supervisors = "the pathfinder and the chief medical officer" //VOREStation Edit selection_color = "#013D3B" //VOREStation Edit - idtype = /obj/item/weapon/card/id/medical + idtype = /obj/item/weapon/card/id/medical/sar //VOREStation Edit economic_modifier = 6 //VOREStation Edit - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_psychiatrist, access_explorer) - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_explorer) + access = list(access_medical, access_medical_equip, access_surgery, access_chemistry, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_pilot) //VOREStation Edit + minimal_access = list(access_medical, access_medical_equip, access_pilot) //VOREStation Edit outfit_type = /decl/hierarchy/outfit/job/medical/sar \ No newline at end of file diff --git a/maps/southern_cross/southern_cross_jobs_vr.dm b/maps/southern_cross/southern_cross_jobs_vr.dm index 600ace3885..c162d63d53 100644 --- a/maps/southern_cross/southern_cross_jobs_vr.dm +++ b/maps/southern_cross/southern_cross_jobs_vr.dm @@ -1,7 +1,13 @@ var/const/PATHFINDER =(1<<13) //VOREStation Edit - Added Pathfinder -/obj/item/weapon/card/id/science/head/pathfinder +/obj/item/weapon/card/id/explorer/head/ + name = "identification card" desc = "A card which represents discovery of the unknown." + icon_state = "greenGold" + primary_color = rgb(47,189,0) + secondary_color = rgb(127,223,95) + +/obj/item/weapon/card/id/explorer/head/pathfinder assignment = "Pathfinder" rank = "Pathfinder" job_access_type = /datum/job/pathfinder @@ -16,7 +22,7 @@ var/const/PATHFINDER =(1<<13) //VOREStation Edit - Added Pathfinder spawn_positions = 1 supervisors = "the research director" selection_color = "#AD6BAD" - idtype = /obj/item/weapon/card/id/science/head/pathfinder + idtype = /obj/item/weapon/card/id/explorer/head/pathfinder economic_modifier = 7 access = list(access_eva, access_maint_tunnels, access_external_airlocks, access_pilot, access_explorer, access_research, access_gateway) diff --git a/maps/southern_cross/structures/closets/misc.dm b/maps/southern_cross/structures/closets/misc.dm index 7fc5b62889..b24b33ce9f 100644 --- a/maps/southern_cross/structures/closets/misc.dm +++ b/maps/southern_cross/structures/closets/misc.dm @@ -82,7 +82,7 @@ //SAR Lockers /obj/structure/closet/secure_closet/sar - name = "search and rescue locker" + name = "field medic locker" //VOREStation Edit desc = "Supplies for a wilderness first responder." icon_state = "medical1" icon_closed = "medical" @@ -99,8 +99,12 @@ /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline, /obj/item/weapon/reagent_containers/glass/bottle/antitoxin, /obj/item/weapon/storage/belt/medical/emt, - /obj/item/weapon/material/knife/tacknife/survival, //VOREStation Add, - /obj/item/clothing/mask/gas, + //VOREStation Block Edit - Start, + /obj/item/weapon/material/knife/tacknife/survival, + /obj/item/weapon/gun/energy/frontier/locked/holdout, + /obj/item/clothing/mask/gas/explorer, + /obj/item/clothing/suit/storage/hooded/explorer/medic, + //VOREStation Block Edit - End, /obj/item/clothing/suit/storage/hooded/wintercoat/medical/sar, /obj/item/clothing/shoes/boots/winter/explorer, /obj/item/device/radio/headset/sar, @@ -111,6 +115,7 @@ /obj/item/device/healthanalyzer, /obj/item/device/radio/off, /obj/random/medical, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 3, //VOREStation Add, /obj/item/weapon/tool/crowbar, /obj/item/weapon/extinguisher/mini, /obj/item/weapon/storage/box/freezer, @@ -129,15 +134,17 @@ starts_with = list( /obj/item/weapon/storage/backpack/parachute, /obj/item/weapon/material/knife/tacknife/survival, + /obj/item/weapon/gun/energy/frontier/locked/holdout, //VOREStation Add, /obj/item/clothing/head/pilot, /obj/item/clothing/under/rank/pilot1, /obj/item/clothing/suit/storage/toggle/bomber/pilot, + /obj/item/clothing/shoes/boots/winter/explorer, //VOREStation Add, /obj/item/clothing/mask/gas/half, /obj/item/clothing/shoes/black, /obj/item/clothing/gloves/fingerless, /obj/item/device/radio/headset/pilot/alt, /obj/item/device/flashlight, - /obj/item/weapon/reagent_containers/food/snacks/liquidfood, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 2, //VOREStation Edit, /obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle, /obj/item/weapon/storage/box/flare, /obj/item/weapon/cell/device, diff --git a/maps/tether/tether_shuttles.dm b/maps/tether/tether_shuttles.dm index 3ac68483b8..24f425deb1 100644 --- a/maps/tether/tether_shuttles.dm +++ b/maps/tether/tether_shuttles.dm @@ -174,7 +174,7 @@ name = "shuttle control console" shuttle_tag = "Excursion Shuttle" req_access = list() - req_one_access = list(access_explorer,access_pilot) + req_one_access = list(access_pilot) var/wait_time = 45 MINUTES /obj/machinery/computer/shuttle_control/web/excursion/ui_interact() diff --git a/maps/tether/tether_things.dm b/maps/tether/tether_things.dm index 859ef9667c..49f6e340b2 100644 --- a/maps/tether/tether_things.dm +++ b/maps/tether/tether_things.dm @@ -396,8 +396,6 @@ var/global/list/latejoin_tram = list() ..() for(var/i = 1 to 4) new /obj/item/weapon/gun/energy/frontier/locked(src) - for(var/i = 1 to 4) - new /obj/item/weapon/gun/energy/frontier/locked/holdout(src) // Underdark mob spawners /obj/tether_away_spawner/underdark_normal diff --git a/vorestation.dme b/vorestation.dme index bea34dbe10..518ef9d997 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1609,6 +1609,7 @@ #include "code\modules\clothing\suits\bio.dm" #include "code\modules\clothing\suits\bio_vr.dm" #include "code\modules\clothing\suits\hooded.dm" +#include "code\modules\clothing\suits\hooded_vr.dm" #include "code\modules\clothing\suits\jobs.dm" #include "code\modules\clothing\suits\labcoat.dm" #include "code\modules\clothing\suits\miscellaneous.dm" From e3889b60cb92a581dd3f65571c589f7e97a56baf Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 24 Oct 2018 00:03:24 -0400 Subject: [PATCH 004/174] Moved everything into mapfile --- code/modules/mining/shelter_atoms.dm | 33 +++++----------------------- maps/submaps/shelters/shelter_1.dmm | 11 ++++++++++ maps/submaps/shelters/shelter_2.dmm | 21 +++++++++++++++++- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/code/modules/mining/shelter_atoms.dm b/code/modules/mining/shelter_atoms.dm index ea73c19875..4348ae442e 100644 --- a/code/modules/mining/shelter_atoms.dm +++ b/code/modules/mining/shelter_atoms.dm @@ -209,32 +209,13 @@ light_power = 1.2 light_color = "#DDFFD3" pixel_y = -4 - max_n_of_items = 10 - var/empty = FALSE - var/luxury = FALSE + max_n_of_items = 100 -/obj/machinery/smartfridge/survival_pod/initialize(mapload) +/obj/machinery/smartfridge/survival_pod/initialize() . = ..() - if(empty) - return - for(var/i in 1 to 5) - var/obj/item/weapon/reagent_containers/food/snacks/liquidfood/W = new(src) - stock(W) - for(var/i in 1 to 5) - var/obj/item/device/fbp_backup_cell/W = new(src) - stock(W) - var/obj/item/weapon/storage/pill_bottle/dice_nerd/D = new(src) - stock(D) - if(luxury) - for(var/i in 1 to 5) - var/obj/item/weapon/reagent_containers/food/snacks/liquidfood/W = new(src) - stock(W) - var/obj/item/device/healthanalyzer/B = new(src) - stock(B) - var/obj/item/weapon/storage/firstaid/adv/A = new(src) - stock(A) - var/obj/item/weapon/storage/pill_bottle/antitox/F = new(src) - stock(F) + for(var/obj/item/O in loc) + if(accept_check(O)) + stock(O) /obj/machinery/smartfridge/survival_pod/accept_check(obj/item/O) return isitem(O) @@ -242,10 +223,6 @@ /obj/machinery/smartfridge/survival_pod/empty name = "dusty survival pod storage" desc = "A heated storage unit. This one's seen better days." - empty = TRUE - -/obj/machinery/smartfridge/survival_pod/luxury - luxury = TRUE //Fans /obj/structure/fans diff --git a/maps/submaps/shelters/shelter_1.dmm b/maps/submaps/shelters/shelter_1.dmm index 8b85eea25e..66eba3541d 100644 --- a/maps/submaps/shelters/shelter_1.dmm +++ b/maps/submaps/shelters/shelter_1.dmm @@ -14,6 +14,17 @@ /area/survivalpod) "d" = ( /obj/machinery/smartfridge/survival_pod, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/device/fbp_backup_cell, +/obj/item/device/fbp_backup_cell, +/obj/item/device/fbp_backup_cell, +/obj/item/device/fbp_backup_cell, +/obj/item/device/fbp_backup_cell, +/obj/item/weapon/storage/pill_bottle/dice, /turf/simulated/shuttle/floor/voidcraft, /area/survivalpod) "e" = ( diff --git a/maps/submaps/shelters/shelter_2.dmm b/maps/submaps/shelters/shelter_2.dmm index a4ce468480..b7e8721544 100644 --- a/maps/submaps/shelters/shelter_2.dmm +++ b/maps/submaps/shelters/shelter_2.dmm @@ -31,7 +31,25 @@ /turf/simulated/shuttle/floor/voidcraft, /area/survivalpod) "f" = ( -/obj/machinery/smartfridge/survival_pod/luxury, +/obj/machinery/smartfridge/survival_pod, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/weapon/reagent_containers/food/snacks/liquidfood, +/obj/item/device/fbp_backup_cell, +/obj/item/device/fbp_backup_cell, +/obj/item/device/fbp_backup_cell, +/obj/item/device/fbp_backup_cell, +/obj/item/device/fbp_backup_cell, +/obj/item/weapon/storage/pill_bottle/antitox, +/obj/item/device/healthanalyzer, +/obj/item/weapon/storage/pill_bottle/dice_nerd, /turf/simulated/shuttle/floor/voidcraft, /area/survivalpod) "g" = ( @@ -87,6 +105,7 @@ /area/survivalpod) "p" = ( /obj/structure/table/survival_pod, +/obj/item/weapon/storage/firstaid/adv, /turf/simulated/shuttle/floor/voidcraft, /area/survivalpod) "q" = ( From 74fe1a61c5e696ed10254f32dd68001e9cc93057 Mon Sep 17 00:00:00 2001 From: Unknown Date: Wed, 24 Oct 2018 23:27:34 -0400 Subject: [PATCH 005/174] Feedback Changes 1 --- code/modules/virus2/disease2.dm | 7 +++++++ maps/southern_cross/southern_cross_jobs.dm | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/code/modules/virus2/disease2.dm b/code/modules/virus2/disease2.dm index 3670f60bea..9c0c80c28d 100644 --- a/code/modules/virus2/disease2.dm +++ b/code/modules/virus2/disease2.dm @@ -88,6 +88,13 @@ else resistance += rand(1,9) + //VOREStation Add Start - Corophazine can treat higher stages + var/antibiotics = mob.chem_effects[CE_ANTIBIOTIC] + if(antibiotics == ANTIBIO_SUPER) + if(prob(70)) + src.cure(mob) + //VOREStation Add End + //Resistance is capped at 90 without being manually set to 100 if(resistance > 90 && resistance < 100) resistance = 90 diff --git a/maps/southern_cross/southern_cross_jobs.dm b/maps/southern_cross/southern_cross_jobs.dm index 452b52c724..db05c6b04a 100644 --- a/maps/southern_cross/southern_cross_jobs.dm +++ b/maps/southern_cross/southern_cross_jobs.dm @@ -132,6 +132,6 @@ var/const/access_explorer = 43 selection_color = "#013D3B" //VOREStation Edit idtype = /obj/item/weapon/card/id/medical/sar //VOREStation Edit economic_modifier = 6 //VOREStation Edit - access = list(access_medical, access_medical_equip, access_surgery, access_chemistry, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_pilot) //VOREStation Edit - minimal_access = list(access_medical, access_medical_equip, access_pilot) //VOREStation Edit + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_eva, access_maint_tunnels, access_external_airlocks, access_pilot) //VOREStation Edit + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_pilot) //VOREStation Edit outfit_type = /decl/hierarchy/outfit/job/medical/sar \ No newline at end of file From 2833407b01ee971ba5333d6c40a230c631e672ec Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 26 Oct 2018 18:04:16 -0400 Subject: [PATCH 006/174] Custom PDAs for all roles Also fixes the issue of explorers getting cargo cartridges 4noraisin. --- code/game/objects/items/devices/PDA/PDA_vr.dm | 8 ++++++++ maps/southern_cross/job/outfits.dm | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/code/game/objects/items/devices/PDA/PDA_vr.dm b/code/game/objects/items/devices/PDA/PDA_vr.dm index e21fcbea8b..20558c965c 100644 --- a/code/game/objects/items/devices/PDA/PDA_vr.dm +++ b/code/game/objects/items/devices/PDA/PDA_vr.dm @@ -6,4 +6,12 @@ /obj/item/device/pda/pathfinder default_cartridge = /obj/item/weapon/cartridge/signal/science + icon_state = "pda-lawyer-old" + +/obj/item/device/pda/explorer + default_cartridge = /obj/item/weapon/cartridge/signal/science + icon_state = "pda-det" + +/obj/item/device/pda/sar + default_cartridge = /obj/item/weapon/cartridge/medical icon_state = "pda-h" diff --git a/maps/southern_cross/job/outfits.dm b/maps/southern_cross/job/outfits.dm index bb9bc7aef8..4fd254a249 100644 --- a/maps/southern_cross/job/outfits.dm +++ b/maps/southern_cross/job/outfits.dm @@ -13,7 +13,7 @@ Keep outfits simple. Spawn with basic uniforms and minimal gear. Gear instead go l_ear = /obj/item/device/radio/headset/explorer id_slot = slot_wear_id pda_slot = slot_l_store - pda_type = /obj/item/device/pda/cargo // Brown looks more rugged + pda_type = /obj/item/device/pda/explorer //VORESTation Edit - Better Brown id_type = /obj/item/weapon/card/id/explorer/explorer //VOREStation Edit id_pda_assignment = "Explorer" flags = OUTFIT_HAS_BACKPACK|OUTFIT_COMPREHENSIVE_SURVIVAL @@ -46,7 +46,7 @@ Keep outfits simple. Spawn with basic uniforms and minimal gear. Gear instead go l_ear = /obj/item/device/radio/headset/pilot/alt id_slot = slot_wear_id pda_slot = slot_belt - pda_type = /obj/item/device/pda/cargo // Brown looks more rugged + pda_type = /obj/item/device/pda/explorer //VORESTation Edit - Better Brown id_type = /obj/item/weapon/card/id/explorer/pilot //VOREStation Edit id_pda_assignment = "Pilot" flags = OUTFIT_HAS_BACKPACK|OUTFIT_COMPREHENSIVE_SURVIVAL @@ -60,7 +60,7 @@ Keep outfits simple. Spawn with basic uniforms and minimal gear. Gear instead go l_hand = /obj/item/weapon/storage/firstaid/regular belt = /obj/item/weapon/storage/belt/medical/emt pda_slot = slot_l_store - pda_type = /obj/item/device/pda/viro //VOREStation Add + pda_type = /obj/item/device/pda/sar //VOREStation Add id_type = /obj/item/weapon/card/id/medical/sar id_pda_assignment = "Field Medic" //VOREStation Edit flags = OUTFIT_HAS_BACKPACK|OUTFIT_EXTENDED_SURVIVAL|OUTFIT_COMPREHENSIVE_SURVIVAL From 84358c4fd069e7b1e7ca8d5899b7ca04c68d8080 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 28 Oct 2018 19:38:05 -0400 Subject: [PATCH 007/174] Adds Subspace Radio Pathfinder special equipment, currently spawns in the Pathfinder's locker. It is a backpack radio that functionally works similar to a defibrillator, that can send and receive messages from anywhere regardless of telecoms. Is still susceptible to the usual methods of signal disruption such as jamming. --- .../game/objects/items/devices/radio/radio.dm | 11 +- .../objects/items/devices/radio/radio_vr.dm | 127 ++++++++++++++++++ .../structures/closets/misc_vr.dm | 1 + vorestation.dme | 1 + 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 code/game/objects/items/devices/radio/radio_vr.dm diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 231693305f..aa58d5e3e5 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -40,6 +40,7 @@ var/global/list/default_medbay_channels = list( var/list/channels = list() //see communications.dm for full list. First channel is a "default" for :h var/subspace_transmission = 0 var/adhoc_fallback = FALSE //Falls back to 'radio' mode if subspace not available + var/bluespace_radio = FALSE //VOREStation Add - Long Range Radio var/syndie = 0//Holder to see if it's a syndicate encrypted radio var/centComm = 0//Holder to see if it's a CentCom encrypted radio flags = CONDUCT @@ -474,6 +475,13 @@ var/global/list/default_medbay_channels = list( // we're done here. return 1 +//VOREStation Add Start + if(bluespace_radio) + return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), + src, message, displayname, jobname, real_name, M.voice_name, + 0, signal.data["compression"], list(0), connection.frequency,verb,speaking) +//VOREStation Add End + // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level. // Send a mundane broadcast with limited targets: @@ -518,7 +526,8 @@ var/global/list/default_medbay_channels = list( if(!(0 in level)) var/turf/position = get_turf(src) if(!position || !(position.z in level)) - return -1 + if(!bluespace_radio && !(src.bluespace_radio)) //VOREStation Edit + return -1 if(freq in ANTAG_FREQS) if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys return -1 diff --git a/code/game/objects/items/devices/radio/radio_vr.dm b/code/game/objects/items/devices/radio/radio_vr.dm new file mode 100644 index 0000000000..99e93729f5 --- /dev/null +++ b/code/game/objects/items/devices/radio/radio_vr.dm @@ -0,0 +1,127 @@ +//Pathfinder's Subspace Radio +/obj/item/device/subspaceradio + name = "subspace radio" + desc = "A powerful new radio recently gifted to Nanotrasen from KHI, this communications device has the ability to send and recieve transmissions from anywhere." + icon = 'icons/vore/custom_items_vr.dmi' + icon_state = "radiopack" + item_state = "parachute" + slot_flags = SLOT_BACK + force = 5 + throwforce = 6 + preserve_item = 1 + w_class = ITEMSIZE_LARGE + action_button_name = "Remove/Replace Headset" + + var/obj/item/device/radio/subspaceradio/linked/headset + +/obj/item/device/subspaceradio/New() //starts without a cell for rnd + ..() + if(ispath(headset)) + headset = new headset(src, src) + else + headset = new(src, src) + +/obj/item/device/subspaceradio/Destroy() + . = ..() + QDEL_NULL(headset) + +/obj/item/device/subspaceradio/ui_action_click() + toggle_headset() + +/obj/item/device/subspaceradio/attack_hand(mob/user) + if(loc == user) + toggle_headset() + else + ..() + +/obj/item/device/subspaceradio/MouseDrop() + if(ismob(src.loc)) + if(!CanMouseDrop(src)) + return + var/mob/M = src.loc + if(!M.unEquip(src)) + return + src.add_fingerprint(usr) + M.put_in_any_hand_if_possible(src) + +/obj/item/device/subspaceradio/attackby(obj/item/weapon/W, mob/user, params) + if(W == headset) + reattach_headset(user) + else + return ..() + +/obj/item/device/subspaceradio/verb/toggle_headset() + set name = "Toggle Headset" + set category = "Object" + + var/mob/living/carbon/human/user = usr + if(!headset) + to_chat(user, "The headset is missing!") + return + + if(headset.loc != src) + reattach_headset(user) //Remove from their hands and back onto the defib unit + return + + if(!slot_check()) + to_chat(user, "You need to equip [src] before taking out [headset].") + else + if(!usr.put_in_hands(headset)) //Detach the headset into the user's hands + to_chat(user, "You need a free hand to hold the headset!") + update_icon() //success + +//checks that the base unit is in the correct slot to be used +/obj/item/device/subspaceradio/proc/slot_check() + var/mob/M = loc + if(!istype(M)) + return 0 //not equipped + + if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_back) == src) + return 1 + if((slot_flags & SLOT_BELT) && M.get_equipped_item(slot_belt) == src) + return 1 + + return 0 + +/obj/item/device/subspaceradio/dropped(mob/user) + ..() + reattach_headset(user) //headset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit + +/obj/item/device/subspaceradio/proc/reattach_headset(mob/user) + if(!headset) return + + if(ismob(headset.loc)) + var/mob/M = headset.loc + if(M.drop_from_inventory(headset, src)) + to_chat(user, "\The [headset] snap back into the main unit.") + else + headset.forceMove(src) + +//Subspace Radio Headset +/obj/item/device/radio/subspaceradio + name = "subspace radio headset" + desc = "A large walkie talkie attached to the subspace radio by a retractable cord. It sits comfortably on a slot in the radio when not in use." + bluespace_radio = TRUE + icon_state = "signaller" + slot_flags = null + w_class = ITEMSIZE_LARGE + +/obj/item/device/radio/subspaceradio/linked + var/obj/item/device/subspaceradio/base_unit + +/obj/item/device/radio/subspaceradio/linked/New(newloc, obj/item/device/subspaceradio/radio) + base_unit = radio + ..(newloc) + +/obj/item/device/radio/subspaceradio/linked/Destroy() + if(base_unit) + //ensure the base unit's icon updates + if(base_unit.headset == src) + base_unit.headset = null + base_unit = null + return ..() + +/obj/item/device/radio/subspaceradio/linked/dropped(mob/user) + ..() //update twohanding + if(base_unit) + base_unit.reattach_headset(user) //headset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit diff --git a/maps/southern_cross/structures/closets/misc_vr.dm b/maps/southern_cross/structures/closets/misc_vr.dm index 27b0150331..3d5af628aa 100644 --- a/maps/southern_cross/structures/closets/misc_vr.dm +++ b/maps/southern_cross/structures/closets/misc_vr.dm @@ -23,6 +23,7 @@ /obj/item/device/geiger, /obj/item/weapon/cell/device, /obj/item/device/radio, + /obj/item/device/subspaceradio, /obj/item/stack/marker_beacon/thirty, /obj/item/weapon/material/knife/tacknife/survival, /obj/item/weapon/material/knife/machete/deluxe, diff --git a/vorestation.dme b/vorestation.dme index bea34dbe10..61df8daa12 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -1000,6 +1000,7 @@ #include "code\game\objects\items\devices\radio\intercom.dm" #include "code\game\objects\items\devices\radio\jammer.dm" #include "code\game\objects\items\devices\radio\radio.dm" +#include "code\game\objects\items\devices\radio\radio_vr.dm" #include "code\game\objects\items\robot\robot_items.dm" #include "code\game\objects\items\robot\robot_parts.dm" #include "code\game\objects\items\robot\robot_upgrades.dm" From 83e7c7e79073319c51a1b36c92f4c88fbe4ffce3 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 28 Oct 2018 19:39:44 -0400 Subject: [PATCH 008/174] Splits Subspace Radio off --- maps/southern_cross/job/outfits.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maps/southern_cross/job/outfits.dm b/maps/southern_cross/job/outfits.dm index 4fd254a249..159701decd 100644 --- a/maps/southern_cross/job/outfits.dm +++ b/maps/southern_cross/job/outfits.dm @@ -46,7 +46,7 @@ Keep outfits simple. Spawn with basic uniforms and minimal gear. Gear instead go l_ear = /obj/item/device/radio/headset/pilot/alt id_slot = slot_wear_id pda_slot = slot_belt - pda_type = /obj/item/device/pda/explorer //VORESTation Edit - Better Brown + pda_type = /obj/item/device/pda //VOREStation Edit - Civilian id_type = /obj/item/weapon/card/id/explorer/pilot //VOREStation Edit id_pda_assignment = "Pilot" flags = OUTFIT_HAS_BACKPACK|OUTFIT_COMPREHENSIVE_SURVIVAL From bf63a5152e0bba7db351d67ea1bc1dc655ef6f0a Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 30 Oct 2018 02:36:20 -0400 Subject: [PATCH 009/174] Requested Changes 1 --- .../game/objects/items/devices/radio/radio.dm | 15 ++-- .../objects/items/devices/radio/radio_vr.dm | 77 ++++++++++--------- 2 files changed, 47 insertions(+), 45 deletions(-) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index aa58d5e3e5..b723da353f 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -40,7 +40,6 @@ var/global/list/default_medbay_channels = list( var/list/channels = list() //see communications.dm for full list. First channel is a "default" for :h var/subspace_transmission = 0 var/adhoc_fallback = FALSE //Falls back to 'radio' mode if subspace not available - var/bluespace_radio = FALSE //VOREStation Add - Long Range Radio var/syndie = 0//Holder to see if it's a syndicate encrypted radio var/centComm = 0//Holder to see if it's a CentCom encrypted radio flags = CONDUCT @@ -475,6 +474,12 @@ var/global/list/default_medbay_channels = list( // we're done here. return 1 + // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level. + // Send a mundane broadcast with limited targets: + + //THIS IS TEMPORARY. YEAH RIGHT + if(!connection) return 0 //~Carn + //VOREStation Add Start if(bluespace_radio) return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), @@ -482,12 +487,6 @@ var/global/list/default_medbay_channels = list( 0, signal.data["compression"], list(0), connection.frequency,verb,speaking) //VOREStation Add End - // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level. - // Send a mundane broadcast with limited targets: - - //THIS IS TEMPORARY. YEAH RIGHT - if(!connection) return 0 //~Carn - return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), src, message, displayname, jobname, real_name, M.voice_name, filter_type, signal.data["compression"], list(position.z), connection.frequency,verb,speaking) @@ -526,7 +525,7 @@ var/global/list/default_medbay_channels = list( if(!(0 in level)) var/turf/position = get_turf(src) if(!position || !(position.z in level)) - if(!bluespace_radio && !(src.bluespace_radio)) //VOREStation Edit + if(!bluespace_radio) //VOREStation Edit return -1 if(freq in ANTAG_FREQS) if(!(src.syndie))//Checks to see if it's allowed on that frequency, based on the encryption keys diff --git a/code/game/objects/items/devices/radio/radio_vr.dm b/code/game/objects/items/devices/radio/radio_vr.dm index 99e93729f5..00bc5fca70 100644 --- a/code/game/objects/items/devices/radio/radio_vr.dm +++ b/code/game/objects/items/devices/radio/radio_vr.dm @@ -1,3 +1,6 @@ +/obj/item/device/radio + var/bluespace_radio = FALSE + //Pathfinder's Subspace Radio /obj/item/device/subspaceradio name = "subspace radio" @@ -10,27 +13,27 @@ throwforce = 6 preserve_item = 1 w_class = ITEMSIZE_LARGE - action_button_name = "Remove/Replace Headset" + action_button_name = "Remove/Replace Handset" - var/obj/item/device/radio/subspaceradio/linked/headset + var/obj/item/device/radio/subspacehandset/linked/handset /obj/item/device/subspaceradio/New() //starts without a cell for rnd ..() - if(ispath(headset)) - headset = new headset(src, src) + if(ispath(handset)) + handset = new handset(src, src) else - headset = new(src, src) + handset = new(src, src) /obj/item/device/subspaceradio/Destroy() . = ..() - QDEL_NULL(headset) + QDEL_NULL(handset) /obj/item/device/subspaceradio/ui_action_click() - toggle_headset() + toggle_handset() /obj/item/device/subspaceradio/attack_hand(mob/user) if(loc == user) - toggle_headset() + toggle_handset() else ..() @@ -45,29 +48,29 @@ M.put_in_any_hand_if_possible(src) /obj/item/device/subspaceradio/attackby(obj/item/weapon/W, mob/user, params) - if(W == headset) - reattach_headset(user) + if(W == handset) + reattach_handset(user) else return ..() -/obj/item/device/subspaceradio/verb/toggle_headset() - set name = "Toggle Headset" +/obj/item/device/subspaceradio/verb/toggle_handset() + set name = "Toggle Handset" set category = "Object" var/mob/living/carbon/human/user = usr - if(!headset) - to_chat(user, "The headset is missing!") + if(!handset) + to_chat(user, "The handset is missing!") return - if(headset.loc != src) - reattach_headset(user) //Remove from their hands and back onto the defib unit + if(handset.loc != src) + reattach_handset(user) //Remove from their hands and back onto the defib unit return if(!slot_check()) - to_chat(user, "You need to equip [src] before taking out [headset].") + to_chat(user, "You need to equip [src] before taking out [handset].") else - if(!usr.put_in_hands(headset)) //Detach the headset into the user's hands - to_chat(user, "You need a free hand to hold the headset!") + if(!usr.put_in_hands(handset)) //Detach the handset into the user's hands + to_chat(user, "You need a free hand to hold the handset!") update_icon() //success //checks that the base unit is in the correct slot to be used @@ -85,43 +88,43 @@ /obj/item/device/subspaceradio/dropped(mob/user) ..() - reattach_headset(user) //headset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit + reattach_handset(user) //handset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit -/obj/item/device/subspaceradio/proc/reattach_headset(mob/user) - if(!headset) return +/obj/item/device/subspaceradio/proc/reattach_handset(mob/user) + if(!handset) return - if(ismob(headset.loc)) - var/mob/M = headset.loc - if(M.drop_from_inventory(headset, src)) - to_chat(user, "\The [headset] snap back into the main unit.") + if(ismob(handset.loc)) + var/mob/M = handset.loc + if(M.drop_from_inventory(handset, src)) + to_chat(user, "\The [handset] snap back into the main unit.") else - headset.forceMove(src) + handset.forceMove(src) -//Subspace Radio Headset -/obj/item/device/radio/subspaceradio - name = "subspace radio headset" +//Subspace Radio Handset +/obj/item/device/radio/subspacehandset + name = "subspace radio handset" desc = "A large walkie talkie attached to the subspace radio by a retractable cord. It sits comfortably on a slot in the radio when not in use." bluespace_radio = TRUE icon_state = "signaller" slot_flags = null w_class = ITEMSIZE_LARGE -/obj/item/device/radio/subspaceradio/linked +/obj/item/device/radio/subspacehandset/linked var/obj/item/device/subspaceradio/base_unit -/obj/item/device/radio/subspaceradio/linked/New(newloc, obj/item/device/subspaceradio/radio) +/obj/item/device/radio/subspacehandset/linked/New(newloc, obj/item/device/subspaceradio/radio) base_unit = radio ..(newloc) -/obj/item/device/radio/subspaceradio/linked/Destroy() +/obj/item/device/radio/subspacehandset/linked/Destroy() if(base_unit) //ensure the base unit's icon updates - if(base_unit.headset == src) - base_unit.headset = null + if(base_unit.handset == src) + base_unit.handset = null base_unit = null return ..() -/obj/item/device/radio/subspaceradio/linked/dropped(mob/user) +/obj/item/device/radio/subspacehandset/linked/dropped(mob/user) ..() //update twohanding if(base_unit) - base_unit.reattach_headset(user) //headset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit + base_unit.reattach_handset(user) //handset attached to a base unit should never exist outside of their base unit or the mob equipping the base unit From ee756cfb290a39a4e9b9404aada6679c6da2c3fc Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 30 Oct 2018 10:31:24 -0400 Subject: [PATCH 010/174] Default Internal Channels Update --- .../game/objects/items/devices/radio/radio.dm | 19 +++++++++++-------- .../objects/items/devices/radio/radio_vr.dm | 16 ++++++++++++++++ maps/tether/tether-01-surface1.dmm | 15 ++------------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index b723da353f..f7e34fe47d 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -1,25 +1,28 @@ // Access check is of the type requires one. These have been carefully selected to avoid allowing the janitor to see channels he shouldn't +//VOREStation Edit Start - Updating this for Virgo var/global/list/default_internal_channels = list( num2text(PUB_FREQ) = list(), - num2text(AI_FREQ) = list(access_synth), num2text(ENT_FREQ) = list(), + num2text(AI_FREQ) = list(access_synth), num2text(ERT_FREQ) = list(access_cent_specops), num2text(COMM_FREQ)= list(access_heads), num2text(ENG_FREQ) = list(access_engine_equip, access_atmospherics), - num2text(MED_FREQ) = list(access_medical_equip), - num2text(MED_I_FREQ)=list(access_medical_equip), + num2text(MED_FREQ) = list(access_medical), + num2text(MED_I_FREQ)=list(access_medical), num2text(SEC_FREQ) = list(access_security), num2text(SEC_I_FREQ)=list(access_security), - num2text(SCI_FREQ) = list(access_tox,access_robotics,access_xenobiology), - num2text(SUP_FREQ) = list(access_cargo), - num2text(SRV_FREQ) = list(access_janitor, access_hydroponics) + num2text(SCI_FREQ) = list(access_research), + num2text(SUP_FREQ) = list(access_cargo, access_mining), + num2text(SRV_FREQ) = list(access_janitor, access_hydroponics), + num2text(EXP_FREQ) = list(access_pilot, access_explorer, access_rd) ) var/global/list/default_medbay_channels = list( num2text(PUB_FREQ) = list(), - num2text(MED_FREQ) = list(access_medical_equip), - num2text(MED_I_FREQ) = list(access_medical_equip) + num2text(MED_FREQ) = list(), + num2text(MED_I_FREQ) = list() ) +//VOREStation Edit End /obj/item/device/radio icon = 'icons/obj/radio_vr.dmi' //VOREStation Edit diff --git a/code/game/objects/items/devices/radio/radio_vr.dm b/code/game/objects/items/devices/radio/radio_vr.dm index 00bc5fca70..e546a62298 100644 --- a/code/game/objects/items/devices/radio/radio_vr.dm +++ b/code/game/objects/items/devices/radio/radio_vr.dm @@ -1,6 +1,22 @@ +/obj/item/device/radio/phone + subspace_transmission = 1 + canhear_range = 0 + adhoc_fallback = TRUE + /obj/item/device/radio var/bluespace_radio = FALSE +/obj/item/device/radio/emergency + name = "Medbay Emergency Radio Link" + icon_state = "med_walkietalkie" + frequency = MED_I_FREQ + subspace_transmission = 1 + adhoc_fallback = TRUE + +/obj/item/device/radio/emergency/New() + ..() + internal_channels = default_medbay_channels.Copy() + //Pathfinder's Subspace Radio /obj/item/device/subspaceradio name = "subspace radio" diff --git a/maps/tether/tether-01-surface1.dmm b/maps/tether/tether-01-surface1.dmm index 2f3d16e25a..b7c6a77749 100644 --- a/maps/tether/tether-01-surface1.dmm +++ b/maps/tether/tether-01-surface1.dmm @@ -13773,11 +13773,7 @@ "aGx" = ( /obj/structure/table/glass, /obj/item/weapon/storage/toolbox/emergency, -/obj/item/device/radio{ - frequency = 1487; - icon_state = "med_walkietalkie"; - name = "Medbay Emergency Radio Link" - }, +/obj/item/device/radio/emergency, /obj/machinery/recharger, /turf/simulated/floor/tiled/white, /area/tether/surfacebase/medical/first_aid_west) @@ -14750,14 +14746,7 @@ /area/engineering/atmos/processing) "aJj" = ( /obj/structure/table/glass, -/obj/item/device/radio{ - anchored = 1; - broadcasting = 0; - canhear_range = 1; - frequency = 1487; - icon = 'icons/obj/items.dmi'; - icon_state = "red_phone"; - listening = 1; +/obj/item/device/radio/phone/medbay{ name = "Medical Emergency Phone" }, /obj/machinery/atmospherics/unary/vent_pump/on{ From fd8d32896e625eb91690bdd7c6e040e35730d0c6 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 30 Oct 2018 11:28:55 -0400 Subject: [PATCH 011/174] Remove Srcs (Thanks Aro) --- .../game/objects/items/devices/radio/radio_vr.dm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/code/game/objects/items/devices/radio/radio_vr.dm b/code/game/objects/items/devices/radio/radio_vr.dm index e546a62298..e54741c1fa 100644 --- a/code/game/objects/items/devices/radio/radio_vr.dm +++ b/code/game/objects/items/devices/radio/radio_vr.dm @@ -54,14 +54,14 @@ ..() /obj/item/device/subspaceradio/MouseDrop() - if(ismob(src.loc)) - if(!CanMouseDrop(src)) - return - var/mob/M = src.loc - if(!M.unEquip(src)) - return - src.add_fingerprint(usr) - M.put_in_any_hand_if_possible(src) + if(ismob(loc)) + if(!CanMouseDrop(src)) + return + var/mob/M = loc + if(!M.unEquip(src)) + return + add_fingerprint(usr) + M.put_in_any_hand_if_possible(src) /obj/item/device/subspaceradio/attackby(obj/item/weapon/W, mob/user, params) if(W == handset) From 9d382a8df53c3ee8f100efccc7d1ae15e47f54b2 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 30 Oct 2018 11:33:12 -0400 Subject: [PATCH 012/174] Minor changes --- code/game/objects/items/devices/radio/radio.dm | 2 +- code/game/objects/items/devices/radio/radio_vr.dm | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index f7e34fe47d..f5b9c456d8 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -13,7 +13,7 @@ var/global/list/default_internal_channels = list( num2text(SEC_I_FREQ)=list(access_security), num2text(SCI_FREQ) = list(access_research), num2text(SUP_FREQ) = list(access_cargo, access_mining), - num2text(SRV_FREQ) = list(access_janitor, access_hydroponics), + num2text(SRV_FREQ) = list(access_janitor, access_library, access_hydroponics, access_bar, access_kitchen), num2text(EXP_FREQ) = list(access_pilot, access_explorer, access_rd) ) diff --git a/code/game/objects/items/devices/radio/radio_vr.dm b/code/game/objects/items/devices/radio/radio_vr.dm index e54741c1fa..d3681423f3 100644 --- a/code/game/objects/items/devices/radio/radio_vr.dm +++ b/code/game/objects/items/devices/radio/radio_vr.dm @@ -1,11 +1,11 @@ +/obj/item/device/radio + var/bluespace_radio = FALSE + /obj/item/device/radio/phone subspace_transmission = 1 canhear_range = 0 adhoc_fallback = TRUE -/obj/item/device/radio - var/bluespace_radio = FALSE - /obj/item/device/radio/emergency name = "Medbay Emergency Radio Link" icon_state = "med_walkietalkie" From 6018fe8d238b9ba3bdc812935c8eb33fba508a5d Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 30 Oct 2018 13:47:32 -0400 Subject: [PATCH 013/174] Travis plz --- code/game/objects/items/devices/radio/radio.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index f5b9c456d8..f688efc6cf 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -2,8 +2,8 @@ //VOREStation Edit Start - Updating this for Virgo var/global/list/default_internal_channels = list( num2text(PUB_FREQ) = list(), - num2text(ENT_FREQ) = list(), num2text(AI_FREQ) = list(access_synth), + num2text(ENT_FREQ) = list(), num2text(ERT_FREQ) = list(access_cent_specops), num2text(COMM_FREQ)= list(access_heads), num2text(ENG_FREQ) = list(access_engine_equip, access_atmospherics), From 433fa46ca8bcebb56937f9426d382ee353e461c5 Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 5 Nov 2018 14:23:35 -0500 Subject: [PATCH 014/174] Fixes some oversights --- .../loadout/loadout_accessories.dm | 8 ++++---- .../preference_setup/loadout/loadout_eyes.dm | 2 +- .../preference_setup/loadout/loadout_suit.dm | 2 +- .../loadout/loadout_utility_vr.dm | 2 +- maps/southern_cross/items/headset_sc.dm | 8 ++++---- .../loadout/loadout_accessories.dm | 16 ++++++++-------- maps/tether/tether-05-station1.dmm | 6 +++--- maps/tether/tether-06-station2.dmm | 4 ++-- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories.dm b/code/modules/client/preference_setup/loadout/loadout_accessories.dm index 5e6dfae145..b2727143c7 100644 --- a/code/modules/client/preference_setup/loadout/loadout_accessories.dm +++ b/code/modules/client/preference_setup/loadout/loadout_accessories.dm @@ -150,17 +150,17 @@ /datum/gear/accessory/brown_drop_pouches display_name = "drop pouches, brown" path = /obj/item/clothing/accessory/storage/brown_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Search and Rescue") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Field Medic") //VOREStation Edit /datum/gear/accessory/black_drop_pouches display_name = "drop pouches, black" path = /obj/item/clothing/accessory/storage/black_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Search and Rescue") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Field Medic") //VOREStation Edit /datum/gear/accessory/white_drop_pouches display_name = "drop pouches, white" path = /obj/item/clothing/accessory/storage/white_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Search and Rescue") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Field Medic") //VOREStation Edit /datum/gear/accessory/fannypack display_name = "fannypack selection" @@ -239,7 +239,7 @@ /datum/gear/accessory/stethoscope display_name = "stethoscope" path = /obj/item/clothing/accessory/stethoscope - allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Search and Rescue") + allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic") //VOREStation Edit /datum/gear/accessory/locket display_name = "locket" diff --git a/code/modules/client/preference_setup/loadout/loadout_eyes.dm b/code/modules/client/preference_setup/loadout/loadout_eyes.dm index 5981424753..85e007da90 100644 --- a/code/modules/client/preference_setup/loadout/loadout_eyes.dm +++ b/code/modules/client/preference_setup/loadout/loadout_eyes.dm @@ -57,7 +57,7 @@ /datum/gear/eyes/medical display_name = "Medical HUD (Medical)" path = /obj/item/clothing/glasses/hud/health - allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Search and Rescue") + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") //VOREStation Edit /datum/gear/eyes/medical/prescriptionmed display_name = "Medical HUD, prescription (Medical)" diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm index 41d37b8a67..87382eae60 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -480,7 +480,7 @@ datum/gear/suit/duster /datum/gear/suit/snowsuit/medical display_name = "snowsuit, medical" path = /obj/item/clothing/suit/storage/snowsuit/medical - allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Search and Rescue") + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") //VOREStation Edit /datum/gear/suit/snowsuit/science display_name = "snowsuit, science" diff --git a/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm b/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm index 65bc2af8bd..9845ba9855 100644 --- a/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm @@ -35,7 +35,7 @@ /datum/gear/utility/dufflebag/med display_name = "medical dufflebag" path = /obj/item/weapon/storage/backpack/dufflebag/med - allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist","Psychiatrist","Search and Rescue") + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist","Psychiatrist","Field Medic") /datum/gear/utility/dufflebag/med/emt display_name = "EMT dufflebag" diff --git a/maps/southern_cross/items/headset_sc.dm b/maps/southern_cross/items/headset_sc.dm index ec73fbc7c7..6007317792 100644 --- a/maps/southern_cross/items/headset_sc.dm +++ b/maps/southern_cross/items/headset_sc.dm @@ -28,13 +28,13 @@ icon_state = "exp_headset_alt" /obj/item/device/radio/headset/sar - name = "sar radio headset" - desc = "A headset for search and rescue." + name = "fm radio headset" //VOREStation Edit + desc = "A headset for field medics." //VOREStation Edit icon_state = "sar_headset" adhoc_fallback = TRUE ks2type = /obj/item/device/encryptionkey/sar /obj/item/device/radio/headset/sar/alt - name = "sar radio bowman headset" - desc = "A bowman headset for search and rescue." + name = "fm radio bowman headset" //VOREStation Edit + desc = "A bowman headset for field medics." //VOREStation Edit icon_state = "sar_headset_alt" diff --git a/maps/southern_cross/loadout/loadout_accessories.dm b/maps/southern_cross/loadout/loadout_accessories.dm index 1a23e18e9c..4a6b234183 100644 --- a/maps/southern_cross/loadout/loadout_accessories.dm +++ b/maps/southern_cross/loadout/loadout_accessories.dm @@ -1,34 +1,34 @@ /datum/gear/accessory/brown_vest display_name = "webbing, brown" path = /obj/item/clothing/accessory/storage/brown_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit /datum/gear/accessory/black_vest display_name = "webbing, black" path = /obj/item/clothing/accessory/storage/black_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit /datum/gear/accessory/white_vest display_name = "webbing, white" path = /obj/item/clothing/accessory/storage/white_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit /datum/gear/accessory/brown_drop_pouches display_name = "drop pouches, brown" path = /obj/item/clothing/accessory/storage/brown_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit /datum/gear/accessory/black_drop_pouches display_name = "drop pouches, black" path = /obj/item/clothing/accessory/storage/black_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit /datum/gear/accessory/white_drop_pouches display_name = "drop pouches, white" path = /obj/item/clothing/accessory/storage/white_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit /datum/gear/accessory/holster - display_name = "holster selection (Security, CD, HoP)" + display_name = "holster selection (Security, CD, HoP, Explorer)" //VOREStation Edit path = /obj/item/clothing/accessory/holster - allowed_roles = list("Colony Director","Head of Personnel","Security Officer","Warden","Head of Security","Detective","Explorer") \ No newline at end of file + allowed_roles = list("Colony Director","Head of Personnel","Security Officer","Warden","Head of Security","Detective","Explorer","Pathfinder") //VOREStation Edit \ No newline at end of file diff --git a/maps/tether/tether-05-station1.dmm b/maps/tether/tether-05-station1.dmm index f8d7b4cf9f..ac19de75b0 100644 --- a/maps/tether/tether-05-station1.dmm +++ b/maps/tether/tether-05-station1.dmm @@ -21787,7 +21787,7 @@ icon_state = "1-8" }, /obj/effect/landmark/start{ - name = "Search and Rescue" + name = "Field Medic" }, /turf/simulated/floor/tiled, /area/tether/station/explorer_meeting) @@ -21851,7 +21851,7 @@ dir = 1 }, /obj/effect/landmark/start{ - name = "Search and Rescue" + name = "Field Medic" }, /turf/simulated/floor/tiled, /area/tether/station/explorer_meeting) @@ -21912,7 +21912,7 @@ dir = 4 }, /obj/effect/landmark/start{ - name = "Search and Rescue" + name = "Field Medic" }, /turf/simulated/floor/tiled, /area/tether/station/explorer_meeting) diff --git a/maps/tether/tether-06-station2.dmm b/maps/tether/tether-06-station2.dmm index fd09396865..efe884efd5 100644 --- a/maps/tether/tether-06-station2.dmm +++ b/maps/tether/tether-06-station2.dmm @@ -16384,7 +16384,7 @@ /obj/effect/floor_decal/corner/paleblue/diagonal, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/effect/landmark/start{ - name = "Search and Rescue" + name = "Field Medic" }, /turf/simulated/floor/tiled/white, /area/crew_quarters/medbreak) @@ -16500,7 +16500,7 @@ }, /obj/effect/floor_decal/corner/paleblue/diagonal, /obj/effect/landmark/start{ - name = "Search and Rescue" + name = "Field Medic" }, /turf/simulated/floor/tiled/white, /area/crew_quarters/medbreak) From dcfecf6e857ee06fdd93918d9d62791ff099a611 Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 5 Nov 2018 20:00:31 -0500 Subject: [PATCH 015/174] Wrong file edited, loadouts configured proper --- .../loadout/loadout_accessories.dm | 16 ++++++++-------- .../loadout/loadout_accessories.dm | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories.dm b/code/modules/client/preference_setup/loadout/loadout_accessories.dm index b2727143c7..7629ce19a2 100644 --- a/code/modules/client/preference_setup/loadout/loadout_accessories.dm +++ b/code/modules/client/preference_setup/loadout/loadout_accessories.dm @@ -77,9 +77,9 @@ gear_tweaks += new/datum/gear_tweak/path(sortAssoc(wcoats)) /datum/gear/accessory/holster - display_name = "holster selection (Security, CD, HoP)" + display_name = "holster selection (Security, CD, HoP, Explorer)" path = /obj/item/clothing/accessory/holster - allowed_roles = list("Colony Director", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective") + allowed_roles = list("Colony Director", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective","Explorer","Pathfinder") //VOREStation Edit /datum/gear/accessory/holster/New() ..() @@ -135,32 +135,32 @@ /datum/gear/accessory/brown_vest display_name = "webbing, brown" path = /obj/item/clothing/accessory/storage/brown_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit /datum/gear/accessory/black_vest display_name = "webbing, black" path = /obj/item/clothing/accessory/storage/black_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit /datum/gear/accessory/white_vest display_name = "webbing, white" path = /obj/item/clothing/accessory/storage/white_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor") + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit /datum/gear/accessory/brown_drop_pouches display_name = "drop pouches, brown" path = /obj/item/clothing/accessory/storage/brown_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Field Medic") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit /datum/gear/accessory/black_drop_pouches display_name = "drop pouches, black" path = /obj/item/clothing/accessory/storage/black_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Field Medic") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit /datum/gear/accessory/white_drop_pouches display_name = "drop pouches, white" path = /obj/item/clothing/accessory/storage/white_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Field Medic") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit /datum/gear/accessory/fannypack display_name = "fannypack selection" diff --git a/maps/southern_cross/loadout/loadout_accessories.dm b/maps/southern_cross/loadout/loadout_accessories.dm index 4a6b234183..1a23e18e9c 100644 --- a/maps/southern_cross/loadout/loadout_accessories.dm +++ b/maps/southern_cross/loadout/loadout_accessories.dm @@ -1,34 +1,34 @@ /datum/gear/accessory/brown_vest display_name = "webbing, brown" path = /obj/item/clothing/accessory/storage/brown_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") /datum/gear/accessory/black_vest display_name = "webbing, black" path = /obj/item/clothing/accessory/storage/black_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") /datum/gear/accessory/white_vest display_name = "webbing, white" path = /obj/item/clothing/accessory/storage/white_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") /datum/gear/accessory/brown_drop_pouches display_name = "drop pouches, brown" path = /obj/item/clothing/accessory/storage/brown_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") /datum/gear/accessory/black_drop_pouches display_name = "drop pouches, black" path = /obj/item/clothing/accessory/storage/black_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") /datum/gear/accessory/white_drop_pouches display_name = "drop pouches, white" path = /obj/item/clothing/accessory/storage/white_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Explorer","Shaft Miner","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Search and Rescue","Explorer","Shaft Miner") /datum/gear/accessory/holster - display_name = "holster selection (Security, CD, HoP, Explorer)" //VOREStation Edit + display_name = "holster selection (Security, CD, HoP)" path = /obj/item/clothing/accessory/holster - allowed_roles = list("Colony Director","Head of Personnel","Security Officer","Warden","Head of Security","Detective","Explorer","Pathfinder") //VOREStation Edit \ No newline at end of file + allowed_roles = list("Colony Director","Head of Personnel","Security Officer","Warden","Head of Security","Detective","Explorer") \ No newline at end of file From f4b652ff065f8ef72b12b81861daf205426dfb18 Mon Sep 17 00:00:00 2001 From: Verkister Date: Sat, 10 Nov 2018 11:47:07 +0200 Subject: [PATCH 016/174] Enables riding for a bunch of animals. -Basically adds a simpleanimal version of the taur/borg riding mechanics. -Only enabled for player controlled mobs, so no resources wasted on slapping the datums and verbs onto every existing mob. -As with the new taur and borg riding, only the mount can buckle the rider onto them using the verb. -The mounting offsets can be defined for each mobtype, thus supporting many shapes and sizes. -Currently enabled for horses, oties, corrupt hounds, giant rats, panthers, dragons, hippos, and deathclaws. -Also fixed faulty sprite centering for a bunch of voremobs (panther, dragon, deathclaw, whitecarp, regularcarp, bears, hippo, fennec) --- .../living/simple_animal/simple_animal_vr.dm | 95 ++++++++++++++++++ .../simple_animal/vore/corrupt_hounds.dm | 14 +++ .../living/simple_animal/vore/deathclaw.dm | 16 +++ .../mob/living/simple_animal/vore/dragon.dm | 16 +++ .../mob/living/simple_animal/vore/hippo.dm | 17 +++- .../mob/living/simple_animal/vore/horse.dm | 15 +++ .../mob/living/simple_animal/vore/otie.dm | 17 +++- .../mob/living/simple_animal/vore/panther.dm | 15 +++ .../mob/living/simple_animal/vore/rat.dm | 15 +++ icons/mob/vore.dmi | Bin 190294 -> 190396 bytes icons/mob/vore64x64.dmi | Bin 142827 -> 142782 bytes 11 files changed, 218 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/simple_animal/simple_animal_vr.dm b/code/modules/mob/living/simple_animal/simple_animal_vr.dm index dea5206448..a6f7aa8339 100644 --- a/code/modules/mob/living/simple_animal/simple_animal_vr.dm +++ b/code/modules/mob/living/simple_animal/simple_animal_vr.dm @@ -32,6 +32,9 @@ var/vore_fullness = 0 // How "full" the belly is (controls icons) var/vore_icons = 0 // Bitfield for which fields we have vore icons for. + var/mount_offset_x = 5 // Horizontal riding offset. + var/mount_offset_y = 8 // Vertical riding offset + // Release belly contents before being gc'd! /mob/living/simple_animal/Destroy() release_vore_contents() @@ -241,3 +244,95 @@ if(a_intent == I_GRAB && isliving(A) && !has_hands) animal_nom(A) + +// Riding +/datum/riding/simple_animal + keytype = /obj/item/weapon/material/twohanded/fluff/riding_crop // Crack! + nonhuman_key_exemption = FALSE // If true, nonhumans who can't hold keys don't need them, like borgs and simplemobs. + key_name = "a riding crop" // What the 'keys' for the thing being rided on would be called. + only_one_driver = TRUE // If true, only the person in 'front' (first on list of riding mobs) can drive. + +/datum/riding/simple_animal/handle_vehicle_layer() + if(ridden.has_buckled_mobs()) + if(ridden.dir != NORTH) + ridden.layer = ABOVE_MOB_LAYER + else + ridden.layer = initial(ridden.layer) + else + ridden.layer = initial(ridden.layer) + +/datum/riding/simple_animal/ride_check(mob/living/M) + var/mob/living/L = ridden + if(L.stat) + force_dismount(M) + return FALSE + return TRUE + +/datum/riding/simple_animal/force_dismount(mob/M) + . =..() + ridden.visible_message("[M] stops riding [ridden]!") + +/datum/riding/simple_animal/get_offsets(pass_index) // list(dir = x, y, layer) + var/mob/living/simple_animal/L = ridden + var/scale = L.size_multiplier + + var/list/values = list( + "[NORTH]" = list(0, L.mount_offset_y*scale, ABOVE_MOB_LAYER), + "[SOUTH]" = list(0, L.mount_offset_y*scale, BELOW_MOB_LAYER), + "[EAST]" = list(-L.mount_offset_x*scale, L.mount_offset_y*scale, ABOVE_MOB_LAYER), + "[WEST]" = list(L.mount_offset_x*scale, L.mount_offset_y*scale, ABOVE_MOB_LAYER)) + + return values + +/mob/living/simple_animal/buckle_mob(mob/living/M, forced = FALSE, check_loc = TRUE) + if(forced) + return ..() // Skip our checks + if(!riding_datum) + return FALSE + if(lying) + return FALSE + if(!ishuman(M)) + return FALSE + if(M in buckled_mobs) + return FALSE + if(M.size_multiplier > size_multiplier * 1.2) + to_chat(src,"This isn't a pony show! You need to be bigger for them to ride.") + return FALSE + + var/mob/living/carbon/human/H = M + + if(H.loc != src.loc) + if(H.Adjacent(src)) + H.forceMove(get_turf(src)) + + . = ..() + if(.) + buckled_mobs[H] = "riding" + +/mob/living/simple_animal/attack_hand(mob/user as mob) + if(riding_datum && LAZYLEN(buckled_mobs)) + //We're getting off! + if(user in buckled_mobs) + riding_datum.force_dismount(user) + //We're kicking everyone off! + if(user == src) + for(var/rider in buckled_mobs) + riding_datum.force_dismount(rider) + else + . = ..() + +/mob/living/simple_animal/proc/animal_mount(var/mob/living/M in living_mobs(1)) + set name = "Animal Mount/Dismount" + set category = "Abilities" + set desc = "Let people ride on you." + + if(LAZYLEN(buckled_mobs)) + for(var/rider in buckled_mobs) + riding_datum.force_dismount(rider) + return + if (stat != CONSCIOUS) + return + if(!can_buckle || !istype(M) || !M.Adjacent(src) || M.buckled) + return + if(buckle_mob(M)) + visible_message("[M] starts riding [name]!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/vore/corrupt_hounds.dm b/code/modules/mob/living/simple_animal/vore/corrupt_hounds.dm index b8801ea2c9..eb62a21086 100644 --- a/code/modules/mob/living/simple_animal/vore/corrupt_hounds.dm +++ b/code/modules/mob/living/simple_animal/vore/corrupt_hounds.dm @@ -49,6 +49,11 @@ minbodytemp = 150 maxbodytemp = 900 + max_buckled_mobs = 1 //Yeehaw + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + var/image/eye_layer = null @@ -114,3 +119,12 @@ remove_eyes() if(stat == CONSCIOUS && !resting) add_eyes() + +/mob/living/simple_animal/hostile/corrupthound/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_animal(src) + verbs |= /mob/living/simple_animal/proc/animal_mount + +/mob/living/simple_animal/hostile/corrupthound/MouseDrop_T(mob/living/M, mob/living/user) + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/vore/deathclaw.dm b/code/modules/mob/living/simple_animal/vore/deathclaw.dm index 2f99179bd9..91fe552ccc 100644 --- a/code/modules/mob/living/simple_animal/vore/deathclaw.dm +++ b/code/modules/mob/living/simple_animal/vore/deathclaw.dm @@ -22,6 +22,13 @@ pixel_x = -16 pixel_y = 0 + max_buckled_mobs = 1 //Yeehaw + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + mount_offset_x = 5 + mount_offset_y = 30 + // Activate Noms! /mob/living/simple_animal/hostile/deathclaw vore_active = 1 @@ -30,3 +37,12 @@ vore_min_size = RESIZE_SMALL vore_pounce_chance = 0 // Beat them into crit before eating. vore_icons = SA_ICON_LIVING + +/mob/living/simple_animal/hostile/deathclaw/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_animal(src) + verbs |= /mob/living/simple_animal/proc/animal_mount + +/mob/living/simple_animal/hostile/deathclaw/MouseDrop_T(mob/living/M, mob/living/user) + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/vore/dragon.dm b/code/modules/mob/living/simple_animal/vore/dragon.dm index 7f64ee0da2..2d839fb149 100644 --- a/code/modules/mob/living/simple_animal/vore/dragon.dm +++ b/code/modules/mob/living/simple_animal/vore/dragon.dm @@ -30,6 +30,13 @@ pixel_x = -16 pixel_y = 0 + max_buckled_mobs = 1 //Yeehaw + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + mount_offset_x = -11 + mount_offset_y = 16 + /mob/living/simple_animal/hostile/dragon/Process_Spacemove(var/check_drift = 0) return 1 //No drifting in space for space dragons! @@ -49,3 +56,12 @@ maxHealth = 200 health = 200 faction = "virgo3b" + +/mob/living/simple_animal/hostile/dragon/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_animal(src) + verbs |= /mob/living/simple_animal/proc/animal_mount + +/mob/living/simple_animal/hostile/dragon/MouseDrop_T(mob/living/M, mob/living/user) + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/vore/hippo.dm b/code/modules/mob/living/simple_animal/vore/hippo.dm index bfd28aafb5..ae9d8775e1 100644 --- a/code/modules/mob/living/simple_animal/vore/hippo.dm +++ b/code/modules/mob/living/simple_animal/vore/hippo.dm @@ -49,13 +49,19 @@ meat_amount = 10 //Infinite meat! meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + max_buckled_mobs = 1 //Yeehaw + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + mount_offset_y = 20 + // Activate Noms! /mob/living/simple_animal/retaliate/hippo //I don't know why it's in a seperate line but everyone does it so i do it vore_active = 1 vore_capacity = 1 vore_bump_chance = 15 vore_bump_emote = "lazily wraps its tentacles around" - vore_standing_too = 1 + vore_standing_too = 1 vore_ignores_undigestable = 0 vore_default_mode = DM_HOLD vore_digest_chance = 10 @@ -64,3 +70,12 @@ vore_stomach_name = "rumen" //First stomach of a ruminant. It's where the pre digestion bacteria stuff happens. Very warm. vore_stomach_flavor = "You are squeezed into the sweltering insides of the herbivore rumen." vore_icons = SA_ICON_LIVING + +/mob/living/simple_animal/retaliate/hippo/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_animal(src) + verbs |= /mob/living/simple_animal/proc/animal_mount + +/mob/living/simple_animal/retaliate/hippo/MouseDrop_T(mob/living/M, mob/living/user) + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/vore/horse.dm b/code/modules/mob/living/simple_animal/vore/horse.dm index 59bbfa4550..80cd80dd0e 100644 --- a/code/modules/mob/living/simple_animal/vore/horse.dm +++ b/code/modules/mob/living/simple_animal/vore/horse.dm @@ -31,7 +31,22 @@ meat_amount = 4 meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + max_buckled_mobs = 1 //Yeehaw + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + mount_offset_x = 0 + // Activate Noms! /mob/living/simple_animal/horse vore_active = 1 vore_icons = SA_ICON_LIVING + +/mob/living/simple_animal/horse/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_animal(src) + verbs |= /mob/living/simple_animal/proc/animal_mount + +/mob/living/simple_animal/horse/MouseDrop_T(mob/living/M, mob/living/user) + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/vore/otie.dm b/code/modules/mob/living/simple_animal/vore/otie.dm index 5220f861c5..c7d171ed84 100644 --- a/code/modules/mob/living/simple_animal/vore/otie.dm +++ b/code/modules/mob/living/simple_animal/vore/otie.dm @@ -46,6 +46,12 @@ pixel_x = -16 pixel_y = 0 + max_buckled_mobs = 1 //Yeehaw + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + mount_offset_y = 10 + var/glowyeyes = FALSE var/image/eye_layer = null var/eyetype @@ -382,4 +388,13 @@ /mob/living/simple_animal/otie/death(gibbed, deathmessage = "dies!") .=..() resting = 0 - icon_state = icon_dead \ No newline at end of file + icon_state = icon_dead + +/mob/living/simple_animal/otie/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_animal(src) + verbs |= /mob/living/simple_animal/proc/animal_mount + +/mob/living/simple_animal/otie/MouseDrop_T(mob/living/M, mob/living/user) + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/vore/panther.dm b/code/modules/mob/living/simple_animal/vore/panther.dm index 454ef4d927..45a18cf300 100644 --- a/code/modules/mob/living/simple_animal/vore/panther.dm +++ b/code/modules/mob/living/simple_animal/vore/panther.dm @@ -27,9 +27,24 @@ pixel_x = -16 pixel_y = 0 + max_buckled_mobs = 1 //Yeehaw + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + mount_offset_y = 12 + // Activate Noms! /mob/living/simple_animal/hostile/panther vore_active = 1 vore_capacity = 2 vore_pounce_chance = 10 vore_icons = SA_ICON_LIVING | SA_ICON_REST + +/mob/living/simple_animal/hostile/panther/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_animal(src) + verbs |= /mob/living/simple_animal/proc/animal_mount + +/mob/living/simple_animal/hostile/panther/MouseDrop_T(mob/living/M, mob/living/user) + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/vore/rat.dm b/code/modules/mob/living/simple_animal/vore/rat.dm index 40b1800aa0..1f6be2e3f4 100644 --- a/code/modules/mob/living/simple_animal/vore/rat.dm +++ b/code/modules/mob/living/simple_animal/vore/rat.dm @@ -36,6 +36,12 @@ pixel_x = -16 pixel_y = 0 + max_buckled_mobs = 1 //Yeehaw + can_buckle = TRUE + buckle_movable = TRUE + buckle_lying = FALSE + mount_offset_y = 10 + vore_active = TRUE vore_capacity = 1 vore_pounce_chance = 45 @@ -159,3 +165,12 @@ /mob/living/simple_animal/hostile/rat/death() playsound(src, 'sound/effects/mouse_squeak_loud.ogg', 50, 1) ..() + +/mob/living/simple_animal/hostile/rat/Login() + . = ..() + if(!riding_datum) + riding_datum = new /datum/riding/simple_animal(src) + verbs |= /mob/living/simple_animal/proc/animal_mount + +/mob/living/simple_animal/hostile/rat/MouseDrop_T(mob/living/M, mob/living/user) + return \ No newline at end of file diff --git a/icons/mob/vore.dmi b/icons/mob/vore.dmi index 0357739b9cf8ce00d011dcd19e8d5c96771aa097..4479212c1be27dcada25e880728cd50422c94790 100644 GIT binary patch literal 190396 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

sq0xbj-&)C#v}UZ})&AwfkDg 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 XP5H0-7#^K$06ZS6>ZnvIS%?20ctzPh diff --git a/icons/mob/vore64x64.dmi b/icons/mob/vore64x64.dmi index 8454da40d783fbedfe6a5606b8a9af4794ec49a7..13a67edbe38a118e0cffd8d9ad9112c9e7089510 100644 GIT binary patch literal 142782 zcmafa1yodB8}1CCq@;q#n=juog4t?0t5fH=gHtgH@Gf@8MD6K_HNO@^VsY5D2C>_$Q8Y7yQL-9&-%> zfqr#+`_4hi*xty_+{VG&+6n@3NiB_Qv--_V5HK{L{?3MjteU3o1Ius+^aIqj!-Da3 z%Hx^gMX$gz{IT~RJH{2Bd7{n`&ihY9N;CL{-&yjn`j(7YVm`i4WFMVcbB_1=9XaEh z+-Dn2L|Y?daokBkQlIxS{ru%VaHJQy15jOGS-iKl{>dDPsmyUBzAA55prwx{`=uF+ zx>VR0R6= z?xNZ6iMAq)a*d1g`#|$q{nOw-ER*P)XgHVbWIC}X^&EhmA_g$YwjeF*wGT| z=EBo6jeeYjeBm9iju^>F4|6VL8^A{cLU5tq%FRp!5%>Aa+IsZac z;#*#%^m_tif6((EC)}8Q-`n1uhgr@Vrk3fP_ghk`D${Yeb@B0&i&}GNjz>)dqPDl$ zu~}Jh*en&P1|-dm-pg3gI1k+1pKN+iQcHDm=M2|ZmS>932Lhpo$VyoaJMVvf%I_mk6zGS6UBD1wEt;!D8y3c%-=B2akr+D5)>{!?YOADE`RirGQ-|CB z_7S1?rfD?*5)BCMVM_0$o(Z>}%^~Wh(4|KoERIAgYHRPda})gg$w7HA?@)-rheVqh zIdkQbT?PUSAO8DswWmRy+_)d^G}qViy@HP+&eKDq>Q}c2(iGBT>9|^t8b2J=>9vx$ zi~i3hmdiqD39=8fThy*!$PFLddeX_a7_AQd=V4*CpX4B2LD>{jlPss?s^)&C->#Kga(VBf^YL~ILP4t?KAAzlt0lEa=}P0narf97yk^~j4w1aR1`8jKhK*n z$o*O>4YOOmDoBA+S0c)b-;$Eeb4V)Zzq43VUKR9q(0%juJ6oIie}Dx< zS`qSVeLeSTvB7V(z648QV}5!X0@*)2l<~BN!|5PDs;lEHP`TvZuJ}*MO#gjwm%uO! z3C_8{fgIN7DDE%C#BskP6O)pH$$eZyVcxjn8P_2TDJlqImb|f=dWFTW!`a^dHoTCK zL9^{LJo!z+N%b1L^j0Wjd3o93qqTK8Hl(VmD!8T3-#|xdd3E)p3LY#%1&jQ_4U?S$ z^q%F?^4zg#F?}rlXyUJbH;OyVE-5{oHs}|xL(_t0(66X#CKD4A$o7*gX+jsplG4%# zj#mF!aR?-e`<=Enlv{}I`#>~InTo=9#Ri(lODfktFaS0C@VfBX#tU*QeCb+57YBw> z7*V9ymE#f4XzUfL`c2wb!-pq{D5|b&af!mQo$BW=N9B~qHt(7|@G@3+#XA1e>cQPR z4I}_JrZp`m=8vvUEuz-=uR2p?#rDvfqR8E2RO{`4J0y7nr)$v~GJR{-`cCO7UEtx` z?~K)qt+nw%Xchck6(D8;$fRU;zGC~{zEUhX8WN+1qpYL^HB;#*86XjVN|nd=x>5>j z?a)wsK;KsDG(Z5dOab$- zU$18oWAAE4GSGS+?uptVX$9DYw{{MQMnoNu+x1ohG_SmdT2BY&u&=H(HN_5^3y7Bf z4xJrY6j&OmJYE~tku&7kEIajm;g9N!tFW~Zf)8W?y_N21;HZje~rVNHG{IzdAq(M3T=e3fdi?q*@|4V!`eW!UazFo z6ybXfX|-sK!bZ**Aq3BrUJmCVES^97M=*ywSq_E?(8Gg>{IlP> zxGoA&H9a|!B!e$lIj`3^5oCt`%EuFLc$$CTTWUt)$Joxfrj{0Vc~wa9JLto+mOTOG zq>tRpw?Wf(ev8OI;g(QF4V}>04JX2O&a3K58;?U!c-1?SK0iY2s zsEa9R{T3$10Lj;~17cl_C^mJRMm}qmM!W(=RG89@gY=(Kam(!qE&YK!v~sGzNT_*J z1@n_7XBf)?<=os{d}GN&)1ZMrK~%5MN< z;=6TcaK~+iXIOeFhC*$*;18~4-fp$H*xTG``;QGm$ClObuUAktq-ZmWAp%y< zf11vm56e88s-eZeMhey$nL*&9!M$s5%rj}Wp z2EN7*Cfd*|U5-Pf8kMs8hXWM$CM-f{3+Ac_O zbv@wW?Lj|=p*MV+12lbSX#IR&3U z)WybzdxdPyL-;NSWqnP}%ec!&!dsC+!9g5cTx-4Y&m^bTzRnJ>f_**jl;LR@o|rAW zEB!i>4i=%1E@!AH=&E;eU+w!`X=!OXIWF^xtl2@BEZ}|L9%4KQ?_L>6r73M+%}Fft zhv*imQA+29gn3Vc%31CH#z83Y*}V|h&sZ7PZQ%t;>}lunn}$9)&)6$g2g{!X z=6h`y!Wz{lA_q$^zXIj`(ainmKkaB&ckOF+BH%Vpg>M#JjwpaXDlx&`9^#__Ux0h1 z_wFwM>bxg(Xs^o3xGmy!Ip?N@SLf?Jtm(&ITO-zRD#-C^oVe>HarDbhm2V0%&{2Vht|a!Bk#y)Z+3z`@HWV`0Yr;)y;G`Z|{x7OI=)8QqOiP0$ zmoAb~6tbF#0zSz(mcKUq54Gj*2dLs$6q!RQQ9qoTc;lv%Xo)&5-Y-kx{^S7qrNmk0 z**@S(RexHwn#T(_f zc5&3Y)Mp{4rR~>|5t@|r2#gw5kvC`cpwT$luNwMBd1roZZtq5lGMGv54?FKxq->?f zGo#PZzg5>&2B{ozUwR!p&z=X(b8Kwv?$!E@?wNMe!r`(|jhsPpCE0uCs4}XD%|w)H zNN6-hlrXzpM|5}9*@<*_b)f&G;a^|3c0$)2-**8Xk-qgFgqP4t2u3|^{#z4EAD?Wx zi1(Dqn-3eXFai^jBCgV6=~;hlR|Z^~t;q?G^lr9013PtNB_#;~)t4&vm^(FnZ{qNi zuc>=|1m2D+--s3!qsGK+NoUurbob#aDx%9dWG?$lOMK^If;Vc%H?wXF`}!ZAam9X_ ziv+eyquCf#xe(4-d7c;>7l4)*UpO{lWKk?m+*v+vU}^^)N{KOwY0Lhl5*A&n+Y#rxa|qVy6?2P*l<$*F-rsqGV&gO2LGhQ~Ll$unv=ZYCV(6T@)H5lza7Kj! zfy0e*yd@DbWiv0`H;&5+2&fF*HBW1K4U;}zq61+_%Tncme@D?bjJvtT`1-Tj(M1N@ z`}(y+OP6=Vs!_`>&5ffSy9Pj|Rqpail)*)?xIEbbFtXLMcs3!)?pOyfi+~UNsm-l1 z(;8ZR1xzn_S`_L|q0fj?jV~_%<0S;M2Rhw7p!d}XECjjWvK9{UUgU3qLK3fgR3H#K zk7KB}7qg9{qvNNMa4>d%%B4eKo&!&Ps{YEy>(S}E_!oV<=_03f6%|ke-rsC0+HG19BsU)FXh723s zzuuCbgVo(MaCxdZzW03Q($b7tk<*Dj>!JllbePOLC>?9zV(+4O{VS9|vVKdnpiS!Y znAW5~k5jqc{bmV>ecuL*b#4M%GpK`p2}ul|+};Y&F3j$>_Kb@8#z|JEHruK5&FMxkcx;3tn>=n# zJ4Q!ImD5FL&ZIH)S`*I2Zg&~qvR1Tq)9v0BFyDI*qhG zbX&R(c#7Pvvh@+Z1{0|o5+C!9jLTV8gO3zm3{K9@fof)BQuk{L^_Qomasn!mBBxzj z7>yA`3jq5;J1a&ShN@Gowk*A8ngglry0EMnQ9d(hRP~s(@9K(nt#OOp=Hy^A1`1>F z_TCD}j7ikkZ5_~?59*Evwo)JDVYhxG$M(ySF)wF&lGUKyvKJTqB%Lyu_i3S??4r9b zI7e|W%QyBV#_mu0(2t+LSDK3NkateQd>HYo_qHSlRqy~#$?!^pA2IZGD826ooC2Jf z#scCYIhg5CQBg4s-hi$rlH4;XP5P*5$uVPnc%<3wlKpsGU6PI_f1-xvUK}}oFo~{l zloZmu$X_iNnjWqy1&8D6ZGc*FGx8BAIX7aY(sy##h&Yr-gM+{cF72vp@*?)A?f zxkT_OWCZ&YLt{Y*CoA_lvQz3#Z0^(3h9AE68;T^CT=KJ4M(R?1l>l?GMq`lP!U#Wh zNyKG&@EvV^p26nJIi)K2R7FdWTNn=eDBAPw1fb=5WHdrOUu-(Gde=B_h{VLyRwFlL zR51J_ZZOP?=z2F+98Lu~f@|j(D{rrz(Y!1+`Y1)L-b=gF^_~x3?jNuB#5OAz#O^xt zp8UyyR~$?`QtMAkS2j1()X-qDj)-|vKeA2jExhyQ- zl`$_YF^0-RH5t>ugkLCtk8ulwjfGLJBi~{4W@nzqkq-t_vHUJ;53N}6~5%KrXxCkXT8tq^uri)-0#CPcW?n#S^WAz8@~;k@*FT06_)s- z{#Yd{Gw%$&{4NJotW+@~M?^LRN>8`uTA|<*p0GeamAbi3i}L2X2GcxOd8OuN=3g8l znhqN+xSh+sH+Z+0J+k4gJGwqu^Yw)Iv975vK?G~Q?{9!uy_$EQQT~c{ zorI0^Zg7-A?C+@E?bXgudCqp+npaP<>kWE+)eDrN4{6xt$mG|Z*2v(L&l%-5m>6!N znOS3x5=$xC={v<#`spIk&tn(d2?!+6pj=UD7wx$I))=t)07J+9zf4IuCoSwv#=$q} z;N_J!mI=mByn}o*sfHe7gvK#PGVPvzOcz) zrzpE4QW!BB^vE(RMjv@R(6%MNTd0WO`dYc4A-i2Ir%wr8-X$7i|Dj%sVhv92wYV5D zK}0@|yBC#!nyLWG)jJ~FwIXu2=}h5pICJ(EDdf~1tv9IWyl^Lzj^?|a^l)=?^9RMJ zYxDE-s#u}fiSHfcLLj*1k&M|s+Vng{d8%r+c|S8MNM30NidBZND6?f3+3t=PMHiNZ zGCy-uqcPziV`YPENG@X2Y`MMjr^OTdxHY;y@#g)FBerT&#NYU)WciU0A4}RxCA>r@ zT{Y||TBJuhb{b|-vHDcIe$!%NV`lCBWcSlhx|U!NBNLyK667*#3h;+rWS3zN&;7KH zNbtxYbLVPc>K;;5~)Sm(eWiMxSy$)7VyKWSyPQq-HuN$iR zf%Wn+_A`$1!1lcXemsu_7)s$hJ!;sG#;=B#-Gp*M3yb{Xjk|`uxN3l=dML zh=!@WM+`~G(Z4kZFziqyzb&e3NSaDshhvtc?Ge2e`a8jlZCj%QR2 zfzdX{6gic-%8@>CslLPE-QvBTU5rNpf(;XE%I^EwEbrHehC9C&qXpiwYJ`Y$qCeIt zEbEXHzwgjW;n$w>q;F8F-?aa1JioL=N8**`)~|p?i2G}3$zI0N4365%Df;@J6_w!p z;SM)Do+NT3P3xq_`r=t2*9G;OV*MuVdJ-)OC3FaL_c%q>ORIqnj97gw2nTR`m>%eEwm z4aTU=72CFCDXh|;Xus8EbRJF$_vVBIGcq=s_b{TF znQ3FF^=oj(JLsY{vD9b+$o0VNczhFa)602B-|T@hhf@7+VL5(+l8x?&t;C{E2K~8o zXl#rdlgo0UI=ZiYiW%whx{yd{bITb+UlI<`>i^-q^L#*tYxT*^tDQu|B?RN zI_wmp59sOeCcO-K+iiQc4IH<9>v5=Mjl=*+$P91t_CJ{Xj}~-@;F`VuPr--r_ZZS( z2hI|q=RZB`pJzm7Yud8Mh#X{W+H|TW@RbFZKlfi=gSlC(H#d%l<5$72c*LvrVN!+g z<6tr65d@XV;~hRC?acd)3kwU|qRYKsQ%`sqxd%d0G_vyw)NVZgAjGb4bxW21!uA@W z;vq;|6I0XX(AH~BhR2grQ#qRM1C-EnJn@#57p&F9xYC$L>Jq^16kQw4%k{1TUHIJb zOI&R!gV|Ho4bbWMaCts=YoB`_O;namJ6t*MDlK8Y@|AJ#WzY6eX<%`id@d%l*6;H) z`@eZ>Ibm9rJ`H*xO?oLeQ=2X?DG8H8@bUAXp=aLKgYK&LwY8Cv(XpF} z3Cl?Si8RM2CV6UTn4=f^pXt)(yWVaRkKX+K_G+td?3bWkNrRMb*ss19qz_+k4LGJc zvBS!&h-!&_8{F>bI)Af2G5%?k^He%ZP=aSLa%ix6s2ex8b;I;8rn*S$VNPTo&v>Y9UqXPkoV@yU+A##63OaJFg%A_`5)~Q8@$j_QUjPOdd#=X>E zZD#0|bj=pghQE2Z*Ga)Hf~Fzt3XDj;(P{)Ql=+gq&I%D_8JFp4!<+98T zk)kJ45pAqxjGijadv$6F@mOa-S1*;fe6e*ZpsSna7#&bIx;lMIIS3k9f7-P;d{K&X9uKR&UuV7O#|~4eD#BD*yc!FZ zg|D|9@n-mwvtRe}TmQ*{?MBijY@9Q z(NTP*b`8%$GgMC}?bB6q?#4bC2UjUPy!q=# zKj*~dss(98nq&Rwnl}lFx%aU~uFU*GGXh%_h>jnQutc7CvRX28@1&CJ-{5LtWpchD zEbVW>=5zi{F7^Ih@;1HmxqzKUL!~V0DM%_948cX`W_Luhc z|LiX%SxJ}tR|}vrb^wza6$%aBo@j=1%hW+_qBW3?LTPV{Y6Dh&Q$gdX+F=b&yakdW z;s!1i6b3Gi&nc3xpT)47XL=|rD9Y05poN&UE@QhL5`64k1e}A;*63&j?UP@yr8R>= z&T@tI;-Ri!1mF~(gO*CZ`4}T402dcCj{c-%SUkJKgP`l&2XLz3Fr_*;#r=CosMW7J z;*5NG5>cTKWPXlEbX{o=Pbiv(^l`}GGK*nSkek3Pv*mdT>j+ZoS&@ggv_Q)~q^Ny} z2_x}O*KsN{k?fQR{eeI!a;N+`tU5eP&z@%e`POW&by>KN=U_T=;W=N2vM-m~+sV}O zpVX_>&jj<^_-F5pAJL|O`)84eD#($)EPo|&tdp@ z?3j@VR0|ar`1VUe$eZ(|z6E-i5sK3YyJ)%AObm{)TPh#*L@zf+T>gpr<7LJZr@fGnkDjvt7z)e0z|im-#KjcP7g#Qd*1Q#vNy{&iWjN==MO>FuSZxj! zvjcyRbRBgL0?*!Kv7}LSfVot%c;U~AKJPJGPF7VRn3C6Ud^6qqkz8%n?CvwI;28ik z_TV(?a^}rlPFpgR>op1Pk&MBvFCBS9vYtE>p_B=lzl3_&9_)9(7VYyBr25UNI{D+P zV>eUQk+#>X#GoUcX~=j$H{cw8XtASrPUWxbAzfW6QV}NEFn}IFOaAk}MFTFW*}~Ub zY%-q!!(0Y+yh10DJil8ZPNm}q&G)26iZZ1>oQWmJEoOxuv!p>(c=P%LvJ5Ymz*EjG zbA_+iC>|CHhUV>{X*XQ=B>Ng**b$?H73BhsOKL0qMe}0<*t8@KEe9Uv`PNB`*)YAuF(xU zw#f`4#^n!+CH|wZ2{0VTC;N}c1C)9geZ>|3pK=$TEH!9-ef_kK zu5PvHm;Z=^j0m`aRyTGGv;TI$!;%Q$CCGXEWgXP?{JzC(=MFpNf&MR-bt%L;*g`e)&@P$J*N3$lSbV_dj!z^8;JF_zSdvEwCYKI5FNyAW!4A zlnwJNq-|U{97ceN_U;MjQX#7NC$dcM7}jwp7VJYI1nPk#ZPgu%eYSO1#B1*&%PMmZ z^mkv!9td{`)Xp*faO3Umz{-rqXU3?jtCN+YAEw~d#fn*eq{XmJ>Lvc7BO{t_e#G*- zz01jJXW^Q1nvn4$sEx4Od$>WhF9?AIgOmP1sppHrT1|9yTQFmp5qr@N zG+NBtO%w>(P2)gCV%S*Rk+w%air6;z1fIJ}m;s`(zBxvm(p7g9`L9V5}U+d{_7Q8GM59K+k(ugV8TEV<0@ z^7HZWQKpb4zub6H+iJ$hiC~?jzM4N?PDrG#-R5Hta;^Q}cxoPmZthch}K)_12xjp7t;vRpQpIT4&Ne=jJ46oJpHxjtz2Gw_(vymq~ zZT9x|k{Ku1oAruL!L`G(HRHGTRInf@iyx)2)+Y zDYUpCkCE}O;K=NO)-r$d=BAk ztGZC|7hRe8kHTUHo=bO6#l7D2J(NfjV~|d@_@wU+cXHyhXQr1yJ_uJpn$y5)Ub-#q zw^CPg=+x(^=K303>i-nIEvtLF6*(u=$~l0ZyML#o3Z-SJVghu6{2R^UpM;wF)Ehu{ zd<2aj2~oqWKN}S00c9U9H=~b_PBL^pbfFPPYZp`c*uxQTAXDK9OpusLN{Rg-z5WZc z27t$$t}O5sW#PT%+uh=Z@ugrgRyQ6?{%_Njy>G_imiMIAhI z30w6$pNjD7Mxyv?;9UwI!kG@AIRpRU`>=fWrrthu3N`MRuc#(9hgcJ65-RXXJG#d6 z-A?)!YwLvC>@rbc&l2!I#O$^fJ89T3-a0|PxK%bg^akzYNi35($ZWNd5bQd{gute( z^L^^F$XJy>SqUSlTWYBkVxFHi<_O&j5BL+*TFH$Vs$DhLD9$?-Ey~Z&2hWc`h?L>i zhz_7*4K9yZCeeTMabCKj-FUeHbc#7Z{aJRr%;QfoIHXy0QAWO3N{db>ZMz9@QE+^J z7*ukAvZ%3c1W4CQmTQ6HE%?}py;j*FDT4zUi1wg?m}idBoTdj0cdH>&mCn{-$$yKX*aWIG8)4Dkhk^>jWC!f^e)Z z8ML>@XzNEArav?2hJz(IfD(YCMId#)Qme?UK-%9`Lt?@LvPqs0{U5E2?!(}3;tE` z3`BrAejPlix4fq6c@F%S3k>kNMF9RXkRCRd=_^nI^RVGYy!DmHO$V9p)Op%;@R|}m z%dOhPca@*&a-_KH(LSoPvXdk&iG$vCH3?jLlwk&4VIO-(&aCQ$?OH_2Mgof?3YBl< z=qV258R5pF5D)?E5P&1AKyA?)c=Wh7W1@ry{CoX%S=mDwL)iStm@F<>55@llI|)6NCJW-qziSSzPJ!^%{%S@{-^s5aya` z8M?6_fDSW4jR&zOFArj;WDd%}ZTQUuI99^-nKKm;&YpE8PM zQ)+O3CfQ!x@*iFj@wNk8%oq=2`IY$f(E~lg8FM&Q)z!SO(n7dMyTf@0{EnE*LBEl@ zHG10RX_lkHlVRSv(r215m0I1;`H7jhu;4nU6+FJ3qobo8mzTBn%xXdp&1F(zrzOnR zTdgjSLa^420j%;Xp%B>Z+WC9Tc)r7kF>a9Rg_ zThW+A>|`1>4OUF~Y9jWU6~L1NFnkZgfE+49vco1&*WIuSOr;8VF9c@*_cVH{H^?{y z_G`cO-X$FsE`i~tegw(_qcWroOolVez5*HUST?q{6|86ZH{Ey(9{12Qx3{-)Xh)hY z!Rn-v)krqG@|0<6&&9jOTU<+L;b3w^miUd=F<%<{MwA^B^fJ+ZbYn^`kNO~m#VACX zV8HXMy?(MPxFm=Kq>AjfhPZm000Y@SG{ko{qfc8wO_aEwoBWNvm4N--$m|RXYt|$s!e*B@`Vv?<^;`Bu4#A}% z17l7B*V9i!{G{6{h=NyCDWCr|?J_!XdW%HxMQ4D*s%qi)^*Q}-#I1lxE6a-|MFRkG z%sh;Ta9*hbo{n7Ji8l+CH2>?Cj`#-9dh3P&&!54;!H-r}%6iQeZ2jo=@d>n%R&2Pr zuL)pvS_!A~7)kt|4r~HbQvjF41JyF5c{Z!R=gaLELYnFAY_wCK)!xevQgJqaJF@85 zY;BSm*}eRQRhht$KUjS`v6~``p;yeWDIJE2#I$pdZzN_y^MVQxF}RGF_}FwS-)_dA z3csH$UIVk=yVuPN7vV$(r_h=ea+z8DxA=Z#1AGiI@A>0v4L<7Fnl`n+@^ zU06Le+nv6Dy>VkC+q1c~Rit!b#;huwE`t_IBLu(ujwsy-m+ehP5KV;c%_EV#twh`9 z$9Jfe1`FiqyJ55>u0zWrFCLSZ3&a4m=%9tc$(na&ur13I-WY{#zc$XJUXH_ z|IHuR$|^j{+)6ff^Jn=x{}c4JXVkH~mIcoB_@Z#cv zR@IW3_+c&3hO?aXqW+cbp};JT0olP);N;zFPrQ}a8-358f0qVgN?88tc1KMiHoI0p z$Kv7#uD4U5M6lV{*H=PzWbO-~eZWeb8 zlgtaWK0?C{F^C`Db3CK!ljU4DJ*8GMvanD%P&8t3Y7~memZm3;K;8(2=jG+$Hn@bT z$Qh?y(bHjN5hm90mr^^R?2IOcz06$0Ydd8~B^89!1+U$caoByqsK2DFjBx0Vfiivg zbkpSIgl=KP z?8&vWt$pZjv8pnNHU;m>)^B5ARBQn4sCys+hMWP|>pmmUgXcWJKkltzAG4b#cIt!s zJ#VYTihCnh3xApOC}EvHx&~7NTl0=ndJvf{i0Z;kcN59*MI*Vptu$KK<|ONp!ZaXXNC5ejdJP;tMWV>d6uH-`7v6+LjEJscS>$ zh07xnuI_FhVam80`upk677{wouZzC6;1?G{B%p{U-q3nNmk)_ftDQ5g&wg!-Dv-&` z839;Jp?Ct{m%weFzr0JukGPWV^UmROM_!+_iFyBRd4C1VNhmG-*#zef?I%v*5YNNN zCKQ=D_A0X-cjDEV@eIrm4bKp|0UD$s#*$OxR&1YFcbV5u7ObWkl*HpZ=8+;^Oh+=r zvuj-PvTaPNNFOey&(fT>DV>HfOZbA-t}DC8H5MVSLa{Jz`AA%stKbJ85HgUB6Uz$t zQ|y@}_CWTnBJ~0rkO?jf(kDN`&UNNy);`de!Kqx_dnX+82y)su_UlMTZ6#p>jl1*f z2&6b>&C-hu7!U>H_Ka_b%HDpuEq!_bfF&!xSuu(@&6Wc?LaX6k?7+P2;t;%2<8y{K zf7+%~t}&)xI%4fJ;AahC3omW-<3#BNpvX3XturduS@eu zbYD#w-}jbzf5V9D=U`<#$OSJQXj)NqlbDQ1UJmCgAAxiD|E{s4mlbLP5s0>1=2K!% z28oZuj;+_2a4V}p;p82ikxlM`C_#;M?8Qw&%g z;f>}F)$UE2UR)e>$hh8PzxXzZHj=#stpLM{tQ>S-!QL*9(0p78Xc#_HEiKj}$uw4lx z`sgcnqxBen5MuuF)!Q$`swNaaGrV9qZYo z*bHoq_SUzy!dACyI?)@y>BTFd^k{;T6qG%ysM8y=gm8 z%D{b$gTyG3AC7nEhk+s#pOFTH-wa%IKO=|)E~N&*Hg&a8z;8|HMtCexx}n|F@@MIp z+fgNN$D&6gnz-mFB-#7OVE zL2b!BgC)l5K?!1jj05YCpuq7}}sZo5SWLX%W zHqtA4t5Jd%CxskYI$y>q#pn<|Rc$YSO8Ss$g5Kp1(fMBIhFtf3??xv(hV|`)Zy0hp zTBl+NTC78{)Q_V1@K!Cbcu7_H-eX=&1eV~>m`6P0 zb-QO!=w&8(MeIoYN1o0~o1QAnEqgnxTHI3o zcsPHtjEx_@povI`CcF6%Bs2$)k;~j`a;(qbS+@ijWb2eJiTw6*4lgq%o%=omWfnzE zz|feXBpM#GpH=er1t{F8WOanZa#kp8CD6cU_+bZj4TCWr>BUk`1ttfk%fVQfuC@`2W7Rjm6fAw?r@d`C$Qor zj%6CIMk0qVsHCD|+E|-E2Xl6q1Cs_vO$C;(c3$Zq;zC@1$2=wvbH4S%+`JiynzD(Cd{#94CWTeZ+-hrU-Kwz{J3}dS$PQln@_p9CZXb-bWm~I{bz~NkXC9u7FdPq@mL8}KYUPu@ zB```Y3I#HrfVjSP^zTB>93j`7_+ngpbeKzT7}onao-jAnfNLg2kn4!1AcvL^_Zn|D zj0G5m@mC9cQEmtQdkUu zEQ9e!jK9|c{uZ(N$zUsCQOewO1hqayyd-EZ3>l|t`&%d5$&@Ddnub9R>%CB+;r&RE zFPjD#68XPmL%SJqat?^UX?Ny5?3y;j3<7X!4cxJK>jV$ z1uu7DrC`2e;4`29_eSonKrqlJhk{-H|6Be)X_7Xq|FATM54#c#{!Z=R-i#Ev|5qO5 zpOODxkIAd}mtoo0@T3XgDf>Tf$}?gD%btlR>Hg9!G7bOR<+47vum4afn^KcY4^96s zF2xvxrm!J@E0QnZv-=s7KG}QxRS-~nm%8mB)O>T}dT|yX|DXmk$sJ(3upJ(L@8bI4 zz>=MVL!0J5tC#l?4HtX60hp3||E(wH9RU@c5PB+%2L@mf>$Ybo`aH`Glvzj~fg#sF zv<#*=M+aTFfqxW}uEP)q2Df6YESAyHM*W<7kZp;qmiFi+mt$VfCCx|bIZKjvqLfPQ zNZ|bK0kFjg>PKaMG&hTm;&^-cxG-g6L|RDNEVyJoS{>>pI^5=)5d7(K@^Rd@Ksl|F zVd=Ho}Qk!+B9pI$b#V3 zR$Cl9&u0LZ`le3QEG;i)*CDjvyOK-dvv!J1um^=WCfU-NAf?l2gl<*V zLjm(l-dN#SW@h1%5d>gubWJ`9J16JmM>`KaqObWuu!J?PIjEqB<;%-=vjP}$evCe4 z*jN>yjZSvTnt`*AtAO}YT{fq{D0<@`N=9iL-^i>Ab#!oW0l5l-2i1xKdZB{EEvB&H zbiSdAEx7vunEEsA?gI%eM48r~ytQ7}%~^IbGMsMq+1W`bR~Yi%(F%O|;sx2nAc%v# zHwVnka;{&@Doyguak|!AzC}}d= z8Q92W_mV(c#+JN!`x#6IX}f;5#GM>;0}LDkAknFvg*P`d(*`PS$g& zMiFKh)|<`F{}S1}4SzNc>lkEsIjaKJiswD>TJyUHp8rc=($? zAA+u{?v`RWf=}Y|sLFG>8@=(uPbPLwl|meGKxY$)OhFOl%)o9S;3+LNV`|xIH32LO z2ru>HU}k9e^Y5oX`P}sM!^THQZ}tbGFJImnfGWDL;o{*>m6Ci)+mpPIxM%o3ID7AK zIK!`Nbo832L9`J>i6{vn1S7gg)F1>Sk%$tVh&pQY-l7gcbP^%DQKCmDBZ$uEZS-m9 z@q6F*obO!cJAa(>ha{J4GBeNp>}TJ5ueJ8N0|Ejzh!Rd_Hk>o!$ z`d3%qIGb}5JF36DKC=VWn1RhwR?=$WlONRgI2Sgf11CK6M%@=?RU$lAr|F2hTf-G@3e3QFdiwpgrpp+y5PObH4S zV1OxM_<~Z7l)g zmHYZ<+Q0pO;AHkM#G$ij#7twtIw9OHivR)0I~Il4tP>J-bA->|s>|;cF1Zg5PQwWP zEmz!&)F*)JIR5I?L?sSwO6lGFGI&RWD5yby|4IsdlP@a25A1Rbx}tX&48uM4EnzrM z&@LO9hbPvJRL@{NB=2fU*(ju#>-N9!Co`~$XIHC6mNY^}E2?goX0i5`@GOp0x&v<^ z+@w_ivLU=0_y%(X8#f68q()I?iNgrtau#tx2$0|jQe#{5FdOn4t)oWJ7SOStr#kGLF8!C!4L z#Q~X@j+ah1d}A_jGBjv{hF$Z2R!`bvxt5P8KM_$u4cBEnz2!Vk@Ld~N+@IEeiqFG< z%hcDGT8w(F-Arf*CKbo7j>tNm^1AeC@>jaXO zH3A(ekHlQiFKOb+rG_IP3wXDZ_tHH*z62SOl;PzkS#CMv@eZlyTahxv-h9@nundkt z^kt!(-%+Gs%Yygu>;m~{^>%G?!ZxudJ`+u`A^r8-wg6x^qOQHxr4Y)fPzKa^j zH(V^ZgMnvdbsct5sGA9n8tLZ8$@k+!-pW1)Fzy<{3nEI+e@s6JOK6j~gzs5S>B691 zz$f4p(FXg0Yb*HxoE)HfKVA@2i0LDoNu`4M;;;Q%93|Dg$aKmyuS(-X_o!4d6$!UMdrtZlM|A#m+(wMv$M96xh+&NyQrKJe~ za{3&1wW5$jn*c4wmoID_+t;uRp5I^rvDg`O4J;Vx%k>{hW#KL$P!N1Q!&@i&gYc^C z8w{V@1MKaa%kg+=j5EtR0dw`>GZOLpm22M4na;SqBO+put36K&{rmx-UnDL)fU(8= zfh%f&ZX;g#g0pONzn`PArY1?4GuwvIG0lh*y@)=Wlbkd)9EMEcVrAwjK^y)n9H78R zIF=N2&_MwPJ#3<=se+onboeD?^?L&rai;*J^suPpf1y{{iGJS~6sk@g8(@Y7{)KVu zK%^PjIsuv)gXHYHLkkFLuA1sjM1>}J&a4ySRY9_*&TTE95ISpY$<+O%5OvX^lJwq>pcy-Ap*<9)l&d1Gr`h$;D|6qndk7JR9?WVXT9}k4yrY-t~mg0 zK4cfv&D{x8Czh(s`npDpUP)dD4K7eWW4S%RPL?9V08GoSj8#^4II!=!KQ0Ic9jtbC zjw-lRDw)W<<^y@``xVq14Uwco%Z2vM;)4n(m28qlGWSn;^=Rf! zq4x}AG)6IR^Ay~i`F>nlbKxEaBBw})p-V)zA#RzU5)QiP=*H!(cL`g6f7nZXr2;|| zMIds*R8Pn&1#n&6;V)NrIHeTY_SV8XXtU05!RxRJ8S21X8CS4kI!JH$&J1UNGNeI# z9^jO(pgIuv=%bdR1IJwMk!IaMwA3pc;W9eJsLZ;_rw^@9_UM*-gW=^O3M z^-oatzc`if@?Zu)3j)~jY(98JD)CAFG^RiKzxfpanc$Qa=Z(Y_7ww&jQ&|W`w)5_U zzlS!*wd}yTKR9lZY}pXBe!>5c58*MZc7%Hd! z2e=^_k|8U%9qR~9l$k9!Smm=U0vwJ*EfMH=1HTU<7_W3*9=wiIJOPK_ugU)}yo=!f z@GkCXYnQ34mJ6(+=6S1#Bx8xi88C+$^f__w@85g|r3Km8qv?PDxGDrRcJ}w_@MHIq zYEP?+o&nJ0(7HvR^^X~dxOVQT_%`Hg)gV1!xiZ8*Dg6;MJ_8@bTLVrp5JZJ<-ae@T z#NxKc&=o&q=V9_U3T`rzebakrX#o(Br14@26^<77b?~qatoH>0Nz|W$z}{9f3A&trx-_#%n*^ra4=OJ=ql0fNm~gM|5(Hx>`xlPJJ`mu^0GW14P!Z0%wYI-4 zb(LIKSM`xy?Vfo4zEAW`M1F-&MJOx~b1xp$g3T-o92Hkkp#tt#OpF*5G{W9?i+5Z)y~yAq$LUHK@$HXf7TgW=A4zP z<~{b+VWcZ}`=38)?)(-rpR-B$_pgmY$r4UGsi_dP`dnfFPcty+^m-S13VE*KeM}ZO z8~1S_q)!pf_qKdVxWRkT~8FB zGp2ZZXo4`(g$Mj)sO!C1)sD%&u zhBeYy*Df2OXX!Fu@S`Tk;ni_zX=w)}68k$fssv|jLFtJS+F20;7|;8KKh7f>OmcoV!qrd^zv|<8ZWFDxjc% zrs{Q_c>mj3P@Dg0`o|f#!zep~sa63|L*=R(Fw3`nl~s=U?g_d+=AzcEcPX{C+W^ub z-xKe58c@fld@0Kr6fIpbTwDNXaP$6M_P?V8)g311U*v`ynCbfzzx*jKX6_#S(lyiaQYY8ni(xgDNVV`gj5l5=J;wG|LJ$77 z_jTeMT8tQ7Wu_gtN(PHxqEzPzHeLAg-l^_pzqbO~zfC*yXIeMj#MsyXLtr=h3(-Im ztZRtSwJ(vlFP>bq90kWP?N34|=2m%j9~dCB6?X(JB1)(|!c#Yu`2L zVm;G*k_SHxQUva_t9Vb$pk`D8xj{;GbU_IRd~cq2rLTYH_bhQ*7LUd;nKj3|5^vuJ zt-9flN+<%VF43y`N=3i02j)j%{LwWW|fC_Bt=bk%h|0YG!EP{W8xtCwZ zDh3z6rBs7J&{7TO* zmWwO{lh&Y7XED)kLm$m^0v$SNgn!mG-NoEeS{^~_4^lN|8g`9+Ar=GRtxwrecXHbA zo{o%hpU$)h*JRwm5Do1;KFQr8tnklxcyv#%Lv3 zU>QJEmDg++4gq%+`5Xvu4^>PZ%u!Epb-%Bva%TUg7e?^)FYqmRVwG&W*02$hXc1_g z0HeEv-vm1SR2;_Tg1}E&UPFivr8{KG($Mj3vb{JtT4%ZshGrF~oLv6+vW9G4XJ}ZL zKCrH9bvI{jg~Mqo(dnbDV_RYCO?|K#|8>-IUz3OEkR|E&<#+;Eh5b;ly7c`KVA3!n z&8W06R@MYg_%MR9zyq0S2ub(FN$Ri;GIZ6FA8}<5cK8FFuDa!cS#_r8CEYX?oQ*92w|$v7VZ^Tdzs=$5SP;oCu~G zPi1Fhw`~bpyg}KY6Jd_t&N@bkN`L7Njt1|B(aXzmTNC6NU;KGa@3N8s6U^%G2d z_sQ?^`|JZXgze7ojXs>sx!OgXU?JbVDvxH1Y(Py~i2iboOJ(vgr=Y9T#fibJmI1j> zV9!pephh|_`o9wGitJ#61%LZzV{eo-bOcd&GiQIqCTTib!HT<4(p%2QgQrM*B7T>s( zs=&y`gd07;S4dO3r zDQSU`K?(ge^E~;{D@qAd3#iLgHx)ejOK}d@0gH*W1<#!7Yo~BkY`0z*ZqQ*h(P8hm zOK*2NFF;Vs$@+7`mE!AQc^1Us+Q-J27N(XD4{srJ?DGj-D99k%R@N{ck=I{Y% z{FK!@yTCSS2ElTV!h^7IQc)3djs6~qM@?%pmT*O({wK%MV{O1Jl zPl`w12zT8B*SrB^gPDz=pPY3n%CDQ_o?7g}q0Y9JQcG_1kxjF@efYWye122utp9Cu z93U8B8lOhUoBTb!vM$(iU64Ax5T6jRU}!Hqt+p`a0u&_KO+PV(g^uX95%ia1$~-!D zGR+!07sG_Xd1(e9Tr7mPU$w{H-=?&9&!4a!eMS8v7pj@6q>a6LFGIg}Ow9BX=<5E}>^Y0_20q(6u)Z07Kkkv~BNTt4lEb4< z`ry~B4*(cgtMxTBIUCX?F?WTr+ioWSf}5WLv!d5l7CDf0F&Sq>zoRNXU$Jy@&!5iQ zu9f*p4QZ+y_wGnAvdLHN_>a(bxfuR8DK^FeAlYeVssPpWFxWsA$tZ^0&-r12B2GPDJX)9MGn z)!%ksNJq?IhF4f!uKvNHq49BUVBhVibZ5nY)nfs1Hoe*5u-otzsq(Zj7jq)XB=$%m z@jaXR*p;cQk^eQ|Eh@X#ekD$vjcZpg?lnWA_Z2>vDX3$dk||ROC|wvB>bvM{_A-Df zH4EL^tFuCLrWoA3kKb9Wi=mdTyS{pYK0+Lii+o(Va39?&C|yf!*pmr|>A>sO@Eo7? zo&Pfvl_+_C{MLft;nin6EPy)2SKV|@f&O2M<0|~kZ_a_dYRgl2X`Py9e z`9eM%bfQu1@zo@z1<4+SbA`0CEEN(WY=EKfB1+rs>T8SLVF>&mcjH=}-x4v#x76ZJ z71jLwct-`}wqn+I&}UGe%^NKz>vHqH7%tHEM=Gc9$X`bo9zlE>jdSp zXR7)Ateix2D7l`ZCc1v!oC1V(Lf;R(j9?cV-^@M0tVF+`SUA4>~47o+O1*8FlUS~NuDVuWW}c5TEb5@c}0CXsxrZt|A8)tIpgt%1n=4y4Qg|{ zFP^GtP!ZNhgafi_Y5Kc0N%)I$!GJh{6bK6=jiF3{y(T^VbDIp_7rjCWF?#(Df5=rz zuI<1M#5S65Th=gfe_z>6Y_LA$cWU;Z%4!l;Et~p8qIjzzW@5BKcGe26WWNXGB)2AmVl;_jy)_>x}G{WdJxcB;YFI zvYHMJfp;L92ga#}>1c=0D~?%vf`MHd;@=1z&1Df~U(C<^FUngVvFQ5nB|V=9{w_nC zXek}}s`naZrHB6FsaA2)CIz#VG%18exCU=;2%*AK7Y3lkN3+uOzb zZXH1s7bV%W_Q)?GyTWFDoe?3x&FUS2>-OY$MaWL&{W zK)tuAWAfF57s;9nkvZRa6c26p;znGBOZQOyeDKS40&Q;`7e`?TG0x}XJIvYD4@uS% zp${;$@;_Qg6)y1H<^YI$@r-ql6JUbvDGgB0DmlVx*f;zxbNQDkCsU-a_lwkgaW52p zl5p=P*t&x(Iu9ykkm0<43A+T4V-V4?Ap}sX7<{tOL6~c5ss~27 z;V+!~)GVY9QH%lYE;D>ZhQv6z0koO*`>@d0nbJK4?wpDylKiAZ7PLo5v#f+fB_yx1 zXQ5N6%UkQU`XgjMHp4)Pm4!v%HR&M0e~U|-J#;!Kff{y$T|96Aj|~Wly|}#qydob4 zfOgZ<)9rvYw=*Yi=ksBwdYWBm1fa-`1b%vcCJX4R*2Wf}HLh!{sr}j3y~ORy2M{4< zTPozRz(c};opA9VHdIYcs>A*#JDYj%#k=pq4)&B5{dR*8HzRVhoLk>&f=Ig7zCKs7 zk|Ik;V92|REX%Ka(!Yf4u9o4)e^kIC2}j^5*&w^>2OowvuA)nP4inKtA5Hg!%yUY% zD7x(cE30QF6u%w-yl01Fef9FsS(C%RsnyHtFP(1|Mjk!ax95;II!=v!r+!I0FSeC? z-DjjVezDDW+t*+1zn4Us;+yh}P*i$a+86M=q@-jBVpfc_=s-J!3gM#?9HbH_<`rDW ztQQ=YY!~mQ*5?|sZew&f>z8Z9jlx_^EP&_S8I}A3*dFuhF$H^Ah~4{KjNYI5qk?Z^ z4)7U3!I!SS3S)qH>v5E1QrfB6#P6H_z*bN?!$zoNyLAri7<5#RFEuW8M~~3XmxuhwX1xkKivf5vC}j+M5&umyPbZnr zF3AA(KUx47I{HQi*TeMZzZ=&VKjob= zB!AFSPH4N@Bk6weAiT_(k2Ld{L)sJR3%jiYZ6cIFfL~}`K2qu|(sCbhZ!W%8ixdQ0 zD`94I`_6##)gY#MVmkn+6rZous*xt04|W1$gDc(ONJTMhCJ%AY8xg7`cZ%cg*?np8 zcvT`hBW>BUcnt!Os=RsdeH}457?Y_dQEHDndHVf@w#<$ggFxhC9HQ((1qyCOn#$4C z>z+{>% z+f#$S)z`}$%eFd&6gX`BicswD=^+QeLLVhGOXRJ?Ok*tIxU%Y_U_{S%a0R+tXVLpN zwUjFuOFaIapBMoY09WH0>flH&U0Suekun99r>L|?kgfG9>=q4scX~Dd-YSh?&491`1^$Ph_6W8_;KK!ij zGKU+1-7`dp&1KS3^P&G$PMLu)u(4RktEKP`)rOHmA4v6|^h6*M4p!cRBn-$wjggRy z%Z&Bx@1;{>LNmGt(kgavKZ2@{fA4aCKKkIzLOTMYx-?|4CQU+5uVA!~$n%t3&ti3Jc6gp2^#83^>xsh(o`HLGLb zOeZiv4M5QvitwDAXC7@$k;JigH*>2Z7WVeU0E@vadrn`SefW$Jk?FhF2eFCU(Ru?m!GYFE!FDM_| z0U4`>b9a)}BA^N3+?ui5c7!IvZRcLq9dV?L(Btq>W~dO5r8n$L!%G7-S*TK$NdErl+MYt8)RDBajdo{6)QHGPy>t^kbo`lFBFzaUdILfCW0C zYk+&L+#g$MK%m06>zV=%VDelnv{A!{tRC>YP-u}rAnAx$o4F@gq z=glgZceTJ^&dI3jar`5%@4OMU|wC%d!78hH6#P_aB z4y*4s8J9h?=qPE6I}b1P+g;c>N4;DwAOX3UJ)Hf%GXnD?sY(t~fprK$RASdsA?cJn z@u#*fho}%mz~S#R(0SKAV0=+5n_xyN*l&Ej%Kd7dW(bqK3^+z<+>k%=-+d@dTpmSv zE+F<`|+zdZoy1ZZ02A9dv7D6+7l?tw8agdjlzAWQw&_7^Ri?n>wD~ZY>=tEF%{m#QOx3g`zmW@=;pR6D=?I-JqIe(f8gS@;sUEf39ow@vW|XIKMqmj(e88Z~Jg(yVUj-9ltiA zaQoIRmLERCx@W7NBVp4(#twm1QK^eI3Co1;7k}ZN+6Ut!5;q)J%!O@dFyHMQLt}#b zF7!Fw+z4m;iVu%{VVHmLrSYPL<*Rj89w)2B8snu?5u>t!S4>~#IUnk2n?^OjN|*@~ ztGI*9>4UF+&p%BmMvZ2qWH(jg(f`Uu8>y)L)uYKkKH74p+pAUW-%I3cJx*`Y?QISB z81r3TQSTEz^Y>aBv5Ljko2^ot@mOCEZnq=zXsyD3U3aO>-(M|}mX4cQN$!QO*+ugE z%N&Y(Z{3Orhd#+jkuW9+PO`mpoF*x^EWXPw(}0q=i)LDrVdLelp*l8@Z%LywX;`El z$}^^NvKB?zOwODXfw;SSH}V_M z#f9Dut-Y{kpt_vBkS^whET?*)p#4kL@H(Pa4jlgeI5Gupf(eef)|Ft%dWYDo1 zzR=wpIN!l~>uc7P%YDdMA=C8khpXZ19RwjDZLo|>i?vnwC1ny|*0z<%1QuZd6v3aq zz{E7>Z!o0n_fitg#|a1u`c8hb^go$0-`f8b$)sT4{M|0M&CQkpf;{v3@PXX3v99ih z0;@zV+q1;ZT*9ZPU6WNPfZpOLjCOK%Mjg#YecM}mr7OnJ2PI)M?Uochk~iN;6~k{}Lr}mF=gZMrQ4sU%>!j`!>BVr1rsJ1)#bE*;s|r@Q zZ@L8&S?yl917*qszsnH}${X0}A04;Wp+>IB{_qfkGi7VM=>s-;8 zO}8Q6!n3^>@{vH-xonMG2)*j&=pp>0=kgLW@8Z@2#~_OFv3P%A(+OF*unfS?)k=)? zRw3+MxQ;F@)*^1e?x7XYJnJ%=F8z%GcI)F~e!7f428)N!_3)S@GZ4X;^UX^sXEcy% z8a9~-*Jb>d7nZtNp)pk^qK>iieBtT%#Ffln7gKqY^}c)lZ~8XLj)kJK&s23JWSfsZ zA~Ebh1&G*gl}>>el1d1$-;@`*z;drF%h_T1I{wZ4yfvBpaomvG0e9^Graf2Rxy@?1uaKf@Bta z1|wQ+_1)zJ`0=(%YFY2j@q{x1NRtIO@rBEb+N@$?FrGq!`Fma}9ns&gddAaJ`KUhCIK zz5~+w)we70 zP|6V5U<@{!d1I10LIm$>YH8WI5u~Ub{7IaegtN%axbD9FjBH~27a*L;C;x( z%hgc8?xR>v%U3sAlW6T^2=&^=&a34&2HZl^65H`dxW&=X-xh7_zpQJ?0n3rzP0ohO z_V%G%?E9=6(U=|%hx6m&{hJTlRXO8KP>z};R_nkgD;W4^j727v90*aZP1)@Y+E^1AmE6Rm7V<@Avod% z96J&B`&|)w!qYynUFxl1ttO9!45*>X0w3__12e~F!9uj!%X=SyvHsC_Z?x#<0o}`Q z!(rb&KEP(MjlI&Z&+t|}Cm~BcZC~(}>fkECx|)PP%kf@~uiC!?4uL-LrmBjt5&g!a z5;6V5@7vDom)Y41a0m|f_&6;4)n1HD;#I&$9vqEaSVm#pZ{mos(X@w`EgyE-1I~sC zWT0~};k}vy9zke>@VNJi!t!%flsl8EP*=T%7IboeAA<7KXO%ik|1 zhxme5N$T?v0F35E;@JR0N5>Q8C?8v$>X^{Jv$S+b$h%xwnbp1~hm@g*QV=og*d%pv&4f%DBSL(pz6j^f1eR=;GHRD58$>R8t z4sDU?hI2l15=KSf385eazIUflIgoV*$~1;KpzpDo3;@zb>2)c17T*O>nqB+y=Bg2@ zjZwr611Mfw(-RxhgFdhYQ>5YyEXES;9RNFD!SMNZUO_8;_xu<#0Nixy3ARW}rf_4P zbuxS*gp%8yofwxa;Yjj{ibo_wu{dd@auv4VItX2idx<9|;LbQmzEfBoWGbdF zxaYjIB(Z>h*yXo9&whLvv;IrpS7+iY5dNtrT#uj$gl8X)a(cx}X8rVDE}p1+lF>#7 zAcLQHM?n^amWdinSX+S7W6O7N5PmZ6;&cUoE|>>-e1VEpYe*l^=Wn^)#lTf z{*1jC2F{6hGpF+Ose#fhdv=93uaD_zohru2hT19 zz;P`OyYXlL_RrAT^UquN=Yehhk0qiG`6IXbVP9;Nv;X$X&yw%1TLxUbMK#X&U6ey{ z_P4Z^LLY9}er}C6S!tgS>XQW;D@#WWZ-;m! zioHFhK`mg=C5X`O5PRlr&|I>ucI(s3%S{c6yj8Z2fPwp8EreOd%~_Gs>6^v-=FnBc zIL{1Y5}_N$eC!MQs-!lcTXX~dr)x@?twcrZ_DYlDTJ}P#%Fp2<2ynO6;iWiBE4$m_ zcCho|V3?c1h2jfLC2n^VKS$OM7-q26g3~4|^v>O1?aejj(?67wA}w)CTL|#Yx70$+ z-dIyVPAQS9ewodqw~R&wafV>d_Qh|i0DtbkXmooUgt`oEuRWV>_Ht(1DCB(p2$J_n z`=I>!#zaCV7^AVUaysj*%ZxRj*}PieB?5|M%TcChQbXjnS8y*>R;m5=Y`02(Wo+}A#!0-Zn*{p;{(2r*ZnDg zW3Ylmw<0UrY~+;U{BjLOSKNyOf9+4wUOZKppavar!o@Aq@M}L#Gr|oPR|r)fw3c)Q zE5+Yg7nqguEMID5ezLiNEmYT~{#6T#h(5}A$B~UH&tq^qd=%h8yOQjbY|(nIaJhXp z`oNHs9mfB%mfu#{qnu=P;HpIhIBwza$sRoX;OXES&K-497G53jDI}Ou*x~%+P}0VFY}fQMvJ}wSm;IhtKl%15Dx8=1~5V z$QG0FMnItPYxiE3pd{ew;hpvKnw(tmDcO{!b&qfur1IYC8NcB$K%e$-qfTI$dKeAp zMV-CT>(&|`bILg0--G1LA@+I?lLS6`-MzHX%-8z#FMW){|Ew%kV6^zc{U?DY2FfO4 zN-yc!5GvnueBGd-r2fD}XVobEp!l9cPfLW0>f?5@bn+6;Uu4w6teo}tDe_7AET4$x zX@4y2M;{ynJ+(H|C=#uIc7Ln*?xUgBHGyzud{S`3Xnc};=sc$zj;@dupKK5Z(nkp9 zjL-^MoNsmixGqyYG#U6sI)vs0P~%x&VjpV4MNA~Fr*){+i(7@)EzB&$?N-}!E3w+1 zW}LkE!Q)u<{o#+kCxca_cA*f~nsZpuAuJj1Znk0=*S<4;x#lXCzs9M6f{TXK(zQ zyp4_JtLICNZg1Srngaj&1wyo7Yt=JBDIv3%kqOQAH5E2h6&; z{#L!$kRd*hZ#ikdOEG8#qD?>TopIiE?mb0)B{FaG^X@ zvPS=n9#N#h;ugwPI+o9ZD7bB@M1dn~*l#0WnJtYJ$0i~#q|Zy)3tf8HKX_X<^3uq=R&I zrV=v5LwNh);o%%#@Dp1mS$p*ovdK{t(O;JH`xLJpgc9dOi&GhhBpE#{IxQYcYAI&$ zv#<$dCNAvhJwb?bsb{m4B<5|G8tRU&8VY_!5ey`_Wu-%3F+b?Ed&*WMZe_WVIlGI@ zEPZ#ay>vG=dL~o7d>f6k=1THoF7kFYzpk=-#!sT-a=2Z&%9;?M;kJ~eo&_vgUoeu) zUf1TeO+y}8l%kp2VR;=`>)Yu|%xopjXbjZEQppPUXVEO!G;5G}N|X5fYyO8k8;wkS zYB`{KWK!N$oPxF0>Zwlh>YLkpZ}Q4MY?sa{{y%8 zLY!c^i0k@a`Y(fNQq}v4emVi6rD?@pO7_M>H@3DD%4x2Ae`$MP%EgXg-dA%zg*V6- zuy&Kj4(ggc)rl)0m=cDQ}OFsbT7H2wFl=@+8Jgx<&s{c9Xr zflrQLXgjjO6QXkRb6WpQNDhZi7BO)|A?e{o?}b#b8Rh6>S9YrV(7cLj=AGIe-4Ikw z4lN}KX=Zy{n~65|?{coTto~zfQGb@8*G=T6VBzd0KQkgA`&EI9wI!dz9z!{bPQ?dt z4SB!6T!7{phu%$ziAGDLa%Hu)` z(rsYv46GUJBhuJ;b}5Ww-+i7!KBc}c=9q^S)t+y3^PjI#g2XqVTqd5$cd)kabxBtc8ru%N?eLCP@&HE@4Ae8(_u-|RhY-=XG$ z>MpXkY4cjF+XL8iZjlEE!W;`;sCHy6~N8zvJ^t8&Yb2LH(pM*0SZ@Shnr8g!k@dFgdDBohs8G z`h8?K zi|&0V6gK+smmPf_xrqa|wi^0JD6e9n%&1&0;G`USJNG3p9v_lFxiq(Sc%cw|Kdi2p zXB=tLn76K+xhP)c5Lj|LBDiS3Tinvd-N$}nnCXAy3|)xQoGpUCG<&53pN*J-@J|00 zQtW#2j9Vk_8j-)qlgsDpsmJ+AC3yxk8?z}^Gt_w{r78ID_m_nqcpm*_l?kN3c~&zb z1srR@`u_Mg4UW0UCZ!Wv#+hjUm@^acb~GT=lV#m`($*FsP#Ok`ineSOvOFsCbv;Jz zhY^0fhwDxx-Nr5Pnyze&bwo(3Wlop^<>1lW2YZ+kyOL;jUl_2m78Vvu8Y*CP25-~e zl+gK*a{yB_x~h()N-?lGk4>BX z*8J9^+t7ny6~2EykgUQ48yX~b4Pqjbe~ zR=vDi%qI_cD~|i#xpu%MDu}Z0@dkW_~}Jgx^H!^ z%am4qP^Qpd0h2tooBqlEeTu1yL3Uq4(jKL;^_wV4UXt18Czy52cb9j?)A{w#{_4aO zo)o0)782GbC!Y(E+T6f{RCzg`pzEdb2+88-Y6f~V_NwOV&Pv}7b~FESiG>ms{t4*o zivlT&4ggPWVp!`{Kl=Z!3_Rb&qi4|~B^#jrSo$Daz*K-Ek_Ze*x*3jV^HCa_R3Y)9O267O0NO~-h^M@i(mDnCH*p9xWgzGoB!u>lEz>$;lPgI0ccvIkvdP|BzzwS0<56} z-&>c)M5li=zX)$YJ$LdjUyzIxsHWoy`_tBTW7_+@P^X;rZcqgpoV!03!xz#oP6%wQ0Fn8frq72aNGlkwo=vr}TfY!@wg7`jaaDvJ zq|2P{&YC0$_}YyVYwB%KM9qN76Z`Lwt`|`~VWL;-6vS#V^%N?OvVp;L7(TjNqv2AW za5}LLsHI74i*KyJd7~23@F8G$4B}z64hj8|Aqc`eWasg`I=@Us9Hfp4J@- zo+jI3$sgk}|Dy#+3z=aIM%gkco9a|1@@qDan@ia96X^6NX|Ia7LLbe6!xv>Y%6!t0aBR4214`^O$eeFukoY<-vCl#<~wq20U*ncWW=9L3yc}fkv_U443O^j~7 z&=Y?qP@wVknb&u89oH+1&9k-p45Y*zpMsxyvuAz+Z}pjJWT*!RR4Z+w)9r44lz*o4 zA-PPKd$7mV&$T&wc zgkOd%oe1b7ol}}DzEjQ;e)cT*>M^F?71svEFySK_xL`N!3^Z~)I*g1J z!jy!uYmv2hCuY34XBmZg=R(cfK?6+SMc#xKaM-6+onL!WsFJCmDN-s3wtT5RokBTa8{1-kjDXQ5DOh3UXfXY=JRlVuJwk%leJC4QZaME}mVsO=HSg5K}I z!4r9pnd$&+bMCV4@Wy?IW$N$bY-w}*Nt_J078 zpb3AnDoi>cZNz(i(e5@*I_*m3n@TEBCg{QYk=omQ^gf6-tksRN!{PiWX{t%SF+rDu zAt}ge^`tIX=aL&`qK+QPsotf008@Gna-#||pm}k9nXwwllxm+_f^(;Wh3-yopYi!uY8_kqV=PPcam6z^2CY>c6wY{hfSQ9yubI%}Bi=}{6b&HQ= z$^H;w#CE>J3+(GMJp?6^&b2@p3r`Dt`;&kPVAdn(0{A~kryAF&O5_<9KB7%I8SNuV zDt%{}rOYf)nfxY{yys@%kc(`X`9@ikCMJU~+0J?<%r z{hhR>9I{rkVD3*%YNljQjAkS~kt|CSn7U1S8QRve57nOg?)R3z%zB*IRBUHM^2K%b z$0Q|TGA0=K{OOCr*s3tM%CL5+vx(gZZS>>aR2KU>D)3+t%qqz_D+V~yed$gID@Z+9 zIdTM**c-U%em6w{+}*=?F-x>U)k64W()bVRSXQ_uTikHE%yQHj%V9J}xZ! zxmduv;hj5&{zn-IyK|Pl-(f#jwa)}DA^lB*Veag=+KoQZ05Jj+se>5OvsJ#D2!JVh zjD3C(_~{c1{zdMw_?rZJ(_VYBuzvf?{>!E~{RC~1yZltJo#X}Vx=h%@W#vLyBoC=- zonAN{xMyo>kA52KZ)+=bk0;{KX9b{?Of5z1M_on3j9qU3uWy6dpk;xrOD5(y)+UCyAud9mo9X$oL`Y~CB&GB=tRZV2a z8V#EXn7n^%%*q1!eEerEk;I$lk6Ra0Gvljn|BIZVyTG*z&y1kQt zVDEp5D9@C{8_8Ax&{O;vSfU@~qi@&KQlO%aP7l%r+KqBs-WmKmQa4_@qs8>1O1Olk zjvgyj>ped@N`Ic1fas1TNk0&*wv)P+s9QkBysdNo+idNDtG|qt)N|L|o`w&7h))Ab z3(}=zDbbQ*#1`u>4KV558e(EN)!PZ1LN%7n%Rwi@rzG90n@V;V6Zvm#M^W!W#W(Be zM7UposkV}e{NOIyLvh+Fl;^lP`%cCk}$qi*V5h3*_?C`pz;xYcPMfz1p19OqXpxP_pz>ZLIjArJ$um`D*rz4 zDsi^$j%1T7uUA-82X}?j^?v%A-dob4?d~)ui#gpDiD0HA`%{@YN}Q%mv#vbneN6Lo z91CO+dOG<7kfnbeo9u{@6OdM@F0h|IeWI>fpfZ8k=0|?q`OahK!MEyv{A#Mqh|5g7 zYS#VyV2N=FIVGdzZ*uMPLArRME)&Va4K?-$kEM!9qS&tryFqGF!*Vqz1FnW|>0^p- zF=spT+|*6SrF?Jf!_4?}qIu)*{U5Y_^5o$+V_?h_55>9; z7BkZ}PaC*88+FpQhVWnGT|80ph%Ux^Uij*1c`oNJ2S>zhqqMFKtzbJ&6PQI)g)<}4 z^reJilOzlmZcNYId$(eQgYxo&DGnv0;G@I;re!NS2+cLcr{&GP?`*|x+3`VZbRQ0z zFUcFFmDlazkm9$_Yi&hkO(vt4cG0<3+w%T<^QudKcce0z zE(2cb_K5QjGG|J1b=Ad~6`{d;Pq)HL9h0t&&@!PfzL{FLBq*4+VZsC9SEzUsm#1VE zeWBc@j}<1o7!M6nOwoYaeyUt5o6U1wJchxSSrwcIudw*=o~|wlejr1>XQsXhf^ia| zo3t(A@o_Eh*zeT<=NBSfq2m2X$;jC}Z5xqaDk~j#GdBcY8cTBJi)#nQ9cs(jAMq8B zTJ)8tNYsBl$v!3$b?^D@NvjFJyq4sQtTN`PtpPx``vzYw;ZCYz`I}!A^Ok*_Gv&rB> z;Ifyrq&s&nnHSnkgk&sTb`#~wFh0+J%Flhp znOFali;YC!5CiQUa8An>vw@|O&?fK2gFcBbsPntIZ7nlXjnDU9E!x(O=c0`BC@f~f zu{}hNfC}jH3w$vv=D>P_ZdiOOYoOs}IDF!j9QjuF2LGCu##G?F^m1vBQ@Q+noe#uJeRlDqK zkQh!S{H#X-za5Wz+FenrwRG|p`f@Az$H58npBW-jiyzmKj`F*q5fsx}<@Qzc8R_hu zyyar;;6C|hT~0c%HWRlOApA`r2|OXG`~w!&zJ~PqIc$G8e6p;YKEA>e(4#Bjlhw9g z&77H%7&EZ_m78y1>rrm&au9Sxg(ErOLx~EKH2c0W_056ey?PYS--S%tHD1Y?a zW}z(Mq}$nV$=FWA3tO35?tH*_Z#z3=bkBy0J=UtR;Eg3?iO+-@Qp<6h+~ONBvrs-* zoeU-1VD7r8f8dK{_2_#{KTJtC3!?~omnsdOjQafD{1J@l0fhD7hn~#L2p9ger>dTX z$e)+u%s<0*Acd0I)#j38x9o#9F;0#tI`>p#<@WwuZ4SeKoO;&<-1L9iPu?V=`wZmV zma9N1D(?SW_!kjrO=zQHmCix^=y@~<9HD9VrufSY>Ql0ZdD>?Pv_?mNEc{yE|JuKD z7{_V0TdZhxr(gK;dRe!tL}-pCN#nw*nFZbEe)l)=wTT3=nJC;s&YZ{BT)55LSG`D_CE_3 z1-o~9HB+tk_Q;_bE!-d8de)d2z8R9b8CN}8JHJe5|6L{)Rw zH?H@fFe0_FVVpJ{=y^orwDz0A$v#EO@><{fYyDemPrl^Kl)mv_#Q(lbWN-c{{OgkX z6PNDD4HsDn?`bwiqC#QT0_ioqXVr=!EvcqFjD?!9)GxY|cY2Gp7r3ADf9|6|U%+uRDa>A*+yPlMeV6^lB6#;Mi-9RSIiyNyf_Ew4U^sNs2FAiW1(6_bKwF z?5g(C5>1*xW}#1P>p9WkT5k!@+*{RJQfo3dX_kdo-0evU>%VCSTgxWW4YrL5w2duu zu7s=Hr@e+055xWNRc&8UWz5R?I4GRk048PCuMpv?g38aObnx9(^%dg4q>XSy|BS@7ik;#rebjZo9V*(u~~5s|hD`x=$) zBj$~PaK#)klfSJ$e;tD7;>?tC>bA(C09Ni*f)NvO7Cw3vW)b?r?M1}tvx~iRd>0hn z-C=$E%p3HX66aQQ%p*CBWc7TDlCIibWUp?IDK#{S`^$M6gAeP|=v%vOd-;o%LunfZ zW;SUZSHpG^TuJESEpsMz?%T(SJ)jux>PJil-GhO;WO=v8D!Y1PhCO9lz4R~SOZD}3rD zx)o?0N==R|)0nOV71>XA`8?=amH5TorTKtO+!-dzg;~F(#Hm3;1^vt1f{u8tOLTV^f3I&o3v6n7 z@s)|+Zx=iT)r{8TdX#CQTIn=a>4s`0S3^}WJ45(CY^a_QiP~SI z?Tx(N&jF4Sq_kb~N?>)$wqMpgV8|JQDtjC6#iFvxUXO}Bt zQGvIJJsiS?>!<5h^H{#&8W-7@Nu;y5RR-uXh|p^k=q1nJqb%#Os|@{2$h0|ve-F6c z&kuLZPdY!p(y@E7!z%D**$tM#Y?|{pRHdXhQBdZ-H~WEal9x4P=?_0o(GwGQspe2I zm$aL$&z}s-i27HE`WAg_?#Bl%#cADpk_NHH8B=rh`mcmvavSLyX1w^QQRGn5n@73Z zJS9?ZUg!Q^#fwJy8t-ZpKNDGaMk>{Ze+MQJ6kyUq*4eA^o@Vx`Dma0lse3$We9U7S$yqW=$A~DJN*JOghuL^!T>5r|% zyTNKdn8t)7)g{-YoGCbI38oj|?!78n>2ra%1aI7Y zU{EU46oaC1A!9{O=cR6n#+fTiJjzCWkbGKI$G^sUbJKJzS*4j?Bw6 zd1^~7s|>Tnbh0r#bX8|>a5-qu7TM=8d7^u)IE z;>_U`jH_a!vFJvQ4tRYvtnF;h9PL2-;Zs1VpQqmfh;_}uG`9n9*H`Kb2OdD|JR?Af=+Z3!}CUJoNqB+DIDwQu1Tj zu4-@3p(d#z&3$9;l`k*Mpq#t{k|ZzF(k2vn-yR6y-ta5uxLC<86dPC%YX^J@xj%R2 zfTkJviFRPTL)ctJQzizgWWJNF*%;h2#6k0`p@4xJm9gU7?2An?(s{HTtxGP~ec`_rV=%1)-B}sv(vm=(ike4*|2_b~ z@>C-s#G)UxWaOk7oK<74&hcxNY?_E39gfXSJoygqXNr9A?A3t`h7Ic{4!%WW!BXLy z2gnrK)PM(vnk`}*YT!aUlKuTFUjK?RodZe+KvJ~s0^LaRbZ{cCp`@xG*s$!$r*k=1 zh~w-I)*vMKw|ZujQAVkqKQp)xDR+3(cK7T(U3GHJJcMB^`WT=zSqFvdJCae$<#AdZ z4Vk%iBZp-Y!I(Z!zv^xYx_JrvWu5YdlCjp3!?ebnw#%_xUp+E(p)Vz%y+3$Q%9k$pr_sIbyHaiz z9Rp+4+@I!KX95;2pvxgy0=+6vDN7fYxji%l3Cl&IzXsC^=+^r}e8s}zE!D3?Tjx+) zn~CYqqSFZHyhu){Lli=eRWdCjEkF3J4823J+>QaJ#ocdai!{T0E4CErXlzI>4W33N z4k#>unnZw6JImI#c7HAp^G;Wy`+^QDkpWyepNJJmE zQu6b7z|YU0ShVFlQ;UWwkC5N%g{o#7+L9dmEu~wt5%F|}-6J&0I-}Fd33QV)lEHr} zhAIaKOp+_p*)(l#nks#+0t?(gaVlO-^6EVJ2%(MfYn2%dT=X22KCT)H=<%^GcssV- ze~w%HQh4kKU5unpttIN`p7+Upb2oclW^5Evzz=7lhlO@LEoU(F?Tvb{rwC0ms(n%- zl{xB1bZ@3Ey-R=&J$j@`PjtY9U!=-^z2ST- z=TNO=3VUf~{sYt6&4i9wZvOQsy!TjC zGYDBE@3FnjSodbPkBkEoY@~s0SXxDN@7OVuk^+ILGN zDOff}1tV50@ZTj8dECowtDJsbR3%qPnXitsaNvxZq&v6pxIAY;ispH>8DY2`=3F)G zr2;0a?vtErJJN?B@dn8rHW6Ag;y2r{a>t+6oNng>+0!`n_rL*qq_9945$)z5Z|x^+ z9oRoDV2S+?jF#Yb|2`Y@-b1q7PastiGOxKcJ~%<)do~WsUfaHDy=^N^kX3>iL0hQZp`Yg0ZGU^wj z^j>-aw+ip))!R)I0X)qP4ERn*ql1j0d?fuX6*|LI9r`W@fq_>$3zOSIRtHvcI1A^> z7Ml6Cwv6Zu!mSQx2R*f;XD}EpcI98{Xb3`*azrn8A~8WDXL`|uaAgGoMWtCzYNgw> zwK^Rcx9qw9unsT~+uXXR8aMw|dsZ#02;;Qo)Cvjez5vPM`EBht8x&9v<>j5Z?BI*G zZUp~#n+(fCX}rQ}Wj=v0;r?tBx9&|o2vpKCNh}apL*!XI{{3hf83j@VLgkv9n;xd= zVyF-C9g~ip`0IYF*Vs=bcf0Kl%Kkc7>)CEo88BFw2yV&7o0G!h$LQ5M=OS+U0faF7 zmM2;lzAzH@cKTq5xwMw+?DDXuSzvC1c$U1$?%>`-G8<7Yk`b`9{dskR4~5_$*J|;M ztVz?fQ0l0|p2tyEGep9vwZvk0t54FUEHfpVqdP$Fa@;*dMcVFVZiBmW`FHr5oq{9q zJy};r<=4HRk~ObFULr<{zFqL>dE8LVHP9q@Xtm9zv*l~A*&UPm{=+l&hl_lBKVU3U z!gla3vfnEOixexuo9)^#wWd4&#FI)p4AX$9fxxA^L|pn10B$LqGl+ueZF&2Ns7dWR-HkKX##<)pwif)^m&%th&iA&L)5zW@Q2-978MD0!8g-hh(taL4s z40-ehH}ePw$Ep{jT9lT*WnBCDm{iv#gTWfF+ND~0bKR_=Zn*&jLcUksOhGii_q>W9 zM?7cZ&+iYtQZbY(_~rFQu!F(!3(Njnonr6rrccXJ%R0RUd9z&^5}{u=bSH$Lk&>&I z>B;DMehNB~-+zJ_!e`!|JO>?1!qaTsQz1Zb@x*V};(F<^gY z274o)+K+{WAq)~%K%q3sPlN$bB@JxZHf|;n%;L{ohb*mo=iRB z`EqIC;3Hh_g1VsbpJKTpOeL?N@C^{P0Gs%{d|GJ4^CoZQKui$lH6%8pc)!e0cKNFL#kGu75$H+RC7jLeSTv1(t#rhMPiAm1F9?uPvWPNcdcaK zR2eh^_hVd`r(2-8>QJC(=y_f6ol>0)t_b&Tkp~QAg?ibMvnkVF2>8 zKzkspfVp-0d3>}-iGTrwq89jwD=Rsn zslmA(`?eqpw6!0}>4IYYEcB*p?>4pa$3=jHNjHBqIUZlZD*K;ldBU_a=@1%D4R&um zwA;n)eMEPA*X~nvX}ACOZ@j1gxB3grpU?wK3ghhYmP{b{I^tX3bfJQHs-z=t(qmn# zQW;y-CTo~p_{xyC^_0%>m@BcSj^BT-bPmA!uT4YuO{;rJ8t;Fobb9m`oonMf5A`n| zcHqERwT$X*lAq4`v3WE8`hk1iZ$HKCf4)=4vU%=3;4#)4hjajYG*Dq7x`Vus9#JE0 z;MeJN{=AVog+sZH>+=|`kbI_6N_Ss>xd?oDsUR)GPnp%7+-lbhZ+y{wJpNW|VIOeE zZKR(MB*Tp2s%hAw^%-spy9 zZl;~BoeGohiV47L?RuzKzlU!R5BM!^*2_NPi7yWW(G*lt|GKH-du&N3Ryb=<#`&CE z!vNNN=`j~JILxHdvODTk0fAKarClWtI%tR=X0qcZ%%{dzR|CL~)@gS7uQ5nmhCVYV z4{C)3Hjhn2i3>n!Nkgi4*xRzHF4QG-sTq{kLC61!NS48Qa$=O7NA#!E)OWOtOW!=& zuvioVD`!8<6PH+8T;P9wUC?|*EtyB)!bBevlJ5HIBG97s^|~EKj3aPe``UlC0O=L; zfKhj3ildLr+}gZ}BKWa4s~s^#_Te($3)+*ky(s77uDPpTv5u<|a@&R&ay^{(O<|I9 zDV>pVY_}#`>OJ=VIGX-Eulck2hY2d*QjZy1`N6d<@rB`kN~FI5dCnW%|IW?tHY9+f zjI$lLDS-&iyZu46d~5g5eAa>!#}B%kAbddg1{n2cm)eBRfYd!#|KagR*k(73+ve8m%QO4| ze0c;-K!W4o7J+==#m}Yp1Oi!vtB3$0r`V+kCziWV5 zUIolqE4$GUjPJ{(@i`CGpBAG3lnFS9Flq{i9I-b?G=b zdwvW!L^d%kL_Z^~c`w`wk1%LHNi)fEPIJi*K*q5BZi3&)#k z@sg)oUOto$d0OO5#tGi3WsMLY$UtPPxWxO@=PC;CjBln+aj-_wS)% zlf#3}Xe6NnD!1VzKf5_2Gzbo(`4B9Xn?&>SrCcz| zA)4kf0jGK&CxsB3``r>>2yr#&sNJyx?lIL8XBiF3&(UR^ShK7>O+1IOJ;BlAV+-(r z3lb3Vy~7-wIi$Ng!eH zWiZE4pjYV&)v}ig6rC|i%kky`X>Ra3z|-nnarB4~AcYWW??wssh=u3<@2|xY zy5j1U0ZzlI!3*NcJmh8v74l(m=j!IU4)Gj?QBWPsCHTTq&U2~cMa>ckr#C)MQFTY@ zh2#V1r*QyOKE}l-W~84b@?&%3nyMfISe(WIF$K2L>GhFpSU62cfBdIUN=D_wn(G_} zO4x-30>E=C9yr^FWMtlJ?y78`49`&|G`)VGmlLeVKnm4^Ne9($xaP@X8;?)p+F_0V z^9$%D=wAZ$n{!Zff4<=z)oLMP45jc z@6r4?(1@YBdU>glGpRz&Y=1xoLD>7!ob4a%U4T>yfbLW4cjUhDL5pFSAjxB2Uv$*2 zS@V+BImPTQ5oxs$#aBB1AGy2KH1_$mEmq$GWnsqgf+b&ueK zSS$^s`SY_RJe9Kt^G;@&MQ@Z0Sga$DU4GB}*4Qxmwe~EA24XlTXo1@wI$Vhis(DV_ z3yFPBL0fEqiglXY(qxyD{V1v;8B1Bvr#EjUhBfw#QPl5p{&b7mKqPCJa3t??c+L0p z4m^-QgP|!vg%-W6(Bhy1h4`an-~@#?14-)_FMS(-obYfz}k<16sUbY5T@aZro zxaO=NupibxWyz-$_CROj7D)*)Z#UjNlaD!`;q<~%`wq_sB^ro7j(HK(M?dhxfZ8CW zZ@KEkFHRb3k|$>04toDei}ke0W#RFKN6Rz}0@)h-UV@$4DHi@5N_HD0LY+v(e2((t zWyB-S+NJ#7gd-1VEds~eXX&}WY&@e&-hNpR`M_@Z)aEn0z=ih(9?nvlHgWs$FJnLe z((c-Kd*K>gnd>9khkasY#?f%E3c;@j^^)`}ZX=Kfw9`_GsYNW-pj``P{*f4zzz89m z`KEiWUoy8Y6BLfd72p*GO@VCw0yiM%z9>it*Bcx1g`ZZRNhZ=cIod%L)WPOJv>{k2 z_W8ZW&$`Dxj&aPOrgziecgrh@i$_*Q681R7r}uwJQm~v{oqH?*J*UeOfty&*1!{ak z2OhlXeFew+(Tx~bg(}tia*_<`VoK%lR#v74SI*3Jy~C%NoOJz@#R7{d(lqu7aG>f~ z3S6GD1=@fD=MgDqWq#OPVnYfyW%L5P8QCDmK<;<2`_SOB^3Kzdt|_KHRw(VKd=31- z82x`9te`ud{aD_lJ36g*vx1`i0K2IE84hlDFo+j@iIF0KpL_Q}ZPp&+X>0pa2#puG zQSjX>KR>=zD7fB`7JMoT6g^W{!Q4`r`u)(ML2BUUO+V^R8cs6xWqdrY+>*oWB`gmb za`#r(_f9=bL-Mq7(RYJ@@2Jz?+WyIjSR7`Ld?B^`ZCClwHnLl{NEgF;L=mrG*;Hs+ zI)il1LTxR1FrM-Ie+DSEaL~%f3W(~SYA`E4ZS-}k?&0@+O#k@XVo1l{ zhR2vlN=b$GOE-nvT~l74`CWslJ#EDT?W{UNIfx)uZ9cXh|M?@5{O3)oJO3!2!_Ipi zcvum25B?o_>-*|AO*F!3!`f?`;{ol+DS9W;_kl_-9+A18>m}D&d6$~2 z4fzAT_7wlzz|9pd7_|*dw2Dwq*|a{hDOm)?Edbvk?)ee0$zoSQIwx!WAvwp6DQ>{c zBgMC?39316bifDASm!&EfHM8DC)WV9AkIep)<9E)8Ouh|f%Rm@>O7PbH|znJjtBWL z!iCIBew-p;V9X#bXw`<;B!OfP*CME)P^CA_w~Iqvd;;ecdpjO%Dih2zVruG-Yqgf< zTYo5<#Q-qN?2lbmn2Fz25zi{jCVYp1<3RCQcWVo^Eq3`5Uheo+JvHVD#pD|caEufv zZ(lrKKKFd`*u7DG;hq*8FU!^WbesG@aR-0&kgv~*d2M(8)&=POUCHE`5)put&BbJ` zQeH_;D=?390}LXO!T^@uJDc~Sw9FxNn7#ef^png#=1ZLbxd8640}J^ZbCFf3-Qn2` zd6_HMa;_JksqYXWAFlELV>-;;5IG-M2+^D}g zj&1*&YY3XLewq+P;);d#8y>pv6&zhE2OZ=<*7Xo^^$8uiH*CAjCP|Pukfix0l#h~jy zeYPdzv{a$G$3z}jx>ptdPSIc7IsN)I-k1KW|6~^A=X0AhGnL*6RVMfM{yOqv57ZWB zbY#NR^7u~~6C-S=lnb0N?q&pcJe*CXcbeuZ&Z(CpWrt~)awo#kLa6)t3}VSk9y#14 z>NOOlOquUV`LXA}RC!rj7=Ha_ehPwtOYGJ+T@%MxX(}8b)G`@m8yD_%&XMjixu58>c^(5>#~WP{YVxTZef32+#P`S(N3(+>l| zTI8XkTwvfe_!o}iyT-XIHZrtp5M1x`9!#S`xaYvm-!HPZFy>0EqkE69p{u9207}HL zeWJaT5o-y;+o(f%6n+}0GM4$)OlbGmWAI=@^BTcL`gK&L$qhTfpg@gZZ`)G8yfr{y z?~vd?SRn}Zf9`Bf2|eca$aC!?Ro%zIEeKE8?xAfz|6?Q?Cv(JDauz+M3;(FMN_QYH>eHp(G6 zN#6n3b~0#lhz|f=LGMw;tnFiTqWw$AmJbl8f%xO0kei@~pY{ zTQU;`XsyFkSy+I#HDJ|oV9e(LaoU}TEuZEcNy8RfWulXDc8)y~$B<;v*4TNDTqiBv z+$0OF2bS075|dsNfO}%8Xhh*(AExQT1TGCPE(^L&Pnv?{?Oo<^2@t<$W#uqmPeQ1@ zqTmw+ZvM_omV>~1T!yKBx2k>|ALeXu*^+6ju>u_j&|S2H!VNdys&$kDaOwge-)g{R zy!bqRE600fo5XPEMc*q-OSi%9-hRDZb&>gNsJ3N_7+%WyU`-^l zQu&Y4T!BhujMJnriW>lZCTCN9w0CeMw&%__ZGQPZ;`yTF2Y)Lg{ttRM1KVps$yV5zT@(lN|~8c%M7l&-?c zjL8=L=`^-An-P7$hza-r3LbzlYuwhJ@-p|Fg?@7!I!NKtl5!fGew#i?tGL(S$B_6P z?k`6>nlMj~{|cxUYDj{b#c%cA70HH?4{bcw^mGQ&{oh9f;52kZ2#y?|;VVvNVdjD7 z*^6D82dXS>OT-fgNe?!(yb?p8Z&{Nf)(G3j2CtGu``rY_)LqKSH9WNE$+{3wA8`Dh zv=Cv(j!jW0W@p9H=&YI;Q z#OL~IDxxy$5BWVET6P!n*?A#V{Kx^&q;&dcXd%5kR9gB#Zh)A8KmWbsm9u@h0U0_| zx8>FMx~@eq!g7OgBd1F*P|`^&*6}$bB>Rh!#4!SGXS=J&o`QU1lmYVllU1a_MX~^P z*tX?Q3mkFXg06fIIcn1EeK7Ysx%Vf0BLB$>l46AT9u;B~gBJ)b?2lh7xtOI`FmW6^ zOrxmJoJ)U}_LB}G17ljA01DXpni0x?XZABv)oeL)>m5=ugssaXT2S@7r8D@OYI`Wzo5$ISXp9KKS0@u+->Z#(BOfZxQ#k-m8avO~bp zvI6w@-!Ee;umz}29O_g63UGYaM#5!L3%LlL1`~rvsh^VS;icV(xJx;3mwAmIrE{u? z_~E&ty!MrK8iBnU_bZ}cQB1Y_NMB~`j7fUK*=`Tlnp)#o*%U$mTTzKLl_bEj-Gory z*Pby6#Iin>IRc zSEIA=23phXj4ptx;jY?w9e37m6H;AEu~Gq6{U@bsLVhn_p~_2+o~@up-gJr-x}GO` ztK>i15A@CDq%<5^KZM6n$e}g^9uk%fzm~RPL{yy+Nn)6uqhr_LKpB{?^|iMZo+U}1 ze{yN#j&boP_-qL^z#MvPpD(JFpmuHHv*`XI%)J3Yw87Ac6g~D;nn^rX^e?xKvD1Ov z3B3(wNhFye8u^L1k#Ac9VBLW#Mbc7q=*unLy(t{=V2?fOO^C_=9%s_p4{uOZ)DVH{ zsRpW?U2N&zXLi*qRk3ko6_sY=Q{`1vfbYa?XvfSQdzC;^rax{E`IDnXr_45UzZ%

q`LV28a_v-{6zHX}i6&fGmuV8u9@>6hJSVlb5@IAjTarR32OeNaIcCt)@H~`6{^l+#JY3xPl^7 zc;TP$zZ}4bI2^G#K?z3r~g{Wj;0<$Ix-cNTkP=}1*`yXVym{r~;87H03 z<+sPY`iFc{v1eB?TnVo{UD7cW(NBO=Rp_y&V8f($LZ%l!oGi#Q>6T=9d`$QXSa|b- zo!kSj3`VpdJIY<&5BYmihFfQ*X6nmS_wIplffwW@QK zZ@~oxv~XgFawBOgAatQkc&Kra&<(={uT)Raz=%rMtVlGm!-v4lS^VBVf9I$0r0zYg z?eE={fl;@wN;31M6X`htB zL7l}kVa6j{_$Tkm2NTFn=9S`lN^olWLV$%;G2qTe>alehx;LJQ@E!}f+`R_6dO*vm z<4c-5gQQS^O$CBiP8PcX520OblGypi#U0IsEu8ELAmK=ba;0t8p{iTs1f5ao!hAGc zc#ZYKn`xif?)?$HP~CkL3lltUS@B{YF1AV#%bESCQ}lbS%5_KI`z?F&Z)pOjeb`B| z5M_gJ642Vr!`T;0KTa>qyIitiL)2-DXaM1%V^C4p6zi}ZGZD0$( zwr#_UAsF-x?CrzWtbZ+OhO3=l+Mt)jH21fn>g6GKBm*G>An^e+Q$j1r`AHoZEu|qn zp50;2ePYNHEiGO_EMB^FmQ}U47DYV*O3Qr;RizdX7){zcJ1;1XL|jv1_jdtjNrA|~ zTfH1))7p z{0`v`Xdv^ZGIKOnMcS1x!B;*%s(2Q%C7~AnPXBN*ZYg9{V(5{^N$+I%7*mZ)ahX^F z%}SLWr#Wmu!R35bB_4Y)EqR(GhL^`n751E_bQ_CkrZ6K8C= z7C=lTCNM=ZAf0qM+=e;9xw&fwzt*KC@Mt3C#cWgG>~e#jm?;^WxWqx1NU0?_e_ zo)kw%#m?)W8A?WeT?GEigqbJTLK>bV7xfd+PO#`wbQ@2|0Ow0jY$_@{lF%9+wY|Wj zRl;1*sa6YN&4sBq73PVg%Q_cB`SY-_=E&Plx_#JRe9=1dHsDYOTR zxzCA>{QTuZnnX!EoO3K{`^INuRR0eVz`AgZ8f4P5M;#yl%ii!({k9%Bpky9dbm`;n zp!o!2Wdpxf_yH>_UY!$#wHXqCc~c@&R_1Q0dSl+9l+9={r9X_QLUf4atpA<;5zv5h7$)dtiB> zerY9Z^gCAjG?qY5o_AR-_VRvg7LCft8`<7k8$H=7xkr3ybO{cnGb+inCVhU3KL-+# zMG414(~QIw ze~4K6QsiLjQc#3o3&pBH2{B7z!Ce^)BcLVyUHrP@KF;n8*|aJK7HImzQX9KBiu5H0 zDwxVNg1SsoYl6VDuu0WO;GvQJ0kwpI@$}x)?MfH@osPX!fpKfCR#y0dhDq`6)m}Jt zAp_~=%IO+@@9PTTt@04lvZQU~runT(x1_bt8BMq$$~Qq)=d6#Ou9yOWFBIAhrXJ)y z>KB_M>k_0l%p6hN^A1((5~yiE3go>GXcfP)`$oPjZ8+3x!% zE6RrFrMk~FiU36C1pcSQB(h!`Qdcv#a;&saa0OL1rF_$`ly^M}fCCxYJy`Pn-DA^h zXfO<@q-=;v6FMQnIuK)esPIHwyXTg%l&r(WXp(no^9r2llyDcaK$S9nM^SKZ>vFhtjma?D=at`v&O=(xKbJ4M0M!O0W4qz|${dBLyrho2m8fCJ1BeBS zQZFp$0QMcRw?D@xNZ)i~ME~(%?nk5~Iyoqdp?I;>h8Sa~Dpm5m@6}Bvk1~x#Wm?j} z{-&BDb@D)`ET}Ac!5H#`G^>p>lPb2GhN#seHl0Q)h_EW~LGb!8;p?Mn8gb4(=3p~Wh|ejo4%CCPWWA9O!eWSn6qU41AW3?UTuu!}lj{ROOn&ft zAD6}h8wswc&3*ID8}1KJ`~hCM4dd6G#~nYdd}H&E;h^a9{~g=dU%`#=NCrC}yOgZq zRGokj7RdQ{*6-uAUlbg0CK;fZ?7RQFb`aHH*S?SFHzAIW2Ta5Wxz|1n!GjwpL{L&1 zy{%`9$3C#U-WfQW=|~szUo8NBT_xQ3ye|F>MrMjAch-UN%v`98}(6t%;vx2~7uMH0gBB^NyRvIXuv+D-5kMP$kz_SbCwLngOTGwB3$ zUeAvTi=$+1$nT4cSUNqV`mnj%V&x1JJiN^To5rKhdOlnQw-6}1nNMw{Fgm4mFZkBy z)=5_E)P?rveq+EpMcVGe$dwzyH&My;-*|T)_PJFxBr}Yd`a$lX5GzX2KjDiRH2Ps* zphHkSa#FR___|-}Ml%yDQUty1L`Oq&ZkDs0(a&uO#}$#tI1M|^s&6Cs(ovG;qRTsQ zRzd}?AHD%56kmbgoiljI0!=EbhGj{oD9h%L?sU+TYMQ=GT;=?ANKdju`KntXiDc1z z-R#N+@5}#=l?)`%P-~!Cd@9e+`pM^Zz);E8!4WrgnU~l?Kw6*D=kIBpn7?@cNJ z44>rV(Eet2w!0boWDTB^d%cKh(R1Jix&S!`R1r_ySv2)cyIe7e{SDK25Fng+dbYYt zbUJf7DEkClQe(cQqoepY3wP5zf@B)~@bC6gx7NFrZNl|wbejkR7VngKpaVMAWt1Mu zf2NmzOez4srt^m;jKqAu`8+iwH2D3l{6%`4aW2<1 z+N{>QR{%#}g2wWFTt`(D_ig2~@k*os!mDRjy5;oDFIHL(bT{{b1aEXPj7k$ z$VVk?x36}xq=Bi@*1EP- z|DB(~&AOzrNk!w^{vD_Jcl71Fg;3?c@7Q8tP55mqsL25Qpy`kPS^(Nbky&#<1x{-s%Ti$YHvE8^bvGkrnWX8!F3NSl)$4&)RuOb|#7AiOA){o@t#%z` zO%J-Hr7N)FuPL3B8LzW3!-L>lCuA8HpXTRqroz)JRYGh- z{IFDx_TnDTZd1>p(zYb3`tJx&Cr$(EaJ)aavmF}@Tn|;eE}x44^vJ{7u-5TfuS&4H&}k=AHvkCI(ktIG^8uSb(bRo1LembKm*~ zg~$cn{BnPCHpKzabpai~v1vcu`TF>P?zb4T-2ERj$OQK)@}tEyoX88HruOqP5vP;x zFD~@VXQ;uwhSxAc7HlF;`a_RvqXGD`8p43T`^?+&>|!FyNWEnsL`A*vz7$?uVgP7^ z-JxNCL6r7h4HVOd^sJ`cfxn&VP@5*)5O(lnFi<*l4$t&miF$H!br<0#W4L?X-L1Xz z)cJXvkK)B*X$(#evCibje#RY_n5|U6BA}_n6SE5hc<+UGB$v6JdU$&92+`J~L|LFe zgCNFr_OK6_v5&nvuwfznxxWj@av5n!Gn2^z!o0I@;Af>z^MlqieN&h}UQOR``a2S8 z2DV_AleS_uzF(Z%kL_7^w+nfEL?t-RC~w@QmDwj~aamaw^(Z!W$0_~l8*C;!taKkT zeKr=j8v{S-|BnW4A<9JArje`F+SFYn8l75q;ldtGq z7p%~0tainRXegzS_wP35NxZ3MWKS#E4qLsmgP%1!F;wr5?md0rm@!N)hL^Pt*_uST z=p{hXFWbAvtn`OfW^V z2M$G=pQPbZ>T|FF9sX&YS`w#!GlLnF`T_08O2HS31>5k=yPccEvN19@OqXCtNw4>p zUC27@vRm&hOf+_19K!_X`MYyKG%|69#3i>b#b}(fdIIxl1>2r(V)z0WHh>gY$>DE2 zc7x;=n3JzK+YK_RUvQr)29$P31C&2GJ|}mxhCf>P`$0724nBRIawWYl;~TG+ankeNdevs9dJ^1k(+`*t!(jP$&n4fIR2g@G&g-=fEH`d7DBPUP+`41R zA$}kvdHJGHDkyvJG}KE_Mt+>e1S-jVpz#(T3S`OlOWWrhj%lP_hbwX=UnvB$VXjT? zI)IrKkuz8X+9npr@^=bBJ>{q0DL@XCg_+k;c?uz7woN;XhjffPjqB%N>mv6fU)A@(niY+x$w8WV7lgl?qZBG;mx_{9*`| zR2?v+S_Ub<9?sju+mK0#gI_aOT^l7+ZJ7SHH_%;kOOq;C8nOwQtwdarh=qIg{^^=C zQJH&4d^5sl78ge35R$HQXXjt1@4N}8rs+FzURcucpNeoKQi|JN_y%0!TmPRg1^zhg zZl4l2s}q_Hdnu0L88TmjU+lpSnYB9MemFHFjH2d2vX*!aHNs~4ViWKPzcSr8MD+|J-*DTnu4h?XIpS~%pw`N4}WhS4Cx_~2-?`xjkN^lmC zfOv8wrJjK4^K3rJjlZo!e|>%I^Ul(R`-;aS25phX1_LXtk5|F#^mI%cwK0jYSYwaC zRO^he&Ck!h=@GA=qOZvB;TigVp~tn4Wnf+kzTMqOho%Gf?pjeqqy`7DNEw_(e9k!A z9r8_T!$pA(I5W10=*z;hu`&897+&lN$N{rNE_*nrJNr!j+n>Htx@}=8-(>#M`5B(I zo1jQ+@LBdqO#!|Wv1adbR%>weIL0$_y%p@EnPeZ(02<1O?x~& zlbYUKy=GrHQW?khOo}}TQgeQwq@|W06L&l%>B|5S_i_Asy(299o8dR&ffe+&cS|>I zKl{8h(+{`wd3`dtLpw*D>@zo*nYQl^`8Mmd5<&8(IlIy9PKt3alCMHD%TmY%!aZi{#A6q;5*FIeGc+HMv;5Hcj=w zUwr6|BXQA(50edweG%uCtI6jV6yS^P9er4brYL__YWs9a!t!8trd*sESJu)`n@3&g z;s+jbS85nL(n-&Gu&k(7=B zhS_J$`+e`Z&iQkGo%8Qpa$U}zy=TYTYd!0J?)zSAF*93=-oTPPQNwB)7?S6LyJjD} zwdX53nJaNf7z`d@*LLyB*RrzbjW>LD?=to4h^{hH9T7)-plLh7Hii`cx~ zoVk@}_UUtKN^2}QTYBR52C9M(^jtRW8zl7mV$V1Sv{a~sm$;zyCstRBD#E!+t66L70-qKHhF`PJ)D#|~G~=xq(Zj20xs+3qRR z23v>l^d>OHz)SW0l~GSfurx@Y(XV9PFF@icGRP|>3*)IwvbKB9a+##5Dp%=EI%8aD zu+OQ!-DLZOnvD(A%vh5fKEWB4N%mRW4`znCa&}5&?L%`;B*}k;hIrm%ijI z+0s(KEX41Yz-#X0_Vxg6$y6jdr?mKl$o$^+kIZ@tD=H&tN2}^MQ7joC>O`JHB^bT~ zc@BnKm_oWq+(1HtHYB+sPLYE&B!VwIMeB6<2IF%u%)|{~{dU<&+k&`vt#Ydqp4eeE zlPy_;mqCKM074M`N#U%0Q8q&VOeg{|V{nQ+ocVRdgx)}uFgO=Me){Lkd&b=R@AsCh zfHM&actrrg|FxeTZqKEF$B@{d=Hb%UK98ESE?dr?Y41~1 z5zeX620f>TLRQ21=WmehLTpbP(2}#IKI< zhZeT}cV8JjDm-83tuP1WYyA}tx_TnKMuu$ajlO|3`7iVS6Ov4y$nGaealC?+hL7(? zjrF1atl?Z}X9%~rcw1_3{WueYz0wQXchPTG`#~mlS(V7~bcnxbJjA^{BJi~*P`nCrL5j(Zu$)FncYB~9%fem(*zQSA%bv##_ zc1O$*0s)L_nDaV#muX#y^a1@p4_{f2J&$zsFH*La7CR$UraHgis(L4XrO9ZUcd$o! z>*%z}FgK9w$?Uc4JcW69Pr5(XK;|C}@7X1ulwJ@+!j?w>6kJP?7GI@H+{3(1fNG5DuAf*^+1Cl>pUo<5YcT%i^uVE=d<{a3{fzaO9><~lG=1QCFK_QIA0fH%K@GD zER@+Q&O*QMNaXJ*%k^w@*c?JRqn#!F6>Fc?+-=mKAj0ejyoDB}LXyxlkiE!hmcNj$ z3$|NKi}T~)iO2@an0H|pQh0L#i1>X!!AcO(rcJKgpJvR}@oxqK0M&@Vx?fbF_!Ft9 zFRa)?hWv%u2k5GU>a*#`8O#d0TEgw~Q?y9AS$+75(!;$h{G^M7)q2YFuR?4-waLZJ z@hNlaJ_HgO7+G?+rMUmnu0d`R;9N}Voek=CPW#wBbr&VQR#^qj+z~T~)7cG^&*_UQ z^A|3A^-xDBO^epdJiyoG!P8ZXT3EW9&~YsImigUT9-TX~Glt!D+q|0V&B3a2xZl8Gb__z=X<*er2L_k!4P(mX z;PN98=lP9pU+?`42Ql}bSUsJJO!80+h_+utO4nfiJk-( zY+~eWF2N1k(@=Zi#sz;c!tma#48&)dr^h^+QJjrz`Zwz;`&x8Blaniw;wYE}?5*Ts zhlbg-AV)WLM<}8&Fdp;@(+``jn2r$ZLz!_KyNAV!F~>77vU%}vIQUNzsby0t69hSD zC~#jb#A@BM5+Y0N#R$F!5aFaHTu21@ zge{=lz@V^bNjkT;Qli(S`62)-vYyUzgk$Bv?LkW8!iDFWlailH5mPw0;`VfbTv6nr z>)uc{4S<{XSh4=@r_a!}O)WpK%ea*PqiFpDSBd!f+$*Bkt)Q#V;PKpE4h*oy_?=*I zioY|+VI7&Uxl|-HWz=w|FULUm-TlXr@bQSzt!Gr9k7!7H`hlD6sXDgwTpykM2ADLv zksR0g6JZ=ZBbim6LBSLO?aa3x-L=ES9c;eulGNt47@-OB9Z0STf#H;c%S{qPwQPysZcqli6@C%&4* z>E!z|`zryh%bA6Cf~eoZ!lUI-^8utKY36bVpTV0qwJ%~iZRK#V8qF2mO+im0Oc(>W zppi$%Ix|-38BZETsEcSjGteh{5be>_=NN%`3gx2`ilVgZSvK5Sq079UK_IUV)j{W% zyP65Vgn=itupjv>zvH-y#cOg?uSwFX>^l5-hp!hlb%2>t;o$;3+N(?=EI|*i zxr>8#aJsqK*f<`a&jP05mb$RSt3J&LM#1gr5>##H?C#XKgnb<(Y=l=}EJ9XM&Kv(2 zyOmP~(^S~5xxk#Su}AC+Sj6}Xf8_CCgTxOiSF)^m(2Q~A@^F&@HXb_~Q-Q9M0sQ&k z#tFez2NJpk=b2KQKPwEN>yoa{!W_6)-S%A2a!cF()sor~Uc9@Nxc?zg+Pham`pVT4 z<^fC&+ll3`nW^$_)sEj<6C%Q^X?Wg5CoEuCAXywpq+4ZQti<66v?~8@R_)N)!(3o! z=m^#O!?+Fzjtd6QvfZ>BC0@d+Db2`d@PRK-ZN3sY@&RW6M{ojs0VHWGCLL@tZg`+g zsPuf|fj=KRD+{TX_$t+B`1 z%^_&ty!sh_D-F&GpCPrC3n)@b(ky}`egp`S8V)y*@9@oHyPH5?a23S1Hb--ItRnN+ zo%2Q0ixy-3t#a^R6G-_T+*`mb3V)d;kB5&?zwhrK89jhf5@)Y2G$Do@gTs75d$Q10 zHX*t2%=U(;QO%be?u-=3`k==H{c*v5>7X@fwrZ#~0?*rJDYl`KLS@STyt1qJxak4I z?DHfiTYniT(00fWKT+oFCpU2Q&Oufd@Dzl+yv4TY=F-HK&wQQF9)f%(0`N^2$mW_> zimV*>rQui`~^KFh+T;iz+4=F7CCnUV*gLezfH(n@%v3(|~JRe_G@{rhW(XXfN z5PVWY`{$E~Aou>F1Tmo4E(W2(`*$|Fv*u>8GjQk-J4PN;H+su@OWB%{%lH&e^u;gW zI@l@p!W_PBAK?}}04?fGhQhw`749G48i`t*pYt)PptC+X1dW9#sxXhIGF7dh4Jun)BK^&;#v7EK%*U z7B|nO6wgyVm-w%Di=2K)L*fN88#ky1dA6ISnD|2xlSOwUM88od^quO>{%WZob2qVL z#3M_nT44&fT4t`ss4hFK@kwl28HNu8xFrMqA`P-!vek+>A|ugv5`SiHB(26mky)qL zpt&dnhQ?<T)NBJ7n#7v9t$Bq@=Ke;UXf{8l4!!Q@=;sKbJg2xYf0lNm=h|m| zUJ1DQm4@Q~j$;+y*{;W;7EzcXBtAhC?GF(jZJ|ka0q7ZhvLG+tjngR*u@W;7MI9ze zZVWMr#Y;=dLP|osiuD=xz^>teU3O`tey~R<$cH@n!44Q_(Ims^PqSJlvjGWAc&vA| zD}R@}LZOdJaTysH%9O8TE<*IG()1)r?`)+Bu-iUItrj*vOsi>EEkyZ}w9=+`RAnDt z)XN@sHtN{W&~5>g39K>Dhe@lRHor!B5E{R-Q#{S`>ouT`*FUCXR{S}H;Lp}Wluwm{ z^CV+m{aIf`oRc3)7xBUWC>W=Bh6I#J%R}kw(;=sml2(}Wzh^Lgp4ms*bqy5H(7llt zn*Y?JfMYX6OM6yE%a?ZI9T&Ub!Lu3imhNoGse^F{nyZ;T<-{&c=}6Ymb{rvhI!S|e z`$guCQ9otmy~wgsGvHWc5FQQ?R-M0|diE=*80v`l7SMQq@5Z3qwG8vOD^XI|8>km5 zLIk&T4~3gWx@CDS`a7*eFu~RzO1E>(G<)z(pU(4r@Fun3EI$KdlFx#EmUARebVX!u z@XYL0Lb!D2 zMwtNowL0gE6@?EIMw=~Xhat*v0ktU}2WQoJ$H#w@J%$l-Z#C_BI|N3PIy1u^!9p)l4^6IirGCT(x$O5OlBhHU9r!vKdHKZk*l3px7z2aEm`3cipl#j z{GlqkcR7Nqd$n#PKRU-Uj`pQ1h<9ow-LjkK2^gO$BXSU6#w2n+UNN5~`x@q+5L?zN^Gx^Q3NkC>-*s6lt z`inU^9#X{e%03}yp1gNiaSm9EVA5J8toKFFrLXE&=(s5q?(GVQyP-E^Yt7NrAF0|D zQ79$-2WCR-Fh`RSl-#b9T)caHf5b3HWYvQPC^s!tS3m4XCezZmEcmnej7N<}$9`b9 zZT>c`PUC~v=NaHPaP{y0a&@a_XB1s5G{dv|7SZ6jDBK_3${c3xIOk7Z89Q~YttR&E zPahdp#Xsv%E&Uv9xaBz?hno1*jHc5Iif}&Qu??ls0wqQB_$+*LGS$>f9gSnI@nNkA!16_djgH3rlmKzexG&u;>t8>Tx1KA$Bu; zfB*6N+6&h_e^X29vfrXm_)81oMXou~>7tB53FOgJnEfbTMwQ7^>Ok@^E7~Er%OhPo@Zuwcmq=ePM&Q67Y!&L&ZWoyG}m_l0h z1HYfjECmsKeDzf2Rx3QFuq$eCkD9o2Za;EQ&|0TBzY{*cOE`4JR=u=}Jdd(Hh5o4p zoqK%&x!;Ar%nJht47a)7)64h&F`CmiP@q2F_w-%P-zyC?{<$i2qVOS@%+%cBr81}? zUHwPZ1^7oi&iJ~HnD_vb6-gpr`{)vQ@s5hGMbME1nqEK%A4L&a*+@5TZ_$nQi>=eKuWeE0V9)cch*xG&k0!DAmt#<; z#Nx@66hL1VJ{Ne%QLQLE`>5QEr}J;IEovFwMO{~TN-#dF;kgq^l3bHq*uPJ|7+pprt>ObJH?Gg9fM`A?lk-Vi$`}dmz-OFL_R+%KmOJGi!GCEeJPz~=V2_HG^;bzzyj@@C84ZaZ;F(F>E- zGrt>J^2ggOrE{()rdmv%d>?YLOAqf-O7F;KKWu=u=Z!7xmh~?vl@94SI__!t8Xjap*;Hj zA<=!21{hbCR#rge5Wx0fj>k45)K|p*vMwjx5XO^(Ps>?|Zv_6596uQ==KMlTVm`F% zerJUWR(g7%+Hys3hCk+OPe>87j}_^k$XWJ2q)XVur;P)J%=J%IE~;8wgUNB5xTvTQ zftf|%DgnubC76}JSy`d+T)kTRllZ%oL;Z~Vs~dtH3xhO=Jrl$&d@q~t@X;)gye8I4 z|B3HblGCt!$9$6?w_&e{q_N68BitohgrLJ7>e5dJzY}RcoI@wL1b@e6kyJ=ndckiI-2_nmXIQShkZ!v{tPsx z7E9lAbVOKEkWy-m?b*2ov-VIStnhXoAF;b0;~|gg!J@BsFQt_BS_IOZpx^}fjQRxe zpMSKnV9B#VBULn3&+4zVbM-O1T4}lQC<-X=t0FIjJkNpycl!(#^yA5b zfNi5NzQP2uDxk3ToWc?x|*3?OsjTJCTWHHo=0XPYUg%zpz0>!R?Wys%>rHz7F0{-CHVdA zf2ySpmL~i)>>Z*1-ShD0%`^Erm*4a>;%=ULzQS(VxYBqwKoX*fu8#L*LNZ)M}`e*fM@DU zbSttbY$#a1i;~cCoLLw`A+HLwKsy~ceEHHRPoO=`1i{laTwYtE`!0-tH4J)eC))6q zzhO#|dY6*3yw}k!GJnxP6{Q#Zp?m`Y1asRm@B51DS)VnG`$ScipXX9AB z^!b%palWEZyoSuHgqPm~|fRGp_X^i{pz#vNu`rWaH=6La)jYkmoWTxHOx{#Ml(VPAwx1U2hu{*BGHg0|y@XVhiRt;?`X#1dbGhV)2Y~avW2BV;G zS9vp#p1gpVwT5OJWhT0V9O9p z7KWVzK4#d1sXO42HTbux310vi-r-ej${4=?*cOY-0e;&T;my}*0*+$hFP~NxJ3cVf zMrsoaw9{PbJ|ftW^DrpOLK<k9;J{vCRL7kwN&(40o9}V2ikYxKd@*a)cLx!%h>B$ zNe4{U#N5aMksS_xg)Xjbys-`Cz=Um|lc^X=fcqhJ1_y9S>|Q>)%ayQ7nMK5*OxxJ2 z)Wvp(HXNA%(-SJw*5ATU;2;hC=Z{N;bYq+O->p!NQ{oH3;E@0PapIFH{pYYyj%eBt zLU5}8>`;kT`d?4Mfv5i89fWjT3DDL5o;#G3P93`N@7cmhuR>)1p6wRl|G&)t^+Lnr z6PHVSY`5|QTOV&7)fIz#tIxwU$3s_m-bOzsyig z^{m20@L;W!;C*IFZpOytrq*=(0Mi@6u;Eh~%o>B9VJLQ7y=$V1=zDGc|E-p=OJCu<_^Y z{wxTa3E;`DoS?knV|yjtgvz%TwE-6(QL#fkp54#F>-9 z)|VRODv|BZ8rqSCpNU5QTn?X@{J#FxLW|v|DQWr2Fnp%0a|R#wr(f^CUJ*K+7`Zr&~y zw`m!$8TZa(oYKTj<+Atn^gTAY5&;E238TBs)oY9cnFn1QiU*HNr4~T)!Q^NNDLpkw z><>f9OrUIs^cfa5DC`GqE3=C}v%P*zEWK$M;Qfbmm;+i76t?Z8w@=X7>dt&c=@UZZ#DF~cQP8>b;iTXzIN8x_X*h+TA3R^RS~8|AzzsUV2g0SNan?` ztgH)H*BPGo@_nO425i2zbhkd;wpx3{79$+6BCXLs2 zo)wF-e*7mR|1-krl(}Z@m6=6RR$*QE%d%>pKb0=uQ+3W$PLV`)6EI?b>ZY;Uq5Iday8#fc(!K7Zhsr4P^`md+7+nck6UGC%$gyp75b{bz1Q5(M@ zDA6Yr{$U3t7!k5pIfT3;od%fe8i+Zs<%5{W_oT%4;G%PcFGeD5!j_r=8N4s!T5?s9 zqTKy=^u-KmPg=b(?fY@jf3;P1SNemqi+vw8GCpl)?>5={>pjQi5JJ`2jC zhA)EOmlHtW>M;T{GiUZAGM#IS+Y1M~-e+f0r$i2cRKyPSqQ>vC)2|HRU~coW10Ezb z8mZD5uF^?JU$*xm#emS+OH*Cnu%w6?n}vWdvlD2bIK7WSSoZNgS>CHGk%HDu&KIMx zE+v+cRE6J4Kh#7kW-%s4zbum<7O%P9Wc8f=9Y3ou0)oDmcwN>$G7i@8`} zUJ-W+TW-Nd!c%QY?K+KPK4WeHluF`zYTGjGlwWo@?%7?ewzwq zy{(X9^IMQLDr~#pLm9F#8DvkYwHH32o}$Hg;i+0#V>HMo*^pydb!vfej6Xj=QXai7 zF0y_LN8|M@{*I?kip8rtYq#^T@IboE4wF~)WcI7diHWr z+lsIpb?w)p&8*Q=CXo0MK*RUhDZUmYOb5BqWSefT_%o0=&@Y__*NTa2%D#+cCWglM zv0v_;!vIR=J(s}W>4n24wo`qck;v}Dj7}GAN#fUs;ghb}lF8D&E~}4^l8(uf4Qu;}KkEy8antHps5HZr?i_X4!l2HE(0*`s6VJLg-CZhRK}fWWnQrFSD@`Fo zi78gmG7Yq{7Csg;qd5#y9F)QEM2afbhI1)9IkXM zIPW~C>3g^vbF=A}e!T7`oO-q@fS4lbT^0Mb4$DYjz3;?<&~aNyhGb^?nzp~S)X+;5 z1+mJl(91@5)t+QGjxqeUmtacI5X+wpw2B=zV<&_@EhwKH+C{M*jT)Li`_}&Xi{Fkb zW9AcF4;87Bt_R4_Z2|JvT2C=SK13_W+6m-%S(umeIUtvFh%$G+whj7P|bFnA{nj-z!)DMMVMy z;H_9flH>i&H`_kh5lj-!bxoUpT%vpm-mMzw%TlO5@WBkbf8dNCFFZTm)?|GBOrSf1 z)WKvS`K(UFffY*1#A%^e984>m=hG027{o3{y=Y9H$&34}IrJGUr4>d64?0DwU1bbb z=_Hk7$@}&hi>f?>zfCf|DPy8cDyt$SF&&5`zN65GS-cGvT&(|Fv*|Ju5Ey%L{Twk+ z5*K>Me}|t=azbYjrXH5ZZXc0ztni5k^%d2*-LU1pqm&b;RHYW*ILWUP#jIyoaxW+g z^=>NG>TS$Z#BAtPl%rj;oAs7Jg3lAN;3RCP~I{vr)QJf^7uYk6)S`z2By|+u8@GKb|p>>s;AFTfe@E%?KGM<0nDR5OQP}-Qxg= zJ)(R(QL%jBuZ-6iEVJ_Fv62TA;Ca8r3TF6f@0G&Nf2+namwcWi0rsayS{XYmJJho3 zCc^|uGe6=s+r4|#x>IR&YjUUx3!R4Hoa0ku950Kkle8lfaoxRG)OjGuXrX06y6i3N z_5t#-6{3wXH;7Js5jpY-u1O_qqi4rHq;PzqN>%zX-NZVid2!SGfs-WYupd3X`0-nX zX%XchagGjZ$hy?Fby7lw`K9^`O@Qn|yZ`*E4@LxF&j4j_SfITx9eqfMIM@XZ_1m-j z#8ka#;&&qsz9w@@lBL;^CLmNRV%lHNNL|a`X+xn?-$KE9WJUz8QkdG^m9ZCAZzmG3 zIRrO2%^zKtlh-N!+KR>XI=?L5ecmHbm2MZ5_HI4K>(95UKSeMgF@OWJX*D7e|Fw5I zeJoQ;MSJ@J0QJ?0E7*XufsNm{FqlJR!xgEP?n;ajorbs5>tSAO2mJdyi%(wKy#DgB zU4ia<_&HV|dqMC?tBhUh6toU2486zBuALbg54=F7poXV-i7o=F0Y=??NaVvMxpjt- zlstl2nBe^9m{|w>oeK8eFg&IfO|`4$os^^y$*YUrr`5#o9vGrBckYYfnphF(Qxt|1 zS|RSLcTETIb~&69eDMJ@ry#WJmp=?ZIH)t`X_q3?-jkVLRp$lgV$5!ILZpw~7C3zc zesBfxJ)LGw#}K)4RG&ODTTPkG@TgGVf_s(B83~}WVgWiQ;Jr6tz+C&u_l5W>p}%4y z<5?i%`C->%52#X$AEEAk>u}~w@ey@{6-+XhbSMr8WZYhP%A0RE;p0u1MbV^5?;ZS{ zH&-N-jOnU|TAEbwDboAr5G)rsOG+;R94mQIh6k)zGy+K@x`Tl$%G#$fR%>U$Log`) z`}T-9cti%fT>@ofo;;V5_r2lxB-I{jd77w|~=p-97tZMQvF@$8sRMxinF! z*ahCe@Tu*+ChaS91lec)PCp4?A?#EpTiIQ9Jg^rWwCr97j(_}s!1c?p5nFJ;oVwVT zdz++Jel&3QL*oN4<~x(c6!urXJ>~GZ^0-Jn)gE6J)Swt3qX}=b@<)?=d6ykQW?E}; zB&h#uKQSpYzOp+rwRr#d;BnvB5LNlP(lJ+bBds0zrd$CyaZ>Y>Nwgz)ZdOED%>OIID|vi;1lv}qC&=GKM_O4 z5{1?C%*9w3VeS#%?N353@5^xPL=DT~peHs9sYW$`{wL&+H9V#1u^W3?L$5bqBB~UY zf0cWICntmOPKt5)Hb=-S5?CSqH*p_G(-s>NZBtmM zVm?XRcb)rmM&}px;HyNmFQmJ9+fEXcJ{Tc$&D~Y$ihhGPhNe-e+R?n*pB5iRYXk$K z0kG>uQ&<}5(iiovbJt{qB%s=|zlh~yVh56j?ZkF83ViEJW9@y80wTmTarCmh@w;Fk z*O`zK53gDVV`f>g(+0ox748KSit8ZGiJ6mSuY{GCG_A9P@>_@Hx{SgaCyig;eg6`4 zj@+OGZ)3MFLIE_{xtEkAl^2nR@o96+2Bf0#Zx zmX*h9e-w}K#>FxYm*;7$Nte?5Xb-qt)jwSUvBcsO zft*#!1s4Rohgciq%8%>Wmq!b(;p%LQ^0!FGYN5Fq_7yJJW~HsYT%`Z@-`35|^ikb#g)OPINEP*Rqkng$nG3O0=V(^l{#Q`i% z>i+hvg6ZMM;^wDjGKOHeiKifw8Xp()ZX_qEw+9QrDO@e%x-otB(iF)}-Io6>e;F?T z+==lKMMqyUz#qov3mz@4ubL*)LZV8s9NzdKQ#bjZc{X~&$?6tg)< z=;Dj-wtlRJDIj4K{Hp>!ScMlh?Y+;DXB8`-Lu<9&SHJqOv>c|ZtjwAk&9ge<@NoBM zlvlOa>E<0axUY8G+nJt>AZ}@QNcUty@oIe5p9*sRz6&JcVdY|^4o{<`wBn!lydZ^4 zA^(}#7l5%cfM;XR;jYJK6NNW*qKK%?ZK)}##BY&MQBE>?{Ej_9EWum(NOMfg#MKc( z5ZDpe?NKq><9qNGW*J@rJy~(vgTa9{Ek@rF*Huq7%yJH5kp`^ngrWTdC?Q-@^CV{} z*{$GfV_fJBYAN^8j#BFz@U z?#_cY_<$Gd1G5)kXOnkP7t^3RG2)(VPM85>^`rb_t0{@0SzJ8}d?o~KSBoT5 z)of9}IT(x{zVpE(U-gqB`E3s|>5~zg0;Elh&do&0hl;U`vOS*@pep$QtjjWSCt=tX zf3NDO7jED!QmaRFq>IgNyyznk&%9k;IJ2BybsfKIs!hOqKcZA|c0cLPSgGFF=eeXh zYHgvoXjkch;a{Yhngo48hu(mN@@{afmYL7d(bi-p&ZKdq< zImslT)8D=zoBicg@YFU?9Pi;tZ$^3JL4DY>z{+rT4LU~3BYQxTcdo@Zb3`n1IS|kJ z?bIM{mESx^0tg^nOmd6RZC8M6biRHMsaTRN&!`CalDP7Dk>!4(2fnC%0;0kD?(wnd5k)I+rCC*y#Lo(NMj&P^pw)R>G`dvrx1`9o^o(ZM$rM z_>6_gLv~>MpflpLK_TzvZ}tfuOy@ z2u~(I&Xf1|XgC1?IM`sxqkNB{fa|;5A`GhFW$zK=)q})=RmuTX6?~}v?9anJI7iO) zu)Gq2HhZ<7luuc^JiYbzWRdM`cZLlJ{q;3fn zh~34(ZkgEF@wh=S1FsKV^TWu6p>34sTRdGw44~^okN#ZAtEi?qmBzeGRT?N7Qc6Uenv4N~?=X zvf(unlbHJR>YH2e$Bvb#FMy`%8tM4;g9~u8t4<)Aun1diT*ypQEY}Tc15g(+E2?+n zIAXhKJwlk`YE8DqyAyKmHr8=6a@Y-EAiZlyHU<#o$&i68p!moR9SgN_7TRd_;_FnFpk@J zWjR!U%IlA>i{T%75EO$*jE(D4xu7>mDp6_X#;JAd<)=Gu?uB)~?j{{uU5-l`oAqG# zu<4_eTYypwObss%Ol<#G`Tp}XzD>q1L|70X9FrA*!r{#yIdpnoJ`7AwxrUewPCKin zNa6E7aVsgA`RgBGqu4san&G;#Xk&A_H1Ig=ELZCcWWO1l2dr2! zGjdMjwIJ~bj|QIXjet093;vs%bS2Gt27y{Z{g0zIzA3%Wp#wi!I*!-2SlzQYkKo6m z#bw-AiJ-nW*h~*CbM*TS6-*4)#lBZbE}J4YLGHeo|Hm7SA1lX!6puqJF{sp3JyKEo zx8qaqZ0viTvBjH&7kH90m`;yAjCpTZ`uOg1xcn3A4*C*roNq zSHu<>!A6UOw3Su|FnGzuE4I6DvEYNT@tr9^(knkjEo;EYJX@NwNm5ix7U?03-QS$F zQs2n*O_O2iRevu}@E3Zipt&;O0J4lT1OpG?O`-41+Bjc?CN2tZ_oK7Uw0on!ko7P$4e&~Tb zr{~G5_Tdp8dheAHD4TTN~Ofj{f!2W-q14 zL2fZ`{MQ8VvyK8*WYVsm7(u_Dwih#~b|UdT^QggbbY_OwYZ-;5#5=rrO6DQ46}*3C zx5|}H(RJl9bCxLeKdz5&WJH(#GCZuSo9N%5hNs@02bWTw4h21q?3~#t2a$!5CQuu& z2)@0!(WK&jjs((~7$Mrweg=Re+$qTPMjbwH->fuCWJBg`a)ZqJxa*u`;RH=_g6u!f zih318#vR&;1+quzgejAalXGzIX!I=R%SM9EI!rJIBhC+)d#s~F#kiP=1Mj0EgHIRW z+qT7Fy*6$Rd~tt#7|VFgf1)ozhi3>VEn%zR<*k$l1-Gkw^BhsX;JOK#10>wPwgyU{ z`UF)26R>iX76SyVfj`9R!vh5a6!DWKVRdcZ>U~m->s^B0)RC+%?@?}^$MQk%x2M>e zu?sW~W2Mbeh}(M#nh;dj^Ga&;#WwZ!kujNcB~2Y0uCr}0u?a0t+@N$rCP|--y;m?< z_(pHFMs^e~IvrHvY~^)-5lCsje}K-up*nE&P}vDpjFyFJWqF2{pJ$;~w1imm7~#yO zW728S$06OGF7?|^P@Y=LC!LT3B&57XVEc=2`9&S-Xun?nigOG7Bchyh3*~dTVGFL= zLqb*8u`m#VEi5mP0 z*Ri`{62z~rhEe1ur0V5$wawDYtLy-QqUM|xQ;Mk>ZUNV*+RRh?-Khh<^5jDR#uBr|BT42AN&6{yN{v1mW(gqN{#BOz^ujs}i2ZhRngkaFiX~ zzX1bL{MSi3+&obVtGxLXVrd`RDNV?`NQ*1cyG-{V1P1R*!h9%XHX1SK*#FZ3$$tuG z+dLFIZOejIcVO#(WT{vz0&H6kd@oVnfCDS=!2(J(=4^AWw{c>tY<}yq>FCRo0*md8 zbA#rvV?y>GKp8GSpm0Y0ev)(>C7&9@G+$~huN4v{>v?>#o65z;RLK({|A;^+B$ z&bGe&fj5AQUKWk_7dNc(fNpA;K&8BwLB3Q?uv+r=j>e)fb=uLd zSh)J}>n;BDa4_Y>B^~?i>Eb3=o38qc#pWs|WJ#;7v{J56}`xL5)vYpRLfeuY!1%8rh4dqm^Z&Xbdp{xSUbu1V`=d zWfkzGlow^q$e8jLNOqif9J5K;6StzXv9yQ~+phXU z@s^2TE)Gp8&F1qCAO^0~{&1N!W^}zBMrX-+x}bb~@iBLW=hS}C?|l$xlK7op{y`@M zZfW2$ZriZ2_oo9xD?{u!_l^8W2;hw0GHs!5c7W*jYzNtwnTC2;@mkIJ9kX?*45?Ek-lJ-Yt8$+L6+q1V=3_2pGTD&&)eRQ$b)Z8PsG2+@pB z)|Z~w3d@5R=6O^<^T~>6PU-9B@75=;wzti8$Y!E}>#*y+@8+DIak+E7PG6U*bstzW z-nx_vNGQ8E7(3o)^P#i*O|8N}mkxA_@t?yh-xqc#YSCy`AA#zf;mF%`_0V-p+dUvyoT_S7& zkQVgmaX~{>l}*Q6vj(Pg;rYmG6xO3HAFF@Az!fp*1(ogN5gxpuw;ofrs3zz`cuE?6 z*x-vzxN3Os-Qs877H;5aIFfar?rOLeJS-BRQuS9!U@xAPSd@M&PQB6IihU1_1r@E1 zs(5zIrJ(6uu}k9R^D#fWr(a3t%0%`a9zPhswGrsMN9GU~_bIA-j2pY?wwY@7OYHIN zY49l7+FWB;$m!O-w*VXS=)9qV^5lPVFA+oko4aQlT}v;nOi{X&@g4v;2y(nGw4+IC z2bv%#b4Uqj<^K?%PGZET*19YQLhs7y($d!ltxNGCjqVo#MFi$(p$=0b2-sVQp>t6H+7@!3Ro1Mjy zR6*0vlqg%c`_8VR;<(Gr!0TSShB5f^bzjXr{J94#`ENUt z=p8|8-_sahqiElmDdYJuZU$QZ(Weh1%d?==7wVpZazmU?!QZ+>G%7~FZ)W(!Ai9$Z zB{x}0gQr&iUfJJR*f3|rZvgNHFLrgk@{*F96Gz7F78PR z%is95mjJvAfG*U!hF@2BG4mhCk}p)|II0Fl3N5mfb_ftWbI110ab^b4f48(ZIhO0$ntFAA?xz*GteX;V(n~HJ_RREqkKT-?x1NTp(G-Su zebuI?LtjuO@3yK&mQN=pH9OSb#o|~Dvy)++z;FIb@XR?%JoDr)vmm#VaNnq9=)A_8 zt1GT2vpXH3Vt4|j{#)IT^o^xIeRmCyhtZM$TVs~f9v-*Zh{WU*b_mZoY?SQ$u3P5 z-92AGrGdBCrJ(o<7wdfpb-$}M^Xnb`?iVeUkTlVqP(r#!Jw(p@}6rmCH3zk|4O23*BHrF|9H;>eR1^v;N{ZT3`+= zG`(rH5cd&S$AP^Hj~T?Sk7Wgx36-t(K|%kDM7%Ctn%Ztq z9mS^)eqL~8{z<#ec6tP#5jF-)Wvz8GacRlPYYp82*Bl{tgU-TXk6$ z3ISYYvfO2U^dCL!dIDVOVsBVPXd|!hoz`?oy_}M>M|JElLVfP{FZ6IOYS0as@J}nLJcSMVds2emLb5G1 zM zpaa9qnzhb2;&*M z*uG?k1(LAegMPXQ+yT7uzQeezqRYr0833cyxltNh%z|72Bp%e+w-Cpa3Va)V(J=Vt z64Cj`;fX9znGqN%2G?J55Lx@vUWl1*F2x~^)!8JN@XomYHMi;6bE=iN)c;Otb!@~Y z*F4c_day6W3GfCW@f?J1X|l0I$-dn#TMmR#`XwFx8=z*SJmPeAsh>eEY#0wPzonD_ z6E!lnaj<{|usvuj$@cLuH-Oup=pANsK06wnQK%ZJa1po>XCiR{p{Xx7`|WzF!&s9msds@nS*J1pEz60n2;itAMlaA@ndTfT1n z&7Fx{?w(**X8h0rSYxW*)zG@8QOI_d(J(eIsM8Q5TFSYGj4*;E5Ns)}V^DD~-EoMt z#hv?4oz2_n{`aq0UN#smp%f9{->&p_EuadNXW&`fh~PfrSPaQX*p4)hMOZs9#YWHD zJoyym#|4f5&Wf>h96$gaKpJ~}i*-OJ1U>CJ-~;t7;7Byml+;0OJ zVgUmj!)P*as?^+$LjRPHa@Ed zdgVO8FqIVb)B$WZ_=MqUml3fDT@I#dgcw@@2(pp6`0n+(O?oMOREi1B2h++?t-3Ds zc?TC^SVwu!aL9>~v7|7NU)hT7i)B3(s}f)1UIFQG$^Xi)_Q0q*6V+?iz{Mj%Xvi~m zN)R{VZ>7AV1hN3SxXI-$Viaf4F4mHCXOp+eC*31n`q2>T?3kCBTZr}px?-0m&wqW7Saqzgv2|#|`8GjQJ4Xq02S;tF`09(ls$oETZ)jSWEI=C80P%PD ziP}wegc<-p7%~Rm1jkyDcgSQeUjjl@<;k=c8}tZksM>bCwxBHqGGwjy8hE`BNdwJ-xhAB`|HAPS?5u${sGC z!%KsiHQPNio+RAtvCv&8R~FQ@3rnyCdodk7k|hhJy8-hnmdImjp+X`l+tJiAf0Rye zw$~tOAk-;(WTZlTY}+cJCiUmyXN3rPCqgkj8hfCK0#M2@L2P7alB1l%0i+;rIUT^j z225AHkp^-zvp|-OV(f!d%597YV40}+{S6WjBmQ0`nbEm@;fMiHHK4c#JNo-G%9?SP zSEy6RRuZGb?9+Tp zfus_2m(7^J4>q$WUu2K(a=!LQy{1|SjZ`HpC*vgmn1J}BIdz?YZyqD!k|`hIZ0!q$ z68zW62r3{9tG@AZ-FHOd51|H0%HSfc38vCKHK*}pKuZkvzV8<}cjp(n=g|XAFdF%s}0=%uATpozrs7nj@#O#_Dkf9_QyiIj8OOZZ;EqU zL&H*})e7G`$)=_2PrzBwzfmRzGgxtA5>$(l<~eOCfR*5xK)gJ@uo8-ev%0yHY8&UZ zdJ^|7iLs#pfDZxFf4cw_mXK1!Piac6k^EeMKPGGn1w+}7TBc$taJofo%L6gHEXYVSMVny30Z)Q$O&m+nq!PJVCT91mf4jD8_F)4 zc8U7!X`<6T&;zH&z0}(W0Cm_2kj892NJwiv5uzIaN~dlLeiW+#fiqtjxP{2mApxhn z#z_bt;K>df*s+6=*5W>JyA+7dZr}G-6V~__0IxbOe!5ZWL6MI@p+S(W#lNDcyA`!` zxuZ+T7SAzrwMKbF{shND@QxA|18WW8_yOF3a)t?-K-HGZ`vTwzm1`em+RPO}Fc{X+ z1C8bo4;tKy^tO_M%r{tuT^>~hS=qHrvL(biuYK)?(1Z-n z$`40uF~tH%@y0oseB=QEAJ7Lgt(SzQUVDuH&{bMBa1wDo+P{#Sunr7T{-dl9e2ONse8Hw1x zI7o7_%|7rN1OseH}HM1NkK!*|Z zV11mDP6wK1|RT+UsQ77phW4KqG5sMMbf3Wd~}xalyyTd$&}KVqa1p z1FvWRDFnzu<15JAIJ)a3f<&a2@ZKO}=bS7b)2>px%5_}#pDi{X&4yOCLt!|qN-RThd~9d|H*o@T57F<{0A$ayyU0yR)Trsj(qvJJRBpef%QaF+inJ5ELwTc({_ z6s=yJ_UFW|X1VX>P_5gvo`%GeZk>KosY?`BCWJZ>P*~BH?%lmvmZ6B!oYojdxmk#0 ztU4s2G2cqPzek_UQ#G+Vx*I$u1KfZ)3@Z2+>?m0iBSGQd&K+-IA9!IAb{Lihx~(bx%ml7|3e`@^a{^E;=(@Gz zIl#)Cd_%AU4)zhnCIQ95!OLG|&;5L_u4IaeisB|FCbXoaq&gRd9@mM%^l#ooGoN(D zBf$5!_bx>y0QNZDhxo3+wm9U_<|PQ|!p}Y%S)~vNS{aS?Wgi&4on$EBe6P~5}&{R+a(Y7aBK3!&hW%OYxC5Ghs{b+hg64%!e(Jm)>2x_*c z9cEZo`0ctu_*vz~3XM^}*#9MgSX}UP$LXGhJ-zFj1iP45Tm$KgPhPHJ@{QQaslq^g z@k=~PDxQBRwuH+EB~4fYMkfm%O47!&Y`(q#G9F<;>=eEjJ>QsSG*S84zUBG%vgdDR1S=P89syOi7-@iz0)UyZ zBQehyt*o0S)h^dK{tm-sMwPheA;C}p{B%TJ@zFW?@)WXMx~lK=zHmqt5W$ts>6 zg3X^FEInlPNM+wVG%i2XbXg!mMX8Jj8&(53bje+>-RE=;8(#~eyZewEfT7Bs=c%bk zPy{f`wjrphmu}KJIzN_VKcA6&K!9Dgm9E#8W5o)w`fo!8NiPNG+>EXGf^*M;0$6vnaG*8gHHx z9Gw1xV*YVsk8FJX6hJXC8Z-@kut1$FOTii~IM+!GQ<D)e92fmi(2 z#aL7W+8O!+AQ0h*5yw>{(dKOjtt;&BNMXv<0y&ijp9^+EeIiT3=xlV(E~em$|Fy5s zel4s-SUYs6M&{EZ{45YmbEp{+VI*Fgx|+p!HjL-5xq!z%oSl=?oh!B$R*0c1d}_wm z!S;0Zk%oP?4PM8Lb;>p%E>D9N{O{ z$OCDJl3u7l4h)#!@9sLD!9~u<`mAz0F-E(L5Ti${RcWfHFMw^A5I-?CR*tNrZ(@a62;ZaAU_`uC%Y-ls3n6Di4KZS7CLq&`(|Db8`#2cl0Z zkXty}T(EV@lhePeUnF(YRRjye71I8ulf|Z`UX0^h&-Pr>Ku8afsCCd6NUg~2ywp;; zqi?Nd95|Bfy}p@E8)!FG5S-l8b5&Q%)ydYGGoPHKzx$MW%eAo#-H=|oKlXv5DY}l) z5n-MVQzIe{9Qn;SmecM)C;fazk&IOdR8`%1S^eKD$B?Z)XFP4!kXwBf_>fmaB z(Iq2dt%3Dg#EWFH0d9Vkxer3`xSL;Pziaa?ww}2+Y+Ju~9Tu1zI?aN0a&+HRJINF%^4aMgoZ*KEEzG^7zzDqworcQNAw6sm+c8A)J%t^+4r2lV!Oqira^>;0DXYi(nEAb9QKJT>N!%$q7q!X!wpj4PhFu+E+@e3vq zvtjoo_a@8parB*63W*&Ch_=A2y;C~BMYiUQ(#%GWmu|(h+YpoZSzb|Zc*~7%7g>gA ziatVS!hx=s*f8!(yy<{SZ80cx)r3_j)EbRpuVd$7S_Vg$%IoiEdBIaahgg2`Cs#0X z6_Y9#VYt2Ek-+_f#MvnFWyw_0+}cLp%TLmM~;evOzf9NtEzUh>Dm#Wz@x z@roXHfA}eKk!}yVN$mQ{hTqfQGLzmnDSC?mU+uV`)M-0ug_SJm&hk&+Vl_0&+PC^I z-EZmZwH%c%QgwNYXQ>Zo)wV@_mVFv`0)v|WKKNt620Yq-HG|<) z!pRSdP=>36keN`MusyTUr47NiEnj5YQ#>%5y}cnjj1`wivx+RDD!Ry5e@`-h33P$7 zfUWhUrnMXKh4+?NrqL${6`iWp;-_A<-<;Y%bZI-6uiVbQ)iqM=6s`Jb z{tGeN&?UD%NWFZodgKWE(ce$4WW#>$)rDB-y?}@sMtY9EM9i|mSs|i`))!P>=HKak z&o&JO`v^u4ceQK%4W}^IT-8L4H~ylJjW>Ps70}haJRxv6qTPz*5Ol%M=6#A<`lC4e z>U_51Dn-`+gmxDu8vn3X-z7IB`nG2){Dsa{|C5Gmg$R&{&?!4=G)LJG9jzBV)cdg!8v$H=nPzT5I&+x7Ta|G-sjYPw>1qI3#fpA`XSWGzHmuhnb_ zg(dA*IJ+L=Xbb;wz{q6YQd|){v%tX61BHB2t_-)rM2Z8MlW6tzW!aQ^pdGwRkB!ZvATT zI^i9HQ<<-0;+#|B`5Wc*HKjC2VARP=V{LJC4FBOrlk_xhDqn}F5>62vs@dSZ=~%iX zTKMp08d5m4>OLc+t@h{GQ{7S^C@~&f6~zMSJWCgafP9+}mhsq~_tf_+rS)~`9>>#l ztMKdF3N=!}S~FYUnB-#Vp}N_nUr%lXPIA(0vT#~&0DNW6u*$X{{FkpMtTVvxleXv> ztbRdyzGMVTQR=j#O@~Wc{ z0RpyR8SxKTfxQ&C{2MV0ro#>8`_#vM4Bs$?HhlzVZ0}-^X3f_p;d=8Q`SLAXmN0hI zz8uU@3ZCCzdlh-tCXb*9-~(S^XWtIdd(5PZjqq|^d)hN}-Z!jtVKlGykx;isZ#u&R zyad)FyLyxTw6a_lPN*+#o9l6@5aTdk(-Ux(hj4))*wFW;{s;XsCd`L!#0^oBY|Fbd z!{Tu@y6pXS8BxF$|1c@gab>T@j*uW}T+B9eZyxQ%ie>g&A61O#e?f!IgCnc|%ib}r z?8i*z#er=4P&5Vi%@YT5FF}rJ=AwQfe|jki;ifh}__5boHaee+CcDsxGCVo9`5>rb zrAZfhnQ3SnTRgh$GA#P3UwEIgCY3IWihJQ|aQP^7fFAjwlFkx6Nl<-u>1|a5tvKoM4SqzDOz+2M2j$b`o#J0aXYEG5@+e^Ky z3<(h;1#cS$5XOTa62XBrMr$GcHk0C--2J8?_qNa6>q%^-`#~p~%KqUpQ++ZDNe`5m&r5_u= z%H|48dv4hFxdDDw13ibAc}&IB{U}z(Ue|}R!*}622`AO|S!H|vuepa(PnHxs7PSG} z(&2qBkHfvoO=4i^0}W6-dBB(0tT{h$$7WXxMrr7M)DwLhc|XLAP@|AxwEw4ERuT=IRV)uHxAhp^WXYskRrZUJ#@RFbEdDxP{rF#mJcc_R z?NK{73ef>GLLi~Q-IZjX;dg+MSXvV(()bge>U{)}@yBC-wyy||!vCd@HAbrB)Hazl z-z2+?zH}WfeAsRpE8>$O^ruBcd_>{g@_0Ef$Ae<*vp_dg*y#hi1v%?JkqY~}!;pfu z@yy~3p~=wc$;68SbkCo&iMTL`bj`3R`IW6@>(@S4^fh2i;{;r=HHT}Ie4Zu?pvpud z<>`LOLjM5FS(NgN`?Ph5cV|&TcuU+MV-qvq;#saY&sW-$=}jx9$`N}Go)>NKE=tbp zaxnYda?vapI5u@_9?hCy$cpf2*5{KaE3i(cBq!wKHHgg=TFUN@xAp>neI#^$)0qO> zq?JnQ@AQOu=vCnK{N{M0(6g}^`;MccS&8qH|68TA^vK(N#98H!OEBj)G~JLre5Ksz z`^c!`F9N94bx|^yc2uy1k!~+vcj%66mvWZ1dYCH`hm4{RzJCI*C~nyirxiBRDs4!ZQ!@c& z8ck@x4u8h93U2540^njf4+a9{=fN)Z9WS?t&b`E-gKt4*?UQ3-&4(6 zYpOPq|KSz--ob$yi@qpurMTl@aVvutvWQQHhN~`_%J`^1t>;_KOJdwg>1SpF?URcW zh6B3mSD?&b@7RA-)g71};8zG(@~U-HNLU)YwcU`F+CzDR*yCT+f9$Bd5I?bz)AKK>V6 z{uwDSILte%hUXF|qWHj0(V1uC{(UlGR@VAG`I^2T*gb?v2q7tHcLXHa4M9ANKf-A#X%- z*O=V2ckFlUJnYfG5YLc^P{p+vUiV^W2p1aPA%9NtyDnx_=S=H#em$&GlW)H1bz-`_ zXIW!BiIy@`Rh_00fRn~%^!UtKE3`}u2hDho07e2M0UB}g-$o>LoGMSDlwsaA0AIHj zxF3R#wLdZcO=|xlFKK9)#Z?BHxBDU26t!ukNv|Vo0OGa@lM>y^!b~|>ss&7B8h@( znh{J_cJm-oyNQpDUYrm)7t`%b!8-gh{sklV3|?mqWrD<8&SF1jZ7x zAyBf0lSt zoVez>RexVOk!JJ4m&&5A@JKk;pW(-&O_Jn&yIj=?j|aMmhx~CE>30wq(tK5A=u!!u zy1~pv3G3wf+P`^cmVTX=kdxnr)x*L02CM8XTzNC+lkE{tA6p~ z_;Rvf2ANC4OG99md_?sA({=AR&KhIf$}M}ePjb`;On!*0cpphUbj7vc_ElF#q9qt- zCP-(`{CNLUjE3l^-pD4NRF_J^&?|+ktfXr;>ATfV4x&IO@(?@Zp34f?IyB>t|7r7? zf7@JA&tsF|BHHXfEI^jkFm+yw2g+cTIC?C_%WogX#(iECraQA8=@*XG9lJKqX2iJL z(Y-lhoc7ncIw!#rC>1NZ&iIL~Ff7BrFmxBavxz6I5*n`hv?rlT*7%D4-?3PZ*Z@5a zXafI^Sh?cP62u*H@i-EZ%E+tm-wT5~7h9i?ztYH6t(STjqtkT`)-YGEp5)rXw9O`D z9|G~WO)^zEV^yC<_c_}Yz3^Q=Sd~6_;qhy3^4{M`RZ2YgujIe&rvGod(Z#ghRm88@ zmX&}hr7>3WRG8vEyd-_+61O!+Q~Zs-u9`83peR9>S2$+-;v1xZ)bj#Vu#NJ|{%4mn z1w{+86>=B8aH0K1F5mJDes%w*^HG~dGh6qARh|gpYniNliH zeJ#xL?hBLo*vhm#C!Z2$vLsURc%XUt`h?47`J!7@T))Z-`^CII^r{E(iE!h9eRX!i z7S)0{d2W#QKskwoW1DnUQOLej>pIf`pU`U3k%C1K6fUHdiNvw zK~OMkm85d3Q_den5#*abKaa8>)--C{7z~4=tDB`7w;`soh2P^>!g6du1JQV=3y$ES z@ma$>y#dvcm|1D`HZDe#J@xP7@L7%oaTmJ)xneOIijtUXGUy?h)rtnATKGGPYr?L2675h8K} zBd}C?f%qlGYa!S-_4S)2CS?0vDUU%$TSvvi!KN=K^#-`zu|#)m=kyz#ojdIT2=xWL zTTbS3vzpH#P4{|GY0|ES->R%pK;t|$0@`eUGHb4=Jq6Dh0$wb8*Te;N_%WS&<}aEk z?cpn2Fa%yj3A+?V*f;z_97}4wImhb%t{6Q7U-ewM*YdcVx0`cCDtOLcP-=}fSGk8g zli|-x4)Ixa(fB+#P$2Dm_|!BgY%nU;D1}(A*jlxfMXvW%A?Z6bc>q|$Mdd+&960G`CBe3$qG1$6j3 zdTrWl`N`^92gEDNhl;;Q6c!%+8s%`lDg2(AV$36~_2ciSv&)xc%SGqP+x5!n-b+`P zThNsgKd0<#k!EOs!B_gaC7zZCr>Kw&yCLA@#$cS`WwUI0%&*Tbk0(K$qeGedbMm++0`% z!NF4H$lFYXcT}@*73}_E73RMhwQSvI#%SH7p>;Wnum7>fEIo4jVBjU8dp)%efNTD8 zDav`e@O{Q@yfDD~BM1A_EvYVx2xPBG^2c#a4u-Q{5mfH3{JIDuM_P0rKZ?{}mdX`z zX@YhI5}q#{M1!4jW$zau+d$xW(uRm7*MJv)TkK6hWMy%@?esZ8Y=(StA=7?(+k30) zHq|Oy7ZrFm1jLPWdtIuRbEKRBO{x+XN zF`BP4(}&rzYLRT4xOd%ATN^gyee6V&0<{&Vwy=S&8QlDcu$P#= zlAyH!(ua3*(SUmHY-B(iJ@o{XkYM9%h13ft{{arHUuoPS=lbZ)|>`7E2_xzEybxlv3Rl=Hjb08v#=-+Gx zj*?(dh{w?#hd#@kE%hQsikhOUk=)OzF!Qlpk12#rc+3ncXZfM`wh@Y|TNR zBX2sv;h^a3i-z9L&m2j2@A@TbhS@P|s=O|UaU5KNRBZVW8sM-nLoX7`w)?hH?M=2D zDsuA8L_>H~Y{r!>JhnO1*JWUtQT`xQd(tV#ZlzT!Os54Y#F%uAD7@bN8K#tt`D1il z@ZnXzN~k#7Ta9~%rtOXm`q{m=`Uj-clsR$o=a=!HPh_rtmQ+k;v=tiv5UHIh`ehtx z9N_L$EB(`{mLW@#Q^X~KkqQA%?{C|xW~qfxmzGBlwd|c|$n_Wy0^tmKDDa6lLA2PD zIeM=vPr{oPPper(OBV%a2_k-T{POutCKt%n=?og1zA~-Xx5z^%!q3JXAzn*I$p$}z z)-@Ico5{#UhVa_?ppL|Z<6z(m@pl{nMBs})qj|4pzs6J>v%s#+|XP-qjLF?3C5B5X+gF6mqBlt2!+QHqI3Bs)G` zvg|i@BCW}J36ov?d{wj_m)UW$f;U_**zSy}o-m|6F!zzh@@I#?w@4QJ{^s@B^&X;PG z*=?3ddXe;AUyJZ(pMZD0{U7@H4O* zosKH!&LP2!;M}rsT{gzbkJ}_Muc%@Vj@2NvoSrFi0?Hf<;!H0jm{C44Pb}2o25g;v ztbHzK8|KR(Mavzrjdga}-Xx<$PUd*@We*EK!Y!Umcx2&<{E4K_$Hu*tz=Z*KUIA%K zNO7z4T5{>0K!&5}rD*aexIK_C;S9?#C;yl%0-|xji3z2hX6}K!zQKZiz^(!wOUOaa zhi~yGx_L8r{6?ZIn^Nu;JIUNAl{iM9j`M*SR5f~4TkoOr+U!Yajv2T0mv!O?B+>Bo z+^E<~QyqU3+h(uJG(%00Zi9Z#QACT9bumtlPL>W*rk_O?a^u}ynyZZ+VBmAgYLc(Z z-4Y^RJ#FIoW`z~viw@>^&&8>;xZBuvK!iQ1+*__v%zq>CQ3XBU_nFGE{9?ebx_mAL zik2ufW&&$#`WLpQTe}Blli^-h$)XfJ#R*=2i{}gD-T*V`@#{}6q8x>^_Y!UQ@Yvj} zD9ft!DEMl%iY49UZ&MWZm1%Nh0#+&zuadlZGV+Gc?{7*-1Y@;d5H#&BkM2aYEn167 z!D5GD`h$|PFrGimamqwcR4qfaLcjb$OX01F5NrJd_xfPR)Q*5bQiiK2wh#9d3M!B$eOFbzqo;+@kkev*LY6D_0!)f;4E z?BOtDZL1?C{qtLilma|(Wr!SS7?*-?Aji9W#{9>ImQAs35;AJ{Eqj?|o1&&m-moWoF8sYxTPL{t6p-sL_xcUpcZU z1UBb>1{()=$T!rwJ##q9RPP@T}=ud-ggD;jio*z z;s1CbzA(6ud#cO0;~D~lgO+OQ za#Bm?ROsdBjq^y{n>{Hr=L`=Kbrr7`n$gRSw1;~)ougHQ7rjA1h*#n^ozf0UGT*=p zR+*M5FcqV0Y!!buXR#f9_u3X5>;hN}S8&oA6{7A19O@of!Tv=q5n{+(C(2taKS z$YO|>Gy1`RHH}bsEiRDNSDcWP*B;r7Bk4!PMEo>sjMF00_pQ!UN#;n~XGiz;u}${r zDw3f5q>gW?_R9rhtxJsu?)t1-gN6MJewi;QQ)`D*Y#p)l9l5;apwL3uxY<5bQ)%suuNZ)dAJlW=ln4(<-D#V9M+y zT26dxdJ20fr0w@qL^sGEpWd==SKAU{Q+Y)z*hHx`hPV}GDME@ZQOeir$K20i#y{`R zmkPn`-mepL6?3AO6b}t=KMV0CHl*3Vwx^9n?jF}b{#Gwr9d+HI#2^r=dTa$Qj{B)P z&}{#4NR7>m>2IIy(WikZb+DtU`r-@^c4r3eUo0sgR~(yO9$uJS{dg2M&jDPV7=Qhk zMJmZ-)AyGkLR!ghg~_fu;@Ekj;Urj1`>TYrh@==bn||%r#JxzDQLFh8$ZbHh5RDH@ zlda$8f@=y6)lQsO@cd!xaPrMlla;&mm zDIGVb5(66A3@`Jj#Uysym2{axv6fS`yLmRp2Zm7Dm?rH*g`VG+Xdeq&78Bg<&i6PY zLf%p83kTDGRG7?oy>oH9_?RCsVjHI+kADc}fM&<}=H7+K=;7ar9 zJuqy_7#LW3BkWp;gNFB2^b$+hx8_H$d@cLS%1141ZBao!& zF_X889cF(OXNgY{694s9vr@zS>HC6-KlQRzVVW3#dIv+I=r6x2*7Q&&Ztx8O)C|C6 zN$TqC?Hb6-%<9m+DI*OHG)-nUdsfF9SI3LWwb?V5_$+k&MS@)5HlEsRGuf!i$%zH& z9cIV0@!jc#uJhX>^(4z!;m6?EiIRGlI#Uhtd9Z82QCR~9d4;hv1sSAaOM7{ZdSQKhT0;=+i?}&wwu+sxj_zmejISD_Nx^CI>CgnOrAA0h*tg>PN6M%!; zy*Pl2wJ64}-ovr8oQTZVmb69nrSqk^$4`%COOE*u27f$5qs?!ZdhZMP&VN<0@b>zK z>H+&F=@%QR%S0T>rOM@3my(O`aBrp*#3sEJj>rBA#P1Cb9g@(@GhE<;u_^sBfP1&D z&#>t%3XSWS-hN@4l*!lo%|^ns{e7zsqcB}u?L+K$dBzV>xSU*55Fn<+zNXHc`3N}p zRcacdnC!X&RkobEAW?o!zrmd>j^jf)f}^?Cj&nr^LFh*V_^ooK!fBZ9y#HQezhCSE z@UTGb(95;HorMTa8}&poLD0h%}k%4VhVzV z0l{m^x1avcawWDVo;=5DC9z-@+@eaG ztOz`zRy>C)@i9TC?e0XtdfNoBk<~^pX!>CO2M~(Un?CU*8=M4>rPLWT-Y=%L-`%W& z-SKbJbp4Gj_Nfy>tqH>G2qPX=qP7)>!vGy*?s1xG_~W!Yfd`@~b(uim$FlZ@@@X?gp7)DUalNx(k`P z*6zMUdz%kRlU_OR7ZE`x(%L?fh#YK`9-hHyyzwkqkkR6+1XX^e5JYZyV3vOv;?8-_ z>ze-E&A~7rfVfPL>I=SoV1S6UInMa>&KxE0YRy{Ly4q!{4pm(o%G^{?i;g&g1^W;m zXVquzneAGFt?%~0o3B9K_RIYB(d?u@(;*y!PAM^&IFhFJQPJ2FD{X<_ax2!Ay3UQ0 zpH9~a6K5~6dbow-CK!Kl|8)@N>ZY%P)Z&5m!7lm+PTLutR zn3}CA@;a{M)VZq^NLfPA`$91W3T24>yGBqR=V3n4w`(;HY^l8b=aW2uUVp^@vY$|0 zP5}8QuSc^L;a-R6+U@X0*4#Yp;YmeviheApPw(VFaJ5QkT6y(2) z6U4$9l(iTSpe-*X%V>bg{Q%siqu0qUguROMR%Y7sN?n2&NZK`THt|1Y;v0htLQB z{F!f;)se4`(A+j1+(_QOnI6~%U1qklQ2m4H9dezS*c$G4soB#0a-!k{U>S%u>xBzc zYijh@7~;<7;r-=mIZ<+LdE_&s%dY^fnCu_uQK4)P$Ts)gWeIZ6pXK(T z(sMq8RebsK`&3s8PSaJHhS|xX*oR)W&y$*;dR~9^f*}G zi|o`(AWBNLJ&41{jcao`v4iw%&i}TnWfb)KGc_ye>XC5e0&yNii-P;%ZXTB$8EQ^h zt()sNrNUD*U8`{E579WPAEJNOsg-DQ%xLSDa4$C%#1C^5Q7mS15XDeQGXJuMgxMY# z8qsyDvzeWu(eW#hnWv!k5G*VR6(z_=wYuR4#H}i~tDYy#C`Qi2DaUTqJP=Kkf14j~ zA|56waZt6>EIijvz*?DVGx>wbYC*`agf@8&q?&+;GVnu^j9@4~TzJi5 zU#90WaIfd(*XNkZm*ao`G{?Q5C&8R(ws^A_B`cVbQlS z-O@?Y;O)ge%iXFTU{1M0Mljorut15bnPQF6dNx+7TcY?jXUWEPb)ZgX02F8Q7&e7!ZpT*b{9_*IcfAQf`VJ0bgB245+`JmaUXkY#o zo6>9k58P67AgWcCiK5s%irbfv48rR;|4W$Bi1~5hd!dS%WMWQ@cjFlNrGPyEvQwz{ zE1=rSK3rwQl9c>i)MmooVn@gIgW6EG?GbZIQWmxUNgB5ZNt>TT;ftsBxw4lX5i<+= zDC(~pCqE?8%~gk5?Vw{%YTmcCXqcJQ7}x$C=vtkj`w%`Z&NcYEbpF+HRP>W)xyN_; zmRa+KF%%DPtc0!OH(WS%g({5izPdZm%vGZ&yceq1owaErgjFE0yP6lb8Z zQxUogR@>`nKtD`5?2seWos8VZBf#uEMawDOJIra@8PTaNOuby?@T&!U;Ez!@-$157 z73^VZy842Kdh|*Wa*oD>1C4k%^y;cOWFMhA>oGn9a_QO-3gDQ3GIF?-_ef^GYc_?L zWc>}+jw#xy-La4RU{y2Rp$cezB}k4_%_)k2AjHjk!{FYeMNWA+G#wit6cfb0B$uye zhJf#AxFNg=@G(#>gPAmFh6Vx_i4B^7IA%D=5m=vYnGCM|M|z;9!?!l9j&>?6xtM|g z0cHDTHH+Kg!eo`bB3l}57FB-2le5@W*fr;NgJHb{#(PfIEIfoS&aAftEfItMTcQGK z@!I;Tl6$b!f)C@g5TI=x&gl}Bg2s1$yVg8*2V3(lo&D{X8YO1m>r_7*K!R}1*IT>Y z$*M@a;AOt1!@Y8gsD-LBTT~6_FRWOq9sDou-a0DEsC^d(LAnG)8tIlUi4l;H77&mQ zk?zi+kp^igX%JzgLAtx7b3jUR=p1Ighxd2hv%de%`TMNJS`5Ri=XqxDd*5+g*S&W( zg->!?{N>iUqtdnhey`XGX1AUcU5QQgLk-Q{9xJUD%6(qeWDKE^N6z@}d6@od0J&S5 z%N;LHZ+^#w!kdv#TA-;m;-%UOsZ=Qy0oZEE9M*bF?P{-RY!kQc{E0H9CcsC z9jzWfFtGRma-P*_KMODz5jIH_9;M>-5b?-UKv?I~3BU?8jt5=5Dm77sdcnMDpx8a! zuN0E4hlCd+RX4xJt&-oG+gZc()fYX<>3m`|JtQYpE*Arbp8Epz>USZi??3SPM=RKC zz5rCf)t{B;FTLqmc+^J9kKJmaDntue^r;{SXN)Dd)>52kZ2+v~puDCFj#rnkcEd_pOim@*3lPw} z4nAH#QoHGF3MT$^q6$gx?~Sq<_~xU?zf1Gc>E;2=noc)h%Aoz5LTo0DdWI({t`hua#xK zzk&PqEiyfbB=^a_nE4v?K%RCBP+`Rbv4;*UT8#SYVf+OuIjzrjQ#?RM2QRtgI*MV! zF1sz19!XLu0FqU!TZ)mdEk!zc-aa*Z)f$-(tW-M_al7Wn3}ihks31eNf~Rx)zgmEq z8u=>EIt$%ovZe`s0GiWQ9BYE={0!hu1BLMip!Za|Ups!+RD9KP+PL`xG(%%IkCr&W zMB#t%AtHhLd&aw8U?hT~l&62w#pu;%ir4NzT|Job2if^)YSb+fg{QmuG+E}KV=r#V zs~bF}Tvt^GR$>I+iG+TVl5}0^r|SfS)sR+upS9}!+d&VJJv{^Qy;HvE%<7Mj`Sx}x z%k&sW*Vwf2%^w5n8)$emuirFhOII~m z`@Vv4Q+a(kK)Q#_- z7WfhV=eJ|?p1@rf1R3|+IZ`JxCo829+h08<>vW(6rPN?rgeC#84*E-D<#$)Sbg%{q z2od=0+YA8bQ%Tj2fZzB^%RBCLk`zFZs5?<(_mejgMY=9g<0;dv8>>egb`mz)WGHv ze*n|KNSAyCJ$CySYBTa~^h=)<-kK0(mjF1f(Q}Qk2OsTI>1ey!Y;jk%6OT%w=iijA9}RS?kF#2tD7DA0w4&z9Py!~w7hpzK+keF8 zvI3{4t3ZEd>ZX5N@kY8(1OX1lvsrZL7EoM)ZSMk0HGjxAvdm3NyB&ZX@Ga<-n-eny zXB&YWO3d@NXc7#&MG``rOejR%FMl#p#-R{JHT+ySM>fr)_>MyldLBMGE*7=BPUqY` z>fHfd{ys$V41E6BA@J(Pq{SO?yL(G#a{ukgWB0%vpeJng3xe?;xKFCD`q$FZ`s7rN z*RpslB+tX28t`<)0CLg~Am%@AQVF{8B*kktCTgr@fjIvF(eT5aGpOo3V7F$ZXrpkUHpjDgkBN^`bM0v|QsP$4vthv6!h1xxfQ*Lig-mw)-fAo! zb=6U0I6q3HEO3k>SX90*xO$@yzV0_rRsE4bkaC1h;&`xU<}wGM-zKifn@*x4fPjGg9|w+;oD{0(Rgu1ECxlK5#Vx*bsE+RJ#10#g zP(MAu{T?k?!acN}s$&2v04VOWM4YAodZ7|&e3tF2#_|9GBal5J$9eDLw*wJ$>1f`& zM<%mcr6z&*Uk#eTO?iL?FZ$jZy)o*A{C;&(|KfTIkV5Zx#6yhWA9KM7A0dge=;M8f zn96i$-wEf0GtejTcdE@#+Z~1);I;O5diHE&4}I0=UiY$*r#ZaTU|eh`d!**6ybz8z z%&UEnMg*i07ZxbZ(*oz*3i5F<1Rrm^V>~vL_)fr(;(*Vyk}H^;HnQM6!oxjhUwvBG|X*GnB9$@|dCxM4;7qTulC%lP)&IAZ;+BPo@T#cEtp0<0&R`fK)T& z08U}CDHEAQk#ap6aA%r3-Vxx%(`0;*xO#w?q~Ya!HSLUxk}|3R`7o>xBa7UDZwZkdLqS|QJO+`e3$n$bnWs3ul>4Q z-bgo89lhXxfEW%wi?7f7H}N8FlMh7>5CZP{(M};3ZAbw&W*1rT2~~BU7|^>5Bi4V4 z>cZf55C7IAlV5YCQqiYVXUtbipfy&Ub0OBxCR~?U)jl}m9*tJMh{ zADJm(K-wTKyzO7oFm$RkVlr65Mn)z~Y~X~|ez%O~ep$yakyzwsB);46AbBU!ao@+s z?;FvbR=IrXUif!9hY<+1nvU6FT3r`UfBDl_wccT@t{%u|@^3O~4>KDh=3itUXJ2L` zude^qLS0^^O{mw6R7Rp>e3@pJ$8Zz>Eu&`W^pGMud*i~Os9jW|J8$X zbL?iafeZFK_$p%fXyD-dRA7DF&Q4~1VD%_K;AT?p51D}RCT@@`CQ@Z!kAGGY9@}|n z&W{TrZ(%{>41&IP>Gin6M(DwkkE(M>9h{bvzJpg}xxwV;ygOvzBvKmSd{4dwfZzze zo)NlcgpSc@=^$&Q&I*dY3XsSW)O(U(_1&zAB;+8#L)&zYPOvrJh$p2f7WV(JCL|ta zqK9B{D~%o(e+eUgaXy#FB;;1kvtRsegjsN;*G?orfl~|YhQQ0EzvPD{Z@~VdcIzvn z#BJ~9`RldfbffZ~${YYM!pUUX>qUtd=p@%;z$nvVYZ4;}j!c{BOW8HJc&?*EX{;1IzA#5@Q&fI(<< zJ41rJ2`%5&`Fq>(J#O7@TuBJ0dUtL0$N5o~&%Y4tU8nXa6A6g{tM5pWB3DL3pm`w> z=miu#Ur}zlIpsNBin(72dC>$m_ExDJB*QkI5eWFl3ViCCiJ}k)(o~N3*|~SO{)0fM zwFCtItx#jXvC2EbS4ggFKrn^epPcx&C)uIn3D{Q{TzTlRr3Uuc4blWO2e#eEOFG)F z?t5$xA-gSX7s+mYciVk;kD0v?t%EQXkCQE9wcGI9P2jrk+ka)PNr+vH?8SnD%%wh1 zu}dtneI_P7FSqGfzV3ms)(qvFP)0X~jWxpov8+6^LIUsWlh-`RI}`7-sDW9) z?kBPbM3G0ZDRrsSCg0n6Bp@<4U{XjpS#g^vyDNI`R_$=#clSF1S^z;7s_#8s`_*y!Zvr{<&p>4t-Zd)9m>|O6n?tN^*Vz}S&YTGA|QwE3J%}u#LDl_m~9+|e3v-!=TRkkx+0XM`lv`~D$ zpPWKv<00oY{_fWi&`LWj5~^;9RLJ`i$NQvxVJ0Cc@EnXJ+#WJ8`TRN*_9;4qng^|{q(P8TFYmyv zymSv8H#LMtr~k3V)Jde=GS-?wsC$n?-ZZocRwY*fpB48yxOQp_eF1g{JvP~Oc6d~6{?T(An$3XJJ&IZ@w+hgWa@I0&4oDY;%<>shNxQIC%N3`jmo zwkt%i0&f?*IwU({O;y!^w-`g&ebo=P_gG7z=*{l|F#P#%fU3ssaW`KM#hH9uJQEK}mX_YI&sdfZLedv^`Ho_F?a>Jc}T~I)Rp4=`{iE<#En*iNK zJ90j01Q}KR!2a*G1fMEPJ*@*W;!K?&KwepF_&@SW?7r80D!9{ifWFe^Xz|M&AiGx) z&g!pw0?_{D#Z`I#eG-}4U-r*BHy9z$Vm*kX>xi*#PP{INCARSZ@}r^9nHuv*G`6&V zDE5*^xBh=W<+ljScTX(-&)RsY&(UT7y-~<7-ZbVXW&amyclOK&c3_;csqK|kN>K^w zpq`^W4h=9JP7$5tHc9s<%3@?^%gJ$6rSHC7BLl7rJBLkp);#W=s_y3apzoln@)uyA zK);&ml)DZtg8xeaJdeWc09nYzp3CfzNMLzlu`=4P5o@O-f)i`hg_kZLmMg0m`VGQ*!Jj?G~ zbQ{*32O_x>ibyKc47Ot4QOnhQna35D7ArFM5Keh`&vRD=3P=O>3pxMk9McVmnF#O+ zgZm(DW~szP`Zt#Z^kh_wEnFoqoS$iQ`n>sy&FCujN$*t&rGH1HT_SUabAiN@*$&I~ z;(g?16yH2nn8ngXZOn>J9;BjT-o%)uY;G_6_iwUHUcWz?ff*> z9RyH?#Mdqj4B$5+G8=oHt6Zt_*9O3| zTN8YyY)1G?R5_I)Km>K`OA>yfSNUyA@*M|p9XfB&q}N}l3=-W{+Myuj>Q|nvo2|Q< ztmzB&7 zysB^Bv%e_Uwr{_@yw_>1JrJo(cVO4S{9_gJ@+T=U5qA$w{V+%duf7yr!a?JQ5yM#V zKL%B4sNShjm{)P9O$bRtwDkeO_6-0iLuot~uA;vYH>7A4Zo(0b7XSO8t}=K`0bW;Q zA6=(R9@@8xvqm2m^swW|LUAfH&^4hqEacJB9T$KLI+8MlayBZo!Lx|3_k1^D>9Z3e zo5ltv740D7$8Yc0e-!yJI{_X0#QH06|6_po&jF7M35t@}AycmxKU_XNzH~Ls^{}ry z=#;&H%3Xl@9|z#|#96;lwT03{+ReEb7Nob$V^N;&GO)_RhbgVn8f%R^aK6 zr)(dP6Jr`X94}+U3s!M@UGU!!I&o))9J?A?Xo_jEH(#UMWa1O`L>=e2y z3p}O>yp+#_+!;08t=h}O9xeZx-wZx$VuB>NVXC74C+6-Z7NSMdfm3z9YgCx0`C|ut z&40G>#vbZW51pH@UZel=q}o%ER$3t#$425gwkXjj(iBGZ5H`#kH^_^@Mr89>&k+In@mpVQ~ib0=JBB^6Mc{hIL( z1bUd@|A;It@gsdOk$N!E#N^GQ?5pOC#iwNvkP0!=cbtJ`aMO|&o6`Tn&J&>d5pN)H-FL(Q3n!253{rXfPDckwM$mL~YUR2gc^AL;iI= zu{0DLsI#sj&Gmx!LT+UkEuMDY$nr&D;e`Au@@BlESi!U{xNDBPN4Y9M$vZmG@^i+| zAf!4jyzYqz#`jN(65_S`0Nzy%P}WY!+CW;t#tOGykN2!WjeKFbdms5wFsQH{864iF zJRg{wdXM%}?n?pF*QK()l)s8swcPhZFo_+#hE1-t5GJK;3DCfJ%%)z0c}$HZciN|< z9by0IudRwj_R36(Q7^Gfc{A!uORu-r3}6YrX9HSFtx7O6@H6gm1WH?+pazXU90R7I zx=>vc?rS!C*44QO!Ng(p-BB=atP{4)#n>IB%j6d82*(t=SaNWaBl1RvBX~Hgo>(a= z`sKm=3sG9aR^15cr{1*XdKgxBsefKz!!Knh7hwdN)jG7Kgg4ylBEevM{~wVxQrmg= zS%~@{)FV90r?7h3ZS9BhtEZBoR*3=mt$wtXh*-?EYx;Yg%OTt7inDL_#iym)CVyh! zS%VtysMx2w^g!H515RX?9m;E`}LEsAml%vv5vlY7|^kyvBx%zL-#u_rw~2lrxUT5GF`#*GxSPf^1+Y) zrt2WrT}Xe*@|o~NjQrnf;URL?*H7$OOOT~YG_o9KER`OhXUOyG#CNJG-Ch`?>i1k= zjZ@{RGs>`ks#71X32$LfPkh#EJJ;enX6H}>&CR?2Ng!(jKXRDK4R+|-7t8pkxP0Fy zwKr&N$RoU%I3JX{r>zY1ObW2WIL<4YomduPse?mI5Xhi3*3l;jQf}ljGt!`D@)c=( zk|N<|^Q(F#Y;o!?LGI=?!e0(C@-%ooFTedO)HLkom2JA*r(|{_xCWh=Ra?Q?YSUj$ zmZcKqE^AqDWWju67Tne`xWv)1B<73XLp0g9Z_i#xmCBOLceTNM^S&_OVTBit9sa7C zHr?qayahTes%=fwR8+__RpbS2nSox*iJSMw;PsxrF@#`w5L%pkZ9XY{UUB__>1@D& zy((J$Ci7%%*AGR4{stmb_rS}^Y%ZapMpI6${p5N!767~+u`d~tY@_Ez-~aD z=6BF$2S?xVfoNlJ_B-8!KPi#Y>MdwbfyXY}*FcNfXDwKpn>arz9uj(f7r&HDsA z)!8<0YXxu;i6c;iS@D6ICI4RwqvKipdQXlm@RhZtB;ND`1!P9O!V>-ASy486-22F5 zJEZV5RoOQ9;S54th;AgIk7zVTh>W3V=X*t%5l3HZLBzRUjRvDmdE?yX{IuYjip1VZ zuOUa|1`WHF4KMzvH*#vbmNa+%M7P*%lkJEYW<+-hNsS`T6nK$jPSy7+=KFAWrV?hR=As(Qew%r!bqP zjFNYvls$F!l$RU{ftv?*bJyJ0t+GXt*F)8&n zA9#-%eA@>ys5YEc@XGbC7#vqkrls7jylz+Ue;DC3DV(wvY%BPTbW>y&ByV`iz0IA0 z_cwu+C6aY`Rk4M>eqM3R7h zUbcJ@{JdetCr1+t=oICUu&w>;N;~?gS6ZL`3r8|E^Z7XJ3J-WA?At6%zHGBr)vhv2 zdc@TA0Y*>*_o+y^d&PffpF8I}JZ2iL^Cb(H`tpECTlfQXaZpODKjGboA4$Gs)~;<) z(pkMGomb0_uRiVGMl3eG;j*sa+d)1Vsf0q>vR$+J-X# zQ_KF!5O6k^Z;2ns2ajljN~|jCQY^g>UqV`Mj4%cW1pE+BG5V_tjVmI2QJIk&1>eLG+kGtBTkVa(IYJO@sZ|S)IT)y9_lYe8d!Z_hT zl=Q5#Qwo^T1uit!4UX-YOiIy-r(Wyr`&71R1qqmJTL0Q|%Th1ghXFTrsm{CW{ zpPFIDya}Nzuk#;~Se*Z_7T|ei(zwi(?9&^jlLN%{LtP{phHRHM*k>@1(tLZY#EX*i zsKbNvgnA^|3cWdZjto1h(}il0RSfC30S2jDpNx)FUcc`cMX!P47ftO>RlQ+JjK~Ys zHrK_;)N*Khexn=M;a+vW#J1@nu!wgH6B6{4yoPf5rXn#D4;B&3C!9aIka=(8D67R~0l{F-o96I>smZ zJI~NF_+p)?ygd--cI~RV+3*hIiYS_SNJj_i%aA;Tg%|4Wi?`9NeTEBojv&AYsvU5r zW8XuDd;4<_G$CZadiXj!<9k2pyqEQD2Exc2qWC@vijH7I&vSd}7h~p}zG&ClYwmy`4;VjLXf6UPa;^|(bS#FBrtMpt9VCi{g=rM!?DRYdngM617KZg6g zm)E@)uI-h{n14kPeude~xGYl5+lrSxo6(3%KWjo%ee6{2?n?-?s{W0wg9^_DW1GUu zI0=$PFBVsUf68^S9RaI2C+yR-cORsEpXex0wdbIxwud4r-r0rP6{FLBuRq2_252;c zYYGi#b$b+LIrr0_%fw?p|JwAv$fi;b=l)W6(|ZBZ$bTG4P1u?+xh5rT_tP)%))t?% z?s`>C07FCz(ZZ~O?J6|f;k)*J9eM{7-$nw0ve3ac)Rz2hlbWQnj(m*uQxdOeMF!PM zeHKa4-tI%ZfW1J#VVJs@=~P_{SW|*?cTLIp_O`3JB<7#er-$e$$pPlYH9F2K_&ZX+ zUWF-X2UuEt$?6gf8=;cKupZudH8%SUvcgD5W-{#aVO%KvSLzdzd2+pycU)wpn(T~W zqzSezxf2)0p_MfnxyGFL^8svmWpvqe|nQ2uqKM%R|cWxD}J>t zep@(2*g3j25_=3%kyunebOv3@hn5RKcYA5PqBw<~sIR!BqmYYxGj3>Lg(>jmSzi6+ zL=mL)_>oGSuSndO1YF6q=u-}3lwh1~`C^)Fu~MZzy`*S}7N+ZX_pDWjON!<9JuNUI#_^n3-Nj`+2|2!etC~v32HPRn@UFouRr(q`@*-!AE zy>2c9M_q$+9I%$KU6+45^Np2HSHW8!-kM{N=LSY>vL2IeC{iys@XR zV&yce(xllx8xTr?(fC#Ls1|1X`FGZ}6P@OiR#{x{X5Htu)YDS_&{x$fxXzMCwOf`A zkO$5w)jc%wQzaYhQF8j52=)F%GdH;+>G$U&W%Sc+UcNI` zVSyc^9F<#C4PgHclgQVad>@i8__oeJI1mk5rN3$DQ1c5R*YlBpy|vI70Xv-`&+G8E zrHZO`KQ+TyZG1p9>$_8D9na4?uxa0jeKs2C@&GETf+oSp`7)yTew!IPgeM_8mw2oV zd6SIkqS=1^>j*r&Pc{gKIK+Fy`7V#_h8htxn_i!G)TNxw0T>aQUp!Gi;<+we1UDZ4tlsrNrEOw-vrRD%rQ#<*X7tJA+b8JD2%Fkpypd)RWx8jAww6lGHkxP_ z+8ob`1JGvdp1Yo>e;Z0_m}V?m?r2$tF1|n!f&Wu)@Oz_55*Eq2g}gGZrLQ<+s7CLy zAB<;AHin`1=vv+x#4%~2YS2pvZK-&nH6s*?6A~D~aL(13kJkYrXsPssOR0F$*Umo! zx9~(18BZ!YpKamQhYF(03|7804%H4Kn3T*i!e2~}>ukeSEOcoRVBqM3Wc|VXqlH?N z$1y03_cAYF0hEi5;B*F6UP=il_W7Wcwto~D;BMdJPyunV`E;v53i&Eb4b+PpX7N=G6wZz2_M)!o_A>flZhaHlfVf6e%m^~5@CAqT(l z;tuom*|j)%p2hc;>5gQi;gg3k@^x>pDdSG}-eXkDK2wo>9U(a{?ft44{^ax%<$Nkq zjj~?T&F>4aD-U%+HJ15PG+rRnnnDvHQD*v>-SM^!NLpY6yhRTd--N5QO7(|@AmzGe zD5^aJHriO>GEABedTHU7%1-N=j!AHMxanW50&KBQUjzPRwxs0rof~q23FS~G93TfP zTO)7pBd`^c`R{FQ!+gcSivd7@GiMiz9VsThv`w-NY(Jv@fH__FtNR&{_iB2k1t$eC z-Twhfb7ECIjiHGl*R;=NTlr2mC=Yw6v4PWhT+Zw}FKlKi7B;-6iK=wZ7Lu9{2Ue)_ z6{)9FdggdL2?om3)Ah1DdOJ^y?eG6Z%R`80J=xyY*YHi9ug--Cb3DZ$pzb@D$d;$+ zslkrqA~5vgd|@0IuX)9QcJFwGzf9h&Y()Wh>7h@Q^yuL-x!-`?HzOw2E<)%6t4W|=^Rg5naSny_4wHgeLm8;0NQkIjF*L?=|$DJoS8)yrIcQrmt zn5|3xY>i;_hUUlFYb3Z==| z^n9xpi+(_Zpd1}xwVq_so^0qK$vOPn0x$Lix5J;66}f8R-`kFOTYOA4h)%#I1|&SG9j$Fq79ACF~zdIP5;S)%(>t|=2+iCsc)gq zgx_~(Y_xGm&*@b--v%O{BN5zmI7#cHSNLs@k#BQiu;n87dl(w`gW-oqU6tdF1f71){aXNn>K^~sJ zQ7%`jIU{sCky_oaPG#fiwdMRlIH?aouO#X+TW6@eDw`~gHd#ZRD@nh1BI9HaTuo`m zl)Ce^y~Kds6n1QE)?J}+GA>g|b9BGcf7smGaZ!Gn`RteG77cR1h(Dy*DRJU%bDJl~ zi$T(sL;O=Mk0GX8s&$DmUpOWQKjtlUB;1$v;k7@J&HBo<`Wo)-&sYupj1axAOMe*B z=(;Rl4m2s*nVcrjT*00{L6DrJ61GJsiuU$&AdiF&8fhZFr-9!=&b-9VpvtjnX-Lj= zr0OzOyAJneGePdbjWGzMITgkJa~;yd=uzV~Sg?A;)mG zOO?R@FP_B2Mh68f40-v&{|xN}LDkU-zFlsKDvoG|J%AX`+;+YHR?p6(=Qf?cni6sS zGBb$^fS%XToA8p;YiO+3-GMN^VPm{yc=JW{$`ETVZJ(PFkQ~G*C57+d0*K|y#dH>N zP*3MS)SXge`3TbpAmN$e5W~4GuFCD)S{E(ui$UB;2WvsrXDY^iiEvV1iZ3t`$7oeB zm2bVskkI;kILUn|>CbsSy({W@*hJLb^UEF>nZ%r|63q;g>W8+(p-*oCkPm)GSFEjP zj`*t__Lm)Rx?9izPZooJq)47%FIYN^$lF-?CDklLoNbb|VDN#@=!Cl02ZY6tG)Es7 zR@dQ{@+gLmM9ZcAy_i9mBl6g><8SUgh)v+;7DCVVR5P~C?^|y>q&h;X}wz{PkUe%abt&9yhK4(rMKNdEKw4+IX#Hl7D@r6)WSy=Fd zNDk&I0H6Fk`o|~F1SaN_?nQl9E8d_plZf?M`;jG7UjWqzgP3m^;t!DIj z3~$AP@qn7CgIBIihfzo{{$K`M;of>iY1HJka%GAvmNeT}CSv8UO2r>XhZB3>GW2(^ zkj|RVU7HJkxr{TXAnTngTiL28A=EjDC8Otj$@^ur0A*Rsu5@au}@k@^l_+vH>>*!6Z|1Zp(gA~UTQ4uO`8hm zDzO}qYH)}E@Ij7DZBAwk427}}!&m?n!^wZ!10#M{RL%I#+)trQ_Z%qS_lDfGV{li$ ztqHiAcbWnVLYvd_Gf(Se1DQ5CMyh^jBCle9L742jZ26pXo(GyFy_!92?pG>b59#Ph z2?RhQvOrM9bn@rk1)r?$v{iLn!iL(OJrqgVLKMO+6&Ac69LBaJta9&7bZ3ND16L83 znxksO>Isz@__uVb?VYuqCRcIB?}fMg9)2x2LGP}K!XI6~0YnHNSiv`GerRTwnKyx3 zuL=4}lQzV*UG4ft83VtpiYMX7L?72KElX}f_9uO~W{%z#@dt7wKciFN=px{+JKs%u zIC)i6bAR3mV@NfnqFB3t?Wtx$hucM;>mg@UPwlkso?s~dqABI4I3=p#0nQnc

qi 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 F{{z@D;N1WK literal 142827 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 z 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 ee_-YPv#=A%f>YV-d@>05`6#I Date: Mon, 12 Nov 2018 03:56:37 -0800 Subject: [PATCH 017/174] Update accessory_vr.dm --- .../under/accessories/accessory_vr.dm | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/code/modules/clothing/under/accessories/accessory_vr.dm b/code/modules/clothing/under/accessories/accessory_vr.dm index 064f5b37b8..ec3a209068 100644 --- a/code/modules/clothing/under/accessories/accessory_vr.dm +++ b/code/modules/clothing/under/accessories/accessory_vr.dm @@ -27,6 +27,21 @@ icon_state = "collar_bell" item_state = "collar_bell_overlay" overlay_state = "collar_bell_overlay" + var/nospam = 0 + +/obj/item/clothing/accessory/collar/bell/verb/jinglebell() + set name = "Jingle Bell" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)) return + if(usr.stat) return + + if(!nospam) + usr.audible_message("[usr] jingles the [src]'s bell.") + nospam = 1 + sleep(30) + nospam = 0 + return /obj/item/clothing/accessory/collar/shock name = "Shock collar" @@ -164,8 +179,11 @@ item_state = "collar_holo_overlay" overlay_state = "collar_holo_overlay" -/obj/item/clothing/accessory/collar/holo/attack_self(mob/user as mob) - to_chat(user,"[name]'s interface is projected onto your hand.") +/obj/item/clothing/accessory/collar/attack_self(mob/user as mob) + if(istype(src,/obj/item/clothing/accessory/collar/holo)) + to_chat(user,"[name]'s interface is projected onto your hand.") + else + to_chat(user,"You adjust the [name]'s tag.") var/str = copytext(reject_bad_text(input(user,"Tag text?","Set tag","")),1,MAX_NAME_LEN) @@ -193,4 +211,4 @@ /obj/item/clothing/accessory/medal/silver/unity name = "medal of unity" - desc = "A silver medal awarded to a group which has demonstrated exceptional teamwork to achieve a notable feat." \ No newline at end of file + desc = "A silver medal awarded to a group which has demonstrated exceptional teamwork to achieve a notable feat." From 9dc9304204e7d98047177767b3c6c1583f05170f Mon Sep 17 00:00:00 2001 From: ArgobargSoup Date: Mon, 12 Nov 2018 14:21:57 -0800 Subject: [PATCH 018/174] Updates spam prevention variable name --- code/modules/clothing/under/accessories/accessory_vr.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/clothing/under/accessories/accessory_vr.dm b/code/modules/clothing/under/accessories/accessory_vr.dm index ec3a209068..e11bc26bf1 100644 --- a/code/modules/clothing/under/accessories/accessory_vr.dm +++ b/code/modules/clothing/under/accessories/accessory_vr.dm @@ -27,7 +27,7 @@ icon_state = "collar_bell" item_state = "collar_bell_overlay" overlay_state = "collar_bell_overlay" - var/nospam = 0 + var/jingled = 0 /obj/item/clothing/accessory/collar/bell/verb/jinglebell() set name = "Jingle Bell" @@ -36,11 +36,11 @@ if(!istype(usr, /mob/living)) return if(usr.stat) return - if(!nospam) + if(!jingled) usr.audible_message("[usr] jingles the [src]'s bell.") - nospam = 1 + jingled = 1 sleep(30) - nospam = 0 + jingled = 0 return /obj/item/clothing/accessory/collar/shock From 32813814c82d41a503adbe8c4f7fbdb539603a28 Mon Sep 17 00:00:00 2001 From: ArgobargSoup Date: Mon, 12 Nov 2018 16:26:29 -0800 Subject: [PATCH 019/174] Update to scheduler because Aro said so Lets the callback cooldown for bell collars work. --- code/controllers/Processes/scheduler.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/controllers/Processes/scheduler.dm b/code/controllers/Processes/scheduler.dm index c852264bb7..c68ef116b6 100644 --- a/code/controllers/Processes/scheduler.dm +++ b/code/controllers/Processes/scheduler.dm @@ -137,6 +137,7 @@ var/datum/callback/callback /datum/scheduled_task/callback/New(var/trigger_time, var/datum/callback, var/proc/task_after_process, var/list/task_after_process_args) + src.callback = callback ..(trigger_time = trigger_time, task_after_process = task_after_process, task_after_process_args = task_after_process_args) /datum/scheduled_task/callback/process() From 91d1701130541176359d835f3e387a1a57291706 Mon Sep 17 00:00:00 2001 From: ArgobargSoup Date: Mon, 12 Nov 2018 16:28:23 -0800 Subject: [PATCH 020/174] Better bell cooldown, lengthens cooldown --- code/modules/clothing/under/accessories/accessory_vr.dm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/modules/clothing/under/accessories/accessory_vr.dm b/code/modules/clothing/under/accessories/accessory_vr.dm index e11bc26bf1..77b1da3ef5 100644 --- a/code/modules/clothing/under/accessories/accessory_vr.dm +++ b/code/modules/clothing/under/accessories/accessory_vr.dm @@ -39,8 +39,7 @@ if(!jingled) usr.audible_message("[usr] jingles the [src]'s bell.") jingled = 1 - sleep(30) - jingled = 0 + schedule_callback_in(5 SECONDS, VARSET_CALLBACK(src, jingled, 0)) return /obj/item/clothing/accessory/collar/shock From be9950051fa293ffa4a4c3e680dff544877a0d06 Mon Sep 17 00:00:00 2001 From: ArgobargSoup Date: Mon, 12 Nov 2018 19:42:12 -0800 Subject: [PATCH 021/174] More elaborate collar tag functionality. --- .../under/accessories/accessory_vr.dm | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/code/modules/clothing/under/accessories/accessory_vr.dm b/code/modules/clothing/under/accessories/accessory_vr.dm index 77b1da3ef5..d5a9aa5606 100644 --- a/code/modules/clothing/under/accessories/accessory_vr.dm +++ b/code/modules/clothing/under/accessories/accessory_vr.dm @@ -6,6 +6,7 @@ slot_flags = SLOT_TIE | SLOT_OCLOTHING icon = 'icons/obj/clothing/collars_vr.dmi' icon_override = 'icons/obj/clothing/collars_vr.dmi' + var/writtenon = 0 /obj/item/clothing/accessory/collar/silver name = "Silver tag collar" @@ -28,14 +29,14 @@ item_state = "collar_bell_overlay" overlay_state = "collar_bell_overlay" var/jingled = 0 - + /obj/item/clothing/accessory/collar/bell/verb/jinglebell() set name = "Jingle Bell" set category = "Object" set src in usr if(!istype(usr, /mob/living)) return if(usr.stat) return - + if(!jingled) usr.audible_message("[usr] jingles the [src]'s bell.") jingled = 1 @@ -182,6 +183,8 @@ if(istype(src,/obj/item/clothing/accessory/collar/holo)) to_chat(user,"[name]'s interface is projected onto your hand.") else + if(writtenon) + return to_chat(user,"You adjust the [name]'s tag.") var/str = copytext(reject_bad_text(input(user,"Tag text?","Set tag","")),1,MAX_NAME_LEN) @@ -193,7 +196,49 @@ else to_chat(user,"You set the [name]'s tag to '[str]'.") name = initial(name) + " ([str])" - desc = initial(desc) + " The tag says \"[str]\"." + if(istype(src,/obj/item/clothing/accessory/collar/holo)) + desc = initial(desc) + " The tag says \"[str]\"." + else + desc = initial(desc) + " \"[str]\" has been engraved on the tag." + writtenon = 1 + +/obj/item/clothing/accessory/collar/attackby(obj/item/I, mob/user) + if(istype(src,/obj/item/clothing/accessory/collar/holo)) + return + + if(istype(I,/obj/item/weapon/tool/screwdriver)) + update_collartag(user, I, "scratched out", "scratch out", "engraved") + return + + if(istype(I,/obj/item/weapon/pen)) + update_collartag(user, I, "crossed out", "cross out", "written") + return + + to_chat(user,"You need a pen or a screwdriver to edit the tag on this collar.") + +/obj/item/clothing/accessory/collar/proc/update_collartag(mob/user, obj/item/I, var/erasemethod, var/erasing, var/writemethod) + if(!(istype(user.get_active_hand(),I)) || !(istype(user.get_inactive_hand(),src)) || (user.stat)) + return + + var/str = copytext(reject_bad_text(input(user,"Tag text?","Set tag","")),1,MAX_NAME_LEN) + + if(!str || !length(str)) + if(!writtenon) + to_chat(user,"You don't write anything.") + else + to_chat(user,"You [erasing] the words with the [I].") + name = initial(name) + desc = initial(desc) + " The tag has had the words [erasemethod]." + else + if(!writtenon) + to_chat(user,"You write '[str]' on the tag with the [I].") + name = initial(name) + " ([str])" + desc = initial(desc) + " \"[str]\" has been [writemethod] on the tag." + writtenon = 1 + else + to_chat(user,"You [erasing] the words on the tag with the [I], and write '[str]'.") + name = initial(name) + " ([str])" + desc = initial(desc) + " Something has been [erasemethod] on the tag, and it now has \"[str]\" [writemethod] on it." //Machete Holsters /obj/item/clothing/accessory/holster/machete From 552e040aad8ecfe8832916f62e5f41b52e518e59 Mon Sep 17 00:00:00 2001 From: ArgobargSoup Date: Mon, 12 Nov 2018 20:54:44 -0800 Subject: [PATCH 022/174] Update accessory_vr.dm --- code/modules/clothing/under/accessories/accessory_vr.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/clothing/under/accessories/accessory_vr.dm b/code/modules/clothing/under/accessories/accessory_vr.dm index d5a9aa5606..25148b8813 100644 --- a/code/modules/clothing/under/accessories/accessory_vr.dm +++ b/code/modules/clothing/under/accessories/accessory_vr.dm @@ -184,6 +184,7 @@ to_chat(user,"[name]'s interface is projected onto your hand.") else if(writtenon) + to_chat(user,"You need a pen or a screwdriver to edit the tag on this collar.") return to_chat(user,"You adjust the [name]'s tag.") From 98433abd03ef5bc6f0fba2ffa2a3ac6a7af7b708 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 18 Nov 2018 14:28:40 -0500 Subject: [PATCH 023/174] Code cleanup Now with 75% Less //VOREStation Edit --- .../loadout/loadout_accessories.dm | 18 ++-- .../loadout/loadout_accessories_vr.dm | 31 ++++++- .../preference_setup/loadout/loadout_eyes.dm | 2 +- .../loadout/loadout_eyes_vr.dm | 7 ++ .../loadout/loadout_shoes_vr.dm | 6 ++ .../preference_setup/loadout/loadout_suit.dm | 2 +- .../loadout/loadout_suit_vr.dm | 18 ++++ .../loadout/loadout_utility_vr.dm | 2 +- maps/southern_cross/items/encryptionkey_sc.dm | 2 +- maps/southern_cross/items/encryptionkey_vr.dm | 3 + maps/southern_cross/items/headset_sc.dm | 21 ++--- maps/southern_cross/items/headset_vr.dm | 20 ++++ maps/southern_cross/southern_cross_jobs.dm | 71 ++++++--------- maps/southern_cross/southern_cross_jobs_vr.dm | 91 ++++++++++++++++++- .../southern_cross/structures/closets/misc.dm | 29 +----- .../structures/closets/misc_vr.dm | 82 +++++++++++++++++ vorestation.dme | 1 - 17 files changed, 307 insertions(+), 99 deletions(-) diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories.dm b/code/modules/client/preference_setup/loadout/loadout_accessories.dm index 7629ce19a2..5e6dfae145 100644 --- a/code/modules/client/preference_setup/loadout/loadout_accessories.dm +++ b/code/modules/client/preference_setup/loadout/loadout_accessories.dm @@ -77,9 +77,9 @@ gear_tweaks += new/datum/gear_tweak/path(sortAssoc(wcoats)) /datum/gear/accessory/holster - display_name = "holster selection (Security, CD, HoP, Explorer)" + display_name = "holster selection (Security, CD, HoP)" path = /obj/item/clothing/accessory/holster - allowed_roles = list("Colony Director", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective","Explorer","Pathfinder") //VOREStation Edit + allowed_roles = list("Colony Director", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective") /datum/gear/accessory/holster/New() ..() @@ -135,32 +135,32 @@ /datum/gear/accessory/brown_vest display_name = "webbing, brown" path = /obj/item/clothing/accessory/storage/brown_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor") /datum/gear/accessory/black_vest display_name = "webbing, black" path = /obj/item/clothing/accessory/storage/black_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor") /datum/gear/accessory/white_vest display_name = "webbing, white" path = /obj/item/clothing/accessory/storage/white_vest - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor") /datum/gear/accessory/brown_drop_pouches display_name = "drop pouches, brown" path = /obj/item/clothing/accessory/storage/brown_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Search and Rescue") /datum/gear/accessory/black_drop_pouches display_name = "drop pouches, black" path = /obj/item/clothing/accessory/storage/black_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Search and Rescue") /datum/gear/accessory/white_drop_pouches display_name = "drop pouches, white" path = /obj/item/clothing/accessory/storage/white_drop_pouches - allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Field Medic","Pathfinder") //VOREStation Edit + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor", "Search and Rescue") /datum/gear/accessory/fannypack display_name = "fannypack selection" @@ -239,7 +239,7 @@ /datum/gear/accessory/stethoscope display_name = "stethoscope" path = /obj/item/clothing/accessory/stethoscope - allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic") //VOREStation Edit + allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Search and Rescue") /datum/gear/accessory/locket display_name = "locket" diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm b/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm index 86ea0381d0..c3d4adc34b 100644 --- a/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_accessories_vr.dm @@ -30,11 +30,36 @@ display_name = "collar, holo" path = /obj/item/clothing/accessory/collar/holo -/datum/gear/accessory/white_drop_pouches - allowed_roles = list("Paramedic","Chief Medical Officer","Medical Doctor","Chemist") +/datum/gear/accessory/holster + display_name = "holster selection (Security, CD, HoP, Exploration)" + allowed_roles = list("Colony Director", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective","Explorer","Pathfinder") + +/datum/gear/accessory/brown_vest + display_name = "webbing, brown" + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder") + +/datum/gear/accessory/black_vest + display_name = "webbing, black" + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder") /datum/gear/accessory/white_vest - allowed_roles = list("Paramedic","Chief Medical Officer","Medical Doctor","Chemist") + display_name = "webbing, white (Medical)" + allowed_roles = list("Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic") + +/datum/gear/accessory/brown_drop_pouches + display_name = "drop pouches, brown" + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder") + +/datum/gear/accessory/black_drop_pouches + display_name = "drop pouches, black" + allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Security Officer","Detective","Head of Security","Warden","Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic","Pathfinder") + +/datum/gear/accessory/white_drop_pouches + display_name = "drop pouches, white (Medical)" + allowed_roles = list("Paramedic","Chief Medical Officer","Medical Doctor","Chemist","Field Medic") + +/datum/gear/accessory/stethoscope + allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic", "Field Medic") /datum/gear/accessory/khcrystal display_name = "KH Life Crystal" diff --git a/code/modules/client/preference_setup/loadout/loadout_eyes.dm b/code/modules/client/preference_setup/loadout/loadout_eyes.dm index 85e007da90..5981424753 100644 --- a/code/modules/client/preference_setup/loadout/loadout_eyes.dm +++ b/code/modules/client/preference_setup/loadout/loadout_eyes.dm @@ -57,7 +57,7 @@ /datum/gear/eyes/medical display_name = "Medical HUD (Medical)" path = /obj/item/clothing/glasses/hud/health - allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") //VOREStation Edit + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Search and Rescue") /datum/gear/eyes/medical/prescriptionmed display_name = "Medical HUD, prescription (Medical)" diff --git a/code/modules/client/preference_setup/loadout/loadout_eyes_vr.dm b/code/modules/client/preference_setup/loadout/loadout_eyes_vr.dm index 94094428c9..da9ad9021d 100644 --- a/code/modules/client/preference_setup/loadout/loadout_eyes_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_eyes_vr.dm @@ -1,3 +1,10 @@ +/datum/gear/eyes/medical + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") + +/datum/gear/eyes/meson + display_name = "Optical Meson Scanners (Engineering, Science)" + allowed_roles = list("Station Engineer","Chief Engineer","Atmospheric Technician", "Scientist", "Research Director", "Explorer", "Pathfinder") + /datum/gear/eyes/arglasses display_name = "AR glasses" path = /obj/item/clothing/glasses/omnihud diff --git a/code/modules/client/preference_setup/loadout/loadout_shoes_vr.dm b/code/modules/client/preference_setup/loadout/loadout_shoes_vr.dm index ff04b6a989..73cce9a17e 100644 --- a/code/modules/client/preference_setup/loadout/loadout_shoes_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_shoes_vr.dm @@ -1,3 +1,9 @@ +/datum/gear/shoes/boots/winter/science + allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist", "Explorer", "Pathfinder") + +/datum/gear/shoes/boots/winter/medical + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") + /datum/gear/shoes/black/cuffs display_name = "legwraps, black" path = /obj/item/clothing/shoes/black/cuffs diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm index 87382eae60..41d37b8a67 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -480,7 +480,7 @@ datum/gear/suit/duster /datum/gear/suit/snowsuit/medical display_name = "snowsuit, medical" path = /obj/item/clothing/suit/storage/snowsuit/medical - allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") //VOREStation Edit + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Search and Rescue") /datum/gear/suit/snowsuit/science display_name = "snowsuit, science" diff --git a/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm b/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm index 002d032a3d..719ba98a38 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit_vr.dm @@ -1,3 +1,21 @@ +/datum/gear/suit/roles/poncho/cloak/research + allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist", "Explorer", "Pathfinder") + +/datum/gear/suit/roles/poncho/cloak/medical + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") + +/datum/gear/suit/wintercoat/medical + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") + +/datum/gear/suit/wintercoat/science + allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist", "Explorer", "Pathfinder") + +/datum/gear/suit/snowsuit/medical + allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist", "Field Medic") + +/datum/gear/suit/snowsuit/science + allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist", "Explorer", "Pathfinder") + /datum/gear/suit/labcoat_colorable display_name = "labcoat, colorable" path = /obj/item/clothing/suit/storage/toggle/labcoat diff --git a/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm b/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm index 9845ba9855..4edfe9246f 100644 --- a/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_utility_vr.dm @@ -54,4 +54,4 @@ /datum/gear/utility/dufflebag/sci display_name = "science dufflebag" path = /obj/item/weapon/storage/backpack/dufflebag/sci - allowed_roles = list("Research Director","Scientist","Roboticist","Xenobiologist","Explorer") + allowed_roles = list("Research Director","Scientist","Roboticist","Xenobiologist","Explorer","Pathfinder") diff --git a/maps/southern_cross/items/encryptionkey_sc.dm b/maps/southern_cross/items/encryptionkey_sc.dm index 6896bf1c6c..feb63e5894 100644 --- a/maps/southern_cross/items/encryptionkey_sc.dm +++ b/maps/southern_cross/items/encryptionkey_sc.dm @@ -6,7 +6,7 @@ /obj/item/device/encryptionkey/explorer name = "explorer radio encryption key" icon_state = "com_cypherkey" - channels = list("Science" = 1, "Explorer" = 1) //VOREStation Edit + channels = list("Explorer" = 1) /obj/item/device/encryptionkey/sar name = "sar's encryption key" diff --git a/maps/southern_cross/items/encryptionkey_vr.dm b/maps/southern_cross/items/encryptionkey_vr.dm index 54f26ccebe..94b12bdd2c 100644 --- a/maps/southern_cross/items/encryptionkey_vr.dm +++ b/maps/southern_cross/items/encryptionkey_vr.dm @@ -1,3 +1,6 @@ /obj/item/device/encryptionkey/pilot icon_state = "cypherkey" channels = list("Explorer" = 1) + +/obj/item/device/encryptionkey/explorer + channels = list("Science" = 1, "Explorer" = 1) diff --git a/maps/southern_cross/items/headset_sc.dm b/maps/southern_cross/items/headset_sc.dm index 6007317792..eca7eeb27d 100644 --- a/maps/southern_cross/items/headset_sc.dm +++ b/maps/southern_cross/items/headset_sc.dm @@ -1,40 +1,35 @@ /obj/item/device/radio/headset/pilot name = "pilot's headset" - desc = "A headset used by pilots, has access to the explorer channel." //VOREStation Edit + desc = "A headset used by pilots, has access to supply and explorer channels." icon_state = "pilot_headset" adhoc_fallback = TRUE ks2type = /obj/item/device/encryptionkey/pilot /obj/item/device/radio/headset/pilot/alt name = "pilot's bowman headset" - desc = "A bowman headset used by pilots, has access to the explorer channel." //VOREStation Edit - icon_state = "pilot_headset_alt" - -/obj/item/device/radio/headset/pilot/alt - name = "pilot's bowman headset" - desc = "A bowman headset used by pilots, has access to the explorer channel." //VOREStation Edit + desc = "A bowman headset used by pilots, has access to supply and explorer channels." icon_state = "pilot_headset_alt" /obj/item/device/radio/headset/explorer name = "explorer's headset" - desc = "Headset used by explorers for exploring. Access to explorer and science channels." //VOREStation Edit + desc = "Headset used by explorers for exploring. Access to the explorer channel." icon_state = "exp_headset" adhoc_fallback = TRUE ks2type = /obj/item/device/encryptionkey/explorer /obj/item/device/radio/headset/explorer/alt name = "explorer's bowman headset" - desc = "Bowman headset used by explorers for exploring. Access to explorer and science channels." //VOREStation Edit + desc = "Bowman headset used by explorers for exploring. Access to the explorer channel." icon_state = "exp_headset_alt" /obj/item/device/radio/headset/sar - name = "fm radio headset" //VOREStation Edit - desc = "A headset for field medics." //VOREStation Edit + name = "sar radio headset" + desc = "A headset for search and rescue." icon_state = "sar_headset" adhoc_fallback = TRUE ks2type = /obj/item/device/encryptionkey/sar /obj/item/device/radio/headset/sar/alt - name = "fm radio bowman headset" //VOREStation Edit - desc = "A bowman headset for field medics." //VOREStation Edit + name = "sar radio bowman headset" + desc = "A bowman headset for search and rescue." icon_state = "sar_headset_alt" diff --git a/maps/southern_cross/items/headset_vr.dm b/maps/southern_cross/items/headset_vr.dm index c100fcf441..ac737845b5 100644 --- a/maps/southern_cross/items/headset_vr.dm +++ b/maps/southern_cross/items/headset_vr.dm @@ -1,3 +1,23 @@ +/obj/item/device/radio/headset/pilot + desc = "A headset used by pilots, has access to the explorer channel." + +/obj/item/device/radio/headset/pilot/alt + desc = "A bowman headset used by pilots, has access to the explorer channel." + +/obj/item/device/radio/headset/explorer + desc = "Headset used by explorers for exploring. Access to explorer and science channels." + +/obj/item/device/radio/headset/explorer/alt + desc = "Bowman headset used by explorers for exploring. Access to explorer and science channels." + +/obj/item/device/radio/headset/sar + name = "fm radio headset" + desc = "A headset for field medics." + +/obj/item/device/radio/headset/sar/alt + name = "fm radio bowman headset" + desc = "A bowman headset for field medics." + /obj/item/device/radio/headset/volunteer name = "volunteer's headset" desc = "A headset used by volunteers to expedition teams, has access to the exploration channel." diff --git a/maps/southern_cross/southern_cross_jobs.dm b/maps/southern_cross/southern_cross_jobs.dm index db05c6b04a..12166d1478 100644 --- a/maps/southern_cross/southern_cross_jobs.dm +++ b/maps/southern_cross/southern_cross_jobs.dm @@ -1,9 +1,8 @@ -//VORESTATION EDIT!!! This whole file is being included by hand to get these jobs rather than using the map datum itself. // Pilots var/const/SAR =(1<<11) -var/const/PILOT =(1<<15) //VOREStation Edit - Bumped up 2 -var/const/EXPLORER =(1<<12) //VOREStation Edit - Moved to science +var/const/PILOT =(1<<13) +var/const/EXPLORER =(1<<14) var/const/access_pilot = 67 var/const/access_explorer = 43 @@ -21,30 +20,16 @@ var/const/access_explorer = 43 //SC IDs /obj/item/weapon/card/id/medical/sar - //VOREStation Block Edit - Start - assignment = "Field Medic" - rank = "Field Medic" - icon_state = "cyan" - primary_color = rgb(47,189,189) - secondary_color = rgb(127,223,223) - //VOREStation Block Edit - End + assignment = "Search and Rescue" + rank = "Search and Rescue" job_access_type = /datum/job/sar -//VOREStation Block Edit - Start -/obj/item/weapon/card/id/explorer - name = "identification card" - desc = "A card issued to station exploration staff." - icon_state = "green" - primary_color = rgb(47,189,0) - secondary_color = rgb(127,223,95) -//VOREstation Block Edit - End - -/obj/item/weapon/card/id/explorer/pilot +/obj/item/weapon/card/id/civilian/pilot assignment = "Pilot" rank = "Pilot" job_access_type = /datum/job/pilot -/obj/item/weapon/card/id/explorer/explorer +/obj/item/weapon/card/id/civilian/explorer assignment = "Explorer" rank = "Explorer" job_access_type = /datum/job/explorer @@ -91,28 +76,28 @@ var/const/access_explorer = 43 faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "the pathfinder and the head of personnel" //VOREStation Edit + supervisors = "the head of personnel" selection_color = "#515151" - idtype = /obj/item/weapon/card/id/explorer/pilot //VOREStation Edit - economic_modifier = 5 //VOREStation Edit - access = list(access_pilot) //VOREStation Edit - minimal_access = list(access_pilot) //VOREStation Edit + idtype = /obj/item/weapon/card/id/civilian/pilot + economic_modifier = 4 + access = list(access_pilot, access_cargo, access_mining, access_mining_station) + minimal_access = list(access_pilot, access_cargo, access_mining, access_mining_station) outfit_type = /decl/hierarchy/outfit/job/pilot /datum/job/explorer title = "Explorer" flag = EXPLORER - department = "Science" //VOREStation Edit - department_flag = MEDSCI //VOREStation Edit + department = "Civilian" + department_flag = CIVILIAN faction = "Station" - total_positions = 2 //VOREStation Edit - spawn_positions = 2 //VOREStation Edit - supervisors = "the pathfinder and the research director" //VOREStation Edit - selection_color = "#633D63" //VOREStation Edit - idtype = /obj/item/weapon/card/id/explorer/explorer //VOREStation Edit - economic_modifier = 6 //VOREStation Edit - access = list(access_explorer, access_research) //VOREStation Edit - minimal_access = list(access_explorer, access_research) //VOREStation Edit + total_positions = 4 + spawn_positions = 4 + supervisors = "the explorer leader and the head of personnel" + selection_color = "#515151" + idtype = /obj/item/weapon/card/id/civilian/explorer + economic_modifier = 4 + access = list(access_explorer) + minimal_access = list(access_explorer) outfit_type = /decl/hierarchy/outfit/job/explorer2 /* alt_titles = list( @@ -121,17 +106,17 @@ var/const/access_explorer = 43 */ /datum/job/sar - title = "Field Medic" //VOREStation Edit + title = "Search and Rescue" flag = SAR department = "Medical" department_flag = MEDSCI faction = "Station" total_positions = 2 spawn_positions = 2 - supervisors = "the pathfinder and the chief medical officer" //VOREStation Edit - selection_color = "#013D3B" //VOREStation Edit - idtype = /obj/item/weapon/card/id/medical/sar //VOREStation Edit - economic_modifier = 6 //VOREStation Edit - access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_eva, access_maint_tunnels, access_external_airlocks, access_pilot) //VOREStation Edit - minimal_access = list(access_medical, access_medical_equip, access_morgue, access_pilot) //VOREStation Edit + supervisors = "the chief medical officer" + selection_color = "#515151" + idtype = /obj/item/weapon/card/id/medical + economic_modifier = 4 + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_eva, access_maint_tunnels, access_external_airlocks, access_psychiatrist, access_explorer) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_explorer) outfit_type = /decl/hierarchy/outfit/job/medical/sar \ No newline at end of file diff --git a/maps/southern_cross/southern_cross_jobs_vr.dm b/maps/southern_cross/southern_cross_jobs_vr.dm index c162d63d53..90b9f3ce1d 100644 --- a/maps/southern_cross/southern_cross_jobs_vr.dm +++ b/maps/southern_cross/southern_cross_jobs_vr.dm @@ -1,4 +1,45 @@ -var/const/PATHFINDER =(1<<13) //VOREStation Edit - Added Pathfinder +var/const/SAR =(1<<11) +var/const/PILOT =(1<<15) +var/const/EXPLORER =(1<<12) +var/const/PATHFINDER =(1<<13) + +var/const/access_pilot = 67 +var/const/access_explorer = 43 + +/datum/access/pilot + id = access_pilot + desc = "Pilot" + region = ACCESS_REGION_SUPPLY + +/datum/access/explorer + id = access_explorer + desc = "Explorer" + region = ACCESS_REGION_GENERAL + +/obj/item/weapon/card/id/medical/sar + assignment = "Field Medic" + rank = "Field Medic" + icon_state = "cyan" + primary_color = rgb(47,189,189) + secondary_color = rgb(127,223,223) + job_access_type = /datum/job/sar + +/obj/item/weapon/card/id/explorer + name = "identification card" + desc = "A card issued to station exploration staff." + icon_state = "green" + primary_color = rgb(47,189,0) + secondary_color = rgb(127,223,95) + +/obj/item/weapon/card/id/explorer/pilot + assignment = "Pilot" + rank = "Pilot" + job_access_type = /datum/job/pilot + +/obj/item/weapon/card/id/explorer/explorer + assignment = "Explorer" + rank = "Explorer" + job_access_type = /datum/job/explorer /obj/item/weapon/card/id/explorer/head/ name = "identification card" @@ -28,3 +69,51 @@ var/const/PATHFINDER =(1<<13) //VOREStation Edit - Added Pathfinder access = list(access_eva, access_maint_tunnels, access_external_airlocks, access_pilot, access_explorer, access_research, access_gateway) minimal_access = list(access_eva, access_pilot, access_explorer, access_research, access_gateway) outfit_type = /decl/hierarchy/outfit/job/pathfinder + +/datum/job/pilot + title = "Pilot" + flag = PILOT + department = "Civilian" + department_flag = CIVILIAN + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the pathfinder and the head of personnel" + selection_color = "#515151" + idtype = /obj/item/weapon/card/id/explorer/pilot + economic_modifier = 5 + access = list(access_pilot) + minimal_access = list(access_pilot) + outfit_type = /decl/hierarchy/outfit/job/pilot + +/datum/job/explorer + title = "Explorer" + flag = EXPLORER + department = "Science" + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the pathfinder and the research director" + selection_color = "#633D63" + idtype = /obj/item/weapon/card/id/explorer/explorer + economic_modifier = 6 + access = list(access_explorer, access_research) + minimal_access = list(access_explorer, access_research) + outfit_type = /decl/hierarchy/outfit/job/explorer2 + +/datum/job/sar + title = "Field Medic" + flag = SAR + department = "Medical" + department_flag = MEDSCI + faction = "Station" + total_positions = 2 + spawn_positions = 2 + supervisors = "the pathfinder and the chief medical officer" + selection_color = "#013D3B" + idtype = /obj/item/weapon/card/id/medical/sar + economic_modifier = 6 + access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_eva, access_maint_tunnels, access_external_airlocks, access_pilot) + minimal_access = list(access_medical, access_medical_equip, access_morgue, access_pilot) + outfit_type = /decl/hierarchy/outfit/job/medical/sar diff --git a/maps/southern_cross/structures/closets/misc.dm b/maps/southern_cross/structures/closets/misc.dm index b24b33ce9f..82b2bd65cb 100644 --- a/maps/southern_cross/structures/closets/misc.dm +++ b/maps/southern_cross/structures/closets/misc.dm @@ -36,15 +36,6 @@ /obj/structure/closet/secure_closet/explorer name = "explorer locker" - //VOREStation Add begin - icon = 'icons/obj/closet_vr.dmi' //VOREStation Add - icon_state = "secureexp1" - icon_closed = "secureexp" - icon_locked = "secureexp1" - icon_opened = "secureexpopen" - icon_broken = "secureexpbroken" - icon_off = "secureexpoff" - //VOREStation Add end req_access = list(access_explorer) starts_with = list( @@ -60,11 +51,7 @@ /obj/item/device/geiger, /obj/item/weapon/cell/device, /obj/item/device/radio, - /obj/item/stack/marker_beacon/thirty, - /obj/item/weapon/material/knife/tacknife/survival, //VOREStation Add, - /obj/item/weapon/material/knife/machete, //VOREStation Add, - /obj/item/clothing/accessory/holster/machete, //VOREStation Add, - /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 2) //VOREStation Add + /obj/item/stack/marker_beacon/thirty) /obj/structure/closet/secure_closet/explorer/initialize() if(prob(50)) @@ -82,7 +69,7 @@ //SAR Lockers /obj/structure/closet/secure_closet/sar - name = "field medic locker" //VOREStation Edit + name = "search and rescue locker" desc = "Supplies for a wilderness first responder." icon_state = "medical1" icon_closed = "medical" @@ -99,12 +86,7 @@ /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline, /obj/item/weapon/reagent_containers/glass/bottle/antitoxin, /obj/item/weapon/storage/belt/medical/emt, - //VOREStation Block Edit - Start, - /obj/item/weapon/material/knife/tacknife/survival, - /obj/item/weapon/gun/energy/frontier/locked/holdout, - /obj/item/clothing/mask/gas/explorer, - /obj/item/clothing/suit/storage/hooded/explorer/medic, - //VOREStation Block Edit - End, + /obj/item/clothing/mask/gas, /obj/item/clothing/suit/storage/hooded/wintercoat/medical/sar, /obj/item/clothing/shoes/boots/winter/explorer, /obj/item/device/radio/headset/sar, @@ -115,7 +97,6 @@ /obj/item/device/healthanalyzer, /obj/item/device/radio/off, /obj/random/medical, - /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 3, //VOREStation Add, /obj/item/weapon/tool/crowbar, /obj/item/weapon/extinguisher/mini, /obj/item/weapon/storage/box/freezer, @@ -134,17 +115,15 @@ starts_with = list( /obj/item/weapon/storage/backpack/parachute, /obj/item/weapon/material/knife/tacknife/survival, - /obj/item/weapon/gun/energy/frontier/locked/holdout, //VOREStation Add, /obj/item/clothing/head/pilot, /obj/item/clothing/under/rank/pilot1, /obj/item/clothing/suit/storage/toggle/bomber/pilot, - /obj/item/clothing/shoes/boots/winter/explorer, //VOREStation Add, /obj/item/clothing/mask/gas/half, /obj/item/clothing/shoes/black, /obj/item/clothing/gloves/fingerless, /obj/item/device/radio/headset/pilot/alt, /obj/item/device/flashlight, - /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 2, //VOREStation Edit, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood, /obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle, /obj/item/weapon/storage/box/flare, /obj/item/weapon/cell/device, diff --git a/maps/southern_cross/structures/closets/misc_vr.dm b/maps/southern_cross/structures/closets/misc_vr.dm index 27b0150331..11403493d4 100644 --- a/maps/southern_cross/structures/closets/misc_vr.dm +++ b/maps/southern_cross/structures/closets/misc_vr.dm @@ -1,3 +1,85 @@ +/obj/structure/closet/secure_closet/explorer + icon = 'icons/obj/closet_vr.dmi' + icon_state = "secureexp1" + icon_closed = "secureexp" + icon_locked = "secureexp1" + icon_opened = "secureexpopen" + icon_broken = "secureexpbroken" + icon_off = "secureexpoff" + + starts_with = list( + /obj/item/clothing/under/explorer, + /obj/item/clothing/suit/storage/hooded/explorer, + /obj/item/clothing/mask/gas/explorer, + /obj/item/clothing/shoes/boots/winter/explorer, + /obj/item/clothing/gloves/black, + /obj/item/device/radio/headset/explorer, + /obj/item/device/flashlight, + /obj/item/device/gps/explorer, + /obj/item/weapon/storage/box/flare, + /obj/item/device/geiger, + /obj/item/weapon/cell/device, + /obj/item/device/radio, + /obj/item/stack/marker_beacon/thirty, + /obj/item/weapon/material/knife/tacknife/survival, + /obj/item/weapon/material/knife/machete, + /obj/item/clothing/accessory/holster/machete, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 2) + +/obj/structure/closet/secure_closet/sar + name = "field medic locker" + + starts_with = list( + /obj/item/weapon/storage/backpack/dufflebag/emt, + /obj/item/weapon/storage/box/autoinjectors, + /obj/item/weapon/storage/box/syringes, + /obj/item/weapon/reagent_containers/glass/bottle/inaprovaline, + /obj/item/weapon/reagent_containers/glass/bottle/antitoxin, + /obj/item/weapon/storage/belt/medical/emt, + /obj/item/weapon/material/knife/tacknife/survival, + /obj/item/weapon/gun/energy/frontier/locked/holdout, + /obj/item/clothing/mask/gas/explorer, + /obj/item/clothing/suit/storage/hooded/explorer/medic, + /obj/item/clothing/suit/storage/hooded/wintercoat/medical/sar, + /obj/item/clothing/shoes/boots/winter/explorer, + /obj/item/device/radio/headset/sar, + /obj/item/weapon/cartridge/medical, + /obj/item/device/flashlight, + /obj/item/weapon/tank/emergency/oxygen/engi, + /obj/item/clothing/glasses/hud/health, + /obj/item/device/healthanalyzer, + /obj/item/device/radio/off, + /obj/random/medical, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 3, + /obj/item/weapon/tool/crowbar, + /obj/item/weapon/extinguisher/mini, + /obj/item/weapon/storage/box/freezer, + /obj/item/clothing/accessory/storage/white_vest, + /obj/item/taperoll/medical, + /obj/item/device/gps, + /obj/item/device/geiger, + /obj/item/bodybag/cryobag) + +/obj/structure/closet/secure_closet/pilot + starts_with = list( + /obj/item/weapon/storage/backpack/parachute, + /obj/item/weapon/material/knife/tacknife/survival, + /obj/item/weapon/gun/energy/frontier/locked/holdout, + /obj/item/clothing/head/pilot, + /obj/item/clothing/under/rank/pilot1, + /obj/item/clothing/suit/storage/toggle/bomber/pilot, + /obj/item/clothing/shoes/boots/winter/explorer, + /obj/item/clothing/mask/gas/half, + /obj/item/clothing/shoes/black, + /obj/item/clothing/gloves/fingerless, + /obj/item/device/radio/headset/pilot/alt, + /obj/item/device/flashlight, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 2, + /obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle, + /obj/item/weapon/storage/box/flare, + /obj/item/weapon/cell/device, + /obj/item/device/radio) + /obj/structure/closet/secure_closet/pathfinder name = "pathfinder locker" icon = 'icons/obj/closet_vr.dmi' diff --git a/vorestation.dme b/vorestation.dme index 518ef9d997..7ac7195465 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -2955,7 +2955,6 @@ #include "maps\RandomZLevels\listeningpost.dm" #include "maps\RandomZLevels\snowfield.dm" #include "maps\RandomZLevels\zoo.dm" -#include "maps\southern_cross\southern_cross_jobs.dm" #include "maps\southern_cross\southern_cross_jobs_vr.dm" #include "maps\southern_cross\items\encryptionkey_sc.dm" #include "maps\southern_cross\items\encryptionkey_vr.dm" From 3969e2ab09dbb1b18b5d738f9a5cec6639540783 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 18 Nov 2018 15:16:26 -0500 Subject: [PATCH 024/174] Tightens up radio access a bit --- code/game/objects/items/devices/radio/radio.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index f688efc6cf..fd2e504cbd 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -7,14 +7,14 @@ var/global/list/default_internal_channels = list( num2text(ERT_FREQ) = list(access_cent_specops), num2text(COMM_FREQ)= list(access_heads), num2text(ENG_FREQ) = list(access_engine_equip, access_atmospherics), - num2text(MED_FREQ) = list(access_medical), - num2text(MED_I_FREQ)=list(access_medical), + num2text(MED_FREQ) = list(access_medical_equip), + num2text(MED_I_FREQ)=list(access_medical_equip), num2text(SEC_FREQ) = list(access_security), num2text(SEC_I_FREQ)=list(access_security), - num2text(SCI_FREQ) = list(access_research), + num2text(SCI_FREQ) = list(access_tox, access_robotics, access_xenobiology, access_explorer), num2text(SUP_FREQ) = list(access_cargo, access_mining), num2text(SRV_FREQ) = list(access_janitor, access_library, access_hydroponics, access_bar, access_kitchen), - num2text(EXP_FREQ) = list(access_pilot, access_explorer, access_rd) + num2text(EXP_FREQ) = list(access_explorer, access_pilot, access_rd) ) var/global/list/default_medbay_channels = list( From 86ee636b191c88b4bedf11e5722ab87339e3bc4e Mon Sep 17 00:00:00 2001 From: Verkister Date: Thu, 22 Nov 2018 17:27:55 +0200 Subject: [PATCH 025/174] Fixes ingested organs giving way too much fat Oh god I just realized the slow item digestion code made hardvored bodyparts have the calorie content of nuclear fuel. --- code/modules/vore/eating/digest_act_vr.dm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/modules/vore/eating/digest_act_vr.dm b/code/modules/vore/eating/digest_act_vr.dm index 2641a43700..0fe4d295c5 100644 --- a/code/modules/vore/eating/digest_act_vr.dm +++ b/code/modules/vore/eating/digest_act_vr.dm @@ -98,7 +98,11 @@ /obj/item/organ/digest_act(var/atom/movable/item_storage = null) if((. = ..())) - . += 70 //Organs give a little more + if(isbelly(item_storage)) + var/obj/belly/B = item_storage + . += 5 * (B.digest_brute + B.digest_burn) + else + . += 70 //Organs give a little more /obj/item/weapon/storage/digest_act(var/atom/movable/item_storage = null) for(var/obj/item/I in contents) @@ -115,4 +119,4 @@ // Gradual damage measurement /obj/item - var/digest_stage = null \ No newline at end of file + var/digest_stage = null From 797b9fa6e7d71729534743c8b2309908c88d02c5 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 24 Nov 2018 15:10:29 -0500 Subject: [PATCH 026/174] More Access Fixes --- code/game/jobs/job/security.dm | 2 +- code/game/objects/items/devices/radio/radio.dm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index d9623d06e8..0316f824c8 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -18,7 +18,7 @@ access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks)//VOREStation Edit minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory, access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, + access_research, access_engine, access_mining, access_construction, access_mailsorting, access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks)//VOREStation Edit minimum_character_age = 25 minimal_player_age = 14 diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index fd2e504cbd..9e03e7ff63 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -12,7 +12,7 @@ var/global/list/default_internal_channels = list( num2text(SEC_FREQ) = list(access_security), num2text(SEC_I_FREQ)=list(access_security), num2text(SCI_FREQ) = list(access_tox, access_robotics, access_xenobiology, access_explorer), - num2text(SUP_FREQ) = list(access_cargo, access_mining), + num2text(SUP_FREQ) = list(access_cargo, access_mining_station), num2text(SRV_FREQ) = list(access_janitor, access_library, access_hydroponics, access_bar, access_kitchen), num2text(EXP_FREQ) = list(access_explorer, access_pilot, access_rd) ) From a7667382d2160c84b033bef0004b86e8fac67c98 Mon Sep 17 00:00:00 2001 From: Unknown Date: Tue, 27 Nov 2018 17:02:31 -0500 Subject: [PATCH 027/174] Fixes Shuttle Transitions - Fixes Bluespace Transition zone on expedition shuttle - Fixes Virgo four transition zone on expedition shuttle --- maps/tether/tether_turfs.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maps/tether/tether_turfs.dm b/maps/tether/tether_turfs.dm index b755980b95..4ae161d40f 100644 --- a/maps/tether/tether_turfs.dm +++ b/maps/tether/tether_turfs.dm @@ -164,7 +164,7 @@ VIRGO3B_TURF_CREATE(/turf/simulated/mineral/floor) /turf/space/bluespace name = "bluespace" icon_state = "bluespace" -/turf/space/bluespace/New() +/turf/space/bluespace/initialize() ..() icon_state = "bluespace" @@ -173,7 +173,7 @@ VIRGO3B_TURF_CREATE(/turf/simulated/mineral/floor) name = "sand transit" icon = 'icons/turf/transit_vr.dmi' icon_state = "desert_ns" -/turf/space/sandyscroll/New() +/turf/space/sandyscroll/initialize() ..() icon_state = "desert_ns" From 2df5e9fe0ec8ee54c554917e1ffbd29645dd457d Mon Sep 17 00:00:00 2001 From: Atermonera Date: Fri, 30 Nov 2018 21:05:42 -0800 Subject: [PATCH 028/174] Merges AI Branch into Master --- code/ZAS/Airflow.dm | 3 - code/__defines/_planes+layers.dm | 5 +- code/__defines/misc.dm | 2 +- code/__defines/mobs.dm | 40 + code/__defines/subsystems.dm | 2 + code/_global_vars/misc.dm | 2 +- code/_global_vars/mobs.dm | 2 +- code/_helpers/time.dm | 2 +- code/_helpers/unsorted.dm | 2 +- code/_macros.dm | 10 +- code/_onclick/hud/fullscreen.dm | 2 +- code/_onclick/hud/hud.dm | 2 - code/_onclick/hud/other_mobs.dm | 7 +- code/_onclick/item_attack.dm | 2 +- code/_onclick/other_mobs.dm | 55 - code/controllers/subsystems/ai.dm | 36 + code/controllers/subsystems/vote.dm | 746 ++--- code/datums/ghost_query.dm | 7 + code/datums/mind.dm | 17 +- code/game/antagonist/alien/borer.dm | 4 +- code/game/antagonist/station/cultist.dm | 4 +- code/game/gamemodes/cult/construct_spells.dm | 34 +- code/game/gamemodes/cult/cult_items.dm | 8 +- code/game/gamemodes/cult/cult_structures.dm | 13 +- code/game/gamemodes/cult/cultify/mob.dm | 2 +- .../gamemodes/cult}/soulstone.dm | 488 ++-- code/game/gamemodes/events.dm | 2 +- .../gamemodes/events/holidays/Christmas.dm | 2 +- code/game/gamemodes/objective.dm | 4 +- .../technomancer/assistance/golem.dm | 258 -- code/game/gamemodes/technomancer/core_obj.dm | 2 +- .../technomancer/spell_objs_helpers.dm | 23 +- .../technomancer/spells/abjuration.dm | 12 +- .../gamemodes/technomancer/spells/control.dm | 83 +- .../gamemodes/technomancer/spells/gambit.dm | 6 +- .../gamemodes/technomancer/spells/illusion.dm | 123 +- .../technomancer/spells/modifier/modifier.dm | 7 +- .../technomancer/spells/resurrect.dm | 6 +- .../technomancer/spells/spawner/pulsar.dm | 45 +- .../spells/summon/summon_creature.dm | 36 +- .../technomancer/spells/summon/summon_ward.dm | 121 +- code/game/machinery/adv_med.dm | 14 +- code/game/machinery/camera/camera.dm | 2 +- code/game/machinery/computer/prisonshuttle.dm | 2 +- .../machinery/computer/specops_shuttle.dm | 2 +- .../computer/syndicate_specops_shuttle.dm | 2 +- .../computer3/computers/HolodeckControl.dm | 4 +- code/game/machinery/cryo.dm | 23 +- code/game/machinery/cryopod.dm | 7 +- code/game/machinery/doors/airlock.dm | 9 +- code/game/machinery/doors/door.dm | 4 +- code/game/machinery/doors/firedoor.dm | 8 +- code/game/machinery/portable_turret.dm | 10 +- code/game/machinery/teleporter.dm | 2 +- .../mecha/equipment/tools/medical_tools.dm | 7 +- code/game/mecha/equipment/tools/tools.dm | 9 +- code/game/mecha/mecha.dm | 8 +- code/game/mecha/mecha_wreckage.dm | 11 + code/game/mecha/medical/odysseus.dm | 2 +- code/game/objects/effects/overlays.dm | 10 + code/game/objects/effects/spiders.dm | 24 +- code/game/objects/items/devices/aicard.dm | 2 +- .../objects/items/devices/laserpointer.dm | 2 +- code/game/objects/items/devices/scanners.dm | 20 +- code/game/objects/items/glassjar.dm | 2 +- code/game/objects/items/poi_items.dm | 11 - code/game/objects/items/robot/robot_parts.dm | 7 - code/game/objects/items/weapons/AI_modules.dm | 2 +- .../items/weapons/grenades/spawnergrenade.dm | 12 +- code/game/objects/items/weapons/handcuffs.dm | 2 +- .../items/weapons/implants/implantchair.dm | 7 +- .../objects/items/weapons/melee/energy.dm | 9 +- code/game/objects/items/weapons/policetape.dm | 2 +- code/game/objects/items/weapons/stunbaton.dm | 5 +- .../items/weapons/tools/screwdriver.dm | 2 +- .../items/weapons/tools/weldingtool.dm | 7 + .../objects/items/weapons/tools/wrench.dm | 2 +- code/game/objects/random/mob.dm | 168 +- code/game/objects/structures.dm | 4 +- .../structures/crates_lockers/closets.dm | 6 +- .../structures/crates_lockers/largecrate.dm | 12 +- .../structures/ghost_pods/mysterious.dm | 4 +- code/game/objects/structures/girders.dm | 4 +- code/game/objects/structures/gravemarker.dm | 2 +- code/game/objects/structures/loot_piles.dm | 45 +- code/game/objects/structures/musician.dm | 2 +- code/game/objects/structures/plasticflaps.dm | 4 +- code/game/objects/structures/props/nest.dm | 2 +- code/game/objects/structures/simple_doors.dm | 2 +- .../structures/stool_bed_chair_nest/chairs.dm | 2 +- code/game/objects/structures/window.dm | 2 +- code/game/objects/weapons.dm | 14 +- .../game/turfs/simulated/outdoors/outdoors.dm | 2 +- code/game/turfs/simulated/outdoors/snow.dm | 3 + code/game/turfs/simulated/wall_attacks.dm | 12 +- code/game/turfs/simulated/water.dm | 24 +- code/game/turfs/turf.dm | 4 + code/game/turfs/turf_changing.dm | 2 +- code/modules/admin/topic.dm | 26 +- code/modules/admin/verbs/buildmode.dm | 108 + code/modules/admin/verbs/lightning_strike.dm | 214 +- code/modules/ai/__readme.dm | 201 ++ code/modules/ai/_defines.dm | 29 + .../ai/aI_holder_subtypes/simple_mob_ai.dm | 136 + .../ai/aI_holder_subtypes/slime_xenobio_ai.dm | 262 ++ code/modules/ai/ai_holder.dm | 290 ++ code/modules/ai/ai_holder_combat.dm | 308 ++ code/modules/ai/ai_holder_combat_unseen.dm | 43 + code/modules/ai/ai_holder_communication.dm | 134 + code/modules/ai/ai_holder_cooperation.dm | 115 + code/modules/ai/ai_holder_debug.dm | 89 + code/modules/ai/ai_holder_disabled.dm | 95 + code/modules/ai/ai_holder_fleeing.dm | 45 + code/modules/ai/ai_holder_follow.dm | 68 + code/modules/ai/ai_holder_movement.dm | 154 + code/modules/ai/ai_holder_pathfinding.dm | 58 + code/modules/ai/ai_holder_targeting.dm | 237 ++ code/modules/ai/interfaces.dm | 92 + code/modules/ai/say_list.dm | 119 + code/modules/artifice/deadringer.dm | 9 +- code/modules/assembly/mousetrap.dm | 2 +- code/modules/blob2/blobs/factory.dm | 6 +- code/modules/blob2/overmind/overmind.dm | 2 +- code/modules/blob2/overmind/types.dm | 18 +- code/modules/client/client defines.dm | 2 +- .../preference_setup/loadout/loadout_head.dm | 2 +- code/modules/events/carp_migration.dm | 10 +- code/modules/events/infestation.dm | 4 +- code/modules/events/rogue_drones.dm | 8 +- code/modules/food/kitchen/gibber.dm | 472 ++-- code/modules/food/kitchen/microwave.dm | 784 +++--- code/modules/food/kitchen/smartfridge.dm | 4 + .../gamemaster/actions/carp_migration.dm | 4 +- .../actions/surprise_carp_attack.dm | 4 +- code/modules/games/cards.dm | 4 +- code/modules/holodeck/HolodeckControl.dm | 10 +- code/modules/holodeck/HolodeckObjects.dm | 16 +- code/modules/hydroponics/seed.dm | 4 +- code/modules/hydroponics/seed_datums.dm | 2 +- .../core/assemblies/device.dm | 168 +- code/modules/mob/_modifiers/auras.dm | 18 + code/modules/mob/_modifiers/modifiers.dm | 7 + code/modules/mob/_modifiers/modifiers_misc.dm | 69 +- code/modules/mob/_modifiers/traits_phobias.dm | 18 +- code/modules/mob/_modifiers/unholy.dm | 4 +- code/modules/mob/animations.dm | 27 +- code/modules/mob/dead/observer/observer.dm | 4 +- code/modules/mob/emote.dm | 7 + code/modules/mob/freelook/mask/chunk.dm | 4 +- code/modules/mob/hear_say.dm | 19 +- code/modules/mob/language/outsider.dm | 6 +- code/modules/mob/living/bot/secbot.dm | 10 +- .../living/carbon/alien/diona/diona_powers.dm | 2 +- code/modules/mob/living/carbon/brain/brain.dm | 2 +- code/modules/mob/living/carbon/brain/life.dm | 1 - .../mob/living/carbon/carbon_powers.dm | 8 +- code/modules/mob/living/carbon/human/death.dm | 4 +- .../mob/living/carbon/human/human_defense.dm | 35 + .../mob/living/carbon/human/human_helpers.dm | 10 + code/modules/mob/living/carbon/human/life.dm | 4 +- code/modules/mob/living/carbon/human/say.dm | 2 +- .../human/species/station/prometheans.dm | 2 +- code/modules/mob/living/death.dm | 6 + code/modules/mob/living/life.dm | 6 + code/modules/mob/living/living.dm | 37 +- code/modules/mob/living/living_defense.dm | 35 +- code/modules/mob/living/living_defines.dm | 6 +- code/modules/mob/living/login.dm | 5 + code/modules/mob/living/logout.dm | 9 +- .../living/silicon/robot/drone/drone_items.dm | 2 +- .../living/simple_animal/aliens/faithless.dm | 1 - .../living/simple_animal/aliens/hivebot.dm | 4 +- .../mob/living/simple_animal/animals/cat.dm | 201 ++ .../mob/living/simple_animal/animals/corgi.dm | 1 + .../mob/living/simple_animal/animals/mouse.dm | 6 +- .../living/simple_animal/animals/spiderbot.dm | 2 +- .../mob/living/simple_animal/borer/borer.dm | 24 +- .../simple_animal/borer/borer_captive.dm | 8 +- .../simple_animal/borer/borer_powers.dm | 31 +- .../mob/living/simple_animal/borer/say.dm | 2 +- .../simple_animal/constructs/constructs.dm | 488 ---- .../living/simple_animal/humanoids/head.dm | 61 - .../simple_animal/humanoids/mechamobs.dm | 2 +- .../mob/living/simple_animal/simple_animal.dm | 28 +- .../mob/living/simple_animal/slime/slime.dm | 4 +- .../mob/living/simple_mob/appearance.dm | 76 + code/modules/mob/living/simple_mob/combat.dm | 236 ++ code/modules/mob/living/simple_mob/defense.dm | 224 ++ code/modules/mob/living/simple_mob/hands.dm | 143 + code/modules/mob/living/simple_mob/life.dm | 160 ++ .../modules/mob/living/simple_mob/on_click.dm | 48 + .../mob/living/simple_mob/simple_hud.dm | 311 ++ .../mob/living/simple_mob/simple_mob.dm | 290 ++ .../simple_mob/subtypes/animal/animal.dm | 9 + .../simple_mob/subtypes/animal/borer/borer.dm | 237 ++ .../subtypes/animal/borer/borer_captive.dm | 59 + .../subtypes/animal/borer/borer_powers.dm | 354 +++ .../subtypes/animal/farm animals/chicken.dm | 156 + .../subtypes/animal/farm animals/cow.dm | 67 + .../subtypes/animal/farm animals/goat.dm | 81 + .../animal/giant_spider/_giant_spider.dm | 64 + .../subtypes/animal/giant_spider/carrier.dm | 67 + .../subtypes/animal/giant_spider/electric.dm | 45 + .../subtypes/animal/giant_spider/frost.dm | 20 + .../subtypes/animal/giant_spider/hunter.dm | 165 ++ .../subtypes/animal/giant_spider/lurker.dm | 103 + .../subtypes/animal/giant_spider/nurse.dm | 230 ++ .../subtypes/animal/giant_spider/pepper.dm | 21 + .../animal/giant_spider/phorogenic.dm | 56 + .../subtypes/animal/giant_spider/thermic.dm | 20 + .../subtypes/animal/giant_spider/tunneler.dm | 185 ++ .../animal/giant_spider/webslinger.dm | 37 + .../subtypes/animal/passive/crab.dm | 25 + .../subtypes/animal/passive/fish.dm | 77 + .../subtypes/animal/passive/lizard.dm | 24 + .../subtypes/animal/passive/misc.dm | 29 + .../subtypes/animal/passive/mouse.dm | 115 + .../subtypes/animal/passive/passive.dm | 5 + .../subtypes/animal/passive/penguin.dm | 27 + .../simple_mob/subtypes/animal/pets/bird.dm | 93 + .../simple_mob/subtypes/animal/pets/cat.dm | 136 + .../simple_mob/subtypes/animal/pets/dog.dm | 234 ++ .../simple_mob/subtypes/animal/pets/parrot.dm | 252 ++ .../simple_mob/subtypes/animal/sif/diyaab.dm | 35 + .../subtypes/animal/sif/hooligan_crab.dm | 102 + .../simple_mob/subtypes/animal/sif/savik.dm | 48 + .../simple_mob/subtypes/animal/sif/shantak.dm | 70 + .../simple_mob/subtypes/animal/sif/sif.dm | 5 + .../simple_mob/subtypes/animal/space/alien.dm | 118 + .../simple_mob/subtypes/animal/space/bats.dm | 52 + .../simple_mob/subtypes/animal/space/bear.dm | 46 + .../simple_mob/subtypes/animal/space/carp.dm | 128 + .../simple_mob/subtypes/animal/space/goose.dm | 44 + .../simple_mob/subtypes/animal/space/space.dm | 15 + .../living/simple_mob/subtypes/blob/blob.dm | 62 + .../living/simple_mob/subtypes/blob/spore.dm | 148 + .../simple_mob/subtypes/humanoid/clown.dm | 29 + .../simple_mob/subtypes/humanoid/humanoid.dm | 26 + .../subtypes/humanoid/mercs/mercs.dm | 278 ++ .../simple_mob/subtypes/humanoid/pirates.dm | 40 + .../simple_mob/subtypes/humanoid/russian.dm | 35 + .../simple_mob/subtypes/illusion/illusion.dm | 101 + .../subtypes/mechanical/combat_drone.dm | 77 + .../simple_mob/subtypes/mechanical/golem.dm | 152 + .../subtypes/mechanical/hivebot/hivebot.dm | 54 + .../mechanical/hivebot/ranged_damage.dm | 147 + .../subtypes/mechanical/hivebot/support.dm | 87 + .../subtypes/mechanical/hivebot/tank.dm | 166 ++ .../mechanical/mecha/adv_dark_gygax.dm | 261 ++ .../subtypes/mechanical/mecha/combat_mecha.dm | 38 + .../subtypes/mechanical/mecha/durand.dm | 53 + .../subtypes/mechanical/mecha/gygax.dm | 57 + .../subtypes/mechanical/mecha/hoverpod.dm | 27 + .../subtypes/mechanical/mecha/marauder.dm | 44 + .../subtypes/mechanical/mecha/mecha.dm | 141 + .../subtypes/mechanical/mecha/odysseus.dm | 71 + .../subtypes/mechanical/mecha/phazon.dm | 22 + .../subtypes/mechanical/mecha/ripley.dm | 64 + .../subtypes/mechanical/mechanical.dm | 25 + .../subtypes/mechanical/viscerator.dm | 51 + .../subtypes/mechanical/ward/monitor_ward.dm | 106 + .../subtypes/mechanical/ward/ward.dm | 42 + .../subtypes/occult/constructs/_construct.dm | 163 ++ .../subtypes/occult/constructs/artificer.dm | 30 + .../subtypes/occult/constructs/harvester.dm | 40 + .../subtypes/occult/constructs/juggernaut.dm | 143 + .../subtypes/occult/constructs/shade.dm | 48 + .../subtypes/occult/constructs/wraith.dm | 33 + .../simple_mob/subtypes/occult/creature.dm | 68 + .../simple_mob/subtypes/occult/faithless.dm | 77 + .../simple_mob/subtypes/plant/tomato.dm | 27 + .../living/simple_mob/subtypes/plant/tree.dm | 43 + .../simple_mob/subtypes/slime/feral/feral.dm | 94 + .../living/simple_mob/subtypes/slime/slime.dm | 216 ++ .../subtypes/slime/xenobio/combat.dm | 76 + .../subtypes/slime/xenobio/consumption.dm | 169 ++ .../subtypes/slime/xenobio/defense.dm | 54 + .../subtypes/slime/xenobio/discipline.dm | 23 + .../subtypes/slime/xenobio/subtypes.dm | 784 ++++++ .../subtypes/slime/xenobio/xenobio.dm | 284 ++ code/modules/mob/mob.dm | 7 +- code/modules/mob/mob_defines.dm | 3 +- code/modules/mob/mob_helpers.dm | 19 +- code/modules/mob/mob_movement.dm | 29 +- code/modules/mob/transform_procs.dm | 38 +- code/modules/multiz/movement.dm | 50 +- code/modules/multiz/turf.dm | 9 + code/modules/organs/internal/brain.dm | 2 +- code/modules/organs/misc.dm | 2 +- code/modules/paperwork/paperbin.dm | 2 +- code/modules/paperwork/photography.dm | 28 - code/modules/planet/sif.dm | 2 +- code/modules/power/singularity/act.dm | 9 + code/modules/power/tesla/energy_ball.dm | 3 +- code/modules/power/tesla/tesla_act.dm | 3 + code/modules/projectiles/gun.dm | 5 +- .../projectiles/guns/energy/special.dm | 4 +- code/modules/projectiles/projectile.dm | 15 +- code/modules/projectiles/projectile/change.dm | 2 +- code/modules/random_map/drop/droppod.dm | 2 +- code/modules/random_map/noise/desert.dm | 2 +- code/modules/random_map/noise/tundra.dm | 4 +- .../Chemistry-Reagents-Core.dm | 4 +- .../Chemistry-Reagents-Food-Drinks.dm | 6 - .../Chemistry-Reagents-Medicine.dm | 4 + .../Chemistry-Reagents-Other.dm | 2 +- .../reagents/reagent_containers/glass.dm | 4 +- code/modules/shieldgen/directional_shield.dm | 4 + code/modules/shieldgen/energy_field.dm | 6 + code/modules/shuttles/shuttle.dm | 11 + code/modules/spells/aoe_turf/summons.dm | 6 +- code/modules/spells/spell_code.dm | 8 +- code/modules/spells/spellbook.dm | 5 - code/modules/surgery/implant.dm | 4 +- code/modules/tension/tension.dm | 48 +- code/modules/ventcrawl/ventcrawl.dm | 11 +- .../xenoarcheaology/artifacts/autocloner.dm | 28 +- .../xenoarcheaology/artifacts/replicator.dm | 5 + code/modules/xenoarcheaology/finds/special.dm | 4 + code/modules/xenobio/items/extracts.dm | 30 +- code/modules/xenobio/items/slimepotions.dm | 87 +- code/modules/xenobio/items/weapons.dm | 64 +- code/modules/xenobio/machinery/processor.dm | 8 +- code/stylesheet.dm | 7 + html/changelog.html | 8 +- html/changelogs/Neerti-AI.yml | 37 + icons/effects/beam.dmi | Bin 112631 -> 120236 bytes icons/effects/effects.dmi | Bin 401565 -> 416434 bytes icons/mecha/mecha.dmi | Bin 167769 -> 213024 bytes icons/misc/buildmode.dmi | Bin 1452 -> 1520 bytes icons/mob/animal.dmi | Bin 279887 -> 266875 bytes icons/mob/critter.dmi | Bin 34610 -> 34362 bytes icons/mob/hivebot.dmi | Bin 15565 -> 15677 bytes icons/mob/mob.dmi | Bin 194750 -> 211298 bytes icons/mob/modifier_effects.dmi | Bin 16104 -> 20018 bytes icons/mob/screen1.dmi | Bin 167147 -> 167577 bytes icons/mob/slime2.dmi | Bin 32723 -> 28566 bytes maps/RandomZLevels/wildwest.dm | 176 ++ maps/northern_star/polaris-1.dmm | 31 +- maps/northern_star/polaris-2.dmm | 454 +-- maps/northern_star/polaris-5.dmm | 4 +- maps/southern_cross/southern_cross-1.dmm | 13 +- maps/southern_cross/southern_cross-3.dmm | 1824 ++++++++++++ maps/southern_cross/southern_cross-4.dmm | 1398 ++++----- maps/southern_cross/southern_cross-6.dmm | 2505 +++++++++++++++++ maps/southern_cross/southern_cross_areas.dm | 6 +- .../surface_submaps/mountains/BlastMine1.dmm | 4 +- .../surface_submaps/mountains/CaveTrench.dmm | 39 + .../surface_submaps/mountains/Cavelake.dmm | 18 + .../mountains/CrashedMedShuttle1.dmm | 14 +- .../surface_submaps/mountains/Scave1.dmm | 20 + .../surface_submaps/mountains/SwordCave.dmm | 110 +- .../mountains/crashedcontainmentshuttle.dmm | 240 +- .../surface_submaps/mountains/digsite.dmm | 125 +- .../mountains/quarantineshuttle.dmm | 291 +- .../surface_submaps/mountains/vault3.dmm | 2 +- .../surface_submaps/mountains/vault4.dmm | 4 +- .../surface_submaps/mountains/vault5.dmm | 4 +- .../surface_submaps/plains/Oldhouse.dmm | 55 + .../surface_submaps/plains/PooledR.dmm | 6 +- .../surface_submaps/plains/RationCache.dmm | 4 +- .../surface_submaps/plains/Shakden.dmm | 4 +- .../wilderness/Blackshuttledown.dmm | 126 + .../surface_submaps/wilderness/Boombase.dmm | 4 +- .../surface_submaps/wilderness/CaveS.dmm | 43 + .../surface_submaps/wilderness/DJOutpost1.dmm | 2 +- .../surface_submaps/wilderness/DoomP.dmm | 93 + .../surface_submaps/wilderness/Drugden.dmm | 50 + .../surface_submaps/wilderness/MCamp1.dmm | 40 + .../surface_submaps/wilderness/MHR.dmm | 22 + .../surface_submaps/wilderness/Manor1.dmm | 148 + .../surface_submaps/wilderness/Mudpit.dmm | 18 + .../surface_submaps/wilderness/Rockybase.dmm | 230 ++ .../surface_submaps/wilderness/spider1.dmm | 12 + sound/effects/break_stone.ogg | Bin 0 -> 35871 bytes sound/effects/servostep.ogg | Bin 0 -> 10714 bytes sound/effects/suitstep1.ogg | Bin 0 -> 10777 bytes sound/effects/suitstep2.ogg | Bin 0 -> 12220 bytes vorestation.dme | 120 +- 379 files changed, 22460 insertions(+), 4751 deletions(-) create mode 100644 code/controllers/subsystems/ai.dm rename code/{modules/mob/living/simple_animal/constructs => game/gamemodes/cult}/soulstone.dm (88%) delete mode 100644 code/game/gamemodes/technomancer/assistance/golem.dm mode change 100755 => 100644 code/game/objects/items/weapons/AI_modules.dm create mode 100644 code/modules/ai/__readme.dm create mode 100644 code/modules/ai/_defines.dm create mode 100644 code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm create mode 100644 code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm create mode 100644 code/modules/ai/ai_holder.dm create mode 100644 code/modules/ai/ai_holder_combat.dm create mode 100644 code/modules/ai/ai_holder_combat_unseen.dm create mode 100644 code/modules/ai/ai_holder_communication.dm create mode 100644 code/modules/ai/ai_holder_cooperation.dm create mode 100644 code/modules/ai/ai_holder_debug.dm create mode 100644 code/modules/ai/ai_holder_disabled.dm create mode 100644 code/modules/ai/ai_holder_fleeing.dm create mode 100644 code/modules/ai/ai_holder_follow.dm create mode 100644 code/modules/ai/ai_holder_movement.dm create mode 100644 code/modules/ai/ai_holder_pathfinding.dm create mode 100644 code/modules/ai/ai_holder_targeting.dm create mode 100644 code/modules/ai/interfaces.dm create mode 100644 code/modules/ai/say_list.dm create mode 100644 code/modules/mob/_modifiers/auras.dm delete mode 100644 code/modules/mob/living/simple_animal/constructs/constructs.dm delete mode 100644 code/modules/mob/living/simple_animal/humanoids/head.dm create mode 100644 code/modules/mob/living/simple_mob/appearance.dm create mode 100644 code/modules/mob/living/simple_mob/combat.dm create mode 100644 code/modules/mob/living/simple_mob/defense.dm create mode 100644 code/modules/mob/living/simple_mob/hands.dm create mode 100644 code/modules/mob/living/simple_mob/life.dm create mode 100644 code/modules/mob/living/simple_mob/on_click.dm create mode 100644 code/modules/mob/living/simple_mob/simple_hud.dm create mode 100644 code/modules/mob/living/simple_mob/simple_mob.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/animal.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/electric.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/frost.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/pepper.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/phorogenic.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/thermic.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/tunneler.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/webslinger.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/sif/sif.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/blob/blob.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/blob/spore.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/occult/constructs/artificer.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/occult/constructs/harvester.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/occult/constructs/shade.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/occult/constructs/wraith.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/occult/creature.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/plant/tree.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/slime/slime.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm create mode 100644 code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm create mode 100644 html/changelogs/Neerti-AI.yml create mode 100644 sound/effects/break_stone.ogg create mode 100644 sound/effects/servostep.ogg create mode 100644 sound/effects/suitstep1.ogg create mode 100644 sound/effects/suitstep2.ogg diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm index e5d2b7eb11..394fe72478 100644 --- a/code/ZAS/Airflow.dm +++ b/code/ZAS/Airflow.dm @@ -22,9 +22,6 @@ mob/proc/airflow_stun() mob/living/silicon/airflow_stun() return -mob/living/simple_animal/slime/airflow_stun() - return - mob/living/carbon/human/airflow_stun() if(shoes && (shoes.item_flags & NOSLIP)) to_chat(src, "Air suddenly rushes past you!") diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm index 4d258cd91f..e37bef86d4 100644 --- a/code/__defines/_planes+layers.dm +++ b/code/__defines/_planes+layers.dm @@ -58,7 +58,10 @@ What is the naming convention for planes or layers? #define ATMOS_LAYER 2.4 // Pipe-like atmos machinery that goes on the floor, like filters. #define ABOVE_UTILITY 2.5 // Above stuff like pipes and wires #define TURF_PLANE -45 // Turfs themselves, most flooring - #define ABOVE_TURF_LAYER 2.1 // Snow and wallmounted/floormounted equipment + #define WATER_FLOOR_LAYER 2.0 // The 'bottom' of water tiles. + #define UNDERWATER_LAYER 2.5 // Anything on this layer will render under the water layer. + #define WATER_LAYER 3.0 // Layer for water overlays. + #define ABOVE_TURF_LAYER 3.1 // Snow and wallmounted/floormounted equipment #define DECAL_PLANE -44 // Permanent decals #define DIRTY_PLANE -43 // Nonpermanent decals #define BLOOD_PLANE -42 // Blood is really dirty, but we can do special stuff if we separate it diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index aa334224ad..527bf55d93 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -302,4 +302,4 @@ var/global/list/##LIST_NAME = list();\ #define RCD_SHEETS_PER_MATTER_UNIT 4 // Each physical material sheet is worth four matter units. -#define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT \ No newline at end of file +#define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 679962b210..3221a595e4 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -27,6 +27,9 @@ #define BORGXRAY 0x4 #define BORGMATERIAL 8 +#define STANCE_ATTACK 11 // Backwards compatability +#define STANCE_ATTACKING 12 // Ditto +/* #define STANCE_IDLE 1 // Looking for targets if hostile. Does idle wandering. #define STANCE_ALERT 2 // Bears #define STANCE_ATTACK 3 // Attempting to get into attack position @@ -34,6 +37,20 @@ #define STANCE_TIRED 5 // Bears #define STANCE_FOLLOW 6 // Following somone #define STANCE_BUSY 7 // Do nothing on life ticks (Other code is running) +*/ +#define STANCE_SLEEP 0 // Doing (almost) nothing, to save on CPU because nobody is around to notice or the mob died. +#define STANCE_IDLE 1 // The more or less default state. Wanders around, looks for baddies, and spouts one-liners. +#define STANCE_ALERT 2 // A baddie is visible but not too close, and essentially we tell them to go away or die. +#define STANCE_APPROACH 3 // Attempting to get into range to attack them. +#define STANCE_FIGHT 4 // Actually fighting, with melee or ranged. +#define STANCE_BLINDFIGHT 5 // Fighting something that cannot be seen by the mob, from invisibility or out of sight. +#define STANCE_REPOSITION 6 // Relocating to a better position while in combat. Also used when moving away from a danger like grenades. +#define STANCE_MOVE 7 // Similar to above but for out of combat. If a baddie is seen, they'll cancel and fight them. +#define STANCE_FOLLOW 8 // Following somone, without trying to murder them. +#define STANCE_FLEE 9 // Run away from the target because they're too spooky/we're dying/some other reason. +#define STANCE_DISABLED 10 // Used when the holder is afflicted with certain status effects, such as stuns or confusion. + +#define STANCES_COMBAT list(STANCE_ALERT, STANCE_APPROACH, STANCE_FIGHT, STANCE_BLINDFIGHT, STANCE_REPOSITION) #define LEFT 0x1 #define RIGHT 0x2 @@ -279,11 +296,34 @@ #define SA_ROBOTIC 3 #define SA_HUMANOID 4 +// More refined version of SA_* ""intelligence"" seperators. +// Now includes bitflags, so to target two classes you just do 'MOB_CLASS_ANIMAL|MOB_CLASS_HUMANOID' +#define MOB_CLASS_NONE 0 // Default value, and used to invert for _ALL. + +#define MOB_CLASS_PLANT 1 // Unused at the moment. +#define MOB_CLASS_ANIMAL 2 // Animals and beasts like spiders, saviks, and bears. +#define MOB_CLASS_HUMANOID 4 // Non-robotic humanoids, including /simple_mob and /carbon/humans and their alien variants. +#define MOB_CLASS_SYNTHETIC 8 // Silicons, mechanical simple mobs, FBPs, and anything else that would pass is_synthetic() +#define MOB_CLASS_SLIME 16 // Everyone's favorite xenobiology specimen (and maybe prometheans?). +#define MOB_CLASS_ABERRATION 32 // Weird shit. +#define MOB_CLASS_DEMONIC 64 // Cult stuff. +#define MOB_CLASS_BOSS 128 // Future megafauna hopefully someday. +#define MOB_CLASS_ILLUSION 256 // Fake mobs, e.g. Technomancer illusions. +#define MOB_CLASS_PHOTONIC 512 // Holographic mobs like holocarp, similar to _ILLUSION, but that make no attempt to hide their true nature. + +#define MOB_CLASS_ALL (~MOB_CLASS_NONE) + // For slime commanding. Higher numbers allow for more actions. #define SLIME_COMMAND_OBEY 1 // When disciplined. #define SLIME_COMMAND_FACTION 2 // When in the same 'faction'. #define SLIME_COMMAND_FRIEND 3 // When befriended with a slime friendship agent. +// Threshold for mobs being able to damage things like airlocks or reinforced glass windows. +// If the damage is below this, nothing will happen besides a message saying that the attack was ineffective. +// Generally, this was not a define but was commonly set to 10, however 10 may be too low now since simple_mobs now attack twice as fast, +// at half damage compared to the old mob system, meaning mobs who could hurt structures may not be able to now, so now it is 5. +#define STRUCTURE_MIN_DAMAGE_THRESHOLD 5 + //Vision flags, for dealing with plane visibility #define VIS_FULLBRIGHT 1 #define VIS_LIGHTING 2 diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index 5c2d9a8ca3..391aa3a117 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -60,6 +60,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define INIT_ORDER_OVERLAY -6 #define INIT_ORDER_XENOARCH -20 #define INIT_ORDER_CIRCUIT -21 +#define INIT_ORDER_AI -22 // Subsystem fire priority, from lowest to highest priority @@ -67,6 +68,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define FIRE_PRIORITY_SHUTTLES 5 #define FIRE_PRIORITY_ORBIT 8 #define FIRE_PRIORITY_VOTE 9 +#define FIRE_PRIORITY_AI 10 #define FIRE_PRIORITY_GARBAGE 15 #define FIRE_PRIORITY_AIRFLOW 30 #define FIRE_PRIORITY_AIR 35 diff --git a/code/_global_vars/misc.dm b/code/_global_vars/misc.dm index 5e7737f163..504246e304 100644 --- a/code/_global_vars/misc.dm +++ b/code/_global_vars/misc.dm @@ -2,4 +2,4 @@ GLOBAL_LIST_EMPTY(all_observable_events) GLOBAL_VAR_INIT(timezoneOffset, 0) // The difference betwen midnight (of the host computer) and 0 world.ticks. -GLOBAL_VAR_INIT(TAB, "    ") \ No newline at end of file +GLOBAL_VAR_INIT(TAB, "    ") diff --git a/code/_global_vars/mobs.dm b/code/_global_vars/mobs.dm index fac5a14036..a5099a68d2 100644 --- a/code/_global_vars/mobs.dm +++ b/code/_global_vars/mobs.dm @@ -2,4 +2,4 @@ GLOBAL_LIST_EMPTY(admins) //all clients whom are admins GLOBAL_PROTECT(admins) GLOBAL_LIST_EMPTY(deadmins) //all ckeys who have used the de-admin verb. GLOBAL_LIST_EMPTY(stealthminID) -GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client \ No newline at end of file +GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm index ce640731a6..8348796665 100644 --- a/code/_helpers/time.dm +++ b/code/_helpers/time.dm @@ -239,4 +239,4 @@ var/round_start_time = 0 else day = "[truncate ? "day" : "1 day"]" - return "[day][hour][minute][second]" \ No newline at end of file + return "[day][hour][minute][second]" diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 7f1ff432e8..1836fbf30f 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -501,7 +501,7 @@ Turf and target are seperate in case you want to teleport some distance from a t moblist.Add(M) for(var/mob/new_player/M in sortmob) moblist.Add(M) - for(var/mob/living/simple_animal/M in sortmob) + for(var/mob/living/simple_mob/M in sortmob) moblist.Add(M) // for(var/mob/living/silicon/hivebot/M in sortmob) // mob_list.Add(M) diff --git a/code/_macros.dm b/code/_macros.dm index 9cd11a6c15..c977617d4a 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -10,7 +10,7 @@ #define isalien(A) istype(A, /mob/living/carbon/alien) -#define isanimal(A) istype(A, /mob/living/simple_animal) +#define isanimal(A) istype(A, /mob/living/simple_mob) #define isairlock(A) istype(A, /obj/machinery/door/airlock) @@ -18,7 +18,7 @@ #define iscarbon(A) istype(A, /mob/living/carbon) -#define iscorgi(A) istype(A, /mob/living/simple_animal/corgi) +#define iscorgi(A) istype(A, /mob/living/simple_mob/animal/passive/dog/corgi) #define isEye(A) istype(A, /mob/observer/eye) @@ -26,7 +26,7 @@ #define isliving(A) istype(A, /mob/living) -#define ismouse(A) istype(A, /mob/living/simple_animal/mouse) +#define ismouse(A) istype(A, /mob/living/simple_mob/animal/passive/mouse) #define isnewplayer(A) istype(A, /mob/new_player) @@ -42,11 +42,11 @@ #define isvoice(A) istype(A, /mob/living/voice) -#define isslime(A) istype(A, /mob/living/simple_animal/slime) +#define isslime(A) istype(A, /mob/living/simple_mob/slime) #define isbot(A) istype(A, /mob/living/bot) -#define isxeno(A) istype(A, /mob/living/simple_animal/xeno) +#define isxeno(A) istype(A, /mob/living/simple_mob/animal/space/alien) #define isopenspace(A) istype(A, /turf/simulated/open) diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index 94a067200a..2a92ba20bc 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -98,7 +98,7 @@ /obj/screen/fullscreen/flash icon = 'icons/mob/screen1.dmi' screen_loc = "WEST,SOUTH to EAST,NORTH" - icon_state = "flash" + icon_state = "flash_static" /obj/screen/fullscreen/flash/noise icon_state = "noise" diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 8b32d1447e..fef0301c81 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -316,8 +316,6 @@ datum/hud/New(mob/owner) mymob.instantiate_hud(src) else if(isalien(mymob)) larva_hud() - else if(isslime(mymob)) - slime_hud() else if(isAI(mymob)) ai_hud() else if(isobserver(mymob)) diff --git a/code/_onclick/hud/other_mobs.dm b/code/_onclick/hud/other_mobs.dm index f1b9165a26..b0f55f5503 100644 --- a/code/_onclick/hud/other_mobs.dm +++ b/code/_onclick/hud/other_mobs.dm @@ -23,7 +23,7 @@ mymob.client.screen += list(blobpwrdisplay, blobhealthdisplay) mymob.client.screen += mymob.client.void - +/* /datum/hud/proc/slime_hud(ui_style = 'icons/mob/screen1_Midnight.dmi') src.adding = list() @@ -92,10 +92,7 @@ mymob.client.screen += mymob.client.void return - - -/mob/living/simple_animal/construct/instantiate_hud(var/datum/hud/HUD) - ..(HUD) +*/ // HUD.construct_hud() //Archaic. /* diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index d1a61ec8f4..a1f2335a9d 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -60,7 +60,7 @@ avoid code duplication. This includes items that may sometimes act as a standard // Same as above but actually does useful things. // W is the item being used in the attack, if any. modifier is if the attack should be longer or shorter than usual, for whatever reason. /mob/living/get_attack_speed(var/obj/item/W) - var/speed = DEFAULT_ATTACK_COOLDOWN + var/speed = base_attack_cooldown if(W && istype(W)) speed = W.attackspeed for(var/datum/modifier/M in modifiers) diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index 729330ddaa..e367769b07 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -68,58 +68,3 @@ */ /mob/new_player/ClickOn() return - -/* - Animals -*/ -/mob/living/simple_animal/UnarmedAttack(var/atom/A, var/proximity) - if(!(. = ..())) - return - - setClickCooldown(get_attack_speed()) - - if(has_hands && istype(A,/obj) && a_intent != I_HURT) - var/obj/O = A - return O.attack_hand(src) - - switch(a_intent) - if(I_HELP) - if(isliving(A)) - custom_emote(1,"[pick(friendly)] [A]!") - - if(I_HURT) - if(prob(spattack_prob)) - if(spattack_min_range <= 1) - SpecialAtkTarget() - - - else if(melee_damage_upper == 0 && istype(A,/mob/living)) - custom_emote(1,"[pick(friendly)] [A]!") - - - else - DoPunch(A) - - if(I_GRAB) - if(has_hands) - A.attack_hand(src) - - if(I_DISARM) - if(has_hands) - A.attack_hand(src) - -/mob/living/simple_animal/RangedAttack(var/atom/A) - setClickCooldown(get_attack_speed()) - var/distance = get_dist(src, A) - - if(prob(spattack_prob) && (distance >= spattack_min_range) && (distance <= spattack_max_range)) - target_mob = A - SpecialAtkTarget() - target_mob = null - return - - if(ranged && distance <= shoot_range) - target_mob = A - ShootTarget(A) - target_mob = null - diff --git a/code/controllers/subsystems/ai.dm b/code/controllers/subsystems/ai.dm new file mode 100644 index 0000000000..8977818755 --- /dev/null +++ b/code/controllers/subsystems/ai.dm @@ -0,0 +1,36 @@ +SUBSYSTEM_DEF(ai) + name = "AI" + init_order = INIT_ORDER_AI + priority = FIRE_PRIORITY_AI + wait = 5 // This gets run twice a second, however this is technically two loops in one, with the second loop being run every four iterations. + flags = SS_NO_INIT|SS_TICKER + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + + var/list/processing = list() + var/list/currentrun = list() + +/datum/controller/subsystem/ai/stat_entry(msg_prefix) + var/list/msg = list(msg_prefix) + msg += "P:[processing.len]" + ..(msg.Join()) + +/datum/controller/subsystem/ai/fire(resumed = 0) + if (!resumed) + src.currentrun = processing.Copy() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + + while(currentrun.len) + // var/mob/living/L = currentrun[currentrun.len] + var/datum/ai_holder/A = currentrun[currentrun.len] + --currentrun.len + if(!A || QDELETED(A)) // Doesn't exist or won't exist soon. + continue + if(times_fired % 4 == 0 && A.holder.stat != DEAD) + A.handle_strategicals() + if(A.holder.stat != DEAD) // The /TG/ version checks stat twice, presumably in-case processing somehow got the mob killed in that instant. + A.handle_tactics() + + if(MC_TICK_CHECK) + return diff --git a/code/controllers/subsystems/vote.dm b/code/controllers/subsystems/vote.dm index d42cbe7f63..480e0da813 100644 --- a/code/controllers/subsystems/vote.dm +++ b/code/controllers/subsystems/vote.dm @@ -1,373 +1,373 @@ -SUBSYSTEM_DEF(vote) - name = "Vote" - wait = 10 - priority = FIRE_PRIORITY_VOTE - runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT - flags = SS_KEEP_TIMING | SS_NO_INIT - var/list/round_voters = list() - - //Current vote - var/initiator - var/started_time - var/time_remaining - var/duration - var/mode - var/question - var/list/choices = list() - var/list/gamemode_names = list() - var/list/voted = list() - var/list/current_votes = list() - var/list/additional_text = list() - -/datum/controller/subsystem/vote/fire(resumed) - if(mode) - time_remaining = round((started_time + duration - world.time)/10) - if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP) - to_chat(world, "Gamemode vote aborted: Game has already started.") - reset() - return - if(time_remaining <= 0) - result() - reset() - -/datum/controller/subsystem/vote/proc/autotransfer() - initiate_vote(VOTE_CREW_TRANSFER, "the server", 1) - log_debug("The server has called a crew transfer vote.") - -/datum/controller/subsystem/vote/proc/autogamemode() - initiate_vote(VOTE_GAMEMODE, "the server", 1) - log_debug("The server has called a gamemode vote.") - -/datum/controller/subsystem/vote/proc/reset() - initiator = null - started_time = null - duration = null - time_remaining = null - mode = null - question = null - choices.Cut() - voted.Cut() - current_votes.Cut() - additional_text.Cut() - -/datum/controller/subsystem/vote/proc/get_result() // Get the highest number of votes - var/greatest_votes = 0 - var/total_votes = 0 - - for(var/option in choices) - var/votes = choices[option] - total_votes += votes - if(votes > greatest_votes) - greatest_votes = votes - - if(!config.vote_no_default && choices.len) // Default-vote for everyone who didn't vote - var/non_voters = (clients.len - total_votes) - if(non_voters > 0) - if(mode == VOTE_RESTART) - choices["Continue Playing"] += non_voters - if(choices["Continue Playing"] >= greatest_votes) - greatest_votes = choices["Continue Playing"] - else if(mode == VOTE_GAMEMODE) - if(master_mode in choices) - choices[master_mode] += non_voters - if(choices[master_mode] >= greatest_votes) - greatest_votes = choices[master_mode] - else if(mode == VOTE_CREW_TRANSFER) - var/factor = 0.5 - switch(world.time / (10 * 60)) // minutes - if(0 to 60) - factor = 0.5 - if(61 to 120) - factor = 0.8 - if(121 to 240) - factor = 1 - if(241 to 300) - factor = 1.2 - else - factor = 1.4 - choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) - world << "Crew Transfer Factor: [factor]" - greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"]) - - . = list() // Get all options with that many votes and return them in a list - if(greatest_votes) - for(var/option in choices) - if(choices[option] == greatest_votes) - . += option - -/datum/controller/subsystem/vote/proc/announce_result() - var/list/winners = get_result() - var/text - if(winners.len > 0) - if(winners.len > 1) - if(mode != VOTE_GAMEMODE || ticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes - text = "Vote Tied Between:\n" - for(var/option in winners) - text += "\t[option]\n" - . = pick(winners) - - for(var/key in current_votes) - if(choices[current_votes[key]] == .) - round_voters += key // Keep track of who voted for the winning round. - if(mode != VOTE_GAMEMODE || . == "Extended" || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes - text += "Vote Result: [mode == VOTE_GAMEMODE ? gamemode_names[.] : .]" - else - text += "The vote has ended." - - else - text += "Vote Result: Inconclusive - No Votes!" - if(mode == VOTE_ADD_ANTAGONIST) - antag_add_failed = 1 - log_vote(text) - to_chat(world, "[text]") - -/datum/controller/subsystem/vote/proc/result() - . = announce_result() - var/restart = 0 - if(.) - switch(mode) - if(VOTE_RESTART) - if(. == "Restart Round") - restart = 1 - if(VOTE_GAMEMODE) - if(master_mode != .) - world.save_mode(.) - if(ticker && ticker.mode) - restart = 1 - else - master_mode = . - if(VOTE_CREW_TRANSFER) - if(. == "Initiate Crew Transfer") - init_shift_change(null, 1) - if(VOTE_ADD_ANTAGONIST) - if(isnull(.) || . == "None") - antag_add_failed = 1 - else - additional_antag_types |= antag_names_to_ids[.] - - if(mode == VOTE_GAMEMODE) //fire this even if the vote fails. - if(!round_progressing) - round_progressing = 1 - world << "The round will start soon." - - if(restart) - world << "World restarting due to vote..." - feedback_set_details("end_error", "restart vote") - if(blackbox) - blackbox.save_all_data_to_sql() - sleep(50) - log_game("Rebooting due to restart vote") - world.Reboot() - -/datum/controller/subsystem/vote/proc/submit_vote(ckey, newVote) - if(mode) - if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder) - return - if(current_votes[ckey]) - choices[choices[current_votes[ckey]]]-- - if(newVote && newVote >= 1 && newVote <= choices.len) - choices[choices[newVote]]++ - current_votes[ckey] = newVote - else - current_votes[ckey] = null - -/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period) - if(!mode) - if(started_time != null && !(check_rights(R_ADMIN) || automatic)) - var/next_allowed_time = (started_time + config.vote_delay) - if(next_allowed_time > world.time) - return 0 - - reset() - - switch(vote_type) - if(VOTE_RESTART) - choices.Add("Restart Round", "Continue Playing") - if(VOTE_GAMEMODE) - if(ticker.current_state >= GAME_STATE_SETTING_UP) - return 0 - choices.Add(config.votable_modes) - for(var/F in choices) - var/datum/game_mode/M = gamemode_cache[F] - if(!M) - continue - gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works - additional_text.Add("[M.required_players]") - gamemode_names["secret"] = "Secret" - if(VOTE_CREW_TRANSFER) - if(!check_rights(R_ADMIN|R_MOD, 0)) // The gods care not for the affairs of the mortals - if(get_security_level() == "red" || get_security_level() == "delta") - initiator_key << "The current alert status is too high to call for a crew transfer!" - return 0 - if(ticker.current_state <= GAME_STATE_SETTING_UP) - initiator_key << "The crew transfer button has been disabled!" - return 0 - question = "End the shift?" - choices.Add("Initiate Crew Transfer", "Continue The Round") - if(VOTE_ADD_ANTAGONIST) - if(!config.allow_extra_antags || ticker.current_state >= GAME_STATE_SETTING_UP) - return 0 - for(var/antag_type in all_antag_types) - var/datum/antagonist/antag = all_antag_types[antag_type] - if(!(antag.id in additional_antag_types) && antag.is_votable()) - choices.Add(antag.role_text) - choices.Add("None") - if(VOTE_CUSTOM) - question = sanitizeSafe(input(usr, "What is the vote for?") as text|null) - if(!question) - return 0 - for(var/i = 1 to 10) - var/option = capitalize(sanitize(input(usr, "Please enter an option or hit cancel to finish") as text|null)) - if(!option || mode || !usr.client) - break - choices.Add(option) - else - return 0 - - mode = vote_type - initiator = initiator_key - started_time = world.time - duration = time - var/text = "[capitalize(mode)] vote started by [initiator]." - if(mode == VOTE_CUSTOM) - text += "\n[question]" - - log_vote(text) - - world << "[text]\nType vote or click here to place your votes.\nYou have [config.vote_period / 10] seconds to vote." - if(vote_type == VOTE_CREW_TRANSFER || vote_type == VOTE_GAMEMODE || vote_type == VOTE_CUSTOM) - world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3) - - if(mode == VOTE_GAMEMODE && round_progressing) - round_progressing = 0 - world << "Round start has been delayed." - - time_remaining = round(config.vote_period / 10) - return 1 - return 0 - -/datum/controller/subsystem/vote/proc/interface(var/client/C) - if(!istype(C)) - return - var/admin = FALSE - if(C.holder) - if(C.holder.rights & R_ADMIN) - admin = TRUE - - . = "Voting Panel" - if(mode) - if(question) - . += "

Vote: '[question]'

" - else - . += "

Vote: [capitalize(mode)]

" - . += "Time Left: [time_remaining] s
" - . += "" - if(mode == VOTE_GAMEMODE) - .+= "" - - for(var/i = 1 to choices.len) - var/votes = choices[choices[i]] - if(!votes) - votes = 0 - . += "" - var/thisVote = (current_votes[C.ckey] == i) - if(mode == VOTE_GAMEMODE) - . += "" - else - . += "" - if (additional_text.len >= i) - . += additional_text[i] - . += "" - - . += "" - - . += "
ChoicesVotesMinimum Players
[thisVote ? "" : ""][gamemode_names[choices[i]]][thisVote ? "" : ""][votes][thisVote ? "" : ""][choices[i]][thisVote ? "" : ""][votes]
Unvote

" - if(admin) - . += "(Cancel Vote) " - else - . += "

Start a vote:



" - - . += "Close" - -/datum/controller/subsystem/vote/Topic(href, href_list[]) - if(!usr || !usr.client) - return - switch(href_list["vote"]) - if("close") - usr << browse(null, "window=vote") - return - - if("cancel") - if(usr.client.holder) - reset() - if("toggle_restart") - if(usr.client.holder) - config.allow_vote_restart = !config.allow_vote_restart - if("toggle_gamemode") - if(usr.client.holder) - config.allow_vote_mode = !config.allow_vote_mode - - if(VOTE_RESTART) - if(config.allow_vote_restart || usr.client.holder) - initiate_vote(VOTE_RESTART, usr.key) - if(VOTE_GAMEMODE) - if(config.allow_vote_mode || usr.client.holder) - initiate_vote(VOTE_GAMEMODE, usr.key) - if(VOTE_CREW_TRANSFER) - if(config.allow_vote_restart || usr.client.holder) - initiate_vote(VOTE_CREW_TRANSFER, usr.key) - if(VOTE_ADD_ANTAGONIST) - if(config.allow_extra_antags || usr.client.holder) - initiate_vote(VOTE_ADD_ANTAGONIST, usr.key) - if(VOTE_CUSTOM) - if(usr.client.holder) - initiate_vote(VOTE_CUSTOM, usr.key) - - if("unvote") - submit_vote(usr.ckey, null) - - else - var/t = round(text2num(href_list["vote"])) - if(t) // It starts from 1, so there's no problem - submit_vote(usr.ckey, t) - usr.client.vote() - -/client/verb/vote() - set category = "OOC" - set name = "Vote" - - if(SSvote) - src << browse(SSvote.interface(src), "window=vote;size=500x[300 + SSvote.choices.len * 25]") +SUBSYSTEM_DEF(vote) + name = "Vote" + wait = 10 + priority = FIRE_PRIORITY_VOTE + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + flags = SS_KEEP_TIMING | SS_NO_INIT + var/list/round_voters = list() + + //Current vote + var/initiator + var/started_time + var/time_remaining + var/duration + var/mode + var/question + var/list/choices = list() + var/list/gamemode_names = list() + var/list/voted = list() + var/list/current_votes = list() + var/list/additional_text = list() + +/datum/controller/subsystem/vote/fire(resumed) + if(mode) + time_remaining = round((started_time + duration - world.time)/10) + if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP) + to_chat(world, "Gamemode vote aborted: Game has already started.") + reset() + return + if(time_remaining <= 0) + result() + reset() + +/datum/controller/subsystem/vote/proc/autotransfer() + initiate_vote(VOTE_CREW_TRANSFER, "the server", 1) + log_debug("The server has called a crew transfer vote.") + +/datum/controller/subsystem/vote/proc/autogamemode() + initiate_vote(VOTE_GAMEMODE, "the server", 1) + log_debug("The server has called a gamemode vote.") + +/datum/controller/subsystem/vote/proc/reset() + initiator = null + started_time = null + duration = null + time_remaining = null + mode = null + question = null + choices.Cut() + voted.Cut() + current_votes.Cut() + additional_text.Cut() + +/datum/controller/subsystem/vote/proc/get_result() // Get the highest number of votes + var/greatest_votes = 0 + var/total_votes = 0 + + for(var/option in choices) + var/votes = choices[option] + total_votes += votes + if(votes > greatest_votes) + greatest_votes = votes + + if(!config.vote_no_default && choices.len) // Default-vote for everyone who didn't vote + var/non_voters = (clients.len - total_votes) + if(non_voters > 0) + if(mode == VOTE_RESTART) + choices["Continue Playing"] += non_voters + if(choices["Continue Playing"] >= greatest_votes) + greatest_votes = choices["Continue Playing"] + else if(mode == VOTE_GAMEMODE) + if(master_mode in choices) + choices[master_mode] += non_voters + if(choices[master_mode] >= greatest_votes) + greatest_votes = choices[master_mode] + else if(mode == VOTE_CREW_TRANSFER) + var/factor = 0.5 + switch(world.time / (10 * 60)) // minutes + if(0 to 60) + factor = 0.5 + if(61 to 120) + factor = 0.8 + if(121 to 240) + factor = 1 + if(241 to 300) + factor = 1.2 + else + factor = 1.4 + choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) + world << "Crew Transfer Factor: [factor]" + greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"]) + + . = list() // Get all options with that many votes and return them in a list + if(greatest_votes) + for(var/option in choices) + if(choices[option] == greatest_votes) + . += option + +/datum/controller/subsystem/vote/proc/announce_result() + var/list/winners = get_result() + var/text + if(winners.len > 0) + if(winners.len > 1) + if(mode != VOTE_GAMEMODE || ticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes + text = "Vote Tied Between:\n" + for(var/option in winners) + text += "\t[option]\n" + . = pick(winners) + + for(var/key in current_votes) + if(choices[current_votes[key]] == .) + round_voters += key // Keep track of who voted for the winning round. + if(mode != VOTE_GAMEMODE || . == "Extended" || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes + text += "Vote Result: [mode == VOTE_GAMEMODE ? gamemode_names[.] : .]" + else + text += "The vote has ended." + + else + text += "Vote Result: Inconclusive - No Votes!" + if(mode == VOTE_ADD_ANTAGONIST) + antag_add_failed = 1 + log_vote(text) + to_chat(world, "[text]") + +/datum/controller/subsystem/vote/proc/result() + . = announce_result() + var/restart = 0 + if(.) + switch(mode) + if(VOTE_RESTART) + if(. == "Restart Round") + restart = 1 + if(VOTE_GAMEMODE) + if(master_mode != .) + world.save_mode(.) + if(ticker && ticker.mode) + restart = 1 + else + master_mode = . + if(VOTE_CREW_TRANSFER) + if(. == "Initiate Crew Transfer") + init_shift_change(null, 1) + if(VOTE_ADD_ANTAGONIST) + if(isnull(.) || . == "None") + antag_add_failed = 1 + else + additional_antag_types |= antag_names_to_ids[.] + + if(mode == VOTE_GAMEMODE) //fire this even if the vote fails. + if(!round_progressing) + round_progressing = 1 + world << "The round will start soon." + + if(restart) + world << "World restarting due to vote..." + feedback_set_details("end_error", "restart vote") + if(blackbox) + blackbox.save_all_data_to_sql() + sleep(50) + log_game("Rebooting due to restart vote") + world.Reboot() + +/datum/controller/subsystem/vote/proc/submit_vote(ckey, newVote) + if(mode) + if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder) + return + if(current_votes[ckey]) + choices[choices[current_votes[ckey]]]-- + if(newVote && newVote >= 1 && newVote <= choices.len) + choices[choices[newVote]]++ + current_votes[ckey] = newVote + else + current_votes[ckey] = null + +/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period) + if(!mode) + if(started_time != null && !(check_rights(R_ADMIN) || automatic)) + var/next_allowed_time = (started_time + config.vote_delay) + if(next_allowed_time > world.time) + return 0 + + reset() + + switch(vote_type) + if(VOTE_RESTART) + choices.Add("Restart Round", "Continue Playing") + if(VOTE_GAMEMODE) + if(ticker.current_state >= GAME_STATE_SETTING_UP) + return 0 + choices.Add(config.votable_modes) + for(var/F in choices) + var/datum/game_mode/M = gamemode_cache[F] + if(!M) + continue + gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works + additional_text.Add("[M.required_players]") + gamemode_names["secret"] = "Secret" + if(VOTE_CREW_TRANSFER) + if(!check_rights(R_ADMIN|R_MOD, 0)) // The gods care not for the affairs of the mortals + if(get_security_level() == "red" || get_security_level() == "delta") + initiator_key << "The current alert status is too high to call for a crew transfer!" + return 0 + if(ticker.current_state <= GAME_STATE_SETTING_UP) + initiator_key << "The crew transfer button has been disabled!" + return 0 + question = "End the shift?" + choices.Add("Initiate Crew Transfer", "Continue The Round") + if(VOTE_ADD_ANTAGONIST) + if(!config.allow_extra_antags || ticker.current_state >= GAME_STATE_SETTING_UP) + return 0 + for(var/antag_type in all_antag_types) + var/datum/antagonist/antag = all_antag_types[antag_type] + if(!(antag.id in additional_antag_types) && antag.is_votable()) + choices.Add(antag.role_text) + choices.Add("None") + if(VOTE_CUSTOM) + question = sanitizeSafe(input(usr, "What is the vote for?") as text|null) + if(!question) + return 0 + for(var/i = 1 to 10) + var/option = capitalize(sanitize(input(usr, "Please enter an option or hit cancel to finish") as text|null)) + if(!option || mode || !usr.client) + break + choices.Add(option) + else + return 0 + + mode = vote_type + initiator = initiator_key + started_time = world.time + duration = time + var/text = "[capitalize(mode)] vote started by [initiator]." + if(mode == VOTE_CUSTOM) + text += "\n[question]" + + log_vote(text) + + world << "[text]\nType vote or click here to place your votes.\nYou have [config.vote_period / 10] seconds to vote." + if(vote_type == VOTE_CREW_TRANSFER || vote_type == VOTE_GAMEMODE || vote_type == VOTE_CUSTOM) + world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3) + + if(mode == VOTE_GAMEMODE && round_progressing) + round_progressing = 0 + world << "Round start has been delayed." + + time_remaining = round(config.vote_period / 10) + return 1 + return 0 + +/datum/controller/subsystem/vote/proc/interface(var/client/C) + if(!istype(C)) + return + var/admin = FALSE + if(C.holder) + if(C.holder.rights & R_ADMIN) + admin = TRUE + + . = "Voting Panel" + if(mode) + if(question) + . += "

Vote: '[question]'

" + else + . += "

Vote: [capitalize(mode)]

" + . += "Time Left: [time_remaining] s
" + . += "" + if(mode == VOTE_GAMEMODE) + .+= "" + + for(var/i = 1 to choices.len) + var/votes = choices[choices[i]] + if(!votes) + votes = 0 + . += "" + var/thisVote = (current_votes[C.ckey] == i) + if(mode == VOTE_GAMEMODE) + . += "" + else + . += "" + if (additional_text.len >= i) + . += additional_text[i] + . += "" + + . += "" + + . += "
ChoicesVotesMinimum Players
[thisVote ? "" : ""][gamemode_names[choices[i]]][thisVote ? "" : ""][votes][thisVote ? "" : ""][choices[i]][thisVote ? "" : ""][votes]
Unvote

" + if(admin) + . += "(Cancel Vote) " + else + . += "

Start a vote:



" + + . += "Close" + +/datum/controller/subsystem/vote/Topic(href, href_list[]) + if(!usr || !usr.client) + return + switch(href_list["vote"]) + if("close") + usr << browse(null, "window=vote") + return + + if("cancel") + if(usr.client.holder) + reset() + if("toggle_restart") + if(usr.client.holder) + config.allow_vote_restart = !config.allow_vote_restart + if("toggle_gamemode") + if(usr.client.holder) + config.allow_vote_mode = !config.allow_vote_mode + + if(VOTE_RESTART) + if(config.allow_vote_restart || usr.client.holder) + initiate_vote(VOTE_RESTART, usr.key) + if(VOTE_GAMEMODE) + if(config.allow_vote_mode || usr.client.holder) + initiate_vote(VOTE_GAMEMODE, usr.key) + if(VOTE_CREW_TRANSFER) + if(config.allow_vote_restart || usr.client.holder) + initiate_vote(VOTE_CREW_TRANSFER, usr.key) + if(VOTE_ADD_ANTAGONIST) + if(config.allow_extra_antags || usr.client.holder) + initiate_vote(VOTE_ADD_ANTAGONIST, usr.key) + if(VOTE_CUSTOM) + if(usr.client.holder) + initiate_vote(VOTE_CUSTOM, usr.key) + + if("unvote") + submit_vote(usr.ckey, null) + + else + var/t = round(text2num(href_list["vote"])) + if(t) // It starts from 1, so there's no problem + submit_vote(usr.ckey, t) + usr.client.vote() + +/client/verb/vote() + set category = "OOC" + set name = "Vote" + + if(SSvote) + src << browse(SSvote.interface(src), "window=vote;size=500x[300 + SSvote.choices.len * 25]") diff --git a/code/datums/ghost_query.dm b/code/datums/ghost_query.dm index d041558a0c..2f7942a9b2 100644 --- a/code/datums/ghost_query.dm +++ b/code/datums/ghost_query.dm @@ -106,6 +106,13 @@ check_bans = list("AI", "Cyborg", "Syndicate") cutoff_number = 1 +/datum/ghost_query/borer + role_name = "Cortical Borer" + question = "A cortical borer has just been created on the facility. Would you like to play as them?" + be_special_flag = BE_ALIEN + check_bans = list("Syndicate", "Borer") + cutoff_number = 1 + // Surface stuff. /datum/ghost_query/lost_drone role_name = "Lost Drone" diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 5204964bda..fdca613460 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -502,7 +502,7 @@ if(!mind.assigned_role) mind.assigned_role = USELESS_JOB //defualt //VOREStation Edit - Visitor not Assistant //slime -/mob/living/simple_animal/slime/mind_initialize() +/mob/living/simple_mob/slime/mind_initialize() . = ..() mind.assigned_role = "slime" @@ -527,29 +527,30 @@ mind.special_role = "" //Animals -/mob/living/simple_animal/mind_initialize() +/mob/living/simple_mob/mind_initialize() . = ..() - mind.assigned_role = "Animal" + mind.assigned_role = "Simple Mob" -/mob/living/simple_animal/corgi/mind_initialize() +/mob/living/simple_mob/animal/passive/dog/corgi/mind_initialize() . = ..() mind.assigned_role = "Corgi" -/mob/living/simple_animal/shade/mind_initialize() +/mob/living/simple_mob/construct/shade/mind_initialize() . = ..() mind.assigned_role = "Shade" + mind.special_role = "Cultist" -/mob/living/simple_animal/construct/builder/mind_initialize() +/mob/living/simple_mob/construct/artificer/mind_initialize() . = ..() mind.assigned_role = "Artificer" mind.special_role = "Cultist" -/mob/living/simple_animal/construct/wraith/mind_initialize() +/mob/living/simple_mob/construct/wraith/mind_initialize() . = ..() mind.assigned_role = "Wraith" mind.special_role = "Cultist" -/mob/living/simple_animal/construct/armoured/mind_initialize() +/mob/living/simple_mob/construct/juggernaut/mind_initialize() . = ..() mind.assigned_role = "Juggernaut" mind.special_role = "Cultist" diff --git a/code/game/antagonist/alien/borer.dm b/code/game/antagonist/alien/borer.dm index e852bc8418..34b79271f7 100644 --- a/code/game/antagonist/alien/borer.dm +++ b/code/game/antagonist/alien/borer.dm @@ -5,7 +5,7 @@ var/datum/antagonist/borer/borers role_type = BE_ALIEN role_text = "Cortical Borer" role_text_plural = "Cortical Borers" - mob_path = /mob/living/simple_animal/borer + mob_path = /mob/living/simple_mob/animal/borer bantype = "Borer" welcome_text = "Use your Infest power to crawl into the ear of a host and fuse with their brain. You can only take control temporarily, and at risk of hurting your host, so be clever and careful; your host is encouraged to help you however they can. Talk to your fellow borers with :x." antag_indicator = "brainworm" @@ -40,7 +40,7 @@ var/datum/antagonist/borer/borers player.objectives += new /datum/objective/escape() /datum/antagonist/borer/place_mob(var/mob/living/mob) - var/mob/living/simple_animal/borer/borer = mob + var/mob/living/simple_mob/animal/borer/borer = mob if(istype(borer)) var/mob/living/carbon/human/host for(var/mob/living/carbon/human/H in mob_list) diff --git a/code/game/antagonist/station/cultist.dm b/code/game/antagonist/station/cultist.dm index e40085f90a..e5c7d8f652 100644 --- a/code/game/antagonist/station/cultist.dm +++ b/code/game/antagonist/station/cultist.dm @@ -111,12 +111,12 @@ var/datum/antagonist/cultist/cult . = ..() if(.) player << "You catch a glimpse of the Realm of Nar-Sie, the Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of That Which Waits. Assist your new compatriots in their dark dealings. Their goals are yours, and yours are theirs. You serve the Dark One above all else. Bring It back." - if(player.current && !istype(player.current, /mob/living/simple_animal/construct)) + if(player.current && !istype(player.current, /mob/living/simple_mob/construct)) player.current.add_language(LANGUAGE_CULT) /datum/antagonist/cultist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted) . = ..() - if(. && player.current && !istype(player.current, /mob/living/simple_animal/construct)) + if(. && player.current && !istype(player.current, /mob/living/simple_mob/construct)) player.current.remove_language(LANGUAGE_CULT) /datum/antagonist/cultist/can_become_antag(var/datum/mind/player) diff --git a/code/game/gamemodes/cult/construct_spells.dm b/code/game/gamemodes/cult/construct_spells.dm index f8ff57fc35..60a05e2e27 100644 --- a/code/game/gamemodes/cult/construct_spells.dm +++ b/code/game/gamemodes/cult/construct_spells.dm @@ -476,14 +476,14 @@ proc/findNullRod(var/atom/target) /obj/item/weapon/spell/construct/run_checks() if(owner) - if((iscultist(owner) || istype(owner, /mob/living/simple_animal/construct)) && (world.time >= (last_castcheck + cooldown))) //Are they a cultist or a construct, and has the cooldown time passed? + if((iscultist(owner) || istype(owner, /mob/living/simple_mob/construct)) && (world.time >= (last_castcheck + cooldown))) //Are they a cultist or a construct, and has the cooldown time passed? last_castcheck = world.time return 1 return 0 /obj/item/weapon/spell/construct/pay_energy(var/amount) if(owner) - if(istype(owner, /mob/living/simple_animal/construct)) + if(istype(owner, /mob/living/simple_mob/construct)) return 1 if(iscultist(owner) && pay_blood(amount)) return 1 @@ -607,7 +607,7 @@ proc/findNullRod(var/atom/target) name = "sphere of agony" desc = "Call forth a portal to a dimension of naught but pain at your target." - spawner_type = /obj/effect/temporary_effect/pulsar/agonizing_sphere + spawner_type = /obj/effect/temporary_effect/pulse/agonizing_sphere /obj/item/weapon/spell/construct/spawner/agonizing_sphere/on_ranged_cast(atom/hit_atom, mob/user) if(within_range(hit_atom) && pay_energy(10)) @@ -619,7 +619,7 @@ proc/findNullRod(var/atom/target) var/mob/living/L = hit_atom L.add_modifier(/datum/modifier/agonize, 10 SECONDS) -/obj/effect/temporary_effect/pulsar/agonizing_sphere +/obj/effect/temporary_effect/pulse/agonizing_sphere name = "agonizing sphere" desc = "A portal to some hellish place. Its screams wrack your body with pain.." icon_state = "red_static_sphere" @@ -628,19 +628,15 @@ proc/findNullRod(var/atom/target) light_power = 5 light_color = "#FF0000" pulses_remaining = 10 + pulse_delay = 1 SECOND -/obj/effect/temporary_effect/pulsar/agonizing_sphere/pulse_loop() - while(pulses_remaining) - sleep(1 SECONDS) - spawn() - for(var/mob/living/L in view(4,src)) - if(!iscultist(L) && !istype(L, /mob/living/simple_animal/construct)) - L.add_modifier(/datum/modifier/agonize, 2 SECONDS) - if(L.isSynthetic()) - to_chat(L, "Your chassis warps as the [src] pulses!") - L.adjustFireLoss(4) - pulses_remaining-- - qdel(src) +/obj/effect/temporary_effect/pulse/agonizing_sphere/on_pulse() + for(var/mob/living/L in view(4,src)) + if(!iscultist(L) && !istype(L, /mob/living/simple_mob/construct)) + L.add_modifier(/datum/modifier/agonize, 2 SECONDS) + if(L.isSynthetic()) + to_chat(L, "Your chassis warps as the [src] pulses!") + L.adjustFireLoss(4) //Artificer Heal @@ -659,7 +655,7 @@ proc/findNullRod(var/atom/target) L.add_modifier(/datum/modifier/mend_occult, 150) qdel(src) -//Juggernaut + Behemoth Slam +//Juggernaut Slam /obj/item/weapon/spell/construct/slam name = "slam" desc = "Empower your FIST, to send an opponent flying." @@ -672,8 +668,8 @@ proc/findNullRod(var/atom/target) /obj/item/weapon/spell/construct/slam/on_melee_cast(atom/hit_atom, mob/living/user, def_zone) var/attack_message = "slams" - if(istype(user, /mob/living/simple_animal)) - var/mob/living/simple_animal/S = user + if(istype(user, /mob/living/simple_mob)) + var/mob/living/simple_mob/S = user attack_message = pick(S.attacktext) if(isliving(hit_atom)) var/mob/living/L = hit_atom diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 082404b271..7118aafe80 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -14,7 +14,7 @@ return /obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone) - if(iscultist(user) && !istype(user, /mob/living/simple_animal/construct)) + if(iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) return ..() var/zone = (user.hand ? "l_arm":"r_arm") @@ -25,7 +25,7 @@ //random amount of damage between half of the blade's force and the full force of the blade. user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1) user.Weaken(5) - else if(!istype(user, /mob/living/simple_animal/construct)) + else if(!istype(user, /mob/living/simple_mob/construct)) to_chat(user, "An inexplicable force rips through you, tearing the sword from your grasp!") else to_chat(user, "The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.") @@ -39,10 +39,10 @@ return 1 /obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob) - if(!iscultist(user) && !istype(user, /mob/living/simple_animal/construct)) + if(!iscultist(user) && !istype(user, /mob/living/simple_mob/construct)) to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.") user.make_dizzy(120) - if(istype(user, /mob/living/simple_animal/construct)) + if(istype(user, /mob/living/simple_mob/construct)) to_chat(user, "\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.") /obj/item/clothing/head/culthood diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index d588299cc5..e337ca8c60 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -108,18 +108,25 @@ light_range=5 light_color="#ff0000" spawnable=list( +<<<<<<< HEAD /mob/living/simple_animal/hostile/scarybat, /mob/living/simple_animal/hostile/creature/vore, /mob/living/simple_animal/hostile/faithless ) // Vorestation Edit +======= + /mob/living/simple_mob/animal/space/bats, + /mob/living/simple_mob/creature, + /mob/living/simple_mob/faithless + ) +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync /obj/effect/gateway/active/cult light_range=5 light_color="#ff0000" spawnable=list( - /mob/living/simple_animal/hostile/scarybat/cult, - /mob/living/simple_animal/hostile/creature/cult, - /mob/living/simple_animal/hostile/faithless/cult + /mob/living/simple_mob/animal/space/bats/cult, + /mob/living/simple_mob/creature/cult, + /mob/living/simple_mob/faithless/cult ) /obj/effect/gateway/active/cult/cultify() diff --git a/code/game/gamemodes/cult/cultify/mob.dm b/code/game/gamemodes/cult/cultify/mob.dm index 14bb380a19..d78956c40a 100644 --- a/code/game/gamemodes/cult/cultify/mob.dm +++ b/code/game/gamemodes/cult/cultify/mob.dm @@ -16,7 +16,7 @@ /mob/living/cultify() if(iscultist(src) && client) - var/mob/living/simple_animal/construct/harvester/C = new(get_turf(src)) + var/mob/living/simple_mob/construct/harvester/C = new(get_turf(src)) mind.transfer_to(C) C << "The Geometer of Blood is overjoyed to be reunited with its followers, and accepts your body in sacrifice. As reward, you have been gifted with the shell of an Harvester.
Your tendrils can use and draw runes without need for a tome, your eyes can see beings through walls, and your mind can open any door. Use these assets to serve Nar-Sie and bring him any remaining living human in the world.
You can teleport yourself back to Nar-Sie along with any being under yourself at any time using your \"Harvest\" spell.
" dust() diff --git a/code/modules/mob/living/simple_animal/constructs/soulstone.dm b/code/game/gamemodes/cult/soulstone.dm similarity index 88% rename from code/modules/mob/living/simple_animal/constructs/soulstone.dm rename to code/game/gamemodes/cult/soulstone.dm index 6f13789dfc..61db88917a 100644 --- a/code/modules/mob/living/simple_animal/constructs/soulstone.dm +++ b/code/game/gamemodes/cult/soulstone.dm @@ -1,242 +1,246 @@ -/obj/item/device/soulstone/cultify() - return - -/obj/item/device/soulstone - name = "Soul Stone Shard" - icon = 'icons/obj/wizard.dmi' - icon_state = "soulstone" - item_state = "electronic" - desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefacts power." - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_BELT - origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4) - var/imprinted = "empty" - var/possible_constructs = list("Juggernaut","Wraith","Artificer","Harvester") - -//////////////////////////////Capturing//////////////////////////////////////////////////////// - -/obj/item/device/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob) - if(!istype(M, /mob/living/carbon/human))//If target is not a human. - return ..() - if(istype(M, /mob/living/carbon/human/dummy)) - return..() - if(jobban_isbanned(M, "cultist")) - user << "This person's soul is too corrupt and cannot be captured!" - return..() - - if(M.has_brain_worms()) //Borer stuff - RR - user << "This being is corrupted by an alien intelligence and cannot be soul trapped." - return..() - - add_attack_logs(user,M,"Soulstone'd with [src.name]") - transfer_soul("VICTIM", M, user) - return - - -///////////////////Options for using captured souls/////////////////////////////////////// - -/obj/item/device/soulstone/attack_self(mob/user) - if (!in_range(src, user)) - return - user.set_machine(src) - var/dat = "Soul Stone
" - for(var/mob/living/simple_animal/shade/A in src) - dat += "Captured Soul: [A.name]
" - dat += {"Summon Shade"} - dat += "
" - dat += {" Close"} - user << browse(dat, "window=aicard") - onclose(user, "aicard") - return - - - - -/obj/item/device/soulstone/Topic(href, href_list) - var/mob/U = usr - if (!in_range(src, U)||U.machine!=src) - U << browse(null, "window=aicard") - U.unset_machine() - return - - add_fingerprint(U) - U.set_machine(src) - - switch(href_list["choice"])//Now we switch based on choice. - if ("Close") - U << browse(null, "window=aicard") - U.unset_machine() - return - - if ("Summon") - for(var/mob/living/simple_animal/shade/A in src) - A.status_flags &= ~GODMODE - A.canmove = 1 - A << "You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs." - A.forceMove(U.loc) - A.cancel_camera() - src.icon_state = "soulstone" - attack_self(U) - -///////////////////////////Transferring to constructs///////////////////////////////////////////////////// -/obj/structure/constructshell - name = "empty shell" - icon = 'icons/obj/wizard.dmi' - icon_state = "construct" - desc = "A wicked machine used by those skilled in magical arts. It is inactive." - -/obj/structure/constructshell/cultify() - return - -/obj/structure/constructshell/cult - icon_state = "construct-cult" - desc = "This eerie contraption looks like it would come alive if supplied with a missing ingredient." - -/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob) - if(istype(O, /obj/item/device/soulstone)) - var/obj/item/device/soulstone/S = O; - S.transfer_soul("CONSTRUCT",src,user) - - -////////////////////////////Proc for moving soul in and out off stone////////////////////////////////////// -/obj/item/device/soulstone/proc/transfer_human(var/mob/living/carbon/human/T,var/mob/U) - if(!istype(T)) - return; - if(src.imprinted != "empty") - U << "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!" - return - if ((T.health + T.halloss) > config.health_threshold_crit && T.stat != DEAD) - U << "Capture failed!: Kill or maim the victim first!" - return - if(T.client == null) - U << "Capture failed!: The soul has already fled it's mortal frame." - return - if(src.contents.len) - U << "Capture failed!: The soul stone is full! Use or free an existing soul to make room." - return - - for(var/obj/item/W in T) - T.drop_from_inventory(W) - - new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton - T.invisibility = 101 - - var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc ) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = T - flick("dust-h", animation) - qdel(animation) - - var/mob/living/simple_animal/shade/S = new /mob/living/simple_animal/shade( T.loc ) - S.forceMove(src) //put shade in stone - S.status_flags |= GODMODE //So they won't die inside the stone somehow - S.canmove = 0//Can't move out of the soul stone - S.name = "Shade of [T.real_name]" - S.real_name = "Shade of [T.real_name]" - S.icon = T.icon - S.icon_state = T.icon_state - S.overlays = T.overlays - S.color = rgb(254,0,0) - S.alpha = 127 - if (T.client) - T.client.mob = S - S.cancel_camera() - - - src.icon_state = "soulstone2" - src.name = "Soul Stone: [S.real_name]" - to_chat(S, "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs.") - to_chat(U, "Capture successful! : [T.real_name]'s soul has been ripped from their body and stored within the soul stone.") - to_chat(U, "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls.") - src.imprinted = "[S.name]" - qdel(T) - -/obj/item/device/soulstone/proc/transfer_shade(var/mob/living/simple_animal/shade/T,var/mob/U) - if(!istype(T)) - return; - if (T.stat == DEAD) - to_chat(U, "Capture failed!: The shade has already been banished!") - return - if(src.contents.len) - to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") - return - if(T.name != src.imprinted) - to_chat(U, "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!") - return - - T.forceMove(src) //put shade in stone - T.status_flags |= GODMODE - T.canmove = 0 - T.health = T.getMaxHealth() - src.icon_state = "soulstone2" - - to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form") - to_chat(U, "Capture successful! : [T.name]'s has been recaptured and stored within the soul stone.") - -/obj/item/device/soulstone/proc/transfer_construct(var/obj/structure/constructshell/T,var/mob/U) - var/mob/living/simple_animal/shade/A = locate() in src - if(!A) - to_chat(U,"Capture failed!: The soul stone is empty! Go kill someone!") - return; - var/construct_class = input(U, "Please choose which type of construct you wish to create.") as null|anything in possible_constructs - switch(construct_class) - if("Juggernaut") - var/mob/living/simple_animal/construct/armoured/Z = new /mob/living/simple_animal/construct/armoured (get_turf(T.loc)) - Z.key = A.key - if(iscultist(U)) - cult.add_antagonist(Z.mind) - qdel(T) - to_chat(Z,"You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.") - to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - Z.cancel_camera() - qdel(src) - if("Wraith") - var/mob/living/simple_animal/construct/wraith/Z = new /mob/living/simple_animal/construct/wraith (get_turf(T.loc)) - Z.key = A.key - if(iscultist(U)) - cult.add_antagonist(Z.mind) - qdel(T) - to_chat(Z,"You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.") - to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - Z.cancel_camera() - qdel(src) - if("Artificer") - var/mob/living/simple_animal/construct/builder/Z = new /mob/living/simple_animal/construct/builder (get_turf(T.loc)) - Z.key = A.key - if(iscultist(U)) - cult.add_antagonist(Z.mind) - qdel(T) - to_chat(Z,"You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs") - to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - Z.cancel_camera() - qdel(src) - if("Harvester") - var/mob/living/simple_animal/construct/harvester/Z = new /mob/living/simple_animal/construct/harvester (get_turf(T.loc)) - Z.key = A.key - if(iscultist(U)) - cult.add_antagonist(Z.mind) - qdel(T) - to_chat(Z,"You are playing a Harvester. You are relatively weak, but your physical frailty is made up for by your ranged abilities.") - to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - Z.cancel_camera() - qdel(src) - if("Behemoth") - var/mob/living/simple_animal/construct/behemoth/Z = new /mob/living/simple_animal/construct/behemoth (get_turf(T.loc)) - Z.key = A.key - if(iscultist(U)) - cult.add_antagonist(Z.mind) - qdel(T) - to_chat(Z,"You are playing a Behemoth. You are incredibly slow, though your slowness is made up for by the fact your shell is far larger than any of your bretheren. You are the Unstoppable Force, and Immovable Object.") - to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - Z.cancel_camera() - qdel(src) - -/obj/item/device/soulstone/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob) - switch(choice) - if("VICTIM") - transfer_human(target,U) - if("SHADE") - transfer_shade(target,U) - if("CONSTRUCT") - transfer_construct(target,U) +///////////////////////// +// Soulstone +///////////////////////// + +/obj/item/device/soulstone + name = "Soul Stone Shard" + icon = 'icons/obj/wizard.dmi' + icon_state = "soulstone" + item_state = "electronic" + desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefacts power." + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_BELT + origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4) + var/imprinted = "empty" + var/possible_constructs = list("Juggernaut","Wraith","Artificer","Harvester") + +/obj/item/device/soulstone/cultify() + return + +//////////////////////////////Capturing//////////////////////////////////////////////////////// + +/obj/item/device/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob) + if(!istype(M, /mob/living/carbon/human))//If target is not a human. + return ..() + if(istype(M, /mob/living/carbon/human/dummy)) + return..() + if(jobban_isbanned(M, "cultist")) + user << "This person's soul is too corrupt and cannot be captured!" + return..() + + if(M.has_brain_worms()) //Borer stuff - RR + user << "This being is corrupted by an alien intelligence and cannot be soul trapped." + return..() + + add_attack_logs(user,M,"Soulstone'd with [src.name]") + transfer_soul("VICTIM", M, user) + return + + +///////////////////Options for using captured souls/////////////////////////////////////// + +/obj/item/device/soulstone/attack_self(mob/user) + if (!in_range(src, user)) + return + user.set_machine(src) + var/dat = "Soul Stone
" + for(var/mob/living/simple_mob/construct/shade/A in src) + dat += "Captured Soul: [A.name]
" + dat += {"Summon Shade"} + dat += "
" + dat += {" Close"} + user << browse(dat, "window=aicard") + onclose(user, "aicard") + return + + + + +/obj/item/device/soulstone/Topic(href, href_list) + var/mob/U = usr + if (!in_range(src, U)||U.machine!=src) + U << browse(null, "window=aicard") + U.unset_machine() + return + + add_fingerprint(U) + U.set_machine(src) + + switch(href_list["choice"])//Now we switch based on choice. + if ("Close") + U << browse(null, "window=aicard") + U.unset_machine() + return + + if ("Summon") + for(var/mob/living/simple_mob/construct/shade/A in src) + A.status_flags &= ~GODMODE + A.canmove = 1 + A << "You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs." + A.forceMove(U.loc) + A.cancel_camera() + src.icon_state = "soulstone" + attack_self(U) + +///////////////////////////Transferring to constructs///////////////////////////////////////////////////// +/obj/structure/constructshell + name = "empty shell" + icon = 'icons/obj/wizard.dmi' + icon_state = "construct" + desc = "A wicked machine used by those skilled in magical arts. It is inactive." + +/obj/structure/constructshell/cultify() + return + +/obj/structure/constructshell/cult + icon_state = "construct-cult" + desc = "This eerie contraption looks like it would come alive if supplied with a missing ingredient." + +/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob) + if(istype(O, /obj/item/device/soulstone)) + var/obj/item/device/soulstone/S = O; + S.transfer_soul("CONSTRUCT",src,user) + + +////////////////////////////Proc for moving soul in and out off stone////////////////////////////////////// +/obj/item/device/soulstone/proc/transfer_human(var/mob/living/carbon/human/T,var/mob/U) + if(!istype(T)) + return; + if(src.imprinted != "empty") + U << "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!" + return + if ((T.health + T.halloss) > config.health_threshold_crit && T.stat != DEAD) + U << "Capture failed!: Kill or maim the victim first!" + return + if(T.client == null) + U << "Capture failed!: The soul has already fled it's mortal frame." + return + if(src.contents.len) + U << "Capture failed!: The soul stone is full! Use or free an existing soul to make room." + return + + for(var/obj/item/W in T) + T.drop_from_inventory(W) + + new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton + T.invisibility = 101 + + var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc ) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = T + flick("dust-h", animation) + qdel(animation) + + var/mob/living/simple_mob/construct/shade/S = new /mob/living/simple_mob/construct/shade( T.loc ) + S.forceMove(src) //put shade in stone + S.status_flags |= GODMODE //So they won't die inside the stone somehow + S.canmove = 0//Can't move out of the soul stone + S.name = "Shade of [T.real_name]" + S.real_name = "Shade of [T.real_name]" + S.icon = T.icon + S.icon_state = T.icon_state + S.overlays = T.overlays + S.color = rgb(254,0,0) + S.alpha = 127 + if (T.client) + T.client.mob = S + S.cancel_camera() + + + src.icon_state = "soulstone2" + src.name = "Soul Stone: [S.real_name]" + to_chat(S, "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs.") + to_chat(U, "Capture successful! : [T.real_name]'s soul has been ripped from their body and stored within the soul stone.") + to_chat(U, "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls.") + src.imprinted = "[S.name]" + qdel(T) + +/obj/item/device/soulstone/proc/transfer_shade(var/mob/living/simple_mob/construct/shade/T,var/mob/U) + if(!istype(T)) + return; + if (T.stat == DEAD) + to_chat(U, "Capture failed!: The shade has already been banished!") + return + if(src.contents.len) + to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") + return + if(T.name != src.imprinted) + to_chat(U, "Capture failed!: The soul stone has already been imprinted with [src.imprinted]'s mind!") + return + + T.forceMove(src) //put shade in stone + T.status_flags |= GODMODE + T.canmove = 0 + T.health = T.getMaxHealth() + src.icon_state = "soulstone2" + + to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form") + to_chat(U, "Capture successful! : [T.name]'s has been recaptured and stored within the soul stone.") + +/obj/item/device/soulstone/proc/transfer_construct(var/obj/structure/constructshell/T,var/mob/U) + var/mob/living/simple_mob/construct/shade/A = locate() in src + if(!A) + to_chat(U,"Capture failed!: The soul stone is empty! Go kill someone!") + return; + var/construct_class = input(U, "Please choose which type of construct you wish to create.") as null|anything in possible_constructs + switch(construct_class) + if("Juggernaut") + var/mob/living/simple_mob/construct/juggernaut/Z = new /mob/living/simple_mob/construct/juggernaut (get_turf(T.loc)) + Z.key = A.key + if(iscultist(U)) + cult.add_antagonist(Z.mind) + qdel(T) + to_chat(Z,"You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.") + to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + Z.cancel_camera() + qdel(src) + if("Wraith") + var/mob/living/simple_mob/construct/wraith/Z = new /mob/living/simple_mob/construct/wraith (get_turf(T.loc)) + Z.key = A.key + if(iscultist(U)) + cult.add_antagonist(Z.mind) + qdel(T) + to_chat(Z,"You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.") + to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + Z.cancel_camera() + qdel(src) + if("Artificer") + var/mob/living/simple_mob/construct/artificer/Z = new /mob/living/simple_mob/construct/artificer (get_turf(T.loc)) + Z.key = A.key + if(iscultist(U)) + cult.add_antagonist(Z.mind) + qdel(T) + to_chat(Z,"You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs") + to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + Z.cancel_camera() + qdel(src) + if("Harvester") + var/mob/living/simple_mob/construct/harvester/Z = new /mob/living/simple_mob/construct/harvester (get_turf(T.loc)) + Z.key = A.key + if(iscultist(U)) + cult.add_antagonist(Z.mind) + qdel(T) + to_chat(Z,"You are playing a Harvester. You are relatively weak, but your physical frailty is made up for by your ranged abilities.") + to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + Z.cancel_camera() + qdel(src) + if("Behemoth") + var/mob/living/simple_mob/construct/juggernaut/behemoth/Z = new /mob/living/simple_mob/construct/juggernaut/behemoth (get_turf(T.loc)) + Z.key = A.key + if(iscultist(U)) + cult.add_antagonist(Z.mind) + qdel(T) + to_chat(Z,"You are playing a Behemoth. You are incredibly slow, though your slowness is made up for by the fact your shell is far larger than any of your bretheren. You are the Unstoppable Force, and Immovable Object.") + to_chat(Z,"You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + Z.cancel_camera() + qdel(src) + +/obj/item/device/soulstone/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob) + switch(choice) + if("VICTIM") + transfer_human(target,U) + if("SHADE") + transfer_shade(target,U) + if("CONSTRUCT") + transfer_construct(target,U) diff --git a/code/game/gamemodes/events.dm b/code/game/gamemodes/events.dm index 4b2ab0cd7d..573f10a9b4 100644 --- a/code/game/gamemodes/events.dm +++ b/code/game/gamemodes/events.dm @@ -206,7 +206,7 @@ var/hadevent = 0 /proc/carp_migration() // -- Darem for(var/obj/effect/landmark/C in landmarks_list) if(C.name == "carpspawn") - new /mob/living/simple_animal/hostile/carp(C.loc) + new /mob/living/simple_mob/animal/space/carp(C.loc) //sleep(100) spawn(rand(300, 600)) //Delayed announcements to keep the crew on their toes. command_announcement.Announce("Unknown biological entities have been detected near \the [station_name()], please stand-by.", "Lifesign Alert", new_sound = 'sound/AI/commandreport.ogg') diff --git a/code/game/gamemodes/events/holidays/Christmas.dm b/code/game/gamemodes/events/holidays/Christmas.dm index b2c4b68a5a..66e329ac1a 100644 --- a/code/game/gamemodes/events/holidays/Christmas.dm +++ b/code/game/gamemodes/events/holidays/Christmas.dm @@ -9,7 +9,7 @@ /proc/ChristmasEvent() for(var/obj/structure/flora/tree/pine/xmas in world) - var/mob/living/simple_animal/hostile/tree/evil_tree = new /mob/living/simple_animal/hostile/tree(xmas.loc) + var/mob/living/simple_mob/animal/space/tree/evil_tree = new /mob/living/simple_mob/animal/space/tree(xmas.loc) evil_tree.icon_state = xmas.icon_state evil_tree.icon_living = evil_tree.icon_state evil_tree.icon_dead = evil_tree.icon_state diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index ac9a16e8b6..a410ebfe24 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -808,7 +808,7 @@ datum/objective/heist/salvage /datum/objective/borer_survive/check_completion() if(owner) - var/mob/living/simple_animal/borer/B = owner + var/mob/living/simple_mob/animal/borer/B = owner if(istype(B) && B.stat < 2 && B.host && B.host.stat < 2) return 1 return 0 @@ -817,7 +817,7 @@ datum/objective/heist/salvage /datum/objective/borer_reproduce/check_completion() if(owner && owner.current) - var/mob/living/simple_animal/borer/B = owner.current + var/mob/living/simple_mob/animal/borer/B = owner.current if(istype(B) && B.has_reproduced) return 1 return 0 diff --git a/code/game/gamemodes/technomancer/assistance/golem.dm b/code/game/gamemodes/technomancer/assistance/golem.dm deleted file mode 100644 index 6b8654e719..0000000000 --- a/code/game/gamemodes/technomancer/assistance/golem.dm +++ /dev/null @@ -1,258 +0,0 @@ -//An AI-controlled 'companion' for the Technomancer. It's tough, strong, and can also use spells. -/mob/living/simple_animal/technomancer_golem - name = "G.O.L.E.M." - desc = "A rather unusual looking synthetic." - icon = 'icons/mob/mob.dmi' - icon_state = "technomancer_golem" - health = 250 - maxHealth = 250 - stop_automated_movement = 1 - wander = 0 - response_help = "pets" - response_disarm = "pushes away" - response_harm = "punches" - harm_intent_damage = 3 - - heat_damage_per_tick = 0 - cold_damage_per_tick = 0 - - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - unsuitable_atoms_damage = 0 - speed = 0 - - melee_damage_lower = 30 // It has a built in esword. - melee_damage_upper = 30 - attack_sound = 'sound/weapons/blade1.ogg' - attacktext = list("slashed") - friendly = "hugs" - resistance = 0 - melee_miss_chance = 0 - - var/obj/item/weapon/technomancer_core/golem/core = null - var/obj/item/weapon/spell/active_spell = null // Shield and ranged spells - var/mob/living/master = null - - var/list/known_spells = list( - "beam" = /obj/item/weapon/spell/projectile/beam, - "chain lightning" = /obj/item/weapon/spell/projectile/chain_lightning, - "force missile" = /obj/item/weapon/spell/projectile/force_missile, - "ionic bolt" = /obj/item/weapon/spell/projectile/ionic_bolt, - "lightning" = /obj/item/weapon/spell/projectile/lightning, - "blink" = /obj/item/weapon/spell/blink, - "dispel" = /obj/item/weapon/spell/dispel, - "oxygenate" = /obj/item/weapon/spell/oxygenate, - "mend life" = /obj/item/weapon/spell/modifier/mend_life, - "mend synthetic" = /obj/item/weapon/spell/modifier/mend_synthetic, - "mend organs" = /obj/item/weapon/spell/mend_organs, - "purify" = /obj/item/weapon/spell/modifier/purify, - "resurrect" = /obj/item/weapon/spell/resurrect, - "passwall" = /obj/item/weapon/spell/passwall, - "repel missiles" = /obj/item/weapon/spell/modifier/repel_missiles, - "corona" = /obj/item/weapon/spell/modifier/corona, - "haste" = /obj/item/weapon/spell/modifier/haste - ) - - // Holds the overlays, when idle or attacking. - var/image/sword_image = null - var/image/spell_image = null - // These contain icon_states for each frame of an attack animation, which is swapped in and out manually, because BYOND. - // They are assoc lists, to hold the frame duration and the frame icon_state in one list. - var/list/spell_pre_attack_states = list( - "golem_spell_attack_1" = 1, - "golem_spell_attack_2" = 2, - "golem_spell_attack_3" = 2 - ) - var/list/spell_post_attack_states = list( - "golem_spell_attack_4" = 2, - "golem_spell_attack_5" = 3, - "golem_spell_attack_6" = 3 - ) - var/list/sword_pre_attack_states = list( - "golem_sword_attack_1" = 1, - "golem_sword_attack_2" = 5 - ) - var/list/sword_post_attack_states = list( - "golem_sword_attack_3" = 1, - "golem_sword_attack_4" = 3 - ) - -/mob/living/simple_animal/technomancer_golem/New() - ..() - core = new(src) - sword_image = image(icon, src, "golem_sword") - spell_image = image(icon, src, "golem_spell") - update_icon() - -/mob/living/simple_animal/technomancer_golem/Destroy() - qdel(core) - qdel(sword_image) - qdel(spell_image) - return ..() - -/mob/living/simple_animal/technomancer_golem/unref_spell() - active_spell = null - return ..() - -/mob/living/simple_animal/hostile/hivebot/death() - ..() - visible_message("\The [src] disintegrates!") - new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc) - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(3, 1, src) - s.start() - qdel(src) - -/mob/living/simple_animal/technomancer_golem/update_icon() - overlays.Cut() - overlays += sword_image - overlays += spell_image - update_modifier_visuals() - -// Unfortunately, BYOND does not let you flick() images or other overlays, so we need to do this in a terrible way. -/atom/proc/manual_flick(var/list/frames, var/image/I, var/reset_to = null) - // Swap in and out each frame manually. - for(var/frame in frames) - overlays -= I - I.icon_state = frame - overlays += I - sleep(frames[frame]) - if(reset_to) - // One more time to reset it to what it was before. - overlays -= I - I.icon_state = reset_to - overlays += I - -/mob/living/simple_animal/technomancer_golem/proc/spellcast_pre_animation() - setClickCooldown(5) - manual_flick(spell_pre_attack_states, spell_image, reset_to = "golem_spell_attack_3") - -/mob/living/simple_animal/technomancer_golem/proc/spellcast_post_animation() - setClickCooldown(8) - manual_flick(spell_post_attack_states, spell_image, reset_to = "golem_spell") - -/mob/living/simple_animal/technomancer_golem/proc/sword_pre_animation() - setClickCooldown(6) - manual_flick(sword_pre_attack_states, sword_image) - -/mob/living/simple_animal/technomancer_golem/proc/sword_post_animation() - setClickCooldown(3) - manual_flick(sword_post_attack_states, sword_image, reset_to = "golem_sword") - -/mob/living/simple_animal/technomancer_golem/DoPunch(var/atom/A) - sword_pre_animation() - . = ..() // This does the actual attack and will check adjacency again. - sword_post_animation() - -/mob/living/simple_animal/technomancer_golem/isSynthetic() - return TRUE // So Mend Synthetic will work on them. - -/mob/living/simple_animal/technomancer_golem/speech_bubble_appearance() - return "synthetic_evil" - -/mob/living/simple_animal/technomancer_golem/place_spell_in_hand(var/path) - if(!path || !ispath(path)) - return 0 - - if(active_spell) - qdel(active_spell) // Get rid of our old spell. - - var/obj/item/weapon/spell/S = new path(src) - active_spell = S - -/mob/living/simple_animal/technomancer_golem/verb/test_giving_spells() - var/choice = input(usr, "What spell?", "Give spell") as null|anything in known_spells - if(choice) - place_spell_in_hand(known_spells[choice]) - else - qdel(active_spell) - -// Used to cast spells. -/mob/living/simple_animal/technomancer_golem/RangedAttack(var/atom/A, var/params) - if(active_spell) - spellcast_pre_animation() - if(active_spell.cast_methods & CAST_RANGED) - active_spell.on_ranged_cast(A, src) - spellcast_post_animation() - -/mob/living/simple_animal/technomancer_golem/UnarmedAttack(var/atom/A, var/proximity) - if(proximity) - if(active_spell) - spellcast_pre_animation() - if(!Adjacent(A)) // Need to check again since they might've moved while 'warming up'. - spellcast_post_animation() - return - var/effective_cooldown = round(active_spell.cooldown * core.cooldown_modifier, 5) - if(active_spell.cast_methods & CAST_MELEE) - active_spell.on_melee_cast(A, src) - else if(active_spell.cast_methods & CAST_RANGED) - active_spell.on_ranged_cast(A, src) - spellcast_post_animation() - src.setClickCooldown(effective_cooldown) - else - ..() - -/mob/living/simple_animal/technomancer_golem/get_technomancer_core() - return core - -/mob/living/simple_animal/technomancer_golem/proc/bind_to_mob(mob/user) - if(!user || master) - return - master = user - name = "[master]'s [initial(name)]" - -/mob/living/simple_animal/technomancer_golem/examine(mob/user) - ..() - if(user.mind && technomancers.is_antagonist(user.mind)) - user << "Your pride and joy. It's a very special synthetic robot, capable of using functions similar to you, and you built it \ - yourself! It'll always stand by your side, ready to help you out. You have no idea what GOLEM stands for, however..." - -/mob/living/simple_animal/technomancer_golem/Life() - ..() - handle_ai() - -// This is where the real spaghetti begins. -/mob/living/simple_animal/technomancer_golem/proc/handle_ai() - if(!master) - return - if(get_dist(src, master) > 6 || src.z != master.z) - targeted_blink(master) - - // Give our allies buffs and heals. - for(var/mob/living/L in view(src)) - if(L in friends) - support_friend(L) - return - -/mob/living/simple_animal/technomancer_golem/proc/support_friend(var/mob/living/L) - if(L.getBruteLoss() >= 10 || L.getFireLoss() >= 10) - if(L.isSynthetic() && !L.has_modifier_of_type(/datum/modifier/technomancer/mend_synthetic)) - place_spell_in_hand(known_spells["mend synthetic"]) - targeted_blink(L) - UnarmedAttack(L, 1) - else if(!L.has_modifier_of_type(/datum/modifier/technomancer/mend_life)) - place_spell_in_hand(known_spells["mend life"]) - targeted_blink(L) - UnarmedAttack(L, 1) - return - - - // Give them repel missiles if they lack it. - if(!L.has_modifier_of_type(/datum/modifier/technomancer/repel_missiles)) - place_spell_in_hand(known_spells["repel missiles"]) - RangedAttack(L) - return - -/mob/living/simple_animal/technomancer_golem/proc/targeted_blink(var/atom/target) - var/datum/effect/effect/system/spark_spread/spark_system = new() - spark_system.set_up(5, 0, get_turf(src)) - spark_system.start() - src.visible_message("\The [src] vanishes!") - src.forceMove(get_turf(target)) - return \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/core_obj.dm b/code/game/gamemodes/technomancer/core_obj.dm index 1e7488258b..830de5a5f1 100644 --- a/code/game/gamemodes/technomancer/core_obj.dm +++ b/code/game/gamemodes/technomancer/core_obj.dm @@ -130,7 +130,7 @@ for(var/mob/living/L in summoned_mobs) summoned_mobs -= L qdel(L) - for(var/mob/living/simple_animal/ward/ward in wards_in_use) + for(var/mob/living/ward in wards_in_use) wards_in_use -= ward qdel(ward) diff --git a/code/game/gamemodes/technomancer/spell_objs_helpers.dm b/code/game/gamemodes/technomancer/spell_objs_helpers.dm index 105b3b3e9c..cf83640775 100644 --- a/code/game/gamemodes/technomancer/spell_objs_helpers.dm +++ b/code/game/gamemodes/technomancer/spell_objs_helpers.dm @@ -1,12 +1,17 @@ -//Returns 1 if the turf is dense, or if there's dense objects on it, unless told to ignore them. -/turf/proc/check_density(var/ignore_objs = 0) +//Returns 1 if the turf is dense, or if there's dense objects/mobs on it, unless told to ignore them. +/turf/proc/check_density(var/ignore_objs = FALSE, var/ignore_mobs = FALSE) if(density) - return 1 - if(!ignore_objs) + return TRUE + if(!ignore_objs || !ignore_mobs) for(var/atom/movable/stuff in contents) if(stuff.density) - return 1 - return 0 + if(ignore_objs && isobj(stuff)) + continue + else if(ignore_mobs && isliving(stuff)) // Ghosts aren't dense but keeping this limited to living type will probably save headaches in the future. + continue + else + return TRUE + return FALSE // Used to distinguish friend from foe. /obj/item/weapon/spell/proc/is_ally(var/mob/living/L) @@ -14,9 +19,9 @@ return 1 if(L.mind && technomancers.is_antagonist(L.mind)) // This should be done better since we might want opposing technomancers later. return 1 - if(istype(L, /mob/living/simple_animal/hostile)) // Mind controlled simple mobs count as allies too. - var/mob/living/simple_animal/SA = L - if(owner in SA.friends) + if(istype(L, /mob/living/simple_mob)) // Mind controlled simple mobs count as allies too. + var/mob/living/simple_mob/SM = L + if(owner in SM.friends) return 1 return 0 diff --git a/code/game/gamemodes/technomancer/spells/abjuration.dm b/code/game/gamemodes/technomancer/spells/abjuration.dm index 146c90a987..0eb566a637 100644 --- a/code/game/gamemodes/technomancer/spells/abjuration.dm +++ b/code/game/gamemodes/technomancer/spells/abjuration.dm @@ -16,12 +16,12 @@ /obj/item/weapon/spell/abjuration/on_ranged_cast(atom/hit_atom, mob/user) if(istype(hit_atom, /mob/living) && pay_energy(500) && within_range(hit_atom)) var/mob/living/L = hit_atom - var/mob/living/simple_animal/SA = null + var/mob/living/simple_mob/SM = null //Bit of a roundabout typecheck, in order to test for two variables from two different mob types in one line. - if(istype(L, /mob/living/simple_animal)) - SA = L - if(L.summoned || (SA && SA.supernatural) ) + if(istype(L, /mob/living/simple_mob)) + SM = L + if(L.summoned || (SM && SM.supernatural) ) if(L.client) // Player-controlled mobs are immune to being killed by this. user << "\The [L] resists your attempt to banish it!" L << "\The [user] tried to teleport you far away, but failed." @@ -29,8 +29,8 @@ else visible_message("\The [L] vanishes!") qdel(L) - else if(istype(L, /mob/living/simple_animal/construct)) - var/mob/living/simple_animal/construct/evil = L + else if(istype(L, /mob/living/simple_mob/construct)) + var/mob/living/simple_mob/construct/evil = L evil << "\The [user]'s abjuration purges your form!" evil.purge = 3 adjust_instability(5) diff --git a/code/game/gamemodes/technomancer/spells/control.dm b/code/game/gamemodes/technomancer/spells/control.dm index e76e63490c..f86fd79bf5 100644 --- a/code/game/gamemodes/technomancer/spells/control.dm +++ b/code/game/gamemodes/technomancer/spells/control.dm @@ -21,6 +21,7 @@ aspect = ASPECT_BIOMED //Not sure if this should be something else. var/image/control_overlay = null var/list/controlled_mobs = list() +<<<<<<< HEAD var/list/allowed_mobs = list( /mob/living/bot, /mob/living/simple_animal/cat, @@ -45,67 +46,70 @@ /mob/living/simple_animal/hostile/savik, /mob/living/simple_animal/hostile/shantak ) +======= + var/allowed_mob_classes = MOB_CLASS_ANIMAL|MOB_CLASS_SYNTHETIC +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync //This unfortunately is gonna be rather messy due to the various mobtypes involved. /obj/item/weapon/spell/control/proc/select(var/mob/living/L) - if(!(is_type_in_list(L, allowed_mobs))) - return 0 + if(!(L.mob_class & allowed_mob_classes)) + return FALSE - if(istype(L, /mob/living/simple_animal)) - var/mob/living/simple_animal/SA = L - SA.ai_inactive = 1 - SA.friends |= src.owner - SA.stance = STANCE_IDLE + if(!L.has_AI()) + return FALSE - L.overlays |= control_overlay + var/datum/ai_holder/AI = L.ai_holder + AI.hostile = FALSE // The Technomancer chooses the target, not the AI. + AI.retaliate = TRUE + AI.wander = FALSE + AI.forget_everything() + + if(istype(L, /mob/living/simple_mob)) + var/mob/living/simple_mob/SM = L + SM.friends |= src.owner + + L.add_overlay(control_overlay, TRUE) controlled_mobs |= L /obj/item/weapon/spell/control/proc/deselect(var/mob/living/L) if(!(L in controlled_mobs)) - return 0 + return FALSE - if(istype(L, /mob/living/simple_animal)) - var/mob/living/simple_animal/SA = L - SA.ai_inactive = 1 - if(istype(SA, /mob/living/simple_animal/hostile)) - var/mob/living/simple_animal/hostile/SAH = SA - SAH.friends.Remove(owner) + if(L.has_AI()) + var/datum/ai_holder/AI = L.ai_holder + AI.hostile = initial(AI.hostile) + AI.retaliate = initial(AI.retaliate) + AI.wander = initial(AI.wander) + AI.forget_everything() - L.overlays.Remove(control_overlay) + if(istype(L, /mob/living/simple_mob)) + var/mob/living/simple_mob/SM = L + SM.friends -= owner + + L.cut_overlay(control_overlay, TRUE) controlled_mobs.Remove(L) /obj/item/weapon/spell/control/proc/move_all(turf/T) - for(var/mob/living/living in controlled_mobs) - if(living.stat) - deselect(living) + for(var/mob/living/L in controlled_mobs) + if(!L.has_AI() || L.stat) + deselect(L) continue - if(istype(living, /mob/living/simple_animal)) - var/mob/living/simple_animal/SA = living - SA.target_mob = null - SA.stance = STANCE_IDLE - walk_towards(SA,T,SA.speed) - else - walk_towards(living,T,5) + L.ai_holder.give_destination(T, 0, TRUE) /obj/item/weapon/spell/control/proc/attack_all(mob/target) for(var/mob/living/L in controlled_mobs) - if(L.stat) + if(!L.has_AI() || L.stat) deselect(L) continue - if(istype(L, /mob/living/simple_animal/hostile)) - var/mob/living/simple_animal/hostile/SAH - SAH.target_mob = target - else if(istype(L, /mob/living/bot)) - var/mob/living/bot/B = L - B.UnarmedAttack(L) + L.ai_holder.give_target(target) -/obj/item/weapon/spell/control/New() +/obj/item/weapon/spell/control/initialize() control_overlay = image('icons/obj/spells.dmi',"controlled") - ..() + return ..() /obj/item/weapon/spell/control/Destroy() - for(var/mob/living/simple_animal/hostile/SM in controlled_mobs) - deselect(SM) + for(var/mob/living/L in controlled_mobs) + deselect(L) controlled_mobs = list() return ..() @@ -127,11 +131,14 @@ trying to use it on yourself, perhaps you're an exception? Regardless, nothing happens." return 0 - if(is_type_in_list(L, allowed_mobs)) + if(L.mob_class & allowed_mob_classes) if(!(L in controlled_mobs)) //Selecting if(L.client) user << "\The [L] seems to resist you!" return 0 + if(!L.has_AI()) + to_chat(user, span("warning", "\The [L] seems too dim for this to work on them.")) + return FALSE if(pay_energy(500)) select(L) user << "\The [L] is now under your (limited) control." diff --git a/code/game/gamemodes/technomancer/spells/gambit.dm b/code/game/gamemodes/technomancer/spells/gambit.dm index e0116073dc..5e03a8ca19 100644 --- a/code/game/gamemodes/technomancer/spells/gambit.dm +++ b/code/game/gamemodes/technomancer/spells/gambit.dm @@ -75,9 +75,9 @@ for(var/mob/living/L in view(owner)) // Spiders, carp... bears. - if(istype(L, /mob/living/simple_animal)) - var/mob/living/simple_animal/SM = L - if(!is_ally(SM) && SM.hostile) + if(istype(L, /mob/living/simple_mob)) + var/mob/living/simple_mob/SM = L + if(!is_ally(SM) && SM.has_AI() && SM.ai_holder.hostile) hostile_mobs++ if(SM.summoned || SM.supernatural) // Our creations might be trying to kill us. potential_spells |= /obj/item/weapon/spell/abjuration diff --git a/code/game/gamemodes/technomancer/spells/illusion.dm b/code/game/gamemodes/technomancer/spells/illusion.dm index 418df75539..8dccc2b9ec 100644 --- a/code/game/gamemodes/technomancer/spells/illusion.dm +++ b/code/game/gamemodes/technomancer/spells/illusion.dm @@ -14,7 +14,7 @@ aspect = ASPECT_LIGHT cast_methods = CAST_RANGED | CAST_USE var/atom/movable/copied = null - var/mob/living/simple_animal/illusion/illusion = null + var/mob/living/simple_mob/illusion/illusion = null /obj/item/weapon/spell/illusion/on_ranged_cast(atom/hit_atom, mob/user) if(istype(hit_atom, /atom/movable)) @@ -33,17 +33,13 @@ if(pay_energy(500)) illusion = new(T) illusion.copy_appearance(copied) - if(ishuman(copied)) - var/mob/living/carbon/human/H = copied - // This is to try to have the illusion move at the same rate the real mob world. - illusion.step_delay = max(H.movement_delay() + 4, 3) user << "An illusion of \the [copied] is made on \the [T]." user << 'sound/effects/pop.ogg' return 1 else if(pay_energy(100)) - spawn(1) - illusion.walk_loop(T) + var/datum/ai_holder/AI = illusion.ai_holder + AI.give_destination(T) /obj/item/weapon/spell/illusion/on_use_cast(mob/user) if(illusion) @@ -76,116 +72,3 @@ temp_image.transform = M // temp_image.pixel_y = 8 src.overlays.Add(temp_image) - - -/mob/living/simple_animal/illusion - name = "illusion" // gets overwritten - desc = "If you can read me, the game broke. Please report this to a coder." - resistance = 1000 // holograms are tough - wander = 0 - response_help = "pushes a hand through" - response_disarm = "tried to disarm" - response_harm = "tried to punch" - var/atom/movable/copying = null - universal_speak = 1 - var/realistic = 0 - var/list/path = list() //Used for AStar pathfinding. - var/walking = 0 - var/step_delay = 10 - -/mob/living/simple_animal/illusion/update_icon() // We don't want the appearance changing AT ALL unless by copy_appearance(). - return - -/mob/living/simple_animal/illusion/proc/copy_appearance(var/atom/movable/thing_to_copy) - if(!thing_to_copy) - return 0 - name = thing_to_copy.name - desc = thing_to_copy.desc - gender = thing_to_copy.gender - appearance = thing_to_copy.appearance - copying = thing_to_copy - return 1 - -// We use special movement code for illusions, because BYOND's default pathfinding will use diagonal movement if it results -// in the shortest path. As players are incapable of moving in diagonals, we must do this or else illusions will not be convincing. -/mob/living/simple_animal/illusion/proc/calculate_path(var/turf/targeted_loc) - if(!path.len || !path) - spawn(0) - path = AStar(loc, targeted_loc, /turf/proc/CardinalTurfs, /turf/proc/Distance, 0, 10, id = null) - if(!path) - path = list() - return - -/mob/living/simple_animal/illusion/proc/walk_path(var/turf/targeted_loc) - if(path && path.len) - step_to(src, path[1]) - path -= path[1] - return - else - if(targeted_loc) - calculate_path(targeted_loc) - -/mob/living/simple_animal/illusion/proc/walk_loop(var/turf/targeted_loc) - if(walking) //Already busy moving somewhere else. - return 0 - walking = 1 - calculate_path(targeted_loc) - if(!targeted_loc) - walking = 0 - return 0 - if(path.len == 0) - calculate_path(targeted_loc) - while(loc != targeted_loc) - walk_path(targeted_loc) - sleep(step_delay) - walking = 0 - return 1 - -// Because we can't perfectly duplicate some examine() output, we directly examine the AM it is copying. It's messy but -// this is to prevent easy checks from the opposing force. -/mob/living/simple_animal/illusion/examine(mob/user) - if(copying) - copying.examine(user) - return - ..() - -/mob/living/simple_animal/illusion/bullet_act(var/obj/item/projectile/P) - if(!P) - return - - if(realistic) - return ..() - - return PROJECTILE_FORCE_MISS - -/mob/living/simple_animal/illusion/attack_hand(mob/living/carbon/human/M) - if(!realistic) - playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("[M]'s hand goes through \the [src]!") - return - else - switch(M.a_intent) - - if(I_HELP) - var/datum/gender/T = gender_datums[src.get_visible_gender()] - M.visible_message("[M] hugs [src] to make [T.him] feel better!", \ - "You hug [src] to make [T.him] feel better!") // slightly redundant as at the moment most mobs still use the normal gender var, but it works and future-proofs it - playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - - if(I_DISARM) - playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("[M] attempted to disarm [src]!") - M.do_attack_animation(src) - - if(I_GRAB) - ..() - - if(I_HURT) - adjustBruteLoss(harm_intent_damage) - M.visible_message("[M] [response_harm] \the [src]") - M.do_attack_animation(src) - - return - -/mob/living/simple_animal/illusion/ex_act() - return diff --git a/code/game/gamemodes/technomancer/spells/modifier/modifier.dm b/code/game/gamemodes/technomancer/spells/modifier/modifier.dm index a8b9306945..3ea278286e 100644 --- a/code/game/gamemodes/technomancer/spells/modifier/modifier.dm +++ b/code/game/gamemodes/technomancer/spells/modifier/modifier.dm @@ -15,11 +15,13 @@ /obj/item/weapon/spell/modifier/on_melee_cast(atom/hit_atom, mob/user) if(istype(hit_atom, /mob/living)) - on_add_modifier(hit_atom) + return on_add_modifier(hit_atom) + return FALSE /obj/item/weapon/spell/modifier/on_ranged_cast(atom/hit_atom, mob/user) if(istype(hit_atom, /mob/living)) - on_add_modifier(hit_atom) + return on_add_modifier(hit_atom) + return FALSE /obj/item/weapon/spell/modifier/proc/on_add_modifier(var/mob/living/L) @@ -32,6 +34,7 @@ MT.spell_power = calculate_spell_power(1) log_and_message_admins("has casted [src] on [L].") qdel(src) + return TRUE // Technomancer specific subtype which keeps track of spell power and gets targeted specificially by Dispel. /datum/modifier/technomancer diff --git a/code/game/gamemodes/technomancer/spells/resurrect.dm b/code/game/gamemodes/technomancer/spells/resurrect.dm index 90a352c95f..2663a132a9 100644 --- a/code/game/gamemodes/technomancer/spells/resurrect.dm +++ b/code/game/gamemodes/technomancer/spells/resurrect.dm @@ -30,13 +30,13 @@ this point." return 0 user << "You stab \the [L] with a hidden integrated hypo, attempting to bring them back..." - if(istype(L, /mob/living/simple_animal)) - var/mob/living/simple_animal/SM = L + if(istype(L, /mob/living/simple_mob)) + var/mob/living/simple_mob/SM = L SM.health = SM.getMaxHealth() / 3 SM.stat = CONSCIOUS dead_mob_list -= SM living_mob_list += SM - SM.icon_state = SM.icon_living + SM.update_icon() adjust_instability(15) else if(ishuman(L)) var/mob/living/carbon/human/H = L diff --git a/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm b/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm index 5641df2f87..18e5785a05 100644 --- a/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm +++ b/code/game/gamemodes/technomancer/spells/spawner/pulsar.dm @@ -11,7 +11,7 @@ icon_state = "radiance" cast_methods = CAST_RANGED | CAST_THROW aspect = ASPECT_EMP - spawner_type = /obj/effect/temporary_effect/pulsar + spawner_type = /obj/effect/temporary_effect/pulse/pulsar /obj/item/weapon/spell/spawner/pulsar/New() ..() @@ -25,7 +25,29 @@ /obj/item/weapon/spell/spawner/pulsar/on_throw_cast(atom/hit_atom, mob/user) empulse(hit_atom, 1, 1, 1, 1, log=1) -/obj/effect/temporary_effect/pulsar +// Does something every so often. Deletes itself when pulses_remaining hits zero. +/obj/effect/temporary_effect/pulse + var/pulses_remaining = 3 + var/pulse_delay = 2 SECONDS + +/obj/effect/temporary_effect/pulse/initialize() + spawn(0) + pulse_loop() + return ..() + +/obj/effect/temporary_effect/pulse/proc/pulse_loop() + while(pulses_remaining) + sleep(pulse_delay) + on_pulse() + pulses_remaining-- + qdel(src) + +// Override for specific effects. +/obj/effect/temporary_effect/pulse/proc/on_pulse() + + + +/obj/effect/temporary_effect/pulse/pulsar name = "pulsar" desc = "Not a real pulsar, but still emits loads of EMP." icon_state = "shield2" @@ -33,17 +55,14 @@ light_range = 4 light_power = 5 light_color = "#2ECCFA" - var/pulses_remaining = 3 + pulses_remaining = 3 + +/obj/effect/temporary_effect/pulse/pulsar/on_pulse() + empulse(src, 1, 1, 2, 2, log = 1) + + + + -/obj/effect/temporary_effect/pulsar/New() - ..() - spawn(0) - pulse_loop() -/obj/effect/temporary_effect/pulsar/proc/pulse_loop() - while(pulses_remaining) - sleep(2 SECONDS) - empulse(src, 1, 1, 2, 2, log = 1) - pulses_remaining-- - qdel(src) diff --git a/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm b/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm index 9a004d3ea3..7ac370433d 100644 --- a/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm +++ b/code/game/gamemodes/technomancer/spells/summon/summon_creature.dm @@ -16,6 +16,7 @@ desc = "Chitter chitter." summoned_mob_type = null summon_options = list( +<<<<<<< HEAD "Mouse" = /mob/living/simple_animal/mouse, "Lizard" = /mob/living/simple_animal/lizard, "Chicken" = /mob/living/simple_animal/chicken, @@ -34,16 +35,34 @@ "CARP" = /mob/living/simple_animal/hostile/carp, "BEAR" = /mob/living/simple_animal/hostile/bear ) // Vorestation edits to add vore versions. +======= + "Mouse" = /mob/living/simple_mob/animal/passive/mouse, + "Lizard" = /mob/living/simple_mob/animal/passive/lizard, + "Chicken" = /mob/living/simple_mob/animal/passive/chicken, + "Chick" = /mob/living/simple_mob/animal/passive/chick, + "Crab" = /mob/living/simple_mob/animal/passive/crab, + "Parrot" = /mob/living/simple_mob/animal/passive/bird/parrot, + "Goat" = /mob/living/simple_mob/animal/goat, + "Cat" = /mob/living/simple_mob/animal/passive/cat, + "Kitten" = /mob/living/simple_mob/animal/passive/cat/kitten, + "Corgi" = /mob/living/simple_mob/animal/passive/dog/corgi, + "Corgi Pup" = /mob/living/simple_mob/animal/passive/dog/corgi/puppy, + "BAT" = /mob/living/simple_mob/animal/space/bats, + "SPIDER" = /mob/living/simple_mob/animal/giant_spider, + "SPIDER HUNTER" = /mob/living/simple_mob/animal/giant_spider/hunter, + "SPIDER NURSE" = /mob/living/simple_mob/animal/giant_spider/nurse, + "CARP" = /mob/living/simple_mob/animal/space/carp, + "BEAR" = /mob/living/simple_mob/animal/space/bear + ) +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync cooldown = 30 instability_cost = 10 energy_cost = 1000 -/obj/item/weapon/spell/summon/summon_creature/on_summon(var/mob/living/simple_animal/summoned) +/obj/item/weapon/spell/summon/summon_creature/on_summon(var/mob/living/simple_mob/summoned) if(check_for_scepter()) // summoned.faction = "technomancer" - if(istype(summoned, /mob/living/simple_animal/hostile)) - var/mob/living/simple_animal/SA = summoned - SA.friends.Add(owner) + summoned.friends += owner // Makes their new pal big and strong, if they have spell power. summoned.maxHealth = calculate_spell_power(summoned.maxHealth) @@ -51,15 +70,12 @@ summoned.melee_damage_lower = calculate_spell_power(summoned.melee_damage_lower) summoned.melee_damage_upper = calculate_spell_power(summoned.melee_damage_upper) // This makes the summon slower, so the crew has a chance to flee from massive monsters. - summoned.move_to_delay = calculate_spell_power(round(summoned.move_to_delay)) + summoned.movement_cooldown = calculate_spell_power(round(summoned.movement_cooldown)) var/new_size = calculate_spell_power(1) if(new_size != 1) - var/matrix/M = matrix() - M.Scale(new_size) - M.Translate(0, 16*(new_size-1)) - summoned.transform = M + adjust_scale(new_size) // Now we hurt their new pal, because being forcefully abducted by teleportation can't be healthy. - summoned.health = round(summoned.getMaxHealth() * 0.7) \ No newline at end of file + summoned.adjustBruteLoss(summoned.getMaxHealth() * 0.3) // Lose 30% of max health on arrival (but could be healed back up). \ No newline at end of file diff --git a/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm b/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm index c6e5a2804d..9500044581 100644 --- a/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm +++ b/code/game/gamemodes/technomancer/spells/summon/summon_ward.dm @@ -1,9 +1,8 @@ /datum/technomancer/spell/summon_ward - name = "Summon Ward" - desc = "Teleports a prefabricated 'ward' drone to the target location, which will alert you and your allies when it sees entities \ - moving around it, or when it is attacked. They can see for up to five meters." - enhancement_desc = "Wards can detect invisibile entities, and are more specific in relaying information about what it sees. \ - Invisible entities that are spotted by it will be decloaked." + name = "Summon Monitor Ward" + desc = "Teleports a prefabricated 'ward' drone to the target location, which will alert you when it sees entities \ + moving around it, or when it is attacked. They can see for up to five meters. It can also see invisible entities, and \ + forcefully decloak them if close enough." cost = 25 obj_path = /obj/item/weapon/spell/summon/summon_ward category = UTILITY_SPELLS @@ -12,116 +11,10 @@ name = "summon ward" desc = "Finally, someone you can depend on to watch your back." cast_methods = CAST_RANGED - summoned_mob_type = /mob/living/simple_animal/ward + summoned_mob_type = /mob/living/simple_mob/mechanical/ward/monitor cooldown = 10 instability_cost = 5 energy_cost = 500 -/obj/item/weapon/spell/summon/summon_ward/on_summon(var/mob/living/simple_animal/ward/ward) - ward.creator = owner - if(check_for_scepter()) - ward.true_sight = 1 - ward.see_invisible = SEE_INVISIBLE_LEVEL_TWO - -/mob/living/simple_animal/ward - name = "ward" - desc = "It's a little flying drone that seems to be watching you..." - icon = 'icons/mob/critter.dmi' - icon_state = "ward" - resistance = 5 - wander = 0 - response_help = "pets the" - response_disarm = "swats away" - response_harm = "punches" - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - minbodytemp = 0 - maxbodytemp = 0 - unsuitable_atoms_damage = 0 - heat_damage_per_tick = 0 - cold_damage_per_tick = 0 - - var/true_sight = 0 // If true, detects more than what the Technomancer normally can't. - var/mob/living/carbon/human/creator = null - var/list/seen_mobs = list() - -/mob/living/simple_animal/ward/death() - if(creator) - creator << "Your ward inside [get_area(src)] was killed!" - ..() - qdel(src) - -/mob/living/simple_animal/ward/proc/expire() - if(creator && src) - creator << "Your ward inside [get_area(src)] expired." - qdel(src) - -/mob/living/simple_animal/ward/Life() - ..() - detect_mobs() - update_icon() - -/mob/living/simple_animal/ward/proc/detect_mobs() - var/list/things_in_sight = view(5,src) - var/list/newly_seen_mobs = list() - for(var/mob/living/L in things_in_sight) - if(L == creator) // I really wish is_ally() was usable here. - continue - - if(istype(L, /mob/living/simple_animal/ward)) - continue - - if(istype(L, /mob/living/simple_animal)) - var/mob/living/simple_animal/SA = L - if(creator in SA.friends) - continue - - if(!true_sight) - var/turf/T = get_turf(L) - var/light_amount = T.get_lumcount() - if(light_amount <= 0.5) - continue // Too dark to see. - - if(L.alpha <= 127) - continue // Too transparent, as a mercy to camo lings. - - else - L.break_cloak() - - // Warn the Technomancer when it sees a new mob. - if(!(L in seen_mobs)) - seen_mobs.Add(L) - newly_seen_mobs.Add(L) - if(creator) - if(true_sight) - creator << "Your ward at [get_area(src)] detected [english_list(newly_seen_mobs)]." - else - creator << "Your ward at [get_area(src)] detected something." - - // Now get rid of old mobs that left vision. - for(var/mob/living/L in seen_mobs) - if(!(L in things_in_sight)) - seen_mobs.Remove(L) - - -/mob/living/simple_animal/ward/update_icon() - if(seen_mobs.len) - icon_state = "ward_spotted" - set_light(3, 3, l_color = "FF0000") - else - icon_state = "ward" - set_light(3, 3, l_color = "00FF00") - if(true_sight) - overlays.Cut() - var/image/I = image('icons/mob/critter.dmi',"ward_truesight") - overlays.Add(I) - -/mob/living/simple_animal/ward/invisible_detect - true_sight = 1 - see_invisible = SEE_INVISIBLE_LEVEL_TWO +/obj/item/weapon/spell/summon/summon_ward/on_summon(var/mob/living/simple_mob/mechanical/ward/monitor/my_ward) + my_ward.owner = owner diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 2fb2d48a82..2062f92db4 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -49,10 +49,9 @@ if(occupant) to_chat(user, "\The [src] is already occupied!") return - for(var/mob/living/simple_animal/slime/M in range(1, H.affecting)) - if(M.victim == H.affecting) - to_chat(user, "[H.affecting.name] has a slime attached to them, deal with that first.") - return + if(H.affecting.has_buckled_mobs()) + to_chat(user, span("warning", "\The [H.affecting] has other entities attached to it. Remove them first.")) + return var/mob/M = H.affecting if(M.abiotic()) to_chat(user, "Subject cannot have abiotic items on.") @@ -86,10 +85,9 @@ if(O.abiotic()) to_chat(user, "Subject cannot have abiotic items on.") return 0 - for(var/mob/living/simple_animal/slime/M in range(1, O)) - if(M.victim == O) - to_chat(user, "[O] has a slime attached to them, deal with that first.") - return 0 + if(O.has_buckled_mobs()) + to_chat(user, span("warning", "\The [O] has other entities attached to it. Remove them first.")) + return if(O == user) visible_message("[user] climbs into \the [src].") diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 20f5a617cf..20304fa2ba 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -143,7 +143,7 @@ /obj/machinery/camera/attack_generic(mob/user as mob) if(isanimal(user)) - var/mob/living/simple_animal/S = user + var/mob/living/simple_mob/S = user set_status(0) S.do_attack_animation(src) S.setClickCooldown(user.get_attack_speed()) diff --git a/code/game/machinery/computer/prisonshuttle.dm b/code/game/machinery/computer/prisonshuttle.dm index 8c435f5845..8fe6ffef8a 100644 --- a/code/game/machinery/computer/prisonshuttle.dm +++ b/code/game/machinery/computer/prisonshuttle.dm @@ -201,7 +201,7 @@ var/prison_shuttle_timeleft = 0 for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... bug.gib() - for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... + for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... pest.gib() start_location.move_contents_to(end_location) diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm index 1f7c959db5..431d6278d6 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -81,7 +81,7 @@ var/specops_shuttle_timeleft = 0 for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... bug.gib() - for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... + for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... pest.gib() start_location.move_contents_to(end_location) diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm index ba7193b69b..e02ab68474 100644 --- a/code/game/machinery/computer/syndicate_specops_shuttle.dm +++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm @@ -166,7 +166,7 @@ var/syndicate_elite_shuttle_timeleft = 0 for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... bug.gib() - for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... + for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug... pest.gib() start_location.move_contents_to(end_location) diff --git a/code/game/machinery/computer3/computers/HolodeckControl.dm b/code/game/machinery/computer3/computers/HolodeckControl.dm index fcff13c968..da3c2416fd 100644 --- a/code/game/machinery/computer3/computers/HolodeckControl.dm +++ b/code/game/machinery/computer3/computers/HolodeckControl.dm @@ -207,7 +207,7 @@ for(var/obj/effect/decal/cleanable/blood/B in linkedholodeck) qdel(B) - for(var/mob/living/simple_animal/hostile/carp/C in linkedholodeck) + for(var/mob/living/simple_mob/animal/space/carp/C in linkedholodeck) qdel(C) holographic_items = A.copy_contents_to(linkedholodeck , 1) @@ -228,7 +228,7 @@ T.temperature = 5000 T.hotspot_expose(50000,50000,1) if(L.name=="Holocarp Spawn") - new /mob/living/simple_animal/hostile/carp(L.loc) + new /mob/living/simple_mob/animal/space/carp(L.loc) /datum/file/program/holodeck/proc/emergencyShutdown() diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 20a54046a6..6993d988dd 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -199,14 +199,13 @@ return if(occupant) to_chat(user,"\The [src] is already occupied by [occupant].") - for(var/mob/living/simple_animal/slime/M in range(1,grab.affecting)) - if(M.victim == grab.affecting) - to_chat(usr, "[grab.affecting.name] will not fit into the cryo because they have a slime latched onto their head.") - return + if(grab.affecting.has_buckled_mobs()) + to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to it. Remove them first.")) + return var/mob/M = grab.affecting qdel(grab) put_mob(M) - + return /obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to put people into cryo without external assistance @@ -349,14 +348,14 @@ set name = "Move Inside" set category = "Object" set src in oview(1) - for(var/mob/living/simple_animal/slime/M in range(1,usr)) - if(M.victim == usr) - to_chat(usr, "You're too busy getting your life sucked out of you.") + if(isliving(usr)) + var/mob/living/L = usr + if(L.has_buckled_mobs()) + to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) return - if(usr.stat != 0) - return - put_mob(usr) - return + if(L.stat != CONSCIOUS) + return + put_mob(L) /atom/proc/return_air_for_internal_lifeform(var/mob/living/lifeform) return return_air() diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index ffda46f7cf..fd1dbf529d 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -567,9 +567,10 @@ to_chat(usr, "\The [src] is in use.") return - for(var/mob/living/simple_animal/slime/M in range(1,usr)) - if(M.victim == usr) - to_chat(usr, "You're too busy getting your life sucked out of you.") + if(isliving(usr)) + var/mob/living/L = usr + if(L.has_buckled_mobs()) + to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) return visible_message("[usr] [on_enter_visible_message] [src].", 3) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 541e461f27..fe82ca78a7 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -46,11 +46,12 @@ var/bolt_up_sound = 'sound/machines/boltsup.ogg' var/bolt_down_sound = 'sound/machines/boltsdown.ogg' -/obj/machinery/door/airlock/attack_generic(var/mob/user, var/damage) +/obj/machinery/door/airlock/attack_generic(var/mob/living/user, var/damage) if(stat & (BROKEN|NOPOWER)) - if(damage >= 10) + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) if(src.locked || src.welded) visible_message("\The [user] begins breaking into \the [src] internals!") + user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. if(do_after(user,10 SECONDS,src)) src.locked = 0 src.welded = 0 @@ -58,6 +59,7 @@ open(1) if(prob(25)) src.shock(user, 100) + user.set_AI_busy(FALSE) else if(src.density) visible_message("\The [user] forces \the [src] open!") open(1) @@ -508,9 +510,6 @@ About the new airlock wires panel: return ..(user) -/obj/machinery/door/airlock/bumpopen(mob/living/simple_animal/user as mob) - ..(user) - /obj/machinery/door/airlock/proc/isElectrified() if(src.electrified_until != 0) return 1 diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index ddb2001639..01af732f06 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -39,8 +39,8 @@ /obj/machinery/door/attack_generic(var/mob/user, var/damage) if(isanimal(user)) - var/mob/living/simple_animal/S = user - if(damage >= 10) + var/mob/living/simple_mob/S = user + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) visible_message("\The [user] smashes into the [src]!") playsound(src, S.attack_sound, 75, 1) take_damage(damage) diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index fb7609ee05..ac5246d2da 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -216,22 +216,26 @@ return ..() -/obj/machinery/door/firedoor/attack_generic(var/mob/user, var/damage) +/obj/machinery/door/firedoor/attack_generic(var/mob/living/user, var/damage) if(stat & (BROKEN|NOPOWER)) - if(damage >= 10) + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) var/time_to_force = (2 + (2 * blocked)) * 5 if(src.density) visible_message("\The [user] starts forcing \the [src] open!") + user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. if(do_after(user, time_to_force, src)) visible_message("\The [user] forces \the [src] open!") src.blocked = 0 open(1) + user.set_AI_busy(FALSE) else time_to_force = (time_to_force / 2) visible_message("\The [user] starts forcing \the [src] closed!") + user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything. if(do_after(user, time_to_force, src)) visible_message("\The [user] forces \the [src] closed!") close(1) + user.set_AI_busy(FALSE) else visible_message("\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].") return diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 07357ceb7b..4953236007 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -406,16 +406,16 @@ var/list/turret_icons attacked = 0 ..() -/obj/machinery/porta_turret/attack_generic(mob/user as mob, var/damage) - if(isanimal(user)) - var/mob/living/simple_animal/S = user - if(damage >= 10) +/obj/machinery/porta_turret/attack_generic(mob/living/L, damage) + if(isanimal(L)) + var/mob/living/simple_mob/S = L + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) var/incoming_damage = round(damage - (damage / 5)) //Turrets are slightly armored, assumedly. visible_message("\The [S] [pick(S.attacktext)] \the [src]!") take_damage(incoming_damage) S.do_attack_animation(src) return 1 - visible_message("\The [user] bonks \the [src]'s casing!") + visible_message("\The [L] bonks \the [src]'s casing!") return ..() /obj/machinery/porta_turret/emag_act(var/remaining_charges, var/mob/user) diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index be31a5aa56..139fd8ecb9 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -69,7 +69,7 @@ for(var/obj/machinery/teleport/hub/H in range(1)) var/amount = rand(2,5) for(var/i=0;iKinda hard to climb in while handcuffed don't you think?" return - for(var/mob/living/simple_animal/slime/M in range(1,usr)) - if(M.victim == usr) - usr << "You're too busy getting your life sucked out of you." + if(isliving(usr)) + var/mob/living/L = usr + if(L.has_buckled_mobs()) + to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) return //search for a valid passenger compartment @@ -1329,4 +1330,4 @@ /obj/item/mecha_parts/mecha_equipment/tool/jetpack/do_after_cooldown() sleep(equip_cooldown) wait = 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index d3bdbb4158..edb275a2fa 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -1142,10 +1142,12 @@ to_chat(usr,"Access denied") src.log_append_to_last("Permission denied.") return - for(var/mob/living/simple_animal/slime/M in range(1,usr)) - if(M.victim == usr) - to_chat(usr,"You're too busy getting your life sucked out of you.") + if(isliving(usr)) + var/mob/living/L = usr + if(L.has_buckled_mobs()) + to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first.")) return + // usr << "You start climbing into [src.name]" visible_message("\The [usr] starts to climb into [src.name]") diff --git a/code/game/mecha/mecha_wreckage.dm b/code/game/mecha/mecha_wreckage.dm index 73cee1223b..bc20f25b2e 100644 --- a/code/game/mecha/mecha_wreckage.dm +++ b/code/game/mecha/mecha_wreckage.dm @@ -99,6 +99,14 @@ name = "Dark Gygax wreckage" icon_state = "darkgygax-broken" +/obj/effect/decal/mecha_wreckage/gygax/adv + name = "Advanced Dark Gygax wreckage" + icon_state = "darkgygax_adv-broken" + +/obj/effect/decal/mecha_wreckage/gygax/medgax + name = "Medgax wreckage" + icon_state = "medgax-broken" + /obj/effect/decal/mecha_wreckage/marauder name = "Marauder wreckage" icon_state = "marauder-broken" @@ -198,6 +206,9 @@ parts -= part return +/obj/effect/decal/mecha_wreckage/odysseus/murdysseus + icon_state = "murdysseus-broken" + /obj/effect/decal/mecha_wreckage/hoverpod name = "Hover pod wreckage" icon_state = "engineering_pod-broken" diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm index 8a1242d1a2..f7e886dd62 100644 --- a/code/game/mecha/medical/odysseus.dm +++ b/code/game/mecha/medical/odysseus.dm @@ -109,7 +109,7 @@ else if(foundVirus) holder.icon_state = "hudill" else if(patient.has_brain_worms()) - var/mob/living/simple_animal/borer/B = patient.has_brain_worms() + var/mob/living/simple_mob/animal/borer/B = patient.has_brain_worms() if(B.controlling) holder.icon_state = "hudbrainworm" else diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index 7504b6e65f..056d9f7295 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -99,3 +99,13 @@ mouse_opacity = FALSE anchored = TRUE plane = ABOVE_PLANE + +// Similar to the tesla ball but doesn't actually do anything and is purely visual. +/obj/effect/overlay/energy_ball + name = "energy ball" + desc = "An energy ball." + icon = 'icons/obj/tesla_engine/energy_ball.dmi' + icon_state = "energy_ball" + plane = PLANE_LIGHTING_ABOVE + pixel_x = -32 + pixel_y = -32 diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 8e482cc1cb..f82bcefaa3 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -56,13 +56,15 @@ /obj/effect/spider/stickyweb icon_state = "stickyweb1" - New() - if(prob(50)) - icon_state = "stickyweb2" + +/obj/effect/spider/stickyweb/initialize() + if(prob(50)) + icon_state = "stickyweb2" + return ..() /obj/effect/spider/stickyweb/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) if(air_group || (height==0)) return 1 - if(istype(mover, /mob/living/simple_animal/hostile/giant_spider)) + if(istype(mover, /mob/living/simple_mob/animal/giant_spider)) return 1 else if(istype(mover, /mob/living)) if(prob(50)) @@ -80,10 +82,12 @@ var/spiders_min = 6 var/spiders_max = 24 var/spider_type = /obj/effect/spider/spiderling - New() - pixel_x = rand(3,-3) - pixel_y = rand(3,-3) - processing_objects |= src + +/obj/effect/spider/eggcluster/initialize() + pixel_x = rand(3,-3) + pixel_y = rand(3,-3) + processing_objects |= src + return ..() /obj/effect/spider/eggcluster/New(var/location, var/atom/parent) get_light_and_color(parent) @@ -129,10 +133,10 @@ var/amount_grown = -1 var/obj/machinery/atmospherics/unary/vent_pump/entry_vent var/travelling_in_vent = 0 - var/list/grow_as = list(/mob/living/simple_animal/hostile/giant_spider, /mob/living/simple_animal/hostile/giant_spider/nurse, /mob/living/simple_animal/hostile/giant_spider/hunter) + var/list/grow_as = list(/mob/living/simple_mob/animal/giant_spider, /mob/living/simple_mob/animal/giant_spider/nurse, /mob/living/simple_mob/animal/giant_spider/hunter) /obj/effect/spider/spiderling/frost - grow_as = list(/mob/living/simple_animal/hostile/giant_spider/frost) + grow_as = list(/mob/living/simple_mob/animal/giant_spider/frost) /obj/effect/spider/spiderling/New(var/location, var/atom/parent) pixel_x = rand(6,-6) diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index c2af338521..d700008220 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -107,7 +107,7 @@ return 0 if(!user.IsAdvancedToolUser() && isanimal(user)) - var/mob/living/simple_animal/S = user + var/mob/living/simple_mob/S = user if(!S.IsHumanoidToolUser(src)) return 0 diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 343a58ce1a..c35ec18e59 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -166,7 +166,7 @@ outmsg = "You missed the lens of [C] with [src]." //cats! - for(var/mob/living/simple_animal/cat/C in viewers(1,targloc)) + for(var/mob/living/simple_mob/animal/passive/cat/C in viewers(1,targloc)) if (!(C.stat || C.buckled)) if(prob(50) && !(C.client)) C.visible_message("[C] pounces on the light!", "You pounce on the light!") diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index feed24510b..97d352c3d7 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -451,15 +451,15 @@ HALOGEN COUNTER - Radcount on mobs matter = list(DEFAULT_WALL_MATERIAL = 30,"glass" = 20) /obj/item/device/slime_scanner/attack(mob/living/M as mob, mob/living/user as mob) - if(!isslime(M)) - to_chat(user, "This device can only scan slimes!") + if(!istype(M, /mob/living/simple_mob/slime/xenobio)) + to_chat(user, "This device can only scan lab-grown slimes!") return - var/mob/living/simple_animal/slime/S = M + var/mob/living/simple_mob/slime/xenobio/S = M user.show_message("Slime scan results:
[S.slime_color] [S.is_adult ? "adult" : "baby"] slime
Health: [S.health]
Mutation Probability: [S.mutation_chance]") var/list/mutations = list() for(var/potential_color in S.slime_mutation) - var/mob/living/simple_animal/slime/slime = potential_color + var/mob/living/simple_mob/slime/xenobio/slime = potential_color mutations.Add(initial(slime.slime_color)) user.show_message("Potental to mutate into [english_list(mutations)] colors.
Extract potential: [S.cores]
Nutrition: [S.nutrition]/[S.get_max_nutrition()]") @@ -469,12 +469,14 @@ HALOGEN COUNTER - Radcount on mobs user.show_message("Warning: Subject is hungry.") user.show_message("Electric change strength: [S.power_charge]") - if(S.resentment) - user.show_message("Warning: Subject is harboring resentment.") - if(S.docile) + if(S.has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = S.ai_holder + if(AI.resentment) + user.show_message("Warning: Subject is harboring resentment.") + if(AI.rabid) + user.show_message("Subject is enraged and extremely dangerous!") + if(S.harmless) user.show_message("Subject has been pacified.") - if(S.rabid) - user.show_message("Subject is enraged and extremely dangerous!") if(S.unity) user.show_message("Subject is friendly to other slime colors.") diff --git a/code/game/objects/items/glassjar.dm b/code/game/objects/items/glassjar.dm index c04b9b17dd..5de7ae3730 100644 --- a/code/game/objects/items/glassjar.dm +++ b/code/game/objects/items/glassjar.dm @@ -6,7 +6,7 @@ w_class = ITEMSIZE_SMALL matter = list("glass" = 200) flags = NOBLUDGEON - var/list/accept_mobs = list(/mob/living/simple_animal/lizard, /mob/living/simple_animal/mouse) + var/list/accept_mobs = list(/mob/living/simple_mob/animal/passive/lizard, /mob/living/simple_mob/animal/passive/mouse) var/contains = 0 // 0 = nothing, 1 = money, 2 = animal, 3 = spiderling /obj/item/glass_jar/New() diff --git a/code/game/objects/items/poi_items.dm b/code/game/objects/items/poi_items.dm index af1b6c9c7a..30b6e12763 100644 --- a/code/game/objects/items/poi_items.dm +++ b/code/game/objects/items/poi_items.dm @@ -47,14 +47,3 @@ processing_objects -= src return ..() - -//Crashed Cargo Shuttle PoI - -/obj/structure/largecrate/animal/crashedshuttle - name = "SCP" - -/obj/structure/largecrate/animal/crashedshuttle/initialize() - starts_with = list(pick(/mob/living/simple_animal/hostile/statue, /obj/item/cursed_marble, /obj/item/weapon/deadringer)) // Starts_with has to be a list - name = pick("Spicy Crust Pizzeria", "Soap and Care Products", "Sally's Computer Parts", "Steve's Chocolate Pastries", "Smith & Christian's Plastics","Standard Containers & Packaging Co.", "Sanitary Chemical Purgation (LTD)") - name += " delivery crate" - return ..() diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index 2fa9ea63c7..180e261fc2 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -275,13 +275,6 @@ add_flashes(W,user) else add_flashes(W,user) - else if(istype(W, /obj/item/weapon/stock_parts/manipulator)) - to_chat(user, "You install some manipulators and modify the head, creating a functional spider-bot!") - new /mob/living/simple_animal/spiderbot(get_turf(loc)) - user.drop_item() - qdel(W) - qdel(src) - return return /obj/item/robot_parts/head/proc/add_flashes(obj/item/W as obj, mob/user as mob) //Made into a seperate proc to avoid copypasta diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm old mode 100755 new mode 100644 index c5b637d9e5..13c4b29d45 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -23,7 +23,7 @@ AI MODULES /obj/item/weapon/aiModule/proc/install(var/atom/movable/AM, var/mob/living/user) if(!user.IsAdvancedToolUser() && isanimal(user)) - var/mob/living/simple_animal/S = user + var/mob/living/simple_mob/S = user if(!S.IsHumanoidToolUser(src)) return 0 diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index 5a20c49ccd..a6ad6f4592 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -31,19 +31,25 @@ /obj/item/weapon/grenade/spawnergrenade/manhacks name = "manhack delivery grenade" - spawner_type = /mob/living/simple_animal/hostile/viscerator + spawner_type = /mob/living/simple_mob/mechanical/viscerator deliveryamt = 5 origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) +/obj/item/weapon/grenade/spawnergrenade/manhacks/mercenary + spawner_type = /mob/living/simple_mob/mechanical/viscerator/mercenary + +/obj/item/weapon/grenade/spawnergrenade/manhacks/raider + spawner_type = /mob/living/simple_mob/mechanical/viscerator/raider + /obj/item/weapon/grenade/spawnergrenade/spesscarp name = "carp delivery grenade" - spawner_type = /mob/living/simple_animal/hostile/carp + spawner_type = /mob/living/simple_mob/animal/space/carp deliveryamt = 5 origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) /obj/item/weapon/grenade/spawnergrenade/spider name = "spider delivery grenade" - spawner_type = /mob/living/simple_animal/hostile/giant_spider/hunter + spawner_type = /mob/living/simple_mob/animal/giant_spider/hunter deliveryamt = 3 origin_tech = list(TECH_MATERIAL = 3, TECH_MAGNET = 4, TECH_ILLEGAL = 4) diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index 40c074c939..f3e5d05fdf 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -336,4 +336,4 @@ var/last_chew = 0 target.m_intent = "walk" if(target.hud_used && user.hud_used.move_intent) target.hud_used.move_intent.icon_state = "walking" - return 1 \ No newline at end of file + return 1 diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm index c05d52442c..5b1ba3f6c5 100644 --- a/code/game/objects/items/weapons/implants/implantchair.dm +++ b/code/game/objects/items/weapons/implants/implantchair.dm @@ -79,10 +79,9 @@ var/obj/item/weapon/grab/grab = G if(!ismob(grab.affecting)) return - for(var/mob/living/simple_animal/slime/M in range(1,grab.affecting)) - if(M.victim == grab.affecting) - usr << "[grab.affecting.name] will not fit into the [src.name] because they have a slime latched onto their head." - return + if(grab.affecting.has_buckled_mobs()) + to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to them. Remove them first.")) + return var/mob/M = grab.affecting if(put_mob(M)) qdel(G) diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index ad30eb0a57..d70de28a7e 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -233,11 +233,10 @@ playsound(get_turf(target), 'sound/weapons/blade1.ogg', 100, 1) // Make lesser robots really mad at us. - if(istype(target, /mob/living/simple_animal)) - var/mob/living/simple_animal/SA = target - if(SA.intelligence_level == SA_ROBOTIC) - SA.taunt(user) - SA.adjustFireLoss(force * 6) // 30 Burn, for 50 total. + if(target.mob_class & MOB_CLASS_SYNTHETIC) + if(target.has_AI()) + target.taunt(user) + target.adjustFireLoss(force * 6) // 30 Burn, for 50 total. /* *Energy Blade diff --git a/code/game/objects/items/weapons/policetape.dm b/code/game/objects/items/weapons/policetape.dm index aec6a790b2..d9a886029d 100644 --- a/code/game/objects/items/weapons/policetape.dm +++ b/code/game/objects/items/weapons/policetape.dm @@ -292,7 +292,7 @@ var/list/tape_roll_applications = list() add_fingerprint(M) if (!allowed(M)) //only select few learn art of not crumpling the tape M << "You are not supposed to go past [src]..." - if(M.a_intent == I_HELP && !(istype(M, /mob/living/simple_animal))) + if(M.a_intent == I_HELP && !(istype(M, /mob/living/simple_mob))) return 0 crumple() return ..(mover) diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index e37b2f9977..53c092d923 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -269,9 +269,8 @@ /obj/item/weapon/melee/baton/shocker/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) ..(target, user, hit_zone) - if(istype(target, /mob/living/simple_animal) && status) - var/mob/living/simple_animal/SA = target - SA.taunt(user) + if(status && target.has_AI()) + target.taunt(user) // Borg version, for the lost module. /obj/item/weapon/melee/baton/shocker/robot diff --git a/code/game/objects/items/weapons/tools/screwdriver.dm b/code/game/objects/items/weapons/tools/screwdriver.dm index 86ac8f1267..249f49f529 100644 --- a/code/game/objects/items/weapons/tools/screwdriver.dm +++ b/code/game/objects/items/weapons/tools/screwdriver.dm @@ -122,4 +122,4 @@ counterpart.forceMove(get_turf(src)) src.forceMove(counterpart) user.put_in_active_hand(counterpart) - to_chat(user, "You attach the bolt driver bit to [src].") \ No newline at end of file + to_chat(user, "You attach the bolt driver bit to [src].") diff --git a/code/game/objects/items/weapons/tools/weldingtool.dm b/code/game/objects/items/weapons/tools/weldingtool.dm index 7c2f2ea26f..9cc09b4770 100644 --- a/code/game/objects/items/weapons/tools/weldingtool.dm +++ b/code/game/objects/items/weapons/tools/weldingtool.dm @@ -1,3 +1,7 @@ +<<<<<<< HEAD +======= + +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync #define WELDER_FUEL_BURN_INTERVAL 13 /* * Welding Tool @@ -317,7 +321,10 @@ var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] if(!E) return +<<<<<<< HEAD if(H.nif && H.nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION)) return //VOREStation Add - NIF +======= +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync switch(safety) if(1) to_chat(usr, "Your eyes sting a little.") diff --git a/code/game/objects/items/weapons/tools/wrench.dm b/code/game/objects/items/weapons/tools/wrench.dm index e0a6957c3c..13cf3e51cf 100644 --- a/code/game/objects/items/weapons/tools/wrench.dm +++ b/code/game/objects/items/weapons/tools/wrench.dm @@ -68,4 +68,4 @@ counterpart.forceMove(get_turf(src)) src.forceMove(counterpart) user.put_in_active_hand(counterpart) - to_chat(user, "You attach the screw driver bit to [src].") \ No newline at end of file + to_chat(user, "You attach the screw driver bit to [src].") diff --git a/code/game/objects/random/mob.dm b/code/game/objects/random/mob.dm index 6daa10c0f9..966498a2bd 100644 --- a/code/game/objects/random/mob.dm +++ b/code/game/objects/random/mob.dm @@ -18,44 +18,47 @@ var/mob_retaliate = 0 /obj/random/mob/item_to_spawn() - return pick(prob(10);/mob/living/simple_animal/lizard, - prob(6);/mob/living/simple_animal/retaliate/diyaab, - prob(10);/mob/living/simple_animal/cat/fluff, - prob(6);/mob/living/simple_animal/cat/kitten, - prob(10);/mob/living/simple_animal/corgi, - prob(6);/mob/living/simple_animal/corgi/puppy, - prob(10);/mob/living/simple_animal/crab, - prob(10);/mob/living/simple_animal/chicken, - prob(6);/mob/living/simple_animal/chick, - prob(10);/mob/living/simple_animal/cow, - prob(6);/mob/living/simple_animal/retaliate/goat, - prob(10);/mob/living/simple_animal/penguin, - prob(10);/mob/living/simple_animal/mouse, - prob(10);/mob/living/simple_animal/yithian, - prob(10);/mob/living/simple_animal/tindalos, - prob(10);/mob/living/simple_animal/corgi/tamaskan, - prob(3);/mob/living/simple_animal/parrot, - prob(1);/mob/living/simple_animal/giant_crab) + return pick(prob(10);/mob/living/simple_mob/animal/passive/lizard, + prob(6);/mob/living/simple_mob/animal/sif/diyaab, + prob(10);/mob/living/simple_mob/animal/passive/cat, + prob(6);/mob/living/simple_mob/animal/passive/cat, + prob(10);/mob/living/simple_mob/animal/passive/dog/corgi, + prob(6);/mob/living/simple_mob/animal/passive/dog/corgi/puppy, + prob(10);/mob/living/simple_mob/animal/passive/crab, + prob(10);/mob/living/simple_mob/animal/passive/chicken, + prob(6);/mob/living/simple_mob/animal/passive/chick, + prob(10);/mob/living/simple_mob/animal/passive/cow, + prob(6);/mob/living/simple_mob/animal/goat, + prob(10);/mob/living/simple_mob/animal/passive/penguin, + prob(10);/mob/living/simple_mob/animal/passive/mouse, + prob(10);/mob/living/simple_mob/animal/passive/yithian, + prob(10);/mob/living/simple_mob/animal/passive/tindalos, + prob(10);/mob/living/simple_mob/animal/passive/dog/tamaskan, + prob(3);/mob/living/simple_mob/animal/passive/bird/parrot, + prob(1);/mob/living/simple_mob/animal/passive/crab) /obj/random/mob/spawn_item() //These should only ever have simple mobs. var/build_path = item_to_spawn() - var/mob/living/simple_animal/M = new build_path(src.loc) - M.ai_inactive = 1 //Don't fight eachother while we're still setting up! - if(mob_faction) - M.faction = mob_faction - M.returns_home = mob_returns_home - M.wander = mob_wander - M.wander_distance = mob_wander_distance - if(overwrite_hostility) - M.hostile = mob_hostile - M.retaliate = mob_retaliate - M.ai_inactive = 0 //Now you can kill eachother if your faction didn't override. + var/mob/living/simple_mob/M = new build_path(src.loc) + if(!istype(M)) + return + if(M.has_AI()) + var/datum/ai_holder/AI = M.ai_holder + AI.go_sleep() //Don't fight eachother while we're still setting up! + AI.returns_home = mob_returns_home + AI.wander = mob_wander + AI.max_home_distance = mob_wander_distance + if(overwrite_hostility) + AI.hostile = mob_hostile + AI.retaliate = mob_retaliate + AI.go_wake() //Now you can kill eachother if your faction didn't override. if(pixel_x || pixel_y) M.pixel_x = pixel_x M.pixel_y = pixel_y + /obj/random/mob/sif name = "Random Sif Animal" desc = "This is a random cold weather animal." @@ -65,14 +68,14 @@ mob_wander_distance = 10 /obj/random/mob/sif/item_to_spawn() - return pick(prob(30);/mob/living/simple_animal/retaliate/diyaab, - prob(15);/mob/living/simple_animal/crab, - prob(15);/mob/living/simple_animal/penguin, - prob(15);/mob/living/simple_animal/mouse, - prob(15);/mob/living/simple_animal/corgi/tamaskan, - prob(2);/mob/living/simple_animal/hostile/giant_spider/frost, - prob(1);/mob/living/simple_animal/hostile/goose, - prob(20);/mob/living/simple_animal/giant_crab) + return pick(prob(30);/mob/living/simple_mob/animal/sif/diyaab, + prob(15);/mob/living/simple_mob/animal/passive/crab, + prob(15);/mob/living/simple_mob/animal/passive/penguin, + prob(15);/mob/living/simple_mob/animal/passive/mouse, + prob(15);/mob/living/simple_mob/animal/passive/dog/tamaskan, + prob(2);/mob/living/simple_mob/animal/giant_spider/frost, + prob(1);/mob/living/simple_mob/animal/space/goose, + prob(20);/mob/living/simple_mob/animal/passive/crab) /obj/random/mob/sif/peaceful @@ -84,12 +87,12 @@ mob_wander_distance = 12 /obj/random/mob/sif/peaceful/item_to_spawn() - return pick(prob(30);/mob/living/simple_animal/retaliate/diyaab, - prob(15);/mob/living/simple_animal/crab, - prob(15);/mob/living/simple_animal/penguin, - prob(15);/mob/living/simple_animal/mouse, - prob(15);/mob/living/simple_animal/corgi/tamaskan, - prob(20);/mob/living/simple_animal/giant_crab) + return pick(prob(30);/mob/living/simple_mob/animal/sif/diyaab, + prob(15);/mob/living/simple_mob/animal/passive/crab, + prob(15);/mob/living/simple_mob/animal/passive/penguin, + prob(15);/mob/living/simple_mob/animal/passive/mouse, + prob(15);/mob/living/simple_mob/animal/passive/dog/tamaskan, + prob(20);/mob/living/simple_mob/animal/sif/hooligan_crab) /obj/random/mob/sif/hostile name = "Random Hostile Sif Animal" @@ -97,9 +100,9 @@ icon_state = "frost" /obj/random/mob/sif/hostile/item_to_spawn() - return pick(prob(22);/mob/living/simple_animal/hostile/savik, - prob(33);/mob/living/simple_animal/hostile/giant_spider/frost, - prob(45);/mob/living/simple_animal/hostile/shantak) + return pick(prob(22);/mob/living/simple_mob/animal/sif/savik, + prob(33);/mob/living/simple_mob/animal/giant_spider/frost, + prob(45);/mob/living/simple_mob/animal/sif/shantak) /obj/random/mob/spider name = "Random Spider" //Spiders should patrol where they spawn. @@ -110,9 +113,9 @@ mob_wander_distance = 4 /obj/random/mob/spider/item_to_spawn() - return pick(prob(22);/mob/living/simple_animal/hostile/giant_spider/nurse, - prob(33);/mob/living/simple_animal/hostile/giant_spider/hunter, - prob(45);/mob/living/simple_animal/hostile/giant_spider) + return pick(prob(22);/mob/living/simple_mob/animal/giant_spider/nurse, + prob(33);/mob/living/simple_mob/animal/giant_spider/hunter, + prob(45);/mob/living/simple_mob/animal/giant_spider) /obj/random/mob/spider/nurse name = "Random Nurse Spider" @@ -123,8 +126,8 @@ mob_wander_distance = 4 /obj/random/mob/spider/nurse/item_to_spawn() - return pick(prob(22);/mob/living/simple_animal/hostile/giant_spider/nurse/hat, - prob(45);/mob/living/simple_animal/hostile/giant_spider/nurse) + return pick(prob(22);/mob/living/simple_mob/animal/giant_spider/nurse/hat, + prob(45);/mob/living/simple_mob/animal/giant_spider/nurse) /obj/random/mob/spider/mutant name = "Random Mutant Spider" @@ -133,15 +136,15 @@ /obj/random/mob/spider/mutant/item_to_spawn() return pick(prob(5);/obj/random/mob/spider, - prob(10);/mob/living/simple_animal/hostile/giant_spider/webslinger, - prob(10);/mob/living/simple_animal/hostile/giant_spider/carrier, - prob(33);/mob/living/simple_animal/hostile/giant_spider/lurker, - prob(33);/mob/living/simple_animal/hostile/giant_spider/tunneler, - prob(40);/mob/living/simple_animal/hostile/giant_spider/pepper, - prob(20);/mob/living/simple_animal/hostile/giant_spider/thermic, - prob(40);/mob/living/simple_animal/hostile/giant_spider/electric, - prob(1);/mob/living/simple_animal/hostile/giant_spider/phorogenic, - prob(40);/mob/living/simple_animal/hostile/giant_spider/frost) + prob(10);/mob/living/simple_mob/animal/giant_spider/webslinger, + prob(10);/mob/living/simple_mob/animal/giant_spider/carrier, + prob(33);/mob/living/simple_mob/animal/giant_spider/lurker, + prob(33);/mob/living/simple_mob/animal/giant_spider/tunneler, + prob(40);/mob/living/simple_mob/animal/giant_spider/pepper, + prob(20);/mob/living/simple_mob/animal/giant_spider/thermic, + prob(40);/mob/living/simple_mob/animal/giant_spider/electric, + prob(1);/mob/living/simple_mob/animal/giant_spider/phorogenic, + prob(40);/mob/living/simple_mob/animal/giant_spider/frost) /obj/random/mob/robotic name = "Random Robot Mob" @@ -158,17 +161,18 @@ mob_retaliate = 1 /obj/random/mob/robotic/item_to_spawn() //Hivebots have a total number of 'lots' equal to the lesser drone, at 60. - return pick(prob(60);/mob/living/simple_animal/hostile/malf_drone/lesser, - prob(50);/mob/living/simple_animal/hostile/malf_drone, - prob(15);/mob/living/simple_animal/hostile/mecha/malf_drone, - prob(10);/mob/living/simple_animal/hostile/hivebot, - prob(15);/mob/living/simple_animal/hostile/hivebot/swarm, - prob(10);/mob/living/simple_animal/hostile/hivebot/range, - prob(5);/mob/living/simple_animal/hostile/hivebot/range/rapid, - prob(5);/mob/living/simple_animal/hostile/hivebot/range/ion, - prob(5);/mob/living/simple_animal/hostile/hivebot/range/laser, - prob(5);/mob/living/simple_animal/hostile/hivebot/range/strong, - prob(5);/mob/living/simple_animal/hostile/hivebot/range/guard) + return pick(prob(60);/mob/living/simple_mob/mechanical/combat_drone/lesser, + prob(50);/mob/living/simple_mob/mechanical/combat_drone, + prob(15);/mob/living/simple_mob/mechanical/mecha/ripley, + prob(15);/mob/living/simple_mob/mechanical/mecha/odysseus, + prob(10);/mob/living/simple_mob/mechanical/hivebot, + prob(15);/mob/living/simple_mob/mechanical/hivebot/swarm, + prob(10);/mob/living/simple_mob/mechanical/hivebot/ranged_damage, + prob(5);/mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid, + prob(5);/mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion, + prob(5);/mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser, + prob(5);/mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong, + prob(5);/mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard) /obj/random/mob/robotic/hivebot name = "Random Hivebot" @@ -178,14 +182,14 @@ mob_faction = "hivebot" /obj/random/mob/robotic/hivebot/item_to_spawn() - return pick(prob(10);/mob/living/simple_animal/hostile/hivebot, - prob(15);/mob/living/simple_animal/hostile/hivebot/swarm, - prob(10);/mob/living/simple_animal/hostile/hivebot/range, - prob(5);/mob/living/simple_animal/hostile/hivebot/range/rapid, - prob(5);/mob/living/simple_animal/hostile/hivebot/range/ion, - prob(5);/mob/living/simple_animal/hostile/hivebot/range/laser, - prob(5);/mob/living/simple_animal/hostile/hivebot/range/strong, - prob(5);/mob/living/simple_animal/hostile/hivebot/range/guard) + return pick(prob(10);/mob/living/simple_mob/mechanical/hivebot, + prob(15);/mob/living/simple_mob/mechanical/hivebot/swarm, + prob(10);/mob/living/simple_mob/mechanical/hivebot/ranged_damage, + prob(5);/mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid, + prob(5);/mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion, + prob(5);/mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser, + prob(5);/mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong, + prob(5);/mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard) //Mice @@ -195,7 +199,7 @@ icon_state = "mouse_gray" /obj/random/mob/mouse/item_to_spawn() - return pick(prob(15);/mob/living/simple_animal/mouse/white, - prob(30);/mob/living/simple_animal/mouse/brown, - prob(30);/mob/living/simple_animal/mouse/gray, + return pick(prob(15);/mob/living/simple_mob/animal/passive/mouse/white, + prob(30);/mob/living/simple_mob/animal/passive/mouse/brown, + prob(30);/mob/living/simple_mob/animal/passive/mouse/gray, prob(25);/obj/random/mouseremains) //because figuring out how to come up with it picking nothing is beyond my coding ability. diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index faa9f024f0..370b9b866e 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -176,8 +176,8 @@ return 0 return 1 -/obj/structure/attack_generic(var/mob/user, var/damage, var/attack_verb, var/wallbreaker) - if(!breakable || damage < 10 || !wallbreaker) +/obj/structure/attack_generic(var/mob/user, var/damage, var/attack_verb) + if(!breakable || damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) return 0 visible_message("[user] [attack_verb] the [src] apart!") user.do_attack_animation(src) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 4a392c0bfe..1f6ca8395f 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -381,8 +381,8 @@ else icon_state = icon_opened -/obj/structure/closet/attack_generic(var/mob/user, var/damage, var/attack_message = "destroys", var/wallbreaker) - if(damage < 10 || !wallbreaker) +/obj/structure/closet/attack_generic(var/mob/user, var/damage, var/attack_message = "destroys") + if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) return user.do_attack_animation(src) visible_message("[user] [attack_message] the [src]!") @@ -459,4 +459,4 @@ if(src.loc) if(istype(src.loc, /obj/structure/closet)) return (loc.return_air_for_internal_lifeform(L)) - return return_air() \ No newline at end of file + return return_air() diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index 2c739985b2..91642e434e 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -90,23 +90,23 @@ /obj/structure/largecrate/animal/corgi name = "corgi carrier" - starts_with = list(/mob/living/simple_animal/corgi) + starts_with = list(/mob/living/simple_mob/animal/passive/dog/corgi) /obj/structure/largecrate/animal/cow name = "cow crate" - starts_with = list(/mob/living/simple_animal/cow) + starts_with = list(/mob/living/simple_mob/animal/passive/cow) /obj/structure/largecrate/animal/goat name = "goat crate" - starts_with = list(/mob/living/simple_animal/retaliate/goat) + starts_with = list(/mob/living/simple_mob/animal/goat) /obj/structure/largecrate/animal/cat name = "cat carrier" - starts_with = list(/mob/living/simple_animal/cat) + starts_with = list(/mob/living/simple_mob/animal/passive/cat) /obj/structure/largecrate/animal/cat/bones - starts_with = list(/mob/living/simple_animal/cat/fluff/bones) + starts_with = list(/mob/living/simple_mob/animal/passive/cat/bones) /obj/structure/largecrate/animal/chick name = "chicken crate" - starts_with = list(/mob/living/simple_animal/chick = 5) + starts_with = list(/mob/living/simple_mob/animal/passive/chick = 5) diff --git a/code/game/objects/structures/ghost_pods/mysterious.dm b/code/game/objects/structures/ghost_pods/mysterious.dm index 2e6a11a8bb..cd34d91e6f 100644 --- a/code/game/objects/structures/ghost_pods/mysterious.dm +++ b/code/game/objects/structures/ghost_pods/mysterious.dm @@ -15,7 +15,7 @@ /obj/structure/ghost_pod/manual/corgi/create_occupant(var/mob/M) lightning_strike(get_turf(src), cosmetic = TRUE) density = FALSE - var/mob/living/simple_animal/corgi/R = new(get_turf(src)) + var/mob/living/simple_mob/animal/passive/dog/corgi/R = new(get_turf(src)) if(M.mind) M.mind.transfer_to(R) to_chat(M, "You are a Corgi! Woof!") @@ -47,4 +47,4 @@ R.ghost_inhabit(M) visible_message("The blade shines brightly for a brief moment as [usr] pulls it out of the stone!") log_and_message_admins("successfully acquired a cursed sword.") - ..() \ No newline at end of file + ..() diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 058398d850..4a858d5cce 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -83,8 +83,8 @@ health = (displaced_health - round(current_damage / 4)) cover = 25 -/obj/structure/girder/attack_generic(var/mob/user, var/damage, var/attack_message = "smashes apart", var/wallbreaker) - if(!damage || !wallbreaker) +/obj/structure/girder/attack_generic(var/mob/user, var/damage, var/attack_message = "smashes apart") + if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD) return 0 user.do_attack_animation(src) visible_message("[user] [attack_message] the [src]!") diff --git a/code/game/objects/structures/gravemarker.dm b/code/game/objects/structures/gravemarker.dm index 7ef0d49a51..5b39639a9e 100644 --- a/code/game/objects/structures/gravemarker.dm +++ b/code/game/objects/structures/gravemarker.dm @@ -126,7 +126,7 @@ src.set_dir(turn(src.dir, 90)) return else - if(istype(usr,/mob/living/simple_animal/mouse)) + if(ismouse(usr)) return if(!usr || !isturf(usr.loc)) return diff --git a/code/game/objects/structures/loot_piles.dm b/code/game/objects/structures/loot_piles.dm index f8d9977823..44f11b73fe 100644 --- a/code/game/objects/structures/loot_piles.dm +++ b/code/game/objects/structures/loot_piles.dm @@ -574,6 +574,7 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh icon = 'icons/mecha/mecha.dmi' icon_state = "engineering_pod-broken" density = TRUE + anchored = FALSE // In case a dead mecha-mob dies in a bad spot. chance_uncommon = 20 chance_rare = 10 @@ -615,7 +616,7 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/structure/loot_pile/mecha/ripley name = "ripley wreckage" desc = "The ruins of some unfortunate ripley. Perhaps something is salvageable." - icon_states_to_use = list("ripley-broken", "firefighter-broken", "ripley-broken-old") + icon_state = "ripley-broken" common_loot = list( /obj/random/tool, @@ -649,6 +650,12 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/item/mecha_parts/mecha_equipment/weapon/energy/flamer/rigged ) +/obj/structure/loot_pile/mecha/ripley/firefighter + icon_state = "firefighter-broken" + +/obj/structure/loot_pile/mecha/ripley/random_sprite + icon_states_to_use = list("ripley-broken", "firefighter-broken", "ripley-broken-old") + //Death-Ripley, same common, but more combat-exosuit-based /obj/structure/loot_pile/mecha/deathripley name = "strange ripley wreckage" @@ -719,6 +726,14 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/item/mecha_parts/mecha_equipment/shocker ) +/obj/structure/loot_pile/mecha/odysseus/murdysseus + icon_state = "murdysseus-broken" + +/obj/structure/loot_pile/mecha/hoverpod + name = "hoverpod wreckage" + desc = "The ruins of some unfortunate hoverpod. Perhaps something is salvageable." + icon_state = "engineering_pod" + /obj/structure/loot_pile/mecha/gygax name = "gygax wreckage" desc = "The ruins of some unfortunate gygax. Perhaps something is salvageable." @@ -759,6 +774,18 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/heavy ) +/obj/structure/loot_pile/mecha/gygax/dark + icon_state = "darkgygax-broken" + +// Todo: Better loot. +/obj/structure/loot_pile/mecha/gygax/dark/adv + icon_state = "darkgygax_adv-broken" + icon_scale = 1.5 + pixel_y = 8 + +/obj/structure/loot_pile/mecha/gygax/medgax + icon_state = "medgax-broken" + /obj/structure/loot_pile/mecha/durand name = "durand wreckage" desc = "The ruins of some unfortunate durand. Perhaps something is salvageable." @@ -799,6 +826,22 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/heavy ) +/obj/structure/loot_pile/mecha/marauder + name = "marauder wreckage" + desc = "The ruins of some unfortunate marauder. Perhaps something is salvagable." + icon_state = "marauder-broken" + // Todo: Better loot. + +/obj/structure/loot_pile/mecha/marauder/seraph + name = "seraph wreckage" + desc = "The ruins of some unfortunate seraph. Perhaps something is salvagable." + icon_state = "seraph-broken" + +/obj/structure/loot_pile/mecha/marauder/mauler + name = "mauler wreckage" + desc = "The ruins of some unfortunate mauler. Perhaps something is salvagable." + icon_state = "mauler-broken" + /obj/structure/loot_pile/mecha/phazon name = "phazon wreckage" desc = "The ruins of some unfortunate phazon. Perhaps something is salvageable." diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm index 95d2656ed5..26889106ac 100644 --- a/code/game/objects/structures/musician.dm +++ b/code/game/objects/structures/musician.dm @@ -321,7 +321,7 @@ set category = "Object" set src in oview(1) - if(istype(usr,/mob/living/simple_animal/mouse)) + if(istype(usr,/mob/living/simple_mob/animal/passive/mouse)) return else if(!usr || !isturf(usr.loc)) return diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm index 0340356e37..f328eb54fa 100644 --- a/code/game/objects/structures/plasticflaps.dm +++ b/code/game/objects/structures/plasticflaps.dm @@ -10,8 +10,8 @@ explosion_resistance = 5 var/list/mobs_can_pass = list( /mob/living/bot, - /mob/living/simple_animal/slime, - /mob/living/simple_animal/mouse, + /mob/living/simple_mob/slime/xenobio, + /mob/living/simple_mob/animal/passive/mouse, /mob/living/silicon/robot/drone ) diff --git a/code/game/objects/structures/props/nest.dm b/code/game/objects/structures/props/nest.dm index b6a4677f12..4b54d010cf 100644 --- a/code/game/objects/structures/props/nest.dm +++ b/code/game/objects/structures/props/nest.dm @@ -11,7 +11,7 @@ var/last_spawn var/spawn_delay = 150 var/randomize_spawning = FALSE - var/creature_types = list(/mob/living/simple_animal/retaliate/diyaab) + var/creature_types = list(/mob/living/simple_mob/animal/sif/diyaab) var/list/den_mobs var/den_faction //The faction of any spawned creatures. var/max_creatures = 3 //Maximum number of living creatures this nest can have at one time. diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index 4f430d3ec6..23e2ac1f76 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -206,6 +206,6 @@ /obj/structure/simple_door/cult/TryToSwitchState(atom/user) if(isliving(user)) var/mob/living/L = user - if(!iscultist(L) && !istype(L, /mob/living/simple_animal/construct)) + if(!iscultist(L) && !istype(L, /mob/living/simple_mob/construct)) return ..() diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm index 3e76682b19..f1c8217210 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -77,7 +77,7 @@ src.set_dir(turn(src.dir, 90)) return else - if(istype(usr,/mob/living/simple_animal/mouse)) + if(istype(usr,/mob/living/simple_mob/animal/passive/mouse)) return if(!usr || !isturf(usr.loc)) return diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 5c7adf8f0e..251e168293 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -206,7 +206,7 @@ user.setClickCooldown(user.get_attack_speed()) if(!damage) return - if(damage >= 10) + if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD) visible_message("[user] smashes into [src]!") if(reinf) damage = damage / 2 diff --git a/code/game/objects/weapons.dm b/code/game/objects/weapons.dm index 6cb93ab530..19fe4eb03c 100644 --- a/code/game/objects/weapons.dm +++ b/code/game/objects/weapons.dm @@ -27,16 +27,16 @@ cleaving = TRUE var/hit_mobs = 0 - for(var/mob/living/simple_animal/SA in range(get_turf(target), 1)) - if(SA.stat == DEAD) // Don't beat a dead horse. + for(var/mob/living/simple_mob/SM in range(get_turf(target), 1)) + if(SM.stat == DEAD) // Don't beat a dead horse. continue - if(SA == user) // Don't hit ourselves. Simple mobs shouldn't be able to do this but that might change later to be able to hit all mob/living-s. + if(SM == user) // Don't hit ourselves. Simple mobs shouldn't be able to do this but that might change later to be able to hit all mob/living-s. continue - if(SA == target) // We (presumably) already hit the target before cleave() was called. orange() should prevent this but just to be safe... + if(SM == target) // We (presumably) already hit the target before cleave() was called. orange() should prevent this but just to be safe... continue - if(!SA.Adjacent(user) || !SA.Adjacent(target)) // Cleaving only hits mobs near the target mob and user. + if(!SM.Adjacent(user) || !SM.Adjacent(target)) // Cleaving only hits mobs near the target mob and user. continue - if(resolve_attackby(SA, user, attack_modifier = 0.5)) // Hit them with the weapon. This won't cause recursive cleaving due to the cleaving variable being set to true. + if(resolve_attackby(SM, user, attack_modifier = 0.5)) // Hit them with the weapon. This won't cause recursive cleaving due to the cleaving variable being set to true. hit_mobs++ cleave_visual(user, target) @@ -55,4 +55,4 @@ // This is purely the visual effect of cleaving. /obj/item/weapon/proc/cleave_visual(var/mob/living/user, var/mob/living/target) var/obj/effect/temporary_effect/cleave_attack/E = new(get_turf(src)) - E.dir = get_dir(user, target) \ No newline at end of file + E.dir = get_dir(user, target) diff --git a/code/game/turfs/simulated/outdoors/outdoors.dm b/code/game/turfs/simulated/outdoors/outdoors.dm index 968816dc6a..6843a78264 100644 --- a/code/game/turfs/simulated/outdoors/outdoors.dm +++ b/code/game/turfs/simulated/outdoors/outdoors.dm @@ -132,4 +132,4 @@ var/list/turf_edge_cache = list() if(3) if(prob(66)) return - demote() \ No newline at end of file + demote() diff --git a/code/game/turfs/simulated/outdoors/snow.dm b/code/game/turfs/simulated/outdoors/snow.dm index 5fed8af66b..0f9be4ddbf 100644 --- a/code/game/turfs/simulated/outdoors/snow.dm +++ b/code/game/turfs/simulated/outdoors/snow.dm @@ -13,6 +13,9 @@ /turf/simulated/floor/outdoors/snow/Entered(atom/A) if(isliving(A)) + var/mob/living/L = A + if(L.hovering) // Flying things shouldn't make footprints. + return ..() var/mdir = "[A.dir]" crossed_dirs[mdir] = 1 update_icon() diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index 02c6d0d8dc..bd6adfe32f 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -60,9 +60,9 @@ var/damage_lower = 25 var/damage_upper = 75 if(isanimal(user)) - var/mob/living/simple_animal/S = user + var/mob/living/simple_mob/S = user playsound(src, S.attack_sound, 75, 1) - if(!(S.melee_damage_upper >= 10)) + if(!(S.melee_damage_upper >= STRUCTURE_MIN_DAMAGE_THRESHOLD * 2)) to_chat(user, "You bounce against the wall.") return FALSE damage_lower = S.melee_damage_lower @@ -75,7 +75,7 @@ to_chat(user, "You smash through the wall!") user.do_attack_animation(src) if(isanimal(user)) - var/mob/living/simple_animal/S = user + var/mob/living/simple_mob/S = user playsound(src, S.attack_sound, 75, 1) spawn(1) dismantle_wall(1) @@ -115,12 +115,12 @@ try_touch(user, rotting) -/turf/simulated/wall/attack_generic(var/mob/user, var/damage, var/attack_message, var/wallbreaker) +/turf/simulated/wall/attack_generic(var/mob/user, var/damage, var/attack_message) radiate() user.setClickCooldown(user.get_attack_speed()) var/rotting = (locate(/obj/effect/overlay/wallrot) in src) - if(!damage || !wallbreaker) + if(damage < STRUCTURE_MIN_DAMAGE_THRESHOLD * 2) try_touch(user, rotting) return @@ -128,7 +128,7 @@ return success_smash(user) if(reinf_material) - if((wallbreaker == 2) || (damage >= max(material.hardness,reinf_material.hardness))) + if(damage >= max(material.hardness, reinf_material.hardness) ) return success_smash(user) else if(damage >= material.hardness) return success_smash(user) diff --git a/code/game/turfs/simulated/water.dm b/code/game/turfs/simulated/water.dm index 6539348e5d..7ed1ad2965 100644 --- a/code/game/turfs/simulated/water.dm +++ b/code/game/turfs/simulated/water.dm @@ -9,6 +9,9 @@ edge_blending_priority = -1 movement_cost = 4 outdoors = TRUE + + layer = WATER_FLOOR_LAYER + can_dirty = FALSE // It's water var/depth = 1 // Higher numbers indicates deeper water. @@ -19,10 +22,11 @@ /turf/simulated/floor/water/update_icon() ..() // To get the edges. - icon_state = water_state - var/image/floorbed_sprite = image(icon = 'icons/turf/outdoors.dmi', icon_state = under_state) - underlays.Cut() // To clear the old underlay, so the list doesn't expand infinitely - underlays.Add(floorbed_sprite) + + icon_state = under_state // This isn't set at compile time in order for it to show as water in the map editor. + var/image/water_sprite = image(icon = 'icons/turf/outdoors.dmi', icon_state = water_state, layer = WATER_LAYER) + add_overlay(water_sprite) + update_icon_edge() /turf/simulated/floor/water/get_edge_icon_state() @@ -102,6 +106,8 @@ /mob/living/proc/check_submerged() if(buckled) return 0 + if(hovering) + return 0 var/turf/simulated/floor/water/T = loc if(istype(T)) return T.depth @@ -115,6 +121,8 @@ adjust_fire_stacks(-amount * 5) for(var/atom/movable/AM in contents) AM.water_act(amount) + remove_modifiers_of_type(/datum/modifier/fire) + inflict_water_damage(20 * amount) // Only things vulnerable to water will actually be harmed (slimes/prommies). var/list/shoreline_icon_cache = list() @@ -140,7 +148,13 @@ var/list/shoreline_icon_cache = list() var/icon/shoreline_water = icon(src.icon, "shoreline_water", src.dir) var/icon/shoreline_subtract = icon(src.icon, "[initial(icon_state)]_subtract", src.dir) shoreline_water.Blend(shoreline_subtract,ICON_SUBTRACT) + var/image/final = image(shoreline_water) + final.layer = WATER_LAYER - shoreline_icon_cache[cache_string] = shoreline_water + shoreline_icon_cache[cache_string] = final add_overlay(shoreline_icon_cache[cache_string]) +/turf/simulated/floor/water/is_safe_to_enter(mob/living/L) + if(L.get_water_protection() < 1) + return FALSE + return ..() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 0c18524346..005c8ac727 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -323,6 +323,10 @@ var/const/enterloopsanity = 100 /turf/AllowDrop() return TRUE +// Returns false if stepping into a tile would cause harm (e.g. open space while unable to fly, water tile while a slime, lava, etc). +/turf/proc/is_safe_to_enter(mob/living/L) + return TRUE + // This is all the way up here since its the common ancestor for things that need to get replaced with a floor when an RCD is used on them. // More specialized turfs like walls should instead override this. // The code for applying lattices/floor tiles onto lattices could also utilize something similar in the future. diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index decd0f2eb3..5e5d3d9baf 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -28,7 +28,7 @@ if(N == /turf/space) var/turf/below = GetBelow(src) - if(istype(below) && (air_master.has_valid_zone(below) || air_master.has_valid_zone(src))) + if(istype(below) && !istype(below,/turf/space)) N = /turf/simulated/open var/obj/fire/old_fire = fire diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 4b0a1122e9..6fcb620163 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -280,21 +280,21 @@ if("larva") M.change_mob_type( /mob/living/carbon/alien/larva , null, null, delmob ) if("nymph") M.change_mob_type( /mob/living/carbon/alien/diona , null, null, delmob ) if("human") M.change_mob_type( /mob/living/carbon/human , null, null, delmob, href_list["species"]) - if("slime") M.change_mob_type( /mob/living/simple_animal/slime , null, null, delmob ) + if("slime") M.change_mob_type( /mob/living/simple_mob/slime/xenobio , null, null, delmob ) if("monkey") M.change_mob_type( /mob/living/carbon/human/monkey , null, null, delmob ) if("robot") M.change_mob_type( /mob/living/silicon/robot , null, null, delmob ) - if("cat") M.change_mob_type( /mob/living/simple_animal/cat , null, null, delmob ) - if("runtime") M.change_mob_type( /mob/living/simple_animal/cat/fluff/Runtime , null, null, delmob ) - if("corgi") M.change_mob_type( /mob/living/simple_animal/corgi , null, null, delmob ) - if("ian") M.change_mob_type( /mob/living/simple_animal/corgi/Ian , null, null, delmob ) - if("crab") M.change_mob_type( /mob/living/simple_animal/crab , null, null, delmob ) - if("coffee") M.change_mob_type( /mob/living/simple_animal/crab/Coffee , null, null, delmob ) - if("parrot") M.change_mob_type( /mob/living/simple_animal/parrot , null, null, delmob ) - if("polyparrot") M.change_mob_type( /mob/living/simple_animal/parrot/Poly , null, null, delmob ) - if("constructarmoured") M.change_mob_type( /mob/living/simple_animal/construct/armoured , null, null, delmob ) - if("constructbuilder") M.change_mob_type( /mob/living/simple_animal/construct/builder , null, null, delmob ) - if("constructwraith") M.change_mob_type( /mob/living/simple_animal/construct/wraith , null, null, delmob ) - if("shade") M.change_mob_type( /mob/living/simple_animal/shade , null, null, delmob ) + if("cat") M.change_mob_type( /mob/living/simple_mob/animal/passive/cat , null, null, delmob ) + if("runtime") M.change_mob_type( /mob/living/simple_mob/animal/passive/cat/runtime , null, null, delmob ) + if("corgi") M.change_mob_type( /mob/living/simple_mob/animal/passive/dog/corgi , null, null, delmob ) + if("ian") M.change_mob_type( /mob/living/simple_mob/animal/passive/dog/corgi/Ian , null, null, delmob ) + if("crab") M.change_mob_type( /mob/living/simple_mob/animal/passive/crab , null, null, delmob ) + if("coffee") M.change_mob_type( /mob/living/simple_mob/animal/passive/crab/Coffee , null, null, delmob ) + if("parrot") M.change_mob_type( /mob/living/simple_mob/animal/passive/bird/parrot , null, null, delmob ) + if("polyparrot") M.change_mob_type( /mob/living/simple_mob/animal/passive/bird/parrot/poly , null, null, delmob ) + if("constructarmoured") M.change_mob_type( /mob/living/simple_mob/construct/juggernaut , null, null, delmob ) + if("constructbuilder") M.change_mob_type( /mob/living/simple_mob/construct/artificer , null, null, delmob ) + if("constructwraith") M.change_mob_type( /mob/living/simple_mob/construct/wraith , null, null, delmob ) + if("shade") M.change_mob_type( /mob/living/simple_mob/construct/shade , null, null, delmob ) /////////////////////////////////////new ban stuff diff --git a/code/modules/admin/verbs/buildmode.dm b/code/modules/admin/verbs/buildmode.dm index a57b17d15f..4cfa952b67 100644 --- a/code/modules/admin/verbs/buildmode.dm +++ b/code/modules/admin/verbs/buildmode.dm @@ -127,6 +127,18 @@ usr << "Right Mouse Button on turf/obj/mob = Reset glowing" usr << "Right Mouse Button on buildmode button = Change glow properties" usr << "***********************************************************" + if(9) // Control mobs with ai_holders. + usr << "***********************************************************" + usr << "Left Mouse Button on AI mob = Select/Deselect mob" + usr << "Left Mouse Button + alt on AI mob = Toggle hostility on mob" + usr << "Left Mouse Button + ctrl on AI mob = Reset target/following/movement" + usr << "Right Mouse Button on enemy mob = Command selected mobs to attack mob" + usr << "Right Mouse Button on allied mob = Command selected mobs to follow mob" + usr << "Right Mouse Button + shift on any mob = Command selected mobs to follow mob regardless of faction" + usr << "Right Mouse Button on tile = Command selected mobs to move to tile (will cancel if enemies are seen)" + usr << "Right Mouse Button + shift on tile = Command selected mobs to reposition to tile (will not be inturrupted by enemies)" + usr << "Right Mouse Button + alt on obj/turfs = Command selected mobs to attack obj/turf" + usr << "***********************************************************" return 1 /obj/effect/bmode/buildquit @@ -146,6 +158,7 @@ var/obj/effect/bmode/buildmode/buildmode = null var/obj/effect/bmode/buildquit/buildquit = null var/atom/movable/throw_atom = null + var/list/selected_mobs = list() /obj/effect/bmode/buildholder/Destroy() qdel(builddir) @@ -157,9 +170,21 @@ qdel(buildquit) buildquit = null throw_atom = null + for(var/mob/living/unit in selected_mobs) + deselect_AI_mob(cl, unit) + selected_mobs.Cut() cl = null return ..() +/obj/effect/bmode/buildholder/proc/select_AI_mob(client/C, mob/living/unit) + selected_mobs += unit + C.images += unit.selected_image + +/obj/effect/bmode/buildholder/proc/deselect_AI_mob(client/C, mob/living/unit) + selected_mobs -= unit + C.images -= unit.selected_image + + /obj/effect/bmode/buildmode icon_state = "buildmode1" screen_loc = "NORTH,WEST+2" @@ -210,6 +235,9 @@ master.cl.buildmode = 8 src.icon_state = "buildmode8" if(8) + master.cl.buildmode = 9 + src.icon_state = "buildmode9" + if(9) master.cl.buildmode = 1 src.icon_state = "buildmode1" @@ -416,6 +444,86 @@ if(pa.Find("right")) if(object) object.set_light(0, 0, "#FFFFFF") + if(9) // AI control + if(pa.Find("left")) + if(isliving(object)) + var/mob/living/L = object + // Reset processes. + if(pa.Find("ctrl")) + if(!isnull(L.get_AI_stance())) // Null means there's no AI datum or it has one but is player controlled w/o autopilot on. + var/datum/ai_holder/AI = L.ai_holder + AI.forget_everything() + to_chat(user, span("notice", "\The [L]'s AI has forgotten its target/movement destination/leader.")) + else + to_chat(user, span("warning", "\The [L] is not AI controlled.")) + return + + // Toggle hostility + if(pa.Find("alt")) + if(!isnull(L.get_AI_stance())) + var/datum/ai_holder/AI = L.ai_holder + AI.hostile = !AI.hostile + to_chat(user, span("notice", "\The [L] is now [AI.hostile ? "hostile" : "passive"].")) + else + to_chat(user, span("warning", "\The [L] is not AI controlled.")) + return + + // Select/Deselect + if(!isnull(L.get_AI_stance())) + if(L in holder.selected_mobs) + holder.deselect_AI_mob(user.client, L) + to_chat(user, span("notice", "Deselected \the [L].")) + else + holder.select_AI_mob(user.client, L) + to_chat(user, span("notice", "Selected \the [L].")) + else + to_chat(user, span("warning", "\The [L] is not AI controlled.")) + + if(pa.Find("right")) + if(istype(object, /atom)) // Force attack. + var/atom/A = object + + if(pa.Find("alt")) + var/i = 0 + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.give_target(A) + i++ + to_chat(user, span("notice", "Commanded [i] mob\s to attack \the [A].")) + return + + if(isliving(object)) // Follow or attack. + var/mob/living/L = object + var/i = 0 // Attacking mobs. + var/j = 0 // Following mobs. + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + if(L.IIsAlly(unit) || !AI.hostile || pa.Find("shift")) + AI.set_follow(L) + j++ + else + AI.give_target(L) + i++ + var/message = "Commanded " + if(i) + message += "[i] mob\s to attack \the [L]" + if(j) + message += ", and " + else + message += "." + if(j) + message += "[j] mob\s to follow \the [L]." + to_chat(user, span("notice", message)) + + if(isturf(object)) // Move or reposition. + var/turf/T = object + var/i = 0 + for(var/mob/living/unit in holder.selected_mobs) + var/datum/ai_holder/AI = unit.ai_holder + AI.give_destination(T, 1, pa.Find("shift")) // If shift is held, the mobs will not stop moving to attack a visible enemy. + i++ + to_chat(user, span("notice", "Commanded [i] mob\s to move to \the [T].")) + /obj/effect/bmode/buildmode/proc/get_path_from_partial_text(default_path) var/desired_path = input("Enter full or partial typepath.","Typepath","[default_path]") diff --git a/code/modules/admin/verbs/lightning_strike.dm b/code/modules/admin/verbs/lightning_strike.dm index 16cb6b06dd..6adb122d4a 100644 --- a/code/modules/admin/verbs/lightning_strike.dm +++ b/code/modules/admin/verbs/lightning_strike.dm @@ -1,115 +1,99 @@ -/client/proc/admin_lightning_strike() - set name = "Lightning Strike" - set desc = "Causes lightning to strike on your tile. This will hurt things on or nearby it severely." - set category = "Fun" - - if(!check_rights(R_FUN)) - return - - var/result = alert(src, "Really strike your tile with lightning?", "Confirm Badmin" , "No", "Yes (Cosmetic)", "Yes (Real)") - - if(result == "No") - return - var/fake_lightning = result == "Yes (Cosmetic)" - - lightning_strike(get_turf(usr), fake_lightning) - log_and_message_admins("[key_name(src)] has caused [fake_lightning ? "cosmetic":"harmful"] lightning to strike at their position ([src.mob.x], [src.mob.y], [src.mob.z]). \ - (JMP)") - -#define LIGHTNING_REDIRECT_RANGE 28 // How far in tiles certain things draw lightning from. -#define LIGHTNING_ZAP_RANGE 3 // How far the tesla effect zaps, as well as the bad effects from a direct strike. -#define LIGHTNING_POWER 20000 // How much 'zap' is in a strike, used for tesla_zap(). - -// The real lightning proc. -// This is global until I can figure out a better place for it. -// T is the turf that is being struck. If cosmetic is true, the lightning won't actually hurt anything. -/proc/lightning_strike(turf/T, cosmetic = FALSE) - // First, visuals. - - // Do a lightning flash for the whole planet, if the turf belongs to a planet. - var/datum/planet/P = null - P = SSplanets.z_to_planet[T.z] - if(P) - var/datum/weather_holder/holder = P.weather_holder - flick("lightning_flash", holder.special_visuals) - - // Before we do the other visuals, we need to see if something is going to hijack our intended target. - var/obj/machinery/power/grounding_rod/ground = null // Most of the bad effects of lightning will get negated if a grounding rod is nearby. - var/obj/machinery/power/tesla_coil/coil = null // However a tesla coil has higher priority and the strike will bounce. - - for(var/obj/machinery/power/thing in range(LIGHTNING_REDIRECT_RANGE, T)) - if(istype(thing, /obj/machinery/power/tesla_coil)) - var/turf/simulated/coil_turf = get_turf(thing) - if(istype(coil_turf) && thing.anchored && coil_turf.outdoors) - coil = thing - break - - if(istype(thing, /obj/machinery/power/grounding_rod)) - var/turf/simulated/rod_turf = get_turf(thing) - if(istype(rod_turf) && thing.anchored && rod_turf.outdoors) - ground = thing - - if(coil) // Coil gets highest priority. - T = coil.loc - else if(ground) - T = ground.loc - - // Now make the lightning strike sprite. It will fade and delete itself in a second. - new /obj/effect/temporary_effect/lightning_strike(T) - - // For those close up. - playsound(T, 'sound/effects/lightningbolt.ogg', 100, 1) - - // And for those far away. If the strike happens on a planet, everyone on the planet will hear it. - // Otherwise only those on the current z-level will hear it. - var/sound = get_sfx("thunder") - for(var/mob/M in player_list) - if((P && M.z in P.expected_z_levels) || M.z == T.z) - M.playsound_local(get_turf(M), soundin = sound, vol = 70, vary = FALSE, is_global = TRUE) - - if(cosmetic) // Everything beyond here involves potentially damaging things. If we don't want to do that, stop now. - return - - if(ground) // All is well. - ground.tesla_act(LIGHTNING_POWER, FALSE) - return - - else if(coil) // Otherwise lets bounce off the tesla coil. - coil.tesla_act(LIGHTNING_POWER, TRUE) - - else // Striking the turf directly. - tesla_zap(T, zap_range = LIGHTNING_ZAP_RANGE, power = LIGHTNING_POWER, explosive = FALSE, stun_mobs = TRUE) - - // Some extra effects. - // Some apply to those within zap range, others if they were a bit farther away. - for(var/mob/living/L in view(5, T)) - if(get_dist(L, T) <= LIGHTNING_ZAP_RANGE) // They probably got zapped. - // The actual damage/electrocution is handled by tesla_zap(). - L.Paralyse(5) - L.stuttering += 20 - L.make_jittery(20) - L.emp_act(1) - to_chat(L, span("critical", "You've been struck by lightning!")) - - // If a non-player simplemob was struck, inflict huge damage. - // If the damage is fatal, the SA is turned to ash. - if(istype(L, /mob/living/simple_animal) && !L.key) - var/mob/living/simple_animal/SA = L - SA.adjustFireLoss(200) - SA.updatehealth() - if(SA.health <= 0) // Might be best to check/give simple_mobs siemens when this gets ported to new mobs. - SA.visible_message(span("critical", "\The [SA] disintegrates into ash!")) - SA.ash() - continue // No point deafening something that wont exist. - - // Deafen them. - if(L.get_ear_protection() < 2) - L.AdjustSleeping(-100) - if(iscarbon(L)) - var/mob/living/carbon/C = L - C.ear_deaf += 10 - to_chat(L, span("danger", "Lightning struck nearby, and the thunderclap is deafening!")) - -#undef GROUNDING_ROD_RANGE -#undef LIGHTNING_ZAP_RANGE -#undef LIGHTNING_POWER \ No newline at end of file +/client/proc/admin_lightning_strike() + set name = "Lightning Strike" + set desc = "Causes lightning to strike on your tile. This will hurt things on or nearby it severely." + set category = "Fun" + + if(!check_rights(R_FUN)) + return + + var/result = alert(src, "Really strike your tile with lightning?", "Confirm Badmin" , "No", "Yes (Cosmetic)", "Yes (Real)") + + if(result == "No") + return + var/fake_lightning = result == "Yes (Cosmetic)" + + lightning_strike(get_turf(usr), fake_lightning) + log_and_message_admins("[key_name(src)] has caused [fake_lightning ? "cosmetic":"harmful"] lightning to strike at their position ([src.mob.x], [src.mob.y], [src.mob.z]). \ + (JMP)") + +#define LIGHTNING_REDIRECT_RANGE 28 // How far in tiles certain things draw lightning from. +#define LIGHTNING_ZAP_RANGE 3 // How far the tesla effect zaps, as well as the bad effects from a direct strike. +#define LIGHTNING_POWER 20000 // How much 'zap' is in a strike, used for tesla_zap(). + +// The real lightning proc. +// This is global until I can figure out a better place for it. +// T is the turf that is being struck. If cosmetic is true, the lightning won't actually hurt anything. +/proc/lightning_strike(turf/T, cosmetic = FALSE) + // First, visuals. + + // Do a lightning flash for the whole planet, if the turf belongs to a planet. + var/datum/planet/P = null + P = SSplanets.z_to_planet[T.z] + if(P) + var/datum/weather_holder/holder = P.weather_holder + flick("lightning_flash", holder.special_visuals) + + // Before we do the other visuals, we need to see if something is going to hijack our intended target. + var/obj/machinery/power/grounding_rod/ground = null // Most of the bad effects of lightning will get negated if a grounding rod is nearby. + var/obj/machinery/power/tesla_coil/coil = null // However a tesla coil has higher priority and the strike will bounce. + + for(var/obj/machinery/power/thing in range(LIGHTNING_REDIRECT_RANGE, T)) + if(istype(thing, /obj/machinery/power/tesla_coil)) + var/turf/simulated/coil_turf = get_turf(thing) + if(istype(coil_turf) && thing.anchored && coil_turf.outdoors) + coil = thing + break + + if(istype(thing, /obj/machinery/power/grounding_rod)) + var/turf/simulated/rod_turf = get_turf(thing) + if(istype(rod_turf) && thing.anchored && rod_turf.outdoors) + ground = thing + + if(coil) // Coil gets highest priority. + T = coil.loc + else if(ground) + T = ground.loc + + // Now make the lightning strike sprite. It will fade and delete itself in a second. + new /obj/effect/temporary_effect/lightning_strike(T) + + // For those close up. + playsound(T, 'sound/effects/lightningbolt.ogg', 100, 1) + + // And for those far away. If the strike happens on a planet, everyone on the planet will hear it. + // Otherwise only those on the current z-level will hear it. + var/sound = get_sfx("thunder") + for(var/mob/M in player_list) + if((P && M.z in P.expected_z_levels) || M.z == T.z) + M.playsound_local(get_turf(M), soundin = sound, vol = 70, vary = FALSE, is_global = TRUE) + + if(cosmetic) // Everything beyond here involves potentially damaging things. If we don't want to do that, stop now. + return + + if(ground) // All is well. + ground.tesla_act(LIGHTNING_POWER, FALSE) + return + + else if(coil) // Otherwise lets bounce off the tesla coil. + coil.tesla_act(LIGHTNING_POWER, TRUE) + + else // Striking the turf directly. + tesla_zap(T, zap_range = LIGHTNING_ZAP_RANGE, power = LIGHTNING_POWER, explosive = FALSE, stun_mobs = TRUE) + + // Some extra effects. + // Some apply to those within zap range, others if they were a bit farther away. + for(var/mob/living/L in view(5, T)) + if(get_dist(L, T) <= LIGHTNING_ZAP_RANGE) // They probably got zapped. + L.lightning_act() + + // Deafen them. + if(L.get_ear_protection() < 2) + L.AdjustSleeping(-100) + if(iscarbon(L)) + var/mob/living/carbon/C = L + C.ear_deaf += 10 + to_chat(L, span("danger", "Lightning struck nearby, and the thunderclap is deafening!")) + +#undef GROUNDING_ROD_RANGE +#undef LIGHTNING_ZAP_RANGE +#undef LIGHTNING_POWER diff --git a/code/modules/ai/__readme.dm b/code/modules/ai/__readme.dm new file mode 100644 index 0000000000..85b49ec6cb --- /dev/null +++ b/code/modules/ai/__readme.dm @@ -0,0 +1,201 @@ +/* +[Summary] + +This module contains an AI implementation designed to be (at the base level) mobtype-agnostic, +by being held inside a datum instead of being written into the mob directly. More specialized +subtypes of the base AI may be designed with a specific mob type in mind, but the base system +should be compatible with most types of mobs which have the needed Interfaces in place to +support them. + +When designing a new mob, all that is needed to give a mob an AI is to set +its 'ai_holder_type' variable to the path of the AI that is desired. + + +[Seperation] + +In previous iterations of AI systems, the AI is generally written into the mob's code directly, +which has some advantages, but often makes the code rigid, and also tied the speed of the AI +to the mob's own ticker, meaning it could only decide every two seconds. + +Instead, this version has the code for the AI held inside an /datum/ai_holder object, +which is carried by the mob it controls. This gives some advantages; + All /mob/living mobs can potentially have an AI applied to them, and utilize the + same base code while adding specialized code on top. + + Interfaces allow the base AI code to not need to know what particular mode it's controlling. + + The processing of the AI is independant of the mob's Life() cycle, which allows for a + different clock rate. + + Seperating the AI from the mob simplies the mob's code greatly. + + It is more logical to think that a mob is the 'body', where as its ai_holder is + the 'mind'. + + AIs can be applied or disabled on the fly by instantiating or deleting the + ai_holder, if needed. + + +The current implementation also has some disadvantages, but they can perhaps be resolved +in the future. + AI-driven mob movement and attack speed is tied to half-second delays due to the + AI subsystem ticking at that rate. Porting the timer subsystem and integrating + callbacks into basic AI actions (moving, attacking) can potentially resolve that. + + It can be difficult to modify AI variables at mob instantiation without an ugly + delay, as the ai_holder might not exist yet. + + +[Flow of Processing] + +Terrible visual representation here; +AI Subsystem -> Every 0.5s -> /datum/ai_holder/handle_tactics() -> switch(stance)... + -> Every 2.0s -> /datum/ai_holder/handle_strategicals() -> switch(stance)... + +The AI datum is not processed by the mob itself, but instead it is directly processed +by a new AI subsystem. The AI subsystem contains a list of all active ai_holder +objects, which is iterated every tick to process each individual ai_holder +object attached to a mob. + +Each ai_holder actually has two 'tracks' for processing, a 'fast' track +and a 'slow' track. + +The fast track is named handle_tactics(), and is called every 0.5 seconds. + +The slow track is named handle_strategicals(), and is called every 2 seconds. + +When an ai_holder is iterated on inside the AI subsystem's list, it first +calls that ai_holder's handle_tactics(). It will then call that ai_holder's +handle_strategicals() every fourth tick, effectively doing so every two seconds. + +Both functions do different things depending on which 'stance' the +ai_holder is in. See the Stances section for more information. + +The fast track is for 'cheap' processing that needs to happen fast, such as +walking along a path, initiating an attack, or firing a gun. The rate that +it is called allows for the ai_holder to interact with the world through +its mob very often, giving a more convincing appearance of intelligence, +allowing for faster reaction times to certain events, and allowing for +variable attack speeds that would not be possible when bound to a +two second Life() cycle. + +The slow track, on the other hand, is for 'expensive' processing that might +be too demanding on the CPU to do every half a second, such as +re/calculating an A* path (if the mob uses A*), or running a complicated +tension assessment to determine how brave the mob is feeling. This is the +same delay used for certain tasks in the old implementation, but it is less +noticable due to the mob appearing to do things inbetween those two seconds. + +The purpose of having two tracks is to allow for 'fast' and 'slow' actions +to be more easily encapsulated, and ensures that all ai_holders are syncronized +with each other, as opposed to having individual tick counters inside all of +the ai_holder instances. It should be noted that handle_tactics() is always +called first, before handle_strategicals() every two seconds. + +[Process Skipping] + +An ai_holder object can choose to enter a 'busy' state, or a 'sleep' state, +in order to avoid processing. + +When busy, the AI subsystem will skip over the ai_holder until it is no +longer busy. The busy state is intended to be short-term, and is usually +toggled by the mob when doing something with a delay, so that the ai_holder +does not accidentally do something to inturrupt something important, like +a special attack. + +The longer term alternative to the busy state is the sleep state. Unlike +being busy, an ai_holder set to sleep will remove itself from the +AI subsystem's list, meaning it will no longer process until something +else 'wakes' it. This is usually done when the mob dies or a client +logs into an AI-controlled mob (and the AI is not set to ignore that, +with the autopilot variable). If the mob is revived, the AI will be +awakened automatically. + +The ai_holder functions, and mob functions that are called by the +ai_holder, should not be sleep()ed, as it will block the AI Subsystem +from processing the other ai_holders until the sleep() finishes. +Delays on the mob typically have set waitfor = FALSE, or spawn() is used. + + +[Stances] + +The AI has a large number of states that it can be in, called stances. +The AI will act in a specific way depending on which stance it is in, +and only one stance can be active at a time. This effectively creates +a state pattern. + +To change the stance, set_stance() is used, with the new stance as +the first argument. It should be noted that the change is not immediate, +and it will react to the change next tick instead of immediately switching +to the new stance and acting on that in the same tick. This is done to help +avoid infinite loops (I.E. Stance A switches to Stance B, which then +switches to Stance A, and so on...), and the delay is very short so +it should not be an issue. + +See code/__defines/mob.dm for a list of stance defines, and descriptions +about their purpose. Generally, each stance has its own file in the AI +module folder and are mostly self contained, however some files instead +deal with general things that other stances may require, such as targeting +or movement. + +[Interfaces] + +Interfaces are a concept that is used to help bridge the gap between +the ai_holder, and its mob. Because the (base) ai_holder is explicitly +designed to not be specific to any type of mob, all that it knows is +that it is controlling a /mob/living mob. Some mobs work very differently, +between mob types such as /mob/living/simple_mob, /mob/living/silicon/robot, +/mob/living/carbon/human, and more. + +The solution to the vast differences between mob types is to have the +mob itself deal with how to handle a specific task, such as attacking +something, talking, moving, etc. Interfaces exist to do this. + +Interfaces are applied on the mob-side, and are generally specific to +that mob type. This lets the ai_holder not have to worry about specific +implementations and instead just tell the Interface that it wants to attack +something, or move into a tile. The AI does not need to know if the mob its +controlling has hands, instead that is the mob's responsibility. + +Interface functions have an uppercase I at the start of the function name, +and then the function they are bridging between the AI and the mob +(if it exists), e.g. IMove(), IAttack(), ISay(). + +Interfaces are also used for the AI to ask its mob if it can do certain +things, without having to actually know what type of mob it is attached to. +For example, ICheckRangedAttack() tells the AI if it is possible to do a +ranged attack. For simple_mobs, they can if a ranged projectile type was set, +where as for a human mob, it could check if a gun is in a hand. For a borg, +it could check if a gun is inside their current module. + +[Say List] + +A /datum/say_list is a very light datum that holds a list of strings for the +AI to have their mob say based on certain conditions, such as when threatening +to kill another mob. Despite the name, a say_list also can contain emotes +and some sounds. + +The reason that it is in a seperate datum is to allow for multiple mob types +to have the same text, even when inheritence cannot do that, such as +mercenaries and fake piloted mecha mobs. + +The say_list datum is applied to the mob itself and not held inside the AI datum. + +[Subtypes] + +Some subtypes of ai_holder are more specialized, but remain compatible with +most mob types. There are many different subtypes that make the AI act different +by overriding a function, such as kiting their target, moving up close while +using ranged attacks, or running away if not cloaked. + +Other subtypes are very specific about what kind of mob it controls, and trying +to apply them to a different type of mob will likely result in a lot of bugs +or ASSERT() failures. The xenobio slime AI is an example of the latter. + +To use a specific subtype on a mob, all that is needed is setting the mob's +ai_holder_type to the subtype desired, and it will create that subtype when +the mob is initialize()d. Switching to a subtype 'live' will require additional +effort on the coder. + + +*/ \ No newline at end of file diff --git a/code/modules/ai/_defines.dm b/code/modules/ai/_defines.dm new file mode 100644 index 0000000000..e94d26b3c3 --- /dev/null +++ b/code/modules/ai/_defines.dm @@ -0,0 +1,29 @@ +// Defines for the ai_intelligence var. +// Controls if the mob will do 'advanced tactics' like running from grenades. +#define AI_DUMB 1 // Be dumber than usual. +#define AI_NORMAL 2 // Default level. +#define AI_SMART 3 // Will do more processing to be a little smarter, like not walking while confused if it could risk stepping randomly onto a bad tile. + +#define ai_log(M,V) if(debug_ai) ai_log_output(M,V) + +// Logging level defines. +#define AI_LOG_OFF 0 // Don't show anything. +#define AI_LOG_ERROR 1 // Show logs of things likely causing the mob to not be functioning correctly. +#define AI_LOG_WARNING 2 // Show less serious but still helpful to know issues that might be causing things to work incorrectly. +#define AI_LOG_INFO 3 // Important regular events, like selecting a target or switching stances. +#define AI_LOG_DEBUG 4 // More detailed information about the flow of execution. +#define AI_LOG_TRACE 5 // Even more detailed than the last. Will absolutely flood your chatlog. + +// Results of pre-movement checks. +// Todo: Move outside AI code? +#define MOVEMENT_ON_COOLDOWN -1 // Recently moved and needs to try again soon. +#define MOVEMENT_FAILED 0 // Move() returned false for whatever reason and the mob didn't move. +#define MOVEMENT_SUCCESSFUL 1 // Move() returned true and the mob hopefully moved. + +// Reasons for targets to not be valid. Based on why, the AI responds differently. +#define AI_TARGET_VALID 0 // We can fight them. +#define AI_TARGET_INVIS 1 // They were in field of view but became invisible. Switch to STANCE_BLINDFIGHT if no other viable targets exist. +#define AI_TARGET_NOSIGHT 2 // No longer in field of view. Go STANCE_REPOSITION to their last known location if no other targets are seen. +#define AI_TARGET_ALLY 3 // They are an ally. Find a new target. +#define AI_TARGET_DEAD 4 // They're dead. Find a new target. +#define AI_TARGET_INVINCIBLE 5 // Target is currently unable to receive damage for whatever reason. Find a new target or wait. diff --git a/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm b/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm new file mode 100644 index 0000000000..f2d1f05e13 --- /dev/null +++ b/code/modules/ai/aI_holder_subtypes/simple_mob_ai.dm @@ -0,0 +1,136 @@ +// Base AIs for simple mobs. +// Mob-specific AIs are in their mob's file. + +/datum/ai_holder/simple_mob + hostile = TRUE // The majority of simplemobs are hostile. + cooperative = TRUE + returns_home = FALSE + can_flee = FALSE + speak_chance = 1 // If the mob's saylist is empty, nothing will happen. + wander = TRUE + base_wander_delay = 4 + +// For non-hostile animals, and pets like Ian and Runtime. +/datum/ai_holder/simple_mob/passive + hostile = FALSE + can_flee = TRUE + violent_breakthrough = FALSE + +// Won't wander away, ideal for event-spawned mobs like carp or drones. +/datum/ai_holder/simple_mob/event + wander = FALSE + +// Doesn't really act until told to by something on the outside. +/datum/ai_holder/simple_mob/inert + hostile = FALSE + retaliate = FALSE + can_flee = FALSE + wander = FALSE + speak_chance = 0 + cooperative = FALSE + violent_breakthrough = FALSE // So it can open doors but not attack windows and shatter the literal illusion. + +// Used for technomancer illusions, to resemble player movement better. +/datum/ai_holder/simple_mob/inert/astar + use_astar = TRUE + +// Ranged mobs. + +/datum/ai_holder/simple_mob/ranged +// ranged = TRUE + +// Tries to not waste ammo. +/datum/ai_holder/simple_mob/ranged/careful + conserve_ammo = TRUE + +/datum/ai_holder/simple_mob/ranged/pointblank + pointblank = TRUE + +// Runs away from its target if within a certain distance. +/datum/ai_holder/simple_mob/ranged/kiting + pointblank = TRUE // So we don't need to copypaste post_melee_attack(). + var/run_if_this_close = 4 // If anything gets within this range, it'll try to move away. + var/moonwalk = TRUE // If true, mob turns to face the target while kiting, otherwise they turn in the direction they moved towards. + +/datum/ai_holder/simple_mob/ranged/kiting/threatening + threaten = TRUE + threaten_delay = 1 SECOND // Less of a threat and more of pre-attack notice. + threaten_timeout = 30 SECONDS + conserve_ammo = TRUE + +// For event-spawned malf drones. +/datum/ai_holder/simple_mob/ranged/kiting/threatening/event + wander = FALSE + +/datum/ai_holder/simple_mob/ranged/kiting/no_moonwalk + moonwalk = FALSE + +/datum/ai_holder/simple_mob/ranged/kiting/on_engagement(atom/A) + if(get_dist(holder, A) < run_if_this_close) + holder.IMove(get_step_away(holder, A, run_if_this_close)) + if(moonwalk) + holder.face_atom(A) + +// Closes distance from the target even while in range. +/datum/ai_holder/simple_mob/ranged/aggressive + pointblank = TRUE + var/closest_distance = 1 // How close to get to the target. By default they will get into melee range (and then pointblank them). + +/datum/ai_holder/simple_mob/ranged/aggressive/on_engagement(atom/A) + if(get_dist(holder, A) > closest_distance) + holder.IMove(get_step_towards(holder, A)) + holder.face_atom(A) + +// Yakkity saxes while firing at you. +/datum/ai_holder/hostile/ranged/robust/on_engagement(atom/movable/AM) + step_rand(holder) + holder.face_atom(AM) + +// Switches intents based on specific criteria. +// Used for special mobs who do different things based on intents (and aren't slimes). +// Intent switching is generally done in pre_[ranged/special]_attack(), so that the mob can use the right attack for the right time. +/datum/ai_holder/simple_mob/intentional + + +// These try to avoid collateral damage. +/datum/ai_holder/simple_mob/restrained + violent_breakthrough = FALSE + conserve_ammo = TRUE + +// Melee mobs. + +/datum/ai_holder/simple_mob/melee + +// Dances around the enemy its fighting, making it harder to fight back. +/datum/ai_holder/simple_mob/melee/evasive + +/datum/ai_holder/simple_mob/melee/evasive/post_melee_attack(atom/A) + if(holder.Adjacent(A)) + holder.IMove(get_step(holder, pick(alldirs))) + holder.face_atom(A) + + + +// This AI hits something, then runs away for awhile. +// It will (almost) always flee if they are uncloaked, AND their target is not stunned. +/datum/ai_holder/simple_mob/melee/hit_and_run + can_flee = TRUE + +// Used for the 'running' part of hit and run. +/datum/ai_holder/simple_mob/melee/hit_and_run/special_flee_check() + if(!holder.is_cloaked()) + if(isliving(target)) + var/mob/living/L = target + return !L.incapacitated(INCAPACITATION_DISABLED) // Don't flee if our target is stunned in some form, even if uncloaked. This is so the mob keeps attacking a stunned opponent. + return TRUE // We're out in the open, uncloaked, and our target isn't stunned, so lets flee. + return FALSE + + +// Simple mobs that aren't hostile, but will fight back. +/datum/ai_holder/simple_mob/retaliate + hostile = FALSE + retaliate = TRUE + +// Simple mobs that retaliate and support others in their faction who get attacked. +/datum/ai_holder/simple_mob/retaliate/cooperative + cooperative = TRUE \ No newline at end of file diff --git a/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm b/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm new file mode 100644 index 0000000000..c859a523cc --- /dev/null +++ b/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm @@ -0,0 +1,262 @@ +// Specialized AI for slime simplemobs. +// Unlike the parent AI code, this will probably break a lot of things if you put it on something that isn't /mob/living/simple_mob/slime/xenobio + +/datum/ai_holder/simple_mob/xenobio_slime + hostile = TRUE + cooperative = TRUE + firing_lanes = TRUE + var/rabid = FALSE // Will attack regardless of discipline. + var/discipline = 0 // Beating slimes makes them less likely to lash out. In theory. + var/resentment = 0 // 'Unjustified' beatings make this go up, and makes it more likely for abused slimes to go rabid. + var/obedience = 0 // Conversely, 'justified' beatings make this go up, and makes discipline decay slower, potentially making it not decay at all. + + var/always_stun = FALSE // If true, the slime will elect to attempt to permastun the target. + +/datum/ai_holder/simple_mob/xenobio_slime/sapphire + always_stun = TRUE // They know that stuns are godly. + intelligence_level = AI_SMART // Also knows not to walk while confused if it risks death. + +/datum/ai_holder/simple_mob/xenobio_slime/light_pink + discipline = 5 + obedience = 5 + +/datum/ai_holder/simple_mob/xenobio_slime/passive/New() // For Kendrick. + ..() + pacify() + +/datum/ai_holder/simple_mob/xenobio_slime/New() + ..() + ASSERT(istype(holder, /mob/living/simple_mob/slime/xenobio)) + +// Checks if disciplining the slime would be 'justified' right now. +/datum/ai_holder/simple_mob/xenobio_slime/proc/is_justified_to_discipline() + if(rabid) + return TRUE + if(target) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(istype(H.species, /datum/species/monkey)) + return FALSE // Attacking monkeys is okay. + return TRUE // Otherwise attacking other things is bad. + return FALSE // Not attacking anything. + +/datum/ai_holder/simple_mob/xenobio_slime/proc/can_command(mob/living/commander) + if(rabid) + return FALSE + if(!hostile) + return SLIME_COMMAND_OBEY +// if(commander in friends) +// return SLIME_COMMAND_FRIEND + if(holder.IIsAlly(commander)) + return SLIME_COMMAND_FACTION + if(discipline > resentment && obedience >= 5) + return SLIME_COMMAND_OBEY + return FALSE + +/datum/ai_holder/simple_mob/xenobio_slime/proc/adjust_discipline(amount, silent) + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + if(amount > 0) + if(rabid) + return + var/justified = is_justified_to_discipline() + lost_target() // Stop attacking. + + if(justified) + obedience++ + if(!silent) + holder.say(pick("Fine...", "Okay...", "Sorry...", "I yield...", "Mercy...")) + else + if(prob(resentment * 20)) + enrage() + holder.say(pick("Evil...", "Kill...", "Tyrant...")) + else + if(!silent) + holder.say(pick("Why...?", "I don't understand...?", "Cruel...", "Stop...", "Nooo...")) + resentment++ // Done after check so first time will never enrage. + + discipline = between(0, discipline + amount, 10) + my_slime.update_mood() + +// This slime always enrages if disciplined. +/datum/ai_holder/simple_mob/xenobio_slime/red/adjust_discipline(amount, silent) + if(amount > 0 && !rabid) + holder.say("Grrr...") + holder.add_modifier(/datum/modifier/berserk, 30 SECONDS) + enrage() + +/datum/ai_holder/simple_mob/xenobio_slime/handle_special_strategical() + discipline_decay() + +// Handles decay of discipline. +/datum/ai_holder/simple_mob/xenobio_slime/proc/discipline_decay() + if(discipline > 0) + if(!prob(75 + (obedience * 5))) + adjust_discipline(-1) + +/datum/ai_holder/simple_mob/xenobio_slime/handle_special_tactic() + evolve_and_reproduce() + +// Hit the correct verbs to keep the slime species going. +/datum/ai_holder/simple_mob/xenobio_slime/proc/evolve_and_reproduce() + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + if(my_slime.amount_grown >= 10) + // Press the correct verb when we can. + if(my_slime.is_adult) + my_slime.reproduce() // Splits into four new baby slimes. + else + my_slime.evolve() // Turns our holder into an adult slime. + + +// Called when pushed too far (or a red slime core was used). +/datum/ai_holder/simple_mob/xenobio_slime/proc/enrage() + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + if(my_slime.harmless) + return + rabid = TRUE + my_slime.update_mood() + my_slime.visible_message(span("danger", "\The [src] enrages!")) + +// Called when using a pacification agent (or it's Kendrick being initalized). +/datum/ai_holder/simple_mob/xenobio_slime/proc/pacify() + lost_target() // So it stops trying to kill them. + rabid = FALSE + hostile = FALSE + retaliate = FALSE + cooperative = FALSE + +// The holder's attack changes based on intent. This lets the AI choose what effect is desired. +/datum/ai_holder/simple_mob/xenobio_slime/pre_melee_attack(atom/A) + if(istype(A, /mob/living)) + var/mob/living/L = A + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + + if( (!L.lying && prob(30 + (my_slime.power_charge * 7) ) || (!L.lying && always_stun) )) + my_slime.a_intent = I_DISARM // Stun them first. + else if(my_slime.can_consume(L) && L.lying) + my_slime.a_intent = I_GRAB // Then eat them. + else + my_slime.a_intent = I_HURT // Otherwise robust them. + +/datum/ai_holder/simple_mob/xenobio_slime/closest_distance(atom/movable/AM) + if(istype(AM, /mob/living)) + var/mob/living/L = AM + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(istype(H.species, /datum/species/monkey)) + return 1 // Otherwise ranged slimes will eat a lot less often. + if(L.stat >= UNCONSCIOUS) + return 1 // Melee (eat) the target if dead/dying, don't shoot it. + return ..() + +/datum/ai_holder/simple_mob/xenobio_slime/can_attack(atom/movable/AM) + . = ..() + if(.) // Do some additional checks because we have Special Code(tm). + if(ishuman(AM)) + var/mob/living/carbon/human/H = AM + if(istype(H.species, /datum/species/monkey)) // istype() is so they'll eat the alien monkeys too. + return TRUE // Monkeys are always food (sorry Pun Pun). + else if(H.species && H.species.name == SPECIES_PROMETHEAN) + return FALSE // Prometheans are always our friends. + if(discipline && !rabid) + return FALSE // We're a good slime. + +// Commands, reactions, etc +/datum/ai_holder/simple_mob/xenobio_slime/on_hear_say(mob/living/speaker, message) + ai_log("xenobio_slime/on_hear_say([speaker], [message]) : Entered.", AI_LOG_DEBUG) + var/mob/living/simple_mob/slime/xenobio/my_slime = holder + + if((findtext(message, num2text(my_slime.number)) || findtext(message, my_slime.name) || findtext(message, "slimes"))) // Talking to us. + + // First, make sure it's actually a player saying something and not an AI, or else we risk infinite loops. + if(!speaker.client) + return + + // Are all slimes being referred to? + // var/mass_order = FALSE + // if(findtext(message, "slimes")) + // mass_order = TRUE + + // Say hello back. + if(findtext(message, "hello") || findtext(message, "hi") || findtext(message, "greetings")) + delayed_say(pick("Hello...", "Hi..."), speaker) + + // Follow request. + if(findtext(message, "follow") || findtext(message, "come with me")) + if(!can_command(speaker)) + delayed_say(pick("No...", "I won't follow..."), speaker) + return + + delayed_say("Yes... I follow \the [speaker]...", speaker) + set_follow(speaker) + + // Squish request. + if(findtext(message , "squish")) + if(!can_command(speaker)) + delayed_say("No...", speaker) + return + + spawn(rand(1 SECOND, 2 SECONDS)) + if(!src || !holder || !can_act()) // We might've died/got deleted/etc in the meantime. + return + my_slime.squish() + + + // Stop request. + if(findtext(message, "stop") || findtext(message, "halt") || findtext(message, "cease")) + if(my_slime.victim) // We're being asked to stop eatting someone. + if(!can_command(speaker) || !is_justified_to_discipline()) + delayed_say("No...", speaker) + return + else + delayed_say("Fine...", speaker) + adjust_discipline(1, TRUE) + my_slime.stop_consumption() + + if(target) // We're being asked to stop chasing someone. + if(!can_command(speaker) || !is_justified_to_discipline()) + delayed_say("No...", speaker) + return + else + delayed_say("Fine...", speaker) + adjust_discipline(1, TRUE) // This must come before losing the target or it will be unjustified. + lost_target() + + + if(leader) // We're being asked to stop following someone. + if(can_command(speaker) == SLIME_COMMAND_FRIEND || leader == speaker) + delayed_say("Yes... I'll stop...", speaker) + lose_follow() + else + delayed_say("No... I'll keep following \the [leader]...", speaker) + + /* // Commented out since its mostly useless now due to slimes refusing to attack if it would make them naughty. + // Murder request + if(findtext(message, "harm") || findtext(message, "attack") || findtext(message, "kill") || findtext(message, "murder") || findtext(message, "eat") || findtext(message, "consume") || findtext(message, "absorb")) + if(can_command(speaker) < SLIME_COMMAND_FACTION) + delayed_say("No...", speaker) + return + + for(var/mob/living/L in view(7, my_slime) - list(my_slime, speaker)) + if(L == src) + continue // Don't target ourselves. + var/list/valid_names = splittext(L.name, " ") // Should output list("John", "Doe") as an example. + for(var/line in valid_names) // Check each part of someone's name. + if(findtext(message, lowertext(line))) // If part of someone's name is in the command, the slime targets them if allowed to. + if(!(mass_order && line == "slime")) //don't think random other slimes are target + if(can_attack(L)) + delayed_say("Okay... I attack \the [L]...", speaker) + give_target(L) + return + else + delayed_say("No... I won't attack \the [L].", speaker) + return + + // If we're here, it couldn't find anyone with that name. + delayed_say("No... I don't know who to attack...", speaker) + */ + ai_log("xenobio_slime/on_hear_say() : Exited.", AI_LOG_DEBUG) + +/datum/ai_holder/simple_mob/xenobio_slime/can_violently_breakthrough() + if(discipline && !rabid) // Good slimes don't shatter the windows because their buddy in an adjacent cell decided to piss off Slimesky. + return FALSE + return ..() \ No newline at end of file diff --git a/code/modules/ai/ai_holder.dm b/code/modules/ai/ai_holder.dm new file mode 100644 index 0000000000..2c7e02ecef --- /dev/null +++ b/code/modules/ai/ai_holder.dm @@ -0,0 +1,290 @@ +// This is a datum-based artificial intelligence for simple mobs (and possibly others) to use. +// The neat thing with having this here instead of on the mob is that it is independant of Life(), and that different mobs +// can use a more or less complex AI by giving it a different datum. + +/mob/living + var/datum/ai_holder/ai_holder = null + var/ai_holder_type = null // Which ai_holder datum to give to the mob when initialized. If null, nothing happens. + +/mob/living/initialize() + if(ai_holder_type) + ai_holder = new ai_holder_type(src) + return ..() + +/mob/living/Destroy() + QDEL_NULL(ai_holder) + return ..() + +/datum/ai_holder + var/mob/living/holder = null // The mob this datum is going to control. + var/stance = STANCE_IDLE // Determines if the mob should be doing a specific thing, e.g. attacking, following, standing around, etc. + var/intelligence_level = AI_NORMAL // Adjust to make the AI be intentionally dumber, or make it more robust (e.g. dodging grenades). + var/autopilot = FALSE // If true, the AI won't be deactivated if a client gets attached to the AI's mob. + var/busy = FALSE // If true, the ticker will skip processing this mob until this is false. Good for if you need the + // mob to stay still (e.g. delayed attacking). If you need the mob to be inactive for an extended period of time, + // consider sleeping the AI instead. + + + +/datum/ai_holder/hostile + hostile = TRUE + +/datum/ai_holder/retaliate + hostile = TRUE + retaliate = TRUE + +/datum/ai_holder/New(var/new_holder) + ASSERT(new_holder) + holder = new_holder + SSai.processing += src + home_turf = get_turf(holder) + ..() + +/datum/ai_holder/Destroy() + holder = null + SSai.processing -= src // We might've already been asleep and removed, but byond won't care if we do this again and it saves a conditional. + home_turf = null + return ..() + + +// Now for the actual AI stuff. + +// Makes this ai holder not get processed. +// Called automatically when the host mob is killed. +// Potential future optimization would be to sleep AIs which mobs that are far away from in-round players. +/datum/ai_holder/proc/go_sleep() + if(stance == STANCE_SLEEP) + return + forget_everything() // If we ever wake up, its really unlikely that our current memory will be of use. + set_stance(STANCE_SLEEP) + SSai.processing -= src + +// Reverses the above proc. +// Revived mobs will wake their AI if they have one. +/datum/ai_holder/proc/go_wake() + if(stance != STANCE_SLEEP) + return + if(!should_wake()) + return + set_stance(STANCE_IDLE) + SSai.processing += src + +/datum/ai_holder/proc/should_wake() + if(holder.client && !autopilot) + return FALSE + if(holder.stat >= DEAD) + return FALSE + return TRUE + +// Resets a lot of 'memory' vars. +/datum/ai_holder/proc/forget_everything() + // Some of these might be redundant, but hopefully this prevents future bugs if that changes. + lose_follow() + lose_target() + lose_target_position() + give_up_movement() + +// 'Tactical' processes such as moving a step, meleeing an enemy, firing a projectile, and other fairly cheap actions that need to happen quickly. +/datum/ai_holder/proc/handle_tactics() + if(busy) + return + handle_special_tactic() + handle_stance_tactical() + +// 'Strategical' processes that are more expensive on the CPU and so don't get run as often as the above proc, such as A* pathfinding or robust targeting. +/datum/ai_holder/proc/handle_strategicals() + if(busy) + return + handle_special_strategical() + handle_stance_strategical() + +// Override these for special things without polluting the main loop. +/datum/ai_holder/proc/handle_special_tactic() + +/datum/ai_holder/proc/handle_special_strategical() + +/* + //AI Actions + if(!ai_inactive) + //Stanceyness + handle_stance() + + //Movement + if(!stop_automated_movement && wander && !anchored) //Allowed to move? + handle_wander_movement() + + //Speaking + if(speak_chance && stance == STANCE_IDLE) // Allowed to chatter? + handle_idle_speaking() + + //Resisting out buckles + if(stance != STANCE_IDLE && incapacitated(INCAPACITATION_BUCKLED_PARTIALLY)) + handle_resist() + + //Resisting out of closets + if(istype(loc,/obj/structure/closet)) + var/obj/structure/closet/C = loc + if(C.welded) + resist() + else + C.open() +*/ + +// For setting the stance WITHOUT processing it +/datum/ai_holder/proc/set_stance(var/new_stance) + ai_log("set_stance() : Setting stance from [stance] to [new_stance].", AI_LOG_INFO) + stance = new_stance + if(stance_coloring) // For debugging or really weird mobs. + stance_color() + +// This is called every half a second. +/datum/ai_holder/proc/handle_stance_tactical() + ai_log("========= Fast Process Beginning ==========", AI_LOG_TRACE) // This is to make it easier visually to disinguish between 'blocks' of what a tick did. + ai_log("handle_stance_tactical() : Called.", AI_LOG_TRACE) + + if(stance == STANCE_SLEEP) + ai_log("handle_stance_tactical() : Going to sleep.", AI_LOG_TRACE) + go_sleep() + return + + if(target && can_see_target(target)) + track_target_position() + + if(stance != STANCE_DISABLED && is_disabled()) // Stunned/confused/etc + ai_log("handle_stance_tactical() : Disabled.", AI_LOG_TRACE) + set_stance(STANCE_DISABLED) + return + + if(stance in STANCES_COMBAT) + // Should resist? We check this before fleeing so that we can actually flee and not be trapped in a chair. + if(holder.incapacitated(INCAPACITATION_BUCKLED_PARTIALLY)) + ai_log("handle_stance_tactical() : Going to handle_resist().", AI_LOG_TRACE) + handle_resist() + + else if(istype(holder.loc, /obj/structure/closet)) + var/obj/structure/closet/C = holder.loc + ai_log("handle_stance_tactical() : Inside a closet. Going to attempt escape.", AI_LOG_TRACE) + if(C.sealed) + holder.resist() + else + C.open() + + // Should we flee? + if(should_flee()) + ai_log("handle_stance_tactical() : Going to flee.", AI_LOG_TRACE) + set_stance(STANCE_FLEE) + return + + switch(stance) + if(STANCE_IDLE) + if(should_go_home()) + ai_log("handle_stance_tactical() : STANCE_IDLE, going to go home.", AI_LOG_TRACE) + go_home() + + else if(should_follow_leader()) + ai_log("handle_stance_tactical() : STANCE_IDLE, going to follow leader.", AI_LOG_TRACE) + set_stance(STANCE_FOLLOW) + + else if(should_wander()) + ai_log("handle_stance_tactical() : STANCE_IDLE, going to wander randomly.", AI_LOG_TRACE) + handle_wander_movement() + + if(STANCE_ALERT) + ai_log("handle_stance_tactical() : STANCE_ALERT, going to threaten_target().", AI_LOG_TRACE) + threaten_target() + + if(STANCE_APPROACH) + ai_log("handle_stance_tactical() : STANCE_APPROACH, going to walk_to_target().", AI_LOG_TRACE) + walk_to_target() + + if(STANCE_FIGHT) + ai_log("handle_stance_tactical() : STANCE_FIGHT, going to engage_target().", AI_LOG_TRACE) + engage_target() + + if(STANCE_MOVE) + ai_log("handle_stance_tactical() : STANCE_MOVE, going to walk_to_destination().", AI_LOG_TRACE) + walk_to_destination() + + if(STANCE_REPOSITION) // This is the same as above but doesn't stop if an enemy is visible since its an 'in-combat' move order. + ai_log("handle_stance_tactical() : STANCE_REPOSITION, going to walk_to_destination().", AI_LOG_TRACE) + walk_to_destination() + + if(STANCE_FOLLOW) + ai_log("handle_stance_tactical() : STANCE_FOLLOW, going to walk_to_leader().", AI_LOG_TRACE) + walk_to_leader() + + if(STANCE_FLEE) + ai_log("handle_stance_tactical() : STANCE_FLEE, going to flee_from_target().", AI_LOG_TRACE) + flee_from_target() + + if(STANCE_DISABLED) + ai_log("handle_stance_tactical() : STANCE_DISABLED.", AI_LOG_TRACE) + if(!is_disabled()) + ai_log("handle_stance_tactical() : No longer disabled.", AI_LOG_TRACE) + set_stance(STANCE_IDLE) + else + handle_disabled() + + ai_log("handle_stance_tactical() : Exiting.", AI_LOG_TRACE) + ai_log("========= Fast Process Ending ==========", AI_LOG_TRACE) + +// This is called every two seconds. +/datum/ai_holder/proc/handle_stance_strategical() + ai_log("++++++++++ Slow Process Beginning ++++++++++", AI_LOG_TRACE) + ai_log("handle_stance_strategical() : Called.", AI_LOG_TRACE) + + switch(stance) + if(STANCE_IDLE) + + if(speak_chance) // In the long loop since otherwise it wont shut up. + handle_idle_speaking() + + if(hostile) + ai_log("handle_stance_strategical() : STANCE_IDLE, going to find_target().", AI_LOG_TRACE) + find_target() + if(STANCE_APPROACH) + if(target) + ai_log("handle_stance_strategical() : STANCE_APPROACH, going to calculate_path([target]).", AI_LOG_TRACE) + calculate_path(target) + if(STANCE_MOVE) + if(hostile && find_target()) // This will switch its stance. + ai_log("handle_stance_strategical() : STANCE_MOVE, found target and was inturrupted.", AI_LOG_TRACE) + if(STANCE_FOLLOW) + if(hostile && find_target()) // This will switch its stance. + ai_log("handle_stance_strategical() : STANCE_FOLLOW, found target and was inturrupted.", AI_LOG_TRACE) + else if(leader) + ai_log("handle_stance_strategical() : STANCE_FOLLOW, going to calculate_path([leader]).", AI_LOG_TRACE) + calculate_path(leader) + + ai_log("handle_stance_strategical() : Exiting.", AI_LOG_TRACE) + ai_log("++++++++++ Slow Process Ending ++++++++++", AI_LOG_TRACE) + + +// Helper proc to turn AI 'busy' mode on or off without having to check if there is an AI, to simplify writing code. +/mob/living/proc/set_AI_busy(value) + if(ai_holder) + ai_holder.busy = value + +/mob/living/proc/is_AI_busy() + if(!ai_holder) + return FALSE + return ai_holder.busy + +// Helper proc to check for the AI's stance. +// Returns null if there's no AI holder, or the mob has a player and autopilot is not on. +// Otherwise returns the stance. +/mob/living/proc/get_AI_stance() + if(!ai_holder) + return null + if(client && !ai_holder.autopilot) + return null + return ai_holder.stance + +// Similar to above but only returns 1 or 0. +/mob/living/proc/has_AI() + return get_AI_stance() ? TRUE : FALSE + +// 'Taunts' the AI into attacking the taunter. +/mob/living/proc/taunt(atom/movable/taunter, force_target_switch = FALSE) + if(ai_holder) + ai_holder.receive_taunt(taunter, force_target_switch) \ No newline at end of file diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm new file mode 100644 index 0000000000..4a8fbdff69 --- /dev/null +++ b/code/modules/ai/ai_holder_combat.dm @@ -0,0 +1,308 @@ +// This file is for actual fighting. Targeting is in a seperate file. + +/datum/ai_holder + var/firing_lanes = FALSE // If ture, tries to refrain from shooting allies or the wall. + var/conserve_ammo = FALSE // If true, the mob will avoid shooting anything that does not have a chance to hit a mob. Requires firing_lanes to be true. + var/pointblank = FALSE // If ranged is true, and this is true, people adjacent to the mob will suffer the ranged instead of using a melee attack. + + var/can_breakthrough = TRUE // If false, the AI will not try to open a path to its goal, like opening doors. + var/violent_breakthrough = TRUE // If false, the AI is not allowed to destroy things like windows or other structures in the way. Requires above var to be true. + + var/stand_ground = FALSE // If true, the AI won't try to get closer to an enemy if out of range. + + +// This does the actual attacking. +/datum/ai_holder/proc/engage_target() + ai_log("engage_target() : Entering.", AI_LOG_DEBUG) + + // Can we still see them? +// if(!target || !can_attack(target) || (!(target in list_targets())) ) + if(!target || !can_attack(target)) + ai_log("engage_target() : Lost sight of target.", AI_LOG_TRACE) + lose_target() // We lost them. + + if(!find_target()) // If we can't get a new one, then wait for a bit and then time out. + set_stance(STANCE_IDLE) + lost_target() + ai_log("engage_target() : No more targets. Exiting.", AI_LOG_DEBUG) + return + // if(lose_target_time + lose_target_timeout < world.time) + // ai_log("engage_target() : Unseen enemy timed out.", AI_LOG_TRACE) + // set_stance(STANCE_IDLE) // It must've been the wind. + // lost_target() + // ai_log("engage_target() : Exiting.", AI_LOG_DEBUG) + // return + + // // But maybe we do one last ditch effort. + // if(!target_last_seen_turf || intelligence_level < AI_SMART) + // ai_log("engage_target() : No last known position or is too dumb to fight unseen enemies.", AI_LOG_TRACE) + // set_stance(STANCE_IDLE) + // else + // ai_log("engage_target() : Fighting unseen enemy.", AI_LOG_TRACE) + // engage_unseen_enemy() + else + ai_log("engage_target() : Got new target ([target]).", AI_LOG_TRACE) + + var/distance = get_dist(holder, target) + ai_log("engage_target() : Distance to target ([target]) is [distance].", AI_LOG_TRACE) + holder.face_atom(target) + last_conflict_time = world.time + + request_help() // Call our allies. + + // Do a 'special' attack, if one is allowed. +// if(prob(special_attack_prob) && (distance >= special_attack_min_range) && (distance <= special_attack_max_range)) + if(holder.ICheckSpecialAttack(target)) + ai_log("engage_target() : Attempting a special attack.", AI_LOG_TRACE) + on_engagement(target) + if(special_attack(target)) // If this fails, then we try a regular melee/ranged attack. + ai_log("engage_target() : Successful special attack. Exiting.", AI_LOG_DEBUG) + return + + // Stab them. + else if(distance <= 1 && !pointblank) + ai_log("engage_target() : Attempting a melee attack.", AI_LOG_TRACE) + on_engagement(target) + melee_attack(target) + + // Shoot them. + else if(holder.ICheckRangedAttack(target) && (distance <= max_range(target)) ) + on_engagement(target) + if(firing_lanes && !test_projectile_safety(target)) + // Nudge them a bit, maybe they can shoot next time. + step_rand(holder) + holder.face_atom(target) + ai_log("engage_target() : Could not safely fire at target. Exiting.", AI_LOG_DEBUG) + return + + ai_log("engage_target() : Attempting a ranged attack.", AI_LOG_TRACE) + ranged_attack(target) + + // Run after them. + else if(!stand_ground) + ai_log("engage_target() : Target ([target]) too far away. Exiting.", AI_LOG_DEBUG) + set_stance(STANCE_APPROACH) + +// We're not entirely sure how holder will do melee attacks since any /mob/living could be holder, but we don't have to care because Interfaces. +/datum/ai_holder/proc/melee_attack(atom/A) + pre_melee_attack(A) + . = holder.IAttack(A) + if(.) + post_melee_attack(A) + +// Ditto. +/datum/ai_holder/proc/ranged_attack(atom/A) + pre_ranged_attack(A) + . = holder.IRangedAttack(A) + if(.) + post_ranged_attack(A) + +// Most mobs probably won't have this defined but we don't care. +/datum/ai_holder/proc/special_attack(atom/movable/AM) + pre_special_attack(AM) + . = holder.ISpecialAttack(AM) + if(.) + post_special_attack(AM) + +// Called when within striking/shooting distance, however cooldown is not considered. +// Override to do things like move in a random step for evasiveness. +// Note that this is called BEFORE the attack. +/datum/ai_holder/proc/on_engagement(atom/A) + +// Called before a ranged attack is attempted. +/datum/ai_holder/proc/pre_ranged_attack(atom/A) + +// Called before a melee attack is attempted. +/datum/ai_holder/proc/pre_melee_attack(atom/A) + +// Called before a 'special' attack is attempted. +/datum/ai_holder/proc/pre_special_attack(atom/A) + +// Called after a successful (IE not on cooldown) ranged attack. +// Note that this is not whether the projectile actually hit, just that one was launched. +/datum/ai_holder/proc/post_ranged_attack(atom/A) + +// Ditto but for melee. +/datum/ai_holder/proc/post_melee_attack(atom/A) + +// And one more for special snowflake attacks. +/datum/ai_holder/proc/post_special_attack(atom/A) + +// Used to make sure projectiles will probably hit the target and not the wall or a friend. +/datum/ai_holder/proc/test_projectile_safety(atom/movable/AM) + var/mob/living/L = check_trajectory(AM, holder) // This isn't always reliable but its better than the previous method. +// world << "Checked trajectory, would hit [L]." + + if(istype(L)) // Did we hit a mob? +// world << "Hit [L]." + if(holder.IIsAlly(L)) +// world << "Would hit ally, canceling." + return FALSE // We would hit a friend! +// world << "Won't threaten ally, firing." + return TRUE // Otherwise we don't care, even if its not the intended target. + else + if(!isliving(AM)) // If the original target was an object, then let it happen if it doesn't threaten an ally. +// world << "Targeting object, ignoring and firing." + return TRUE +// world << "Not sure." + + return !conserve_ammo // If we have infinite ammo than shooting the wall isn't so bad, but otherwise lets not. + +// Test if we are within range to attempt an attack, melee or ranged. +/datum/ai_holder/proc/within_range(atom/movable/AM) + var/distance = get_dist(holder, AM) + if(distance <= 1) + return TRUE // Can melee. + else if(holder.ICheckRangedAttack(AM) && distance <= max_range(AM)) + return TRUE // Can shoot. + return FALSE + +// Determines how close the AI will move to its target. +/datum/ai_holder/proc/closest_distance(atom/movable/AM) + return max(max_range(AM) - 1, 1) // Max range -1 just because we don't want to constantly get kited + +// Can be used to conditionally do a ranged or melee attack. +/datum/ai_holder/proc/max_range(atom/movable/AM) + return holder.ICheckRangedAttack(AM) ? 7 : 1 + +// Goes to the target, to attack them. +// Called when in STANCE_APPROACH. +/datum/ai_holder/proc/walk_to_target() + ai_log("walk_to_target() : Entering.", AI_LOG_DEBUG) + // Make sure we can still chase/attack them. + if(!target || !can_attack(target)) + ai_log("walk_to_target() : Lost target.", AI_LOG_INFO) + if(!find_target()) + lost_target() + ai_log("walk_to_target() : Exiting.", AI_LOG_DEBUG) + return + else + ai_log("walk_to_target() : Found new target ([target]).", AI_LOG_INFO) + + // Find out where we're going. + var/get_to = closest_distance(target) + var/distance = get_dist(holder, target) + ai_log("walk_to_target() : get_to is [get_to].", AI_LOG_TRACE) + + // We're here! + // Special case: Our holder has a special attack that is ranged, but normally the holder uses melee. + // If that happens, we'll switch to STANCE_FIGHT so they can use it. If the special attack is limited, they'll likely switch back next tick. + if(distance <= get_to || holder.ICheckSpecialAttack(target)) + ai_log("walk_to_target() : Within range.", AI_LOG_INFO) + forget_path() + set_stance(STANCE_FIGHT) + ai_log("walk_to_target() : Exiting.", AI_LOG_DEBUG) + return + + + // Otherwise keep walking. + if(!stand_ground) + walk_path(target, get_to) + + ai_log("walk_to_target() : Exiting.", AI_LOG_DEBUG) + +// Resists out of things. +// Sometimes there are times you want your mob to be buckled to something, so override this for when that is needed. +/datum/ai_holder/proc/handle_resist() + holder.resist() + +// Used to break through windows and barriers to a target on the other side. +// This does two passes, so that if its just a public access door, the windows nearby don't need to be smashed. +/datum/ai_holder/proc/breakthrough(atom/target_atom) + ai_log("breakthrough() : Entering", AI_LOG_TRACE) + + if(!can_breakthrough) + ai_log("breakthrough() : Not allowed to breakthrough. Exiting.", AI_LOG_TRACE) + return FALSE + + if(!isturf(holder.loc)) + ai_log("breakthrough() : Trapped inside \the [holder.loc]. Exiting.", AI_LOG_TRACE) + return FALSE + + var/dir_to_target = get_dir(holder, target_atom) + holder.face_atom(target_atom) + ai_log("breakthrough() : Exiting", AI_LOG_DEBUG) + + // Sometimes the mob will try to hit something diagonally, and generally this fails. + // So instead we will try two more times with some adjustments if the attack fails. + var/list/directions_to_try = list( + dir_to_target, + turn(dir_to_target, 45), + turn(dir_to_target, -45) + ) + + ai_log("breakthrough() : Starting peaceful pass.", AI_LOG_DEBUG) + + var/result = FALSE + + // First, we will try to peacefully make a path, I.E opening a door we have access to. + for(var/direction in directions_to_try) + result = destroy_surroundings(direction, violent = FALSE) + if(result) + break + + // Alright, lets smash some shit instead, if it didn't work and we're allowed to be violent. + if(!result && can_violently_breakthrough()) + ai_log("breakthrough() : Starting violent pass.", AI_LOG_DEBUG) + for(var/direction in directions_to_try) + result = destroy_surroundings(direction, violent = TRUE) + if(result) + break + + ai_log("breakthrough() : Exiting with [result].", AI_LOG_TRACE) + return result + +// Despite the name, this can also be used to help clear a path without any destruction. +/datum/ai_holder/proc/destroy_surroundings(direction, violent = TRUE) + ai_log("destroy_surroundings() : Entering.", AI_LOG_TRACE) + if(!direction) + direction = pick(cardinal) // FLAIL WILDLY + ai_log("destroy_surroundings() : No direction given, picked [direction] randomly.", AI_LOG_DEBUG) + + var/turf/problem_turf = get_step(holder, direction) + + // First, give peace a chance. + if(!violent) + ai_log("destroy_surroundings() : Going to try to peacefully clear [problem_turf].", AI_LOG_DEBUG) + for(var/obj/machinery/door/D in problem_turf) + if(D.density && holder.Adjacent(D) && D.allowed(holder) && D.operable()) + // First, try to open the door if possible without smashing it. We might have access. + ai_log("destroy_surroundings() : Opening closed door.", AI_LOG_INFO) + return D.open() + + // Peace has failed us, can we just smash the things in the way? + else + ai_log("destroy_surroundings() : Going to try to violently clear [problem_turf].", AI_LOG_DEBUG) + // First, kill windows in the way. + for(var/obj/structure/window/W in problem_turf) + if(W.dir == reverse_dir[holder.dir]) // So that windows get smashed in the right order + ai_log("destroy_surroundings() : Attacking side window.", AI_LOG_INFO) + return holder.IAttack(W) + + else if(W.is_fulltile()) + ai_log("destroy_surroundings() : Attacking full tile window.", AI_LOG_INFO) + return holder.IAttack(W) + + // Kill hull shields in the way. + for(var/obj/effect/energy_field/shield in problem_turf) + if(shield.density) // Don't attack shields that are already down. + ai_log("destroy_surroundings() : Attacking hull shield.", AI_LOG_INFO) + return holder.IAttack(shield) + + // Kill common obstacle in the way like tables. + var/obj/structure/obstacle = locate(/obj/structure, problem_turf) + if(istype(obstacle, /obj/structure/window) || istype(obstacle, /obj/structure/closet) || istype(obstacle, /obj/structure/table) || istype(obstacle, /obj/structure/grille)) + ai_log("destroy_surroundings() : Attacking generic structure.", AI_LOG_INFO) + return holder.IAttack(obstacle) + + for(var/obj/machinery/door/D in problem_turf) // Required since firelocks take up the same turf. + if(D.density) + ai_log("destroy_surroundings() : Attacking closed door.", AI_LOG_INFO) + return holder.IAttack(D) + + ai_log("destroy_surroundings() : Exiting due to nothing to attack.", AI_LOG_INFO) + return FALSE // Nothing to attack. + +// Override for special behaviour. +/datum/ai_holder/proc/can_violently_breakthrough() + return violent_breakthrough \ No newline at end of file diff --git a/code/modules/ai/ai_holder_combat_unseen.dm b/code/modules/ai/ai_holder_combat_unseen.dm new file mode 100644 index 0000000000..0cb518f08c --- /dev/null +++ b/code/modules/ai/ai_holder_combat_unseen.dm @@ -0,0 +1,43 @@ +// Used for fighting invisible things. + +// Used when a target is out of sight or invisible. +/datum/ai_holder/proc/engage_unseen_enemy() + // Lets do some last things before giving up. + if(!ranged) + if(get_dist(holder, target_last_seen_turf > 1)) // We last saw them over there. + // Go to where you last saw the enemy. + give_destination(target_last_seen_turf, 1, TRUE) // This will set it to STANCE_REPOSITION. + else // We last saw them next to us, so do a blind attack on that tile. + melee_on_tile(target_last_seen_turf) + + else if(!conserve_ammo) + shoot_near_turf(target_last_seen_turf) + +// This shoots semi-randomly near a specific turf. +/datum/ai_holder/proc/shoot_near_turf(turf/targeted_turf) + if(!ranged) + return // Can't shoot. + if(get_dist(holder, targeted_turf) > max_range(targeted_turf)) + return // Too far to shoot. + + var/turf/T = pick(RANGE_TURFS(2, targeted_turf)) // The turf we're actually gonna shoot at. + on_engagement(T) + if(firing_lanes && !test_projectile_safety(T)) + step_rand(holder) + holder.face_atom(T) + return + + ranged_attack(T) + +// Attempts to attack something on a specific tile. +// TODO: Put on mob/living? +/datum/ai_holder/proc/melee_on_tile(turf/T) + var/mob/living/L = locate() in T + if(!L) + T.visible_message("\The [holder] attacks nothing around \the [T].") + return + + if(holder.IIsAlly(L)) // Don't hurt our ally. + return + + melee_attack(L) \ No newline at end of file diff --git a/code/modules/ai/ai_holder_communication.dm b/code/modules/ai/ai_holder_communication.dm new file mode 100644 index 0000000000..93267d19f4 --- /dev/null +++ b/code/modules/ai/ai_holder_communication.dm @@ -0,0 +1,134 @@ +// Contains code for speaking and emoting. + +/datum/ai_holder + var/threaten = FALSE // If hostile and sees a valid target, gives a 'warning' to the target before beginning the attack. + var/threatening = FALSE // If the mob actually gave the warning, checked so it doesn't constantly yell every tick. + var/threaten_delay = 3 SECONDS // How long a 'threat' lasts, until actual fighting starts. If null, the mob never starts the fight but still does the threat. + var/threaten_timeout = 1 MINUTE // If the mob threatens someone, they leave, and then come back before this timeout period, the mob escalates to fighting immediately. + var/last_conflict_time = null // Last occurance of fighting being used, in world.time. + var/last_threaten_time = null // Ditto but only for threats. + + var/speak_chance = 0 // Probability that the mob talks (this is 'X in 200' chance since even 1/100 is pretty noisy) + + +/datum/ai_holder/proc/should_threaten() + if(!threaten) + return FALSE // We don't negotiate. + if(target in attackers) + return FALSE // They (or someone like them) attacked us before, escalate immediately. + if(!will_threaten(target)) + return FALSE // Pointless to threaten an animal, a mindless drone, or an object. + if(stance in STANCES_COMBAT) + return FALSE // We're probably already fighting or recently fought if not in these stances. + if(last_threaten_time && threaten_delay && last_conflict_time + threaten_timeout > world.time) + return FALSE // We threatened someone recently, so lets show them we mean business. + return TRUE // Lets give them a chance to choose wisely and walk away. + +/datum/ai_holder/proc/threaten_target() + holder.face_atom(target) // Constantly face the target. + + if(!threatening) // First tick. + threatening = TRUE + last_threaten_time = world.time + + if(holder.say_list) + holder.ISay(safepick(holder.say_list.say_threaten)) + playsound(holder.loc, holder.say_list.threaten_sound, 50, 1) // We do this twice to make the sound -very- noticable to the target. + playsound(target.loc, holder.say_list.threaten_sound, 50, 1) // Actual aim-mode also does that so at least it's consistant. + else // Otherwise we are waiting for them to go away or to wait long enough for escalate. + if(target in list_targets()) // Are they still visible? + var/should_escalate = FALSE + + if(threaten_delay && last_threaten_time + threaten_delay < world.time) // Waited too long. + should_escalate = TRUE + else if(last_conflict_time + threaten_timeout > world.time) // We got attacked while threatening them. + should_escalate = TRUE + + if(should_escalate) + threatening = FALSE + set_stance(STANCE_APPROACH) + if(holder.say_list) + holder.ISay(safepick(holder.say_list.say_escalate)) + else + return // Wait a bit. + + else // They left, or so we think. + threatening = FALSE + set_stance(STANCE_IDLE) + if(holder.say_list) + holder.ISay(safepick(holder.say_list.say_stand_down)) + playsound(holder.loc, holder.say_list.stand_down_sound, 50, 1) // We do this twice to make the sound -very- noticable to the target. + playsound(target.loc, holder.say_list.stand_down_sound, 50, 1) // Actual aim-mode also does that so at least it's consistant. + +// Determines what is deserving of a warning when STANCE_ALERT is active. +/datum/ai_holder/proc/will_threaten(mob/living/the_target) + if(!isliving(the_target)) + return FALSE // Turrets don't give a fuck so neither will we. + /* + // Find a nice way of doing this later. + if(istype(the_target, /mob/living/simple_mob) && istype(holder, /mob/living/simple_mob)) + var/mob/living/simple_mob/us = holder + var/mob/living/simple_mob/them = target + + if(them.intelligence_level < us.intelligence_level) // Todo: Bitflag these. + return FALSE // Humanoids don't care about drones/animals/etc. Drones don't care about animals, and so on. + */ + return TRUE + +// Temp defines to make the below code a bit more readable. +#define COMM_SAY "say" +#define COMM_AUDIBLE_EMOTE "audible emote" +#define COMM_VISUAL_EMOTE "visual emote" + +/datum/ai_holder/proc/handle_idle_speaking() + if(rand(0,200) < speak_chance) + // Check if anyone is around to 'appreciate' what we say. + var/alone = TRUE + for(var/m in viewers(holder)) + var/mob/M = m + if(M.client) + alone = FALSE + break + if(alone) // Forever alone. No point doing anything else. + return + + var/list/comm_types = list() // What kinds of things can we do? + if(!holder.say_list) + return + + if(holder.say_list.speak.len) + comm_types += COMM_SAY + if(holder.say_list.emote_hear.len) + comm_types += COMM_AUDIBLE_EMOTE + if(holder.say_list.emote_see.len) + comm_types += COMM_VISUAL_EMOTE + + if(!comm_types.len) + return // All the relevant lists are empty, so do nothing. + + switch(pick(comm_types)) + if(COMM_SAY) + holder.ISay(safepick(holder.say_list.speak)) + if(COMM_AUDIBLE_EMOTE) + holder.audible_emote(safepick(holder.say_list.emote_hear)) + if(COMM_VISUAL_EMOTE) + holder.visible_emote(safepick(holder.say_list.emote_see)) + +#undef COMM_SAY +#undef COMM_AUDIBLE_EMOTE +#undef COMM_VISUAL_EMOTE + +// Handles the holder hearing a mob's say() +// Does nothing by default, override this proc for special behavior. +/datum/ai_holder/proc/on_hear_say(mob/living/speaker, message) + return + +// This is to make responses feel a bit more natural and not instant. +/datum/ai_holder/proc/delayed_say(var/message, var/mob/speak_to) + spawn(rand(1 SECOND, 2 SECONDS)) + if(!src || !holder || !can_act()) // We might've died/got deleted/etc in the meantime. + return + + if(speak_to) + holder.face_atom(speak_to) + holder.ISay(message) diff --git a/code/modules/ai/ai_holder_cooperation.dm b/code/modules/ai/ai_holder_cooperation.dm new file mode 100644 index 0000000000..0f6b0bcfa2 --- /dev/null +++ b/code/modules/ai/ai_holder_cooperation.dm @@ -0,0 +1,115 @@ +// Involves cooperating with other ai_holders. +/datum/ai_holder + var/cooperative = FALSE // If true, asks allies to help when fighting something. + var/call_distance = 14 // How far away calls for help will go for. + var/last_helpask_time = 0 // world.time when a mob asked for help. + var/list/faction_friends = list() // List of all mobs inside the faction with ai_holders that have cooperate on, to call for help without using range(). + // Note that this is only used for sending calls out. Receiving calls doesn't care about this list, only if the mob is in the faction. + // This means the AI could respond to a player's call for help, if a way to do so was implemented. + + // These vars don't do anything currently. They did before but an optimization made them nonfunctional. + // It was probably worth it. + var/call_players = FALSE // (Currently nonfunctional) If true, players get notified of an allied mob calling for help. + var/called_player_message = "needs help!" // (Currently nonfunctional) Part of a message used when above var is true. Full message is "\The [holder] [called_player_message]" + +/datum/ai_holder/New(new_holder) + ..() + if(cooperative) + build_faction_friends() + +/datum/ai_holder/Destroy() + if(faction_friends.len) //This list is shared amongst the faction + faction_friends -= src + return ..() + +// Handles everything about that list. +// Call on initialization or if something weird happened like the mob switched factions. +/datum/ai_holder/proc/build_faction_friends() + if(faction_friends.len) // Already have a list. + // Assume we're moving to a new faction. + faction_friends -= src // Get us out of the current list shared by everyone else. + faction_friends = list() // Then make our list empty and unshared in case we become a loner. + + // Find another AI-controlled mob in the same faction if possible. + var/mob/living/first_friend + for(var/mob/living/L in living_mob_list) + if(L.faction == holder.faction && L.ai_holder) + first_friend = L + break + + if(first_friend) // Joining an already established faction. + faction_friends = first_friend.ai_holder.faction_friends + faction_friends |= holder + else // We're the 'founder' (first and/or only member) of this faction. + faction_friends |= holder + +// Requests help in combat from other mobs possessing ai_holders. +/datum/ai_holder/proc/request_help() + ai_log("request_help() : Entering.", AI_LOG_DEBUG) + if(!cooperative || ((world.time - last_helpask_time) < 10 SECONDS)) + return + + ai_log("request_help() : Asking for help.", AI_LOG_INFO) + last_helpask_time = world.time + +// for(var/mob/living/L in range(call_distance, holder)) + for(var/mob/living/L in faction_friends) + if(L == holder) // Lets not call ourselves. + continue + if(holder.z != L.z) // On seperate z-level. + continue + if(get_dist(L, holder) > call_distance) // Too far to 'hear' the call for help. + continue + + if(holder.IIsAlly(L)) + // This will currently never run sadly, until faction_friends is made to accept players too. + // That might be for the best since I can imagine it getting spammy in a big fight. + if(L.client && call_players) // Dealing with a player. + ai_log("request_help() : Asking [L] (Player) for help.", AI_LOG_INFO) + to_chat(L, "\The [holder] [called_player_message]") + + else if(L.ai_holder) // Dealing with an AI. + ai_log("request_help() : Asking [L] (AI) for help.", AI_LOG_INFO) + L.ai_holder.help_requested(holder) + + ai_log("request_help() : Exiting.", AI_LOG_DEBUG) + +// What allies receive when someone else is calling for help. +/datum/ai_holder/proc/help_requested(mob/living/friend) + ai_log("help_requested() : Entering.", AI_LOG_DEBUG) + if(stance == STANCE_SLEEP) + ai_log("help_requested() : Help requested by [friend] but we are asleep.", AI_LOG_INFO) + return + if(!cooperative) + ai_log("help_requested() : Help requested by [friend] but we're not cooperative.", AI_LOG_INFO) + return + if(stance in STANCES_COMBAT) + ai_log("help_requested() : Help requested by [friend] but we are busy fighting something else.", AI_LOG_INFO) + return + if(!can_act()) + ai_log("help_requested() : Help requested by [friend] but cannot act (stunned or dead).", AI_LOG_INFO) + return + if(!holder.IIsAlly(friend)) // Extra sanity. + ai_log("help_requested() : Help requested by [friend] but we hate them.", AI_LOG_INFO) + return + if(friend.ai_holder && friend.ai_holder.target && !can_attack(friend.ai_holder.target)) + ai_log("help_requested() : Help requested by [friend] but we don't want to fight their target.", AI_LOG_INFO) + return + if(get_dist(holder, friend) <= follow_distance) + ai_log("help_requested() : Help requested by [friend] but we're already here.", AI_LOG_INFO) + return + if(get_dist(holder, friend) <= vision_range) // Within our sight. + ai_log("help_requested() : Help requested by [friend], and within target sharing range.", AI_LOG_INFO) + if(friend.ai_holder) // AI calling for help. + if(friend.ai_holder.target && can_attack(friend.ai_holder.target)) // Friend wants us to attack their target. + last_conflict_time = world.time // So we attack immediately and not threaten. + give_target(friend.ai_holder.target) // This will set us to the appropiate stance. + ai_log("help_requested() : Given target [target] by [friend]. Exiting", AI_LOG_DEBUG) + return + + // Otherwise they're outside our sight, lack a target, or aren't AI controlled, but within call range. + // So assuming we're AI controlled, we'll go to them and see whats wrong. + ai_log("help_requested() : Help requested by [friend], going to go to friend.", AI_LOG_INFO) + set_follow(friend, 10 SECONDS) + ai_log("help_requested() : Exiting.", AI_LOG_DEBUG) + diff --git a/code/modules/ai/ai_holder_debug.dm b/code/modules/ai/ai_holder_debug.dm new file mode 100644 index 0000000000..beee998af8 --- /dev/null +++ b/code/modules/ai/ai_holder_debug.dm @@ -0,0 +1,89 @@ +// Contains settings to make it easier to debug things. + +/datum/ai_holder + var/path_display = FALSE // Displays a visual path when A* is being used. + var/path_icon = 'icons/misc/debug_group.dmi' // What icon to use for the overlay + var/path_icon_state = "red" // What state to use for the overlay + var/image/path_overlay // A reference to the overlay + + var/last_turf_display = FALSE // Similar to above, but shows the target's last known turf visually. + var/last_turf_icon_state = "green" // A seperate icon_state from the previous. + var/image/last_turf_overlay // Another reference for an overlay. + + var/stance_coloring = FALSE // Colors the mob depending on its stance. + + var/debug_ai = AI_LOG_OFF // The level of debugging information to display to people who can see log_debug(). + +/datum/ai_holder/New() + ..() + path_overlay = new(path_icon,path_icon_state) + last_turf_overlay = new(path_icon, last_turf_icon_state) + +/datum/ai_holder/Destroy() + path_overlay = null + last_turf_overlay = null + return ..() + +//For debug purposes! +/datum/ai_holder/proc/ai_log_output(var/msg = "missing message", var/ver = AI_LOG_INFO) + var/span_type + switch(ver) + if(AI_LOG_OFF) + return + if(AI_LOG_ERROR) + span_type = "debug_error" + if(AI_LOG_WARNING) + span_type = "debug_warning" + if(AI_LOG_INFO) + span_type = "debug_info" + if(AI_LOG_DEBUG) + span_type = "debug_debug" // RAS syndrome at work. + if(AI_LOG_TRACE) + span_type = "debug_trace" + if(ver <= debug_ai) + log_debug("AI: ([holder]:\ref[holder] | [holder.x],[holder.y],[holder.z])(@[world.time]): [msg] ") + +// Colors the mob based on stance, to visually tell what stance it is for debugging. +// Probably not something you want for regular use. +/datum/ai_holder/proc/stance_color() + var/new_color = null + switch(stance) + if(STANCE_SLEEP) + new_color = "#FFFFFF" // White + if(STANCE_IDLE) + new_color = "#00FF00" // Green + if(STANCE_ALERT) + new_color = "#FFFF00" // Yellow + if(STANCE_APPROACH) + new_color = "#FF9933" // Orange + if(STANCE_FIGHT) + new_color = "#FF0000" // Red + if(STANCE_MOVE) + new_color = "#0000FF" // Blue + if(STANCE_REPOSITION) + new_color = "#FF00FF" // Purple + if(STANCE_FOLLOW) + new_color = "#00FFFF" // Cyan + if(STANCE_FLEE) + new_color = "#666666" // Grey + if(STANCE_DISABLED) + new_color = "#000000" // Black + holder.color = new_color + +// Turns on all the debugging stuff. +/datum/ai_holder/proc/debug() + stance_coloring = TRUE + path_display = TRUE + last_turf_display = TRUE + debug_ai = AI_LOG_INFO + +/datum/ai_holder/hostile/debug + wander = FALSE + conserve_ammo = FALSE + intelligence_level = AI_SMART + + stance_coloring = TRUE + path_display = TRUE + last_turf_display = TRUE + debug_ai = AI_LOG_INFO + diff --git a/code/modules/ai/ai_holder_disabled.dm b/code/modules/ai/ai_holder_disabled.dm new file mode 100644 index 0000000000..e7cd6aa1f8 --- /dev/null +++ b/code/modules/ai/ai_holder_disabled.dm @@ -0,0 +1,95 @@ +// Handles AI while stunned or otherwise disabled. + +/datum/ai_holder + var/respect_confusion = TRUE // If false, the mob won't wander around recklessly. + +// If our holder is able to do anything. +/datum/ai_holder/proc/can_act() + if(holder.stat) // Dead or unconscious. + ai_log("can_act() : Stat was non-zero ([holder.stat]).", AI_LOG_TRACE) + return FALSE + if(holder.incapacitated(INCAPACITATION_DISABLED)) // Stunned in some form. + ai_log("can_act() : Incapacited.", AI_LOG_TRACE) + return FALSE + return TRUE + +// Test if we should switch to STANCE_DISABLE. +// Currently tests for death, stuns, and confusion. +/datum/ai_holder/proc/is_disabled() + if(!can_act()) + return TRUE + if(is_confused()) + return TRUE + return FALSE + +/datum/ai_holder/proc/is_confused() + return holder.confused > 0 && respect_confusion + +// Called by the main loop. +/datum/ai_holder/proc/handle_disabled() + if(!can_act()) + return // Just sit there and take it. + else if(is_confused()) + dangerous_wander() // Let's bump into allies and hit them. + +// Similar to normal wander, but will walk into tiles that are harmful, and attack anything they bump into, including allies. +// Occurs when confused. +/datum/ai_holder/proc/dangerous_wander() + ai_log("dangerous_wander() : Entered.", AI_LOG_DEBUG) + if(isturf(holder.loc) && can_act()) + // Test if we should refrain from falling/attacking allies, if we're smart enough to realize that. + if(intelligence_level > AI_NORMAL) + var/unsafe = FALSE + + tile_test: + for(var/dir_tested in cardinal) + var/turf/turf_tested = get_step(holder, dir_tested) + // Look for unsafe tiles. + if(!turf_tested.is_safe_to_enter(holder)) + unsafe = TRUE + break + + // Look for allies. + for(var/mob/living/L in turf_tested) + if(holder.IIsAlly(L)) + unsafe = TRUE + break tile_test + + + if(unsafe) + ai_log("dangerous_wander() : Staying still due to risk of harm to self or allies.", AI_LOG_TRACE) + return // Just stay still. + + var/moving_to = 0 + moving_to = pick(cardinal) + var/turf/T = get_step(holder, moving_to) + + var/mob/living/L = locate() in T + if(L) + // Attack whoever's on the tile. Even if it's an ally. + ai_log("dangerous_wander() : Going to confuse-attack [L].", AI_LOG_TRACE) + melee_attack(L) + else + // Move to the tile. Even if it's unsafe. + ai_log("dangerous_wander() : Going to confuse-walk to [T] ([T.x],[T.y],[T.z]).", AI_LOG_TRACE) + holder.IMove(T, safety = FALSE) + ai_log("dangerous_wander() : Exited.", AI_LOG_DEBUG) + +/* +// Wanders randomly in cardinal directions. +/datum/ai_holder/proc/handle_wander_movement() + ai_log("handle_wander_movement() : Entered.", AI_LOG_DEBUG) + if(isturf(holder.loc) && can_act()) + wander_delay-- + if(wander_delay <= 0) + if(!wander_when_pulled && holder.pulledby) + ai_log("handle_wander_movement() : Being pulled and cannot wander. Exiting.", AI_LOG_DEBUG) + return + + var/moving_to = 0 // Apparently this is required or it always picks 4, according to the previous developer for simplemob AI. + moving_to = pick(cardinal) + holder.set_dir(moving_to) + holder.IMove(get_step(holder,moving_to)) + wander_delay = base_wander_delay + ai_log("handle_wander_movement() : Exited.", AI_LOG_DEBUG) +*/ \ No newline at end of file diff --git a/code/modules/ai/ai_holder_fleeing.dm b/code/modules/ai/ai_holder_fleeing.dm new file mode 100644 index 0000000000..83a7be94b8 --- /dev/null +++ b/code/modules/ai/ai_holder_fleeing.dm @@ -0,0 +1,45 @@ +// This code handles what to do inside STANCE_FLEE. + +/datum/ai_holder + var/can_flee = TRUE // If they're even allowed to flee. + var/flee_when_dying = TRUE // If they should flee when low on health. + var/dying_threshold = 0.3 // How low on health the holder needs to be before fleeing. Defaults to 30% or lower health. + var/flee_when_outmatched = FALSE // If they should flee upon reaching a specific tension threshold. + var/outmatched_threshold = 200 // The tension threshold needed for a mob to decide it should run away. + + + +/datum/ai_holder/proc/should_flee(force = FALSE) + if(holder.has_modifier_of_type(/datum/modifier/berserk)) // Berserked mobs will never flee, even if 'forced' to. + return FALSE + if(force) + return TRUE + + if(can_flee) + if(special_flee_check()) + return TRUE + if(!hostile && !retaliate) + return TRUE // We're not hostile and someone attacked us first. + if(flee_when_dying && (holder.health / holder.getMaxHealth()) <= dying_threshold) + return TRUE // We're gonna die! + else if(flee_when_outmatched && holder.get_tension() >= outmatched_threshold) + return TRUE // We're fighting something way way stronger then us. + return FALSE + +// Override for special fleeing conditionally. +/datum/ai_holder/proc/special_flee_check() + return FALSE + +/datum/ai_holder/proc/flee_from_target() + ai_log("flee_from_target() : Entering.", AI_LOG_DEBUG) + + if(!target || !should_flee() || !can_attack(target)) // can_attack() is used since it checks the same things we would need to anyways. + ai_log("flee_from_target() : Lost target to flee from.", AI_LOG_INFO) + lose_target() + set_stance(STANCE_IDLE) + ai_log("flee_from_target() : Exiting.", AI_LOG_DEBUG) + return + + ai_log("flee_from_target() : Stepping away.", AI_LOG_TRACE) + step_away(holder, target, vision_range) + ai_log("flee_from_target() : Exiting.", AI_LOG_DEBUG) \ No newline at end of file diff --git a/code/modules/ai/ai_holder_follow.dm b/code/modules/ai/ai_holder_follow.dm new file mode 100644 index 0000000000..1e7bb0875d --- /dev/null +++ b/code/modules/ai/ai_holder_follow.dm @@ -0,0 +1,68 @@ +// This handles following a specific atom/movable, without violently murdering it. + +/datum/ai_holder + // Following. + var/atom/movable/leader = null // The movable atom that the mob wants to follow. + var/follow_distance = 2 // How far leader must be to start moving towards them. + var/follow_until_time = 0 // world.time when the mob will stop following leader. 0 means it won't time out. + +/datum/ai_holder/proc/walk_to_leader() + ai_log("walk_to_leader() : Entering.",AI_LOG_TRACE) + if(!leader) + ai_log("walk_to_leader() : No leader.", AI_LOG_WARNING) + forget_path() + set_stance(STANCE_IDLE) + ai_log("walk_to_leader() : Exiting.", AI_LOG_TRACE) + return + + // Did we time out? + if(follow_until_time && world.time > follow_until_time) + ai_log("walk_to_leader() : Follow timed out, losing leader.", AI_LOG_INFO) + lose_follow() + set_stance(STANCE_IDLE) + ai_log("walk_to_leader() : Exiting.", AI_LOG_TRACE) + return + + var/get_to = follow_distance + var/distance = get_dist(holder, leader) + ai_log("walk_to_leader() : get_to is [get_to].", AI_LOG_TRACE) + + // We're here! + if(distance <= get_to) + give_up_movement() + set_stance(STANCE_IDLE) + ai_log("walk_to_leader() : Within range, exiting.", AI_LOG_INFO) + return + + ai_log("walk_to_leader() : Walking.", AI_LOG_TRACE) + walk_path(leader, get_to) + ai_log("walk_to_leader() : Exiting.",AI_LOG_DEBUG) + +/datum/ai_holder/proc/set_follow(mob/living/L, follow_for = 0) + ai_log("set_follow() : Entered.", AI_LOG_DEBUG) + if(!L) + ai_log("set_follow() : Was told to follow a nonexistant mob.", AI_LOG_ERROR) + return FALSE + + leader = L + follow_until_time = !follow_for ? 0 : world.time + follow_for + ai_log("set_follow() : Exited.", AI_LOG_DEBUG) + return TRUE + +/datum/ai_holder/proc/lose_follow() + ai_log("lose_follow() : Entered.", AI_LOG_DEBUG) + ai_log("lose_follow() : Going to lose leader [leader].", AI_LOG_INFO) + leader = null + give_up_movement() + ai_log("lose_follow() : Exited.", AI_LOG_DEBUG) + +/datum/ai_holder/proc/should_follow_leader() + if(!leader) + return FALSE + if(follow_until_time && world.time > follow_until_time) + lose_follow() + set_stance(STANCE_IDLE) + return FALSE + if(get_dist(holder, leader) > follow_distance) + return TRUE + return FALSE \ No newline at end of file diff --git a/code/modules/ai/ai_holder_movement.dm b/code/modules/ai/ai_holder_movement.dm new file mode 100644 index 0000000000..55a1098b90 --- /dev/null +++ b/code/modules/ai/ai_holder_movement.dm @@ -0,0 +1,154 @@ +/datum/ai_holder + // General. + var/turf/destination = null // The targeted tile the mob wants to walk to. + var/min_distance_to_destination = 1 // Holds how close the mob should go to destination until they're done. + + // Home. + var/turf/home_turf = null // The mob's 'home' turf. It will try to stay near it if told to do so. This is the turf the AI was initialized on by default. + var/returns_home = FALSE // If true, makes the mob go to its 'home' if it strays too far. + var/home_low_priority = FALSE // If true, the mob will not go home unless it has nothing better to do, e.g. its following someone. + var/max_home_distance = 3 // How far the mob can go away from its home before being told to go_home(). + // Note that there is a 'BYOND cap' of 14 due to limitations of get_/step_to(). + + // Wandering. + var/wander = FALSE // If true, the mob will randomly move in the four cardinal directions when idle. + var/wander_delay = 0 // How many ticks until the mob can move a tile in handle_wander_movement(). + var/base_wander_delay = 2 // What the above var gets set to when it wanders. Note that a tick happens every half a second. + var/wander_when_pulled = FALSE // If the mob will refrain from wandering if someone is pulling it. + + +/datum/ai_holder/proc/walk_to_destination() + ai_log("walk_to_destination() : Entering.",AI_LOG_TRACE) + if(!destination) + ai_log("walk_to_destination() : No destination.", AI_LOG_WARNING) + forget_path() + set_stance(stance == STANCE_REPOSITION ? STANCE_APPROACH : STANCE_IDLE) + ai_log("walk_to_destination() : Exiting.", AI_LOG_TRACE) + return + + var/get_to = min_distance_to_destination + var/distance = get_dist(holder, destination) + ai_log("walk_to_destination() : get_to is [get_to].", AI_LOG_TRACE) + + // We're here! + if(distance <= get_to) + give_up_movement() + set_stance(stance == STANCE_REPOSITION ? STANCE_APPROACH : STANCE_IDLE) + ai_log("walk_to_destination() : Destination reached. Exiting.", AI_LOG_INFO) + return + + ai_log("walk_to_destination() : Walking.", AI_LOG_TRACE) + walk_path(destination, get_to) + ai_log("walk_to_destination() : Exiting.",AI_LOG_TRACE) + +/datum/ai_holder/proc/should_go_home() + if(!returns_home || !home_turf) + return FALSE + if(get_dist(holder, home_turf) > max_home_distance) + if(!home_low_priority) + return TRUE + else if(!leader && !target) + return TRUE + return FALSE +// return (returns_home && home_turf) && (get_dist(holder, home_turf) > max_home_distance) + +/datum/ai_holder/proc/go_home() + if(home_turf) + ai_log("go_home() : Telling holder to go home.", AI_LOG_INFO) + lose_follow() // So they don't try to path back and forth. + give_destination(home_turf, max_home_distance) + else + ai_log("go_home() : Told to go home without home_turf.", AI_LOG_ERROR) + +/datum/ai_holder/proc/give_destination(turf/new_destination, min_distance = 1, combat = FALSE) + ai_log("give_destination() : Entering.", AI_LOG_DEBUG) + + destination = new_destination + min_distance_to_destination = min_distance + + if(new_destination != null) + ai_log("give_destination() : Going to new destination.", AI_LOG_INFO) + set_stance(combat ? STANCE_REPOSITION : STANCE_MOVE) + return TRUE + else + ai_log("give_destination() : Given null destination.", AI_LOG_ERROR) + + ai_log("give_destination() : Exiting.", AI_LOG_DEBUG) + + +// Walk towards whatever. +/datum/ai_holder/proc/walk_path(atom/A, get_to = 1) + ai_log("walk_path() : Entered.", AI_LOG_TRACE) + + if(use_astar) + if(!path.len) // If we're missing a path, make a new one. + ai_log("walk_path() : No path. Attempting to calculate path.", AI_LOG_DEBUG) + calculate_path(A, get_to) + + if(!path.len) // If we still don't have one, then the target's probably somewhere inaccessible to us. Get as close as we can. + ai_log("walk_path() : Failed to obtain path to target. Using get_step_to() instead.", AI_LOG_INFO) + // step_to(holder, A) + if(holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED) + ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_INFO) + breakthrough(A) // We failed to move, time to smash things. + return + + if(move_once() == FALSE) // Start walking the path. + ai_log("walk_path() : Failed to step.", AI_LOG_TRACE) + ++failed_steps + if(failed_steps > 3) // We're probably stuck. + ai_log("walk_path() : Too many failed_steps.", AI_LOG_DEBUG) + forget_path() // So lets try again with a new path. + failed_steps = 0 + + else + // step_to(holder, A) + ai_log("walk_path() : Going to IMove().", AI_LOG_TRACE) + if(holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED ) + ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_INFO) + breakthrough(A) // We failed to move, time to smash things. + + ai_log("walk_path() : Exited.", AI_LOG_TRACE) + + +//Take one step along a path +/datum/ai_holder/proc/move_once() + ai_log("move_once() : Entered.", AI_LOG_TRACE) + if(!path.len) + return + + if(path_display) + var/turf/T = src.path[1] + T.overlays -= path_overlay + +// step_towards(holder, src.path[1]) + if(holder.IMove(get_step_towards(holder, src.path[1])) != MOVEMENT_ON_COOLDOWN) + if(holder.loc != src.path[1]) + ai_log("move_once() : Failed step. Exiting.", AI_LOG_TRACE) + return MOVEMENT_FAILED + else + path -= src.path[1] + ai_log("move_once() : Successful step. Exiting.", AI_LOG_TRACE) + return MOVEMENT_SUCCESSFUL + ai_log("move_once() : Mob movement on cooldown. Exiting.", AI_LOG_TRACE) + return MOVEMENT_ON_COOLDOWN + +/datum/ai_holder/proc/should_wander() + return wander && !leader + +// Wanders randomly in cardinal directions. +/datum/ai_holder/proc/handle_wander_movement() + ai_log("handle_wander_movement() : Entered.", AI_LOG_TRACE) + if(isturf(holder.loc) && can_act()) + wander_delay-- + if(wander_delay <= 0) + if(!wander_when_pulled && holder.pulledby) + ai_log("handle_wander_movement() : Being pulled and cannot wander. Exiting.", AI_LOG_DEBUG) + return + + var/moving_to = 0 // Apparently this is required or it always picks 4, according to the previous developer for simplemob AI. + moving_to = pick(cardinal) + holder.set_dir(moving_to) + holder.IMove(get_step(holder,moving_to)) + wander_delay = base_wander_delay + ai_log("handle_wander_movement() : Exited.", AI_LOG_TRACE) diff --git a/code/modules/ai/ai_holder_pathfinding.dm b/code/modules/ai/ai_holder_pathfinding.dm new file mode 100644 index 0000000000..1a20c6f682 --- /dev/null +++ b/code/modules/ai/ai_holder_pathfinding.dm @@ -0,0 +1,58 @@ +// This handles obtaining a (usually A*) path towards something, such as a target, destination, or leader. +// This interacts heavily with code inside ai_holder_movement.dm + +/datum/ai_holder + // Pathfinding. + var/use_astar = FALSE // Do we use the more expensive A* implementation or stick with BYOND's default step_to()? + var/list/path = list() // A list of tiles that A* gave us as a solution to reach the target. + var/list/obstacles = list() // Things A* will try to avoid. + var/astar_adjacent_proc = /turf/proc/CardinalTurfsWithAccess // Proc to use when A* pathfinding. Default makes them bound to cardinals. + var/failed_steps = 0 // If move_once() fails to move the mob onto the correct tile, this increases. When it reaches 3, the path is recalc'd since they're probably stuck. + +// This clears the stored A* path. +/datum/ai_holder/proc/forget_path() + ai_log("forget_path() : Entering.", AI_LOG_DEBUG) + if(path_display) + for(var/turf/T in path) + T.overlays -= path_overlay + path.Cut() + ai_log("forget_path() : Exiting.", AI_LOG_DEBUG) + +/datum/ai_holder/proc/give_up_movement() + ai_log("give_up_movement() : Entering.", AI_LOG_DEBUG) + forget_path() + destination = null + ai_log("give_up_movement() : Exiting.", AI_LOG_DEBUG) + +/datum/ai_holder/proc/calculate_path(atom/A, get_to = 1) + ai_log("calculate_path([A],[get_to]) : Entering.", AI_LOG_DEBUG) + if(!A) + ai_log("calculate_path() : Called without an atom. Exiting.",AI_LOG_WARNING) + return + + if(!use_astar) // If we don't use A* then this is pointless. + ai_log("calculate_path() : Not using A*, Exiting.", AI_LOG_DEBUG) + return + + get_path(get_turf(A), get_to) + + ai_log("calculate_path() : Exiting.", AI_LOG_DEBUG) + +//A* now, try to a path to a target +/datum/ai_holder/proc/get_path(var/turf/target,var/get_to = 1, var/max_distance = world.view*6) + ai_log("get_path() : Entering.",AI_LOG_DEBUG) + forget_path() + var/list/new_path = AStar(get_turf(holder.loc), target, astar_adjacent_proc, /turf/proc/Distance, min_target_dist = get_to, max_node_depth = max_distance, id = holder.IGetID(), exclude = obstacles) + + if(new_path && new_path.len) + path = new_path + ai_log("get_path() : Made new path.",AI_LOG_DEBUG) + if(path_display) + for(var/turf/T in path) + T.overlays |= path_overlay + else + ai_log("get_path() : Failed to make new path. Exiting.",AI_LOG_DEBUG) + return 0 + + ai_log("get_path() : Exiting.", AI_LOG_DEBUG) + return path.len \ No newline at end of file diff --git a/code/modules/ai/ai_holder_targeting.dm b/code/modules/ai/ai_holder_targeting.dm new file mode 100644 index 0000000000..acb3da2309 --- /dev/null +++ b/code/modules/ai/ai_holder_targeting.dm @@ -0,0 +1,237 @@ +// Used for assigning a target for attacking. + +/datum/ai_holder + var/hostile = FALSE // Do we try to hurt others? + var/retaliate = FALSE // Attacks whatever struck it first. Mobs will still attack back if this is false but hostile is true. + + var/atom/movable/target = null // The thing (mob or object) we're trying to kill. + var/atom/movable/preferred_target = null// If set, and if given the chance, we will always prefer to target this over other options. + var/turf/target_last_seen_turf = null // Where the mob last observed the target being, used if the target disappears but the mob wants to keep fighting. + + var/vision_range = 7 // How far the targeting system will look for things to kill. Note that values higher than 7 are 'offscreen' and might be unsporting. + var/respect_alpha = TRUE // If true, mobs with a sufficently low alpha will be treated as invisible. + var/alpha_vision_threshold = 127 // Targets with an alpha less or equal to this will be considered invisible. Requires above var to be true. + + var/lose_target_time = 0 // world.time when a target was lost. + var/lose_target_timeout = 5 SECONDS // How long until a mob 'times out' and stops trying to find the mob that disappeared. + + var/list/attackers = list() // List of strings of names of people who attacked us before in our life. + // This uses strings and not refs to allow for disguises, and to avoid needing to use weakrefs. + +// A lot of this is based off of /TG/'s AI code. + +// Step 1, find out what we can see. +/datum/ai_holder/proc/list_targets() + . = hearers(vision_range, holder) - src // Remove ourselves to prevent suicidal decisions. + + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha)) + + for(var/HM in typecache_filter_list(range(vision_range, holder), hostile_machines)) + if(can_see(holder, HM, vision_range)) + . += HM + +// Step 2, filter down possible targets to things we actually care about. +/datum/ai_holder/proc/find_target(var/list/possible_targets, var/has_targets_list = FALSE) + if(!hostile) // So retaliating mobs only attack the thing that hit it. + return null + . = list() + if(!has_targets_list) + possible_targets = list_targets() + for(var/possible_target in possible_targets) + var/atom/A = possible_target + if(found(A)) // In case people want to override this. + . = list(A) + break + if(can_attack(A)) // Can we attack it? + . += A + continue + + var/new_target = pick_target(.) + give_target(new_target) + return new_target + +// Step 3, pick among the possible, attackable targets. +/datum/ai_holder/proc/pick_target(list/targets) + if(target != null) // If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets. + targets = target_filter_distance(targets) +// for(var/possible_target in targets) +// var/atom/A = possible_target +// var/target_dist = get_dist(holder, target) +// var/possible_target_distance = get_dist(holder, A) +// if(target_dist < possible_target_distance) +// targets -= A + if(!targets.len) // We found nothing. + return + + var/chosen_target + if(preferred_target && preferred_target in targets) + chosen_target = preferred_target + else + chosen_target = pick(targets) + return chosen_target + +// Step 4, give us our selected target. +/datum/ai_holder/proc/give_target(new_target) + target = new_target + if(target != null) + if(should_threaten()) + set_stance(STANCE_ALERT) + else + set_stance(STANCE_APPROACH) + return TRUE + +// Filters return one or more 'preferred' targets. + +// This one is for closest targets. +/datum/ai_holder/proc/target_filter_distance(list/targets) + for(var/possible_target in targets) + var/atom/A = possible_target + var/target_dist = get_dist(holder, target) + var/possible_target_distance = get_dist(holder, A) + if(target_dist < possible_target_distance) + targets -= A + return targets + +/datum/ai_holder/proc/can_attack(atom/movable/the_target) + if(!can_see_target(the_target)) + return FALSE + + if(istype(the_target, /mob/zshadow)) + return FALSE // no + + if(isliving(the_target)) + var/mob/living/L = the_target + if(L.stat == DEAD) + return FALSE + if(holder.IIsAlly(L)) + return FALSE + return TRUE + + if(istype(the_target, /obj/mecha)) + var/obj/mecha/M = the_target + if(M.occupant) + return can_attack(M.occupant) + + if(istype(the_target, /obj/machinery/porta_turret)) + var/obj/machinery/porta_turret/P = the_target + if(P.stat & BROKEN) + return FALSE // Already dead. + if(P.faction == holder.faction) + return FALSE // Don't shoot allied turrets. + if(!P.raised && !P.raising) + return FALSE // Turrets won't get hurt if they're still in their cover. + return TRUE + + return TRUE +// return FALSE + +// Override this for special targeting criteria. +// If it returns true, the mob will always select it as the target. +/datum/ai_holder/proc/found(atom/movable/the_target) + return FALSE + +//We can't see the target, go look or attack where they were last seen. +/datum/ai_holder/proc/lose_target() + if(target) + target = null + lose_target_time = world.time + + give_up_movement() + + +//Target is no longer valid (?) +/datum/ai_holder/proc/lost_target() + set_stance(STANCE_IDLE) + lose_target_position() + lose_target() + +// Check if target is visible to us. +/datum/ai_holder/proc/can_see_target(atom/movable/the_target, view_range = vision_range) + ai_log("can_see_target() : Entering.", AI_LOG_TRACE) + + if(!the_target) // Nothing to target. + ai_log("can_see_target() : There is no target. Exiting.", AI_LOG_WARNING) + return FALSE + + if(holder.see_invisible < the_target.invisibility) // Real invis. + ai_log("can_see_target() : Target ([the_target]) was invisible to holder. Exiting.", AI_LOG_TRACE) + return FALSE + + if(respect_alpha && the_target.alpha <= alpha_vision_threshold) // Fake invis. + ai_log("can_see_target() : Target ([the_target]) was sufficently transparent to holder and is hidden. Exiting.", AI_LOG_TRACE) + return FALSE + + if(get_dist(holder, the_target) > view_range) // Too far away. + ai_log("can_see_target() : Target ([the_target]) was too far from holder. Exiting.", AI_LOG_TRACE) + return FALSE + + if(!can_see(holder, the_target, view_range)) + ai_log("can_see_target() : Target ([the_target]) failed can_see(). Exiting.", AI_LOG_TRACE) + return FALSE + + ai_log("can_see_target() : Target ([the_target]) can be seen. Exiting.", AI_LOG_TRACE) + return TRUE + +// Updates the last known position of the target. +/datum/ai_holder/proc/track_target_position() + if(!target) + lose_target_position() + + if(last_turf_display && target_last_seen_turf) + target_last_seen_turf.overlays -= last_turf_overlay + + target_last_seen_turf = get_turf(target) + + if(last_turf_display) + target_last_seen_turf.overlays += last_turf_overlay + +// Resets the last known position to null. +/datum/ai_holder/proc/lose_target_position() + if(last_turf_display && target_last_seen_turf) + target_last_seen_turf.overlays -= last_turf_overlay + ai_log("lose_target_position() : Last position is being reset.", AI_LOG_INFO) + target_last_seen_turf = null + +// Responds to a hostile action against its mob. +/datum/ai_holder/proc/react_to_attack(atom/movable/attacker) + if(holder.stat) // We're dead. + ai_log("react_to_attack() : Was attacked by [attacker], but we are dead/unconscious.", AI_LOG_TRACE) + return FALSE + if(!hostile && !retaliate) // Not allowed to defend ourselves. + ai_log("react_to_attack() : Was attacked by [attacker], but we are not allowed to attack back.", AI_LOG_TRACE) + return FALSE + if(holder.IIsAlly(attacker)) // I'll overlook it THIS time... + ai_log("react_to_attack() : Was attacked by [attacker], but they were an ally.", AI_LOG_TRACE) + return FALSE + if(target) // Already fighting someone. Switching every time we get hit would impact our combat performance. + ai_log("react_to_attack() : Was attacked by [attacker], but we already have a target.", AI_LOG_TRACE) + on_attacked(attacker) // So we attack immediately and not threaten. + return FALSE + + if(stance == STANCE_SLEEP) // If we're asleep, try waking up if someone's wailing on us. + ai_log("react_to_attack() : AI is asleep. Waking up.", AI_LOG_TRACE) + go_wake() + + ai_log("react_to_attack() : Was attacked by [attacker].", AI_LOG_INFO) + on_attacked(attacker) // So we attack immediately and not threaten. + return give_target(attacker) // Also handles setting the appropiate stance. + +// Sets a few vars so mobs that threaten will react faster to an attacker or someone who attacked them before. +/datum/ai_holder/proc/on_attacked(atom/movable/AM) + last_conflict_time = world.time + if(isliving(AM)) + var/mob/living/L = AM + attackers |= L.name + +// Causes targeting to prefer targeting the taunter if possible. +// This generally occurs if more than one option is within striking distance, including the taunter. +// Otherwise the default filter will prefer the closest target. +/datum/ai_holder/proc/receive_taunt(atom/movable/taunter, force_target_switch = FALSE) + ai_log("receive_taunt() : Was taunted by [taunter].", AI_LOG_INFO) + preferred_target = taunter + if(force_target_switch) + give_target(taunter) + +/datum/ai_holder/proc/lose_taunt() + ai_log("lose_taunt() : Resetting preferred_target.", AI_LOG_INFO) + preferred_target = null \ No newline at end of file diff --git a/code/modules/ai/interfaces.dm b/code/modules/ai/interfaces.dm new file mode 100644 index 0000000000..dbadac8757 --- /dev/null +++ b/code/modules/ai/interfaces.dm @@ -0,0 +1,92 @@ +// 'Interfaces' are procs that the ai_holder datum uses to communicate its will to the mob its attached. +// The reason for using this proc in the middle is to ensure the AI has some form of compatibility with most mob types, +// since some actions work very differently between mob types (e.g. executing an attack as a simple animal compared to a human). +// The AI can just call holder.IAttack(target) and the mob is responsible for determining how to actually attack the target. + +/mob/living/proc/IAttack(atom/A) + return FALSE + +/mob/living/simple_mob/IAttack(atom/A) + if(!canClick()) // Still on cooldown from a "click". + return FALSE + return attack_target(A) // This will set click cooldown. + +/mob/living/proc/IRangedAttack(atom/A) + return FALSE + +/mob/living/simple_mob/IRangedAttack(atom/A) + if(!canClick()) // Still on cooldown from a "click". + return FALSE + return shoot_target(A) + +// Test if the AI is allowed to attempt a ranged attack. +/mob/living/proc/ICheckRangedAttack(atom/A) + return FALSE + +/mob/living/simple_mob/ICheckRangedAttack(atom/A) + if(needs_reload) + if(reload_count >= reload_max) + try_reload() + return FALSE + return projectiletype ? TRUE : FALSE + +/mob/living/proc/ISpecialAttack(atom/A) + return FALSE + +/mob/living/simple_mob/ISpecialAttack(atom/A) + return special_attack_target(A) + +// Is the AI allowed to attempt to do it? +/mob/living/proc/ICheckSpecialAttack(atom/A) + return FALSE + +/mob/living/simple_mob/ICheckSpecialAttack(atom/A) + return can_special_attack(A) && should_special_attack(A) // Just because we can doesn't mean we should. + +/mob/living/proc/ISay(message) + return say(message) + +/mob/living/proc/IIsAlly(mob/living/L) + return src.faction == L.faction + +/mob/living/simple_mob/IIsAlly(mob/living/L) + . = ..() + if(!.) // Outside the faction, try to see if they're friends. + return L in friends + +/mob/living/proc/IGetID() + +/mob/living/simple_mob/IGetID() + if(myid) + return myid.GetID() + +// Respects move cooldowns as if it had a client. +// Also tries to avoid being superdumb with moving into certain tiles (unless that's desired). +/mob/living/proc/IMove(turf/newloc, safety = TRUE) + if(check_move_cooldown()) +// if(!newdir) +// newdir = get_dir(get_turf(src), newloc) + + // Check to make sure moving to newloc won't actually kill us. e.g. we're a slime and trying to walk onto water. + if(istype(newloc)) + if(safety && !newloc.is_safe_to_enter(src)) + return MOVEMENT_FAILED + + // Move()ing to another tile successfully returns 32 because BYOND. Would rather deal with TRUE/FALSE-esque terms. + // Note that moving to the same tile will be 'successful'. + var/turf/old_T = get_turf(src) + + // An adjacency check to avoid mobs phasing diagonally past windows. + // This might be better in general movement code but I'm too scared to add it, and most things don't move diagonally anyways. + if(!old_T.Adjacent(newloc)) + return MOVEMENT_FAILED + + . = SelfMove(newloc) ? MOVEMENT_SUCCESSFUL : MOVEMENT_FAILED + if(. == MOVEMENT_SUCCESSFUL) + set_dir(get_dir(old_T, newloc)) + // Apply movement delay. + // Player movement has more factors but its all in the client and fixing that would be its own project. + setMoveCooldown(movement_delay()) + return + + . = MOVEMENT_ON_COOLDOWN // To avoid superfast mobs that aren't meant to be superfast. Is actually -1. diff --git a/code/modules/ai/say_list.dm b/code/modules/ai/say_list.dm new file mode 100644 index 0000000000..43732abf0c --- /dev/null +++ b/code/modules/ai/say_list.dm @@ -0,0 +1,119 @@ +// A simple datum that just holds many lists of lines for mobs to pick from. +// This is its own datum in order to be able to have different types of mobs be able to use the same lines if desired, +// even when inheritence wouldn't be able to do so. + +// Also note this also contains emotes, despite its name. +// and now sounds because its probably better that way. + +/mob/living + var/datum/say_list/say_list = null + var/say_list_type = /datum/say_list // Type to give us on initialization. Default has empty lists, so the mob will be silent. + +/mob/living/initialize() + if(say_list_type) + say_list = new say_list_type(src) + return ..() + +/mob/living/Destroy() + QDEL_NULL(say_list) + return ..() + + +/datum/say_list + var/list/speak = list() // Things the mob might say if it talks while idle. + var/list/emote_hear = list() // Hearable emotes it might perform + var/list/emote_see = list() // Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps + + var/list/say_understood = list() // When accepting an order. + var/list/say_cannot = list() // When they cannot comply. + var/list/say_maybe_target = list() // When they briefly see something. + var/list/say_got_target = list() // When a target is first assigned. + var/list/say_threaten = list() // When threatening someone. + var/list/say_stand_down = list() // When the threatened thing goes away. + var/list/say_escalate = list() // When the threatened thing doesn't go away. + + var/threaten_sound = null // Sound file played when the mob's AI calls threaten_target() for the first time. + var/stand_down_sound = null // Sound file played when the mob's AI loses sight of the threatened target. + + + + + + + +// Subtypes. + +// This one's pretty dumb, but pirates are dumb anyways and it makes for a good test. +/datum/say_list/pirate + speak = list("Yarr!") + + say_understood = list("Alright, matey.") + say_cannot = list("No, matey.") + say_maybe_target = list("Eh?") + say_got_target = list("Yarrrr!") + say_threaten = list("You best leave, this booty is mine.", "No plank to walk on, just walk away.") + say_stand_down = list("Good.") + say_escalate = list("Yarr! The booty is mine!") + +// Mercs! +/datum/say_list/merc + speak = list("When are we gonna get out of this chicken-shit outfit?", + "Wish I had better equipment...", + "I knew I should have been a line chef...", + "Fuckin' helmet keeps fogging up.", + "Anyone else smell that?") + emote_see = list("sniffs", "coughs", "taps his foot", "looks around", "checks his equipment") + + say_understood = list("Understood!", "Affirmative!") + say_cannot = list("Negative!") + say_maybe_target = list("Who's there?") + say_got_target = list("Engaging!") + say_threaten = list("Get out of here!", "Hey! Private Property!") + say_stand_down = list("Good.") + say_escalate = list("Your funeral!", "Bring it!") + +/datum/say_list/malf_drone + speak = list("ALERT.","Hostile-ile-ile entities dee-twhoooo-wected.","Threat parameterszzzz- szzet.","Bring sub-sub-sub-systems uuuup to combat alert alpha-a-a.") + emote_see = list("beeps menacingly","whirrs threateningly","scans its immediate vicinity") + + say_understood = list("Affirmative.", "Positive.") + say_cannot = list("Denied.", "Negative.") + say_maybe_target = list("Possible threat detected. Investigating.", "Motion detected.", "Investigating.") + say_got_target = list("Threat detected.", "New task: Remove threat.", "Threat removal engaged.", "Engaging target.") + say_threaten = list("Motion detected, judging target...") + say_stand_down = list("Visual lost.", "Error: Target not found.") + say_escalate = list("Viable target found. Removing.", "Engaging target.", "Target judgement complete. Removal required.") + + threaten_sound = 'sound/effects/turret/move1.wav' + stand_down_sound = 'sound/effects/turret/move2.wav' + +/datum/say_list/mercenary + threaten_sound = 'sound/weapons/TargetOn.ogg' + stand_down_sound = 'sound/weapons/TargetOff.ogg' + + +/datum/say_list/crab + emote_hear = list("clicks") + emote_see = list("clacks") + +/datum/say_list/spider + emote_hear = list("chitters") + +/datum/say_list/hivebot + speak = list( + "Resuming task: Protect area.", + "No threats found.", + "Error: No targets found." + ) + emote_hear = list("hums ominously", "whirrs softly", "grinds a gear") + emote_see = list("looks around the area", "turns from side to side") + say_understood = list("Affirmative.", "Positive.") + say_cannot = list("Denied.", "Negative.") + say_maybe_target = list("Possible threat detected. Investigating.", "Motion detected.", "Investigating.") + say_got_target = list("Threat detected.", "New task: Remove threat.", "Threat removal engaged.", "Engaging target.") + +/datum/say_list/lizard + emote_hear = list("hisses") + +/datum/say_list/crab + emote_hear = list("hisses") \ No newline at end of file diff --git a/code/modules/artifice/deadringer.dm b/code/modules/artifice/deadringer.dm index e12c0b0045..c19da40873 100644 --- a/code/modules/artifice/deadringer.dm +++ b/code/modules/artifice/deadringer.dm @@ -76,13 +76,14 @@ return /obj/item/weapon/deadringer/proc/deathprevent() - for(var/mob/living/simple_animal/D in oviewers(7, src)) - D.LoseTarget() + for(var/mob/living/simple_mob/D in oviewers(7, src)) + if(!D.has_AI()) + continue + D.ai_holder.lose_target() + watchowner.emote("deathgasp") watchowner.alpha = 15 makeacorpse(watchowner) - for(var/mob/living/simple_animal/D in oviewers(7, src)) - D.LoseTarget() return /obj/item/weapon/deadringer/proc/uncloak() diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index e45c6d8beb..7d1c5346c0 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -40,7 +40,7 @@ H.UpdateDamageIcon() H.updatehealth() else if(ismouse(target)) - var/mob/living/simple_animal/mouse/M = target + var/mob/living/simple_mob/animal/passive/mouse/M = target visible_message("SPLAT!") M.splat() playsound(target.loc, 'sound/effects/snap.ogg', 50, 1) diff --git a/code/modules/blob2/blobs/factory.dm b/code/modules/blob2/blobs/factory.dm index 9577f3c14e..08d378f1d0 100644 --- a/code/modules/blob2/blobs/factory.dm +++ b/code/modules/blob2/blobs/factory.dm @@ -15,7 +15,7 @@ var/spore_cooldown = 8 SECONDS /obj/structure/blob/factory/Destroy() - for(var/mob/living/simple_animal/hostile/blob/spore/spore in spores) + for(var/mob/living/simple_mob/blob/spore/spore in spores) if(istype(spore) && spore.factory == src) spore.factory = null else @@ -31,7 +31,7 @@ return flick("blob_factory_glow", src) spore_delay = world.time + spore_cooldown - var/mob/living/simple_animal/hostile/blob/spore/S = null + var/mob/living/simple_mob/blob/spore/S = null if(overmind) S = new overmind.blob_type.spore_type(src.loc, src) S.faction = "blob" @@ -39,10 +39,8 @@ S.overmind = overmind overmind.blob_mobs.Add(S) if(overmind.blob_type.ranged_spores) - S.ranged = TRUE S.projectiletype = overmind.blob_type.spore_projectile S.projectilesound = overmind.blob_type.spore_firesound - S.shoot_range = overmind.blob_type.spore_range else //Other mobs don't add themselves in New. Ew. S.nest = src spores += S diff --git a/code/modules/blob2/overmind/overmind.dm b/code/modules/blob2/overmind/overmind.dm index 8ba73eb1e6..6477c86f22 100644 --- a/code/modules/blob2/overmind/overmind.dm +++ b/code/modules/blob2/overmind/overmind.dm @@ -53,7 +53,7 @@ var/list/overminds = list() B.update_icon() //reset anything that was ours for(var/BLO in blob_mobs) - var/mob/living/simple_animal/hostile/blob/BM = BLO + var/mob/living/simple_mob/blob/spore/BM = BLO if(BM) BM.overmind = null BM.update_icons() diff --git a/code/modules/blob2/overmind/types.dm b/code/modules/blob2/overmind/types.dm index c8aca03cf6..6049ccfc72 100644 --- a/code/modules/blob2/overmind/types.dm +++ b/code/modules/blob2/overmind/types.dm @@ -32,7 +32,7 @@ var/can_build_resources = FALSE // Ditto, for resource blobs. var/can_build_nodes = TRUE // Ditto, for nodes. - var/spore_type = /mob/living/simple_animal/hostile/blob/spore + var/spore_type = /mob/living/simple_mob/blob/spore var/ranged_spores = FALSE // For proper spores of the type above. var/spore_firesound = 'sound/effects/slime_squish.ogg' var/spore_range = 7 // The range the spore can fire. @@ -72,7 +72,7 @@ return // Spore things -/datum/blob_type/proc/on_spore_death(mob/living/simple_animal/hostile/blob/spore/S) +/datum/blob_type/proc/on_spore_death(mob/living/simple_mob/blob/spore/S) return @@ -120,7 +120,7 @@ brute_multiplier = 0.25 burn_multiplier = 0.6 ai_aggressiveness = 50 //Really doesn't like you near it. - spore_type = /mob/living/simple_animal/hostile/hivebot/swarm + spore_type = /mob/living/simple_mob/mechanical/hivebot/swarm /datum/blob_type/fabrication_swarm/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker) if(istype(B, /obj/structure/blob/normal)) @@ -227,9 +227,9 @@ burn_multiplier = 3 ai_aggressiveness = 40 can_build_factories = TRUE - spore_type = /mob/living/simple_animal/hostile/blob/spore/infesting + spore_type = /mob/living/simple_mob/blob/spore/infesting -/datum/blob_type/fungal_bloom/on_spore_death(mob/living/simple_animal/hostile/blob/spore/S) +/datum/blob_type/fungal_bloom/on_spore_death(mob/living/simple_mob/blob/spore/S) if(S.is_infesting) return // Don't make blobs if they were on someone's head. var/turf/T = get_turf(S) @@ -258,11 +258,11 @@ brute_multiplier = 1.5 ai_aggressiveness = 30 // The spores do most of the fighting. can_build_factories = TRUE - spore_type = /mob/living/simple_animal/hostile/blob/spore/weak + spore_type = /mob/living/simple_mob/blob/spore/weak /datum/blob_type/fulminant_organism/on_expand(var/obj/structure/blob/B, var/obj/structure/blob/new_B, var/turf/T, var/mob/observer/blob/O) if(prob(10)) // 10% chance to make a weak spore when expanding. - var/mob/living/simple_animal/hostile/blob/S = new spore_type(T) + var/mob/living/simple_mob/blob/spore/S = new spore_type(T) if(istype(S)) S.overmind = O O.blob_mobs.Add(S) @@ -272,7 +272,7 @@ /datum/blob_type/fulminant_organism/on_death(obj/structure/blob/B) if(prob(33)) // 33% chance to make a spore when dying. - var/mob/living/simple_animal/hostile/blob/S = new spore_type(get_turf(B)) + var/mob/living/simple_mob/blob/spore/S = new spore_type(get_turf(B)) B.visible_message("\The [S] floats free from the [name]!") if(istype(S)) S.overmind = B.overmind @@ -614,7 +614,7 @@ attack_verb = "crashes against" can_build_factories = TRUE can_build_resources = TRUE - spore_type = /mob/living/simple_animal/hostile/blob/spore/weak + spore_type = /mob/living/simple_mob/blob/spore/weak ranged_spores = TRUE spore_range = 3 spore_projectile = /obj/item/projectile/energy/blob/splattering diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 807faab47f..e711006b2a 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -18,7 +18,7 @@ //OTHER// ///////// var/datum/preferences/prefs = null - var/move_delay = 1 + //var/move_delay = 1 var/moving = null var/adminobs = null var/area = null diff --git a/code/modules/client/preference_setup/loadout/loadout_head.dm b/code/modules/client/preference_setup/loadout/loadout_head.dm index e051daf585..32450813bf 100644 --- a/code/modules/client/preference_setup/loadout/loadout_head.dm +++ b/code/modules/client/preference_setup/loadout/loadout_head.dm @@ -370,4 +370,4 @@ /datum/gear/head/circuitry display_name = "headwear, circuitry (empty)" - path = /obj/item/clothing/head/circuitry \ No newline at end of file + path = /obj/item/clothing/head/circuitry diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm index d0f9a8165c..b30a5d4898 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -37,15 +37,15 @@ while (i <= num_groups) var/group_size = rand(group_size_min, group_size_max) for (var/j = 1, j <= group_size, j++) - spawned_carp.Add(new /mob/living/simple_animal/hostile/carp(spawn_locations[i])) + spawned_carp.Add(new /mob/living/simple_mob/animal/space/carp/event(spawn_locations[i])) i++ /datum/event/carp_migration/end() spawn(0) - for(var/mob/living/simple_animal/hostile/C in spawned_carp) - if(!C.stat) - var/turf/T = get_turf(C) + for(var/mob/living/simple_mob/SM in spawned_carp) + if(!SM.stat) + var/turf/T = get_turf(SM) if(istype(T, /turf/space)) if(prob(75)) - qdel(C) + qdel(SM) sleep(1) \ No newline at end of file diff --git a/code/modules/events/infestation.dm b/code/modules/events/infestation.dm index 16f2b78521..3800bc999f 100644 --- a/code/modules/events/infestation.dm +++ b/code/modules/events/infestation.dm @@ -65,11 +65,11 @@ vermin = rand(0,2) switch(vermin) if(VERM_MICE) - spawn_types = list(/mob/living/simple_animal/mouse/gray, /mob/living/simple_animal/mouse/brown, /mob/living/simple_animal/mouse/white) + spawn_types = list(/mob/living/simple_mob/animal/passive/mouse/gray, /mob/living/simple_mob/animal/passive/mouse/brown, /mob/living/simple_mob/animal/passive/mouse/white) max_number = 12 vermstring = "mice" if(VERM_LIZARDS) - spawn_types = list(/mob/living/simple_animal/lizard) + spawn_types = list(/mob/living/simple_mob/animal/passive/lizard) max_number = 6 vermstring = "lizards" if(VERM_SPIDERS) diff --git a/code/modules/events/rogue_drones.dm b/code/modules/events/rogue_drones.dm index 768628f8fb..9cfcc648b5 100644 --- a/code/modules/events/rogue_drones.dm +++ b/code/modules/events/rogue_drones.dm @@ -16,10 +16,8 @@ else num = rand(2,6) for(var/i=0, iThe gibber is locked and running, wait for it to finish." - return - else - src.startgibbing(user) - -/obj/machinery/gibber/examine() - ..() - usr << "The safety guard is [emagged ? "disabled" : "enabled"]." - -/obj/machinery/gibber/emag_act(var/remaining_charges, var/mob/user) - emagged = !emagged - user << "You [emagged ? "disable" : "enable"] the gibber safety guard." - return 1 - -/obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) - var/obj/item/weapon/grab/G = W - - if(default_unfasten_wrench(user, W, 40)) - return - - if(!istype(G)) - return ..() - - if(G.state < 2) - user << "You need a better grip to do that!" - return - - move_into_gibber(user,G.affecting) - // Grab() process should clean up the grab item, no need to del it. - -/obj/machinery/gibber/MouseDrop_T(mob/target, mob/user) - if(user.stat || user.restrained()) - return - move_into_gibber(user,target) - -/obj/machinery/gibber/proc/move_into_gibber(var/mob/user,var/mob/living/victim) - - if(src.occupant) - user << "The gibber is full, empty it first!" - return - - if(operating) - user << "The gibber is locked and running, wait for it to finish." - return - - if(!(istype(victim, /mob/living/carbon)) && !(istype(victim, /mob/living/simple_animal)) ) - user << "This is not suitable for the gibber!" - return - - if(istype(victim,/mob/living/carbon/human) && !emagged) - user << "The gibber safety guard is engaged!" - return - - - if(victim.abiotic(1)) - user << "Subject may not have abiotic items on." - return - - user.visible_message("[user] starts to put [victim] into the gibber!") - src.add_fingerprint(user) - if(do_after(user, 30) && victim.Adjacent(src) && user.Adjacent(src) && victim.Adjacent(user) && !occupant) - user.visible_message("[user] stuffs [victim] into the gibber!") - if(victim.client) - victim.client.perspective = EYE_PERSPECTIVE - victim.client.eye = src - victim.loc = src - src.occupant = victim - update_icon() - -/obj/machinery/gibber/verb/eject() - set category = "Object" - set name = "Empty Gibber" - set src in oview(1) - - if (usr.stat != 0) - return - src.go_out() - add_fingerprint(usr) - return - -/obj/machinery/gibber/proc/go_out() - if(operating || !src.occupant) - return - for(var/obj/O in src) - O.loc = src.loc - if (src.occupant.client) - src.occupant.client.eye = src.occupant.client.mob - src.occupant.client.perspective = MOB_PERSPECTIVE - src.occupant.loc = src.loc - src.occupant = null - update_icon() - return - - -/obj/machinery/gibber/proc/startgibbing(mob/user as mob) - if(src.operating) - return - if(!src.occupant) - visible_message("You hear a loud metallic grinding sound.") - return - - use_power(1000) - visible_message("You hear a loud [occupant.isSynthetic() ? "metallic" : "squelchy"] grinding sound.") - src.operating = 1 - update_icon() - - var/slab_name = occupant.name - var/slab_count = 3 - var/slab_type = /obj/item/weapon/reagent_containers/food/snacks/meat - var/slab_nutrition = src.occupant.nutrition / 15 - - // Some mobs have specific meat item types. - if(istype(src.occupant,/mob/living/simple_animal)) - var/mob/living/simple_animal/critter = src.occupant - if(critter.meat_amount) - slab_count = critter.meat_amount - if(critter.meat_type) - slab_type = critter.meat_type - else if(istype(src.occupant,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = occupant - slab_name = src.occupant.real_name - slab_type = H.isSynthetic() ? /obj/item/stack/material/steel : H.species.meat_type - - // Small mobs don't give as much nutrition. - if(issmall(src.occupant)) - slab_nutrition *= 0.5 - slab_nutrition /= slab_count - - for(var/i=1 to slab_count) - var/obj/item/weapon/reagent_containers/food/snacks/meat/new_meat = new slab_type(src, rand(3,8)) - if(istype(new_meat)) - new_meat.name = "[slab_name] [new_meat.name]" - new_meat.reagents.add_reagent("nutriment",slab_nutrition) - if(src.occupant.reagents) - src.occupant.reagents.trans_to_obj(new_meat, round(occupant.reagents.total_volume/slab_count,1)) - - add_attack_logs(user,occupant,"Used [src] to gib") - - src.occupant.ghostize() - - spawn(gib_time) - - src.operating = 0 - src.occupant.gib() - qdel(src.occupant) - - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) - operating = 0 - for (var/obj/item/thing in contents) - // There's a chance that the gibber will fail to destroy some evidence. - if(istype(thing,/obj/item/organ) && prob(80)) - qdel(thing) - continue - thing.forceMove(get_turf(thing)) // Drop it onto the turf for throwing. - thing.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(0,3),emagged ? 100 : 50) // Being pelted with bits of meat and bone would hurt. - - update_icon() - - + +/obj/machinery/gibber + name = "gibber" + desc = "The name isn't descriptive enough?" + icon = 'icons/obj/kitchen.dmi' + icon_state = "grinder" + density = 1 + anchored = 1 + req_access = list(access_kitchen,access_morgue) + + var/operating = 0 //Is it on? + var/dirty = 0 // Does it need cleaning? + var/mob/living/occupant // Mob who has been put inside + var/gib_time = 40 // Time from starting until meat appears + var/gib_throw_dir = WEST // Direction to spit meat and gibs in. + + use_power = 1 + idle_power_usage = 2 + active_power_usage = 500 + +//auto-gibs anything that bumps into it +/obj/machinery/gibber/autogibber + var/turf/input_plate + +/obj/machinery/gibber/autogibber/New() + ..() + spawn(5) + for(var/i in cardinal) + var/obj/machinery/mineral/input/input_obj = locate( /obj/machinery/mineral/input, get_step(src.loc, i) ) + if(input_obj) + if(isturf(input_obj.loc)) + input_plate = input_obj.loc + gib_throw_dir = i + qdel(input_obj) + break + + if(!input_plate) + log_misc("a [src] didn't find an input plate.") + return + +/obj/machinery/gibber/autogibber/Bumped(var/atom/A) + if(!input_plate) return + + if(ismob(A)) + var/mob/M = A + + if(M.loc == input_plate + ) + M.loc = src + M.gib() + + +/obj/machinery/gibber/New() + ..() + src.overlays += image('icons/obj/kitchen.dmi', "grjam") + +/obj/machinery/gibber/update_icon() + overlays.Cut() + if (dirty) + src.overlays += image('icons/obj/kitchen.dmi', "grbloody") + if(stat & (NOPOWER|BROKEN)) + return + if (!occupant) + src.overlays += image('icons/obj/kitchen.dmi', "grjam") + else if (operating) + src.overlays += image('icons/obj/kitchen.dmi', "gruse") + else + src.overlays += image('icons/obj/kitchen.dmi', "gridle") + +/obj/machinery/gibber/relaymove(mob/user as mob) + src.go_out() + return + +/obj/machinery/gibber/attack_hand(mob/user as mob) + if(stat & (NOPOWER|BROKEN)) + return + if(operating) + user << "The gibber is locked and running, wait for it to finish." + return + else + src.startgibbing(user) + +/obj/machinery/gibber/examine() + ..() + usr << "The safety guard is [emagged ? "disabled" : "enabled"]." + +/obj/machinery/gibber/emag_act(var/remaining_charges, var/mob/user) + emagged = !emagged + user << "You [emagged ? "disable" : "enable"] the gibber safety guard." + return 1 + +/obj/machinery/gibber/attackby(var/obj/item/W, var/mob/user) + var/obj/item/weapon/grab/G = W + + if(default_unfasten_wrench(user, W, 40)) + return + + if(!istype(G)) + return ..() + + if(G.state < 2) + user << "You need a better grip to do that!" + return + + move_into_gibber(user,G.affecting) + // Grab() process should clean up the grab item, no need to del it. + +/obj/machinery/gibber/MouseDrop_T(mob/target, mob/user) + if(user.stat || user.restrained()) + return + move_into_gibber(user,target) + +/obj/machinery/gibber/proc/move_into_gibber(var/mob/user,var/mob/living/victim) + + if(src.occupant) + user << "The gibber is full, empty it first!" + return + + if(operating) + user << "The gibber is locked and running, wait for it to finish." + return + + if(!(istype(victim, /mob/living/carbon)) && !(istype(victim, /mob/living/simple_mob)) ) + user << "This is not suitable for the gibber!" + return + + if(istype(victim,/mob/living/carbon/human) && !emagged) + user << "The gibber safety guard is engaged!" + return + + + if(victim.abiotic(1)) + user << "Subject may not have abiotic items on." + return + + user.visible_message("[user] starts to put [victim] into the gibber!") + src.add_fingerprint(user) + if(do_after(user, 30) && victim.Adjacent(src) && user.Adjacent(src) && victim.Adjacent(user) && !occupant) + user.visible_message("[user] stuffs [victim] into the gibber!") + if(victim.client) + victim.client.perspective = EYE_PERSPECTIVE + victim.client.eye = src + victim.loc = src + src.occupant = victim + update_icon() + +/obj/machinery/gibber/verb/eject() + set category = "Object" + set name = "Empty Gibber" + set src in oview(1) + + if (usr.stat != 0) + return + src.go_out() + add_fingerprint(usr) + return + +/obj/machinery/gibber/proc/go_out() + if(operating || !src.occupant) + return + for(var/obj/O in src) + O.loc = src.loc + if (src.occupant.client) + src.occupant.client.eye = src.occupant.client.mob + src.occupant.client.perspective = MOB_PERSPECTIVE + src.occupant.loc = src.loc + src.occupant = null + update_icon() + return + + +/obj/machinery/gibber/proc/startgibbing(mob/user as mob) + if(src.operating) + return + if(!src.occupant) + visible_message("You hear a loud metallic grinding sound.") + return + + use_power(1000) + visible_message("You hear a loud [occupant.isSynthetic() ? "metallic" : "squelchy"] grinding sound.") + src.operating = 1 + update_icon() + + var/slab_name = occupant.name + var/slab_count = 3 + var/slab_type = /obj/item/weapon/reagent_containers/food/snacks/meat + var/slab_nutrition = src.occupant.nutrition / 15 + + // Some mobs have specific meat item types. + if(istype(src.occupant,/mob/living/simple_mob)) + var/mob/living/simple_mob/critter = src.occupant + if(critter.meat_amount) + slab_count = critter.meat_amount + if(critter.meat_type) + slab_type = critter.meat_type + else if(istype(src.occupant,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = occupant + slab_name = src.occupant.real_name + slab_type = H.isSynthetic() ? /obj/item/stack/material/steel : H.species.meat_type + + // Small mobs don't give as much nutrition. + if(issmall(src.occupant)) + slab_nutrition *= 0.5 + slab_nutrition /= slab_count + + for(var/i=1 to slab_count) + var/obj/item/weapon/reagent_containers/food/snacks/meat/new_meat = new slab_type(src, rand(3,8)) + if(istype(new_meat)) + new_meat.name = "[slab_name] [new_meat.name]" + new_meat.reagents.add_reagent("nutriment",slab_nutrition) + if(src.occupant.reagents) + src.occupant.reagents.trans_to_obj(new_meat, round(occupant.reagents.total_volume/slab_count,1)) + + add_attack_logs(user,occupant,"Used [src] to gib") + + src.occupant.ghostize() + + spawn(gib_time) + + src.operating = 0 + src.occupant.gib() + qdel(src.occupant) + + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) + operating = 0 + for (var/obj/item/thing in contents) + // There's a chance that the gibber will fail to destroy some evidence. + if(istype(thing,/obj/item/organ) && prob(80)) + qdel(thing) + continue + thing.forceMove(get_turf(thing)) // Drop it onto the turf for throwing. + thing.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(0,3),emagged ? 100 : 50) // Being pelted with bits of meat and bone would hurt. + + update_icon() + + diff --git a/code/modules/food/kitchen/microwave.dm b/code/modules/food/kitchen/microwave.dm index 7357dd5568..1ece9225b1 100644 --- a/code/modules/food/kitchen/microwave.dm +++ b/code/modules/food/kitchen/microwave.dm @@ -1,392 +1,392 @@ -/obj/machinery/microwave - name = "microwave" - icon = 'icons/obj/kitchen.dmi' - icon_state = "mw" - density = 1 - anchored = 1 - use_power = 1 - idle_power_usage = 5 - active_power_usage = 100 - flags = OPENCONTAINER | NOREACT - circuit = /obj/item/weapon/circuitboard/microwave - var/operating = 0 // Is it on? - var/dirty = 0 // = {0..100} Does it need cleaning? - var/broken = 0 // ={0,1,2} How broken is it??? - var/global/list/datum/recipe/available_recipes // List of the recipes you can use - var/global/list/acceptable_items // List of the items you can put in - var/global/list/acceptable_reagents // List of the reagents you can put in - var/global/max_n_of_items = 0 - - -// see code/modules/food/recipes_microwave.dm for recipes - -/******************* -* Initialising -********************/ - -/obj/machinery/microwave/New() - ..() - reagents = new/datum/reagents(100) - reagents.my_atom = src - - component_parts = list() - component_parts += new /obj/item/weapon/stock_parts/console_screen(src) - component_parts += new /obj/item/weapon/stock_parts/motor(src) - component_parts += new /obj/item/weapon/stock_parts/capacitor(src) - - if (!available_recipes) - available_recipes = new - for (var/type in (typesof(/datum/recipe)-/datum/recipe)) - available_recipes+= new type - acceptable_items = new - acceptable_reagents = new - for (var/datum/recipe/recipe in available_recipes) - for (var/item in recipe.items) - acceptable_items |= item - for (var/reagent in recipe.reagents) - acceptable_reagents |= reagent - if (recipe.items) - max_n_of_items = max(max_n_of_items,recipe.items.len) - // This will do until I can think of a fun recipe to use dionaea in - - // will also allow anything using the holder item to be microwaved into - // impure carbon. ~Z - acceptable_items |= /obj/item/weapon/holder - acceptable_items |= /obj/item/weapon/reagent_containers/food/snacks/grown - - RefreshParts() - -/******************* -* Item Adding -********************/ - -/obj/machinery/microwave/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(src.broken > 0) - if(src.broken == 2 && O.is_screwdriver()) // If it's broken and they're using a screwdriver - user.visible_message( \ - "\The [user] starts to fix part of the microwave.", \ - "You start to fix part of the microwave." \ - ) - playsound(src, O.usesound, 50, 1) - if (do_after(user,20 * O.toolspeed)) - user.visible_message( \ - "\The [user] fixes part of the microwave.", \ - "You have fixed part of the microwave." \ - ) - src.broken = 1 // Fix it a bit - else if(src.broken == 1 && O.is_wrench()) // If it's broken and they're doing the wrench - user.visible_message( \ - "\The [user] starts to fix part of the microwave.", \ - "You start to fix part of the microwave." \ - ) - if (do_after(user,20 * O.toolspeed)) - user.visible_message( \ - "\The [user] fixes the microwave.", \ - "You have fixed the microwave." \ - ) - src.icon_state = "mw" - src.broken = 0 // Fix it! - src.dirty = 0 // just to be sure - src.flags = OPENCONTAINER | NOREACT - else - to_chat(user, "It's broken!") - return 1 - else if(default_deconstruction_screwdriver(user, O)) - return - else if(default_deconstruction_crowbar(user, O)) - return - else if(default_unfasten_wrench(user, O, 10)) - return - - else if(src.dirty==100) // The microwave is all dirty so can't be used! - if(istype(O, /obj/item/weapon/reagent_containers/spray/cleaner) || istype(O, /obj/item/weapon/soap)) // If they're trying to clean it then let them - user.visible_message( \ - "\The [user] starts to clean the microwave.", \ - "You start to clean the microwave." \ - ) - if (do_after(user,20)) - user.visible_message( \ - "\The [user] has cleaned the microwave.", \ - "You have cleaned the microwave." \ - ) - src.dirty = 0 // It's clean! - src.broken = 0 // just to be sure - src.icon_state = "mw" - src.flags = OPENCONTAINER | NOREACT - else //Otherwise bad luck!! - to_chat(user, "It's dirty!") - return 1 - else if(is_type_in_list(O,acceptable_items)) - if (contents.len>=(max_n_of_items + component_parts.len + 1)) //Adds component_parts to the maximum number of items. The 1 is from the circuit - to_chat(user, "This [src] is full of ingredients, you cannot put more.") - return 1 - if(istype(O, /obj/item/stack) && O:get_amount() > 1) // This is bad, but I can't think of how to change it - var/obj/item/stack/S = O - new O.type (src) - S.use(1) - user.visible_message( \ - "\The [user] has added one of [O] to \the [src].", \ - "You add one of [O] to \the [src].") - return - else - // user.remove_from_mob(O) //This just causes problems so far as I can tell. -Pete - user.drop_item() - O.loc = src - user.visible_message( \ - "\The [user] has added \the [O] to \the [src].", \ - "You add \the [O] to \the [src].") - return - else if(istype(O,/obj/item/weapon/reagent_containers/glass) || \ - istype(O,/obj/item/weapon/reagent_containers/food/drinks) || \ - istype(O,/obj/item/weapon/reagent_containers/food/condiment) \ - ) - if (!O.reagents) - return 1 - for (var/datum/reagent/R in O.reagents.reagent_list) - if (!(R.id in acceptable_reagents)) - to_chat(user, "Your [O] contains components unsuitable for cookery.") - return 1 - return - else if(istype(O,/obj/item/weapon/grab)) - var/obj/item/weapon/grab/G = O - to_chat(user, "This is ridiculous. You can not fit \the [G.affecting] in this [src].") - return 1 - else - to_chat(user, "You have no idea what you can cook with this [O].") - ..() - src.updateUsrDialog() - -/obj/machinery/microwave/attack_ai(mob/user as mob) - if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) - attack_hand(user) - -/obj/machinery/microwave/attack_hand(mob/user as mob) - user.set_machine(src) - interact(user) - -/******************* -* Microwave Menu -********************/ - -/obj/machinery/microwave/interact(mob/user as mob) // The microwave Menu - var/dat = "" - if(src.broken > 0) - dat = {"Bzzzzttttt"} - else if(src.operating) - dat = {"Microwaving in progress!
Please wait...!
"} - else if(src.dirty==100) - dat = {"This microwave is dirty!
Please clean it before use!
"} - else - var/list/items_counts = new - var/list/items_measures = new - var/list/items_measures_p = new - for (var/obj/O in ((contents - component_parts) - circuit)) - var/display_name = O.name - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg)) - items_measures[display_name] = "egg" - items_measures_p[display_name] = "eggs" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu)) - items_measures[display_name] = "tofu chunk" - items_measures_p[display_name] = "tofu chunks" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat - items_measures[display_name] = "slab of meat" - items_measures_p[display_name] = "slabs of meat" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket)) - display_name = "Turnovers" - items_measures[display_name] = "turnover" - items_measures_p[display_name] = "turnovers" - if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat)) - items_measures[display_name] = "fillet of meat" - items_measures_p[display_name] = "fillets of meat" - items_counts[display_name]++ - for (var/O in items_counts) - var/N = items_counts[O] - if (!(O in items_measures)) - dat += {"[capitalize(O)]: [N] [lowertext(O)]\s
"} - else - if (N==1) - dat += {"[capitalize(O)]: [N] [items_measures[O]]
"} - else - dat += {"[capitalize(O)]: [N] [items_measures_p[O]]
"} - - for (var/datum/reagent/R in reagents.reagent_list) - var/display_name = R.name - if (R.id == "capsaicin") - display_name = "Hotsauce" - if (R.id == "frostoil") - display_name = "Coldsauce" - dat += {"[display_name]: [R.volume] unit\s
"} - - if (items_counts.len==0 && reagents.reagent_list.len==0) - dat = {"The microwave is empty
"} - else - dat = {"Ingredients:
[dat]"} - dat += {"

\ -Turn on!
\ -
Eject ingredients!
\ -"} - - to_chat(user, browse("Microwave Controls[dat]", "window=microwave")) - onclose(user, "microwave") - return - - - -/*********************************** -* Microwave Menu Handling/Cooking -************************************/ - -/obj/machinery/microwave/proc/cook() - if(stat & (NOPOWER|BROKEN)) - return - start() - if (reagents.total_volume==0 && !(locate(/obj) in ((contents - component_parts) - circuit))) //dry run - if (!wzhzhzh(10)) - abort() - return - stop() - return - - var/datum/recipe/recipe = select_recipe(available_recipes,src) - var/obj/cooked - if (!recipe) - dirty += 1 - if (prob(max(10,dirty*5))) - if (!wzhzhzh(4)) - abort() - return - muck_start() - wzhzhzh(4) - muck_finish() - cooked = fail() - cooked.loc = src.loc - return - else if (has_extra_item()) - if (!wzhzhzh(4)) - abort() - return - broke() - cooked = fail() - cooked.loc = src.loc - return - else - if (!wzhzhzh(10)) - abort() - return - stop() - cooked = fail() - cooked.loc = src.loc - return - else - var/halftime = round(recipe.time/10/2) - if (!wzhzhzh(halftime)) - abort() - return - if (!wzhzhzh(halftime)) - abort() - cooked = fail() - cooked.loc = src.loc - return - cooked = recipe.make_food(src) - stop() - if(cooked) - cooked.loc = src.loc - return - -/obj/machinery/microwave/proc/wzhzhzh(var/seconds as num) // Whoever named this proc is fucking literally Satan. ~ Z - for (var/i=1 to seconds) - if (stat & (NOPOWER|BROKEN)) - return 0 - use_power(500) - sleep(10) - return 1 - -/obj/machinery/microwave/proc/has_extra_item() - for (var/obj/O in ((contents - component_parts) - circuit)) - if ( \ - !istype(O,/obj/item/weapon/reagent_containers/food) && \ - !istype(O, /obj/item/weapon/grown) \ - ) - return 1 - return 0 - -/obj/machinery/microwave/proc/start() - src.visible_message("The microwave turns on.", "You hear a microwave.") - src.operating = 1 - src.icon_state = "mw1" - src.updateUsrDialog() - -/obj/machinery/microwave/proc/abort() - src.operating = 0 // Turn it off again aferwards - src.icon_state = "mw" - src.updateUsrDialog() - -/obj/machinery/microwave/proc/stop() - playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) - src.operating = 0 // Turn it off again aferwards - src.icon_state = "mw" - src.updateUsrDialog() - -/obj/machinery/microwave/proc/dispose() - for (var/obj/O in ((contents-component_parts)-circuit)) - O.loc = src.loc - if (src.reagents.total_volume) - src.dirty++ - src.reagents.clear_reagents() - usr << "You dispose of the microwave contents." - src.updateUsrDialog() - -/obj/machinery/microwave/proc/muck_start() - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) // Play a splat sound - src.icon_state = "mwbloody1" // Make it look dirty!! - -/obj/machinery/microwave/proc/muck_finish() - playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) - src.visible_message("The microwave gets covered in muck!") - src.dirty = 100 // Make it dirty so it can't be used util cleaned - src.flags = null //So you can't add condiments - src.icon_state = "mwbloody" // Make it look dirty too - src.operating = 0 // Turn it off again aferwards - src.updateUsrDialog() - -/obj/machinery/microwave/proc/broke() - var/datum/effect/effect/system/spark_spread/s = new - s.set_up(2, 1, src) - s.start() - src.icon_state = "mwb" // Make it look all busted up and shit - src.visible_message("The microwave breaks!") //Let them know they're stupid - src.broken = 2 // Make it broken so it can't be used util fixed - src.flags = null //So you can't add condiments - src.operating = 0 // Turn it off again aferwards - src.updateUsrDialog() - -/obj/machinery/microwave/proc/fail() - var/obj/item/weapon/reagent_containers/food/snacks/badrecipe/ffuu = new(src) - var/amount = 0 - for (var/obj/O in (((contents - ffuu) - component_parts) - circuit)) - amount++ - if (O.reagents) - var/id = O.reagents.get_master_reagent_id() - if (id) - amount+=O.reagents.get_reagent_amount(id) - qdel(O) - src.reagents.clear_reagents() - ffuu.reagents.add_reagent("carbon", amount) - ffuu.reagents.add_reagent("toxin", amount/10) - return ffuu - -/obj/machinery/microwave/Topic(href, href_list) - if(..()) - return - - usr.set_machine(src) - if(src.operating) - src.updateUsrDialog() - return - - switch(href_list["action"]) - if ("cook") - cook() - - if ("dispose") - dispose() - return +/obj/machinery/microwave + name = "microwave" + icon = 'icons/obj/kitchen.dmi' + icon_state = "mw" + density = 1 + anchored = 1 + use_power = 1 + idle_power_usage = 5 + active_power_usage = 100 + flags = OPENCONTAINER | NOREACT + circuit = /obj/item/weapon/circuitboard/microwave + var/operating = 0 // Is it on? + var/dirty = 0 // = {0..100} Does it need cleaning? + var/broken = 0 // ={0,1,2} How broken is it??? + var/global/list/datum/recipe/available_recipes // List of the recipes you can use + var/global/list/acceptable_items // List of the items you can put in + var/global/list/acceptable_reagents // List of the reagents you can put in + var/global/max_n_of_items = 0 + + +// see code/modules/food/recipes_microwave.dm for recipes + +/******************* +* Initialising +********************/ + +/obj/machinery/microwave/New() + ..() + reagents = new/datum/reagents(100) + reagents.my_atom = src + + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/console_screen(src) + component_parts += new /obj/item/weapon/stock_parts/motor(src) + component_parts += new /obj/item/weapon/stock_parts/capacitor(src) + + if (!available_recipes) + available_recipes = new + for (var/type in (typesof(/datum/recipe)-/datum/recipe)) + available_recipes+= new type + acceptable_items = new + acceptable_reagents = new + for (var/datum/recipe/recipe in available_recipes) + for (var/item in recipe.items) + acceptable_items |= item + for (var/reagent in recipe.reagents) + acceptable_reagents |= reagent + if (recipe.items) + max_n_of_items = max(max_n_of_items,recipe.items.len) + // This will do until I can think of a fun recipe to use dionaea in - + // will also allow anything using the holder item to be microwaved into + // impure carbon. ~Z + acceptable_items |= /obj/item/weapon/holder + acceptable_items |= /obj/item/weapon/reagent_containers/food/snacks/grown + + RefreshParts() + +/******************* +* Item Adding +********************/ + +/obj/machinery/microwave/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(src.broken > 0) + if(src.broken == 2 && O.is_screwdriver()) // If it's broken and they're using a screwdriver + user.visible_message( \ + "\The [user] starts to fix part of the microwave.", \ + "You start to fix part of the microwave." \ + ) + playsound(src, O.usesound, 50, 1) + if (do_after(user,20 * O.toolspeed)) + user.visible_message( \ + "\The [user] fixes part of the microwave.", \ + "You have fixed part of the microwave." \ + ) + src.broken = 1 // Fix it a bit + else if(src.broken == 1 && O.is_wrench()) // If it's broken and they're doing the wrench + user.visible_message( \ + "\The [user] starts to fix part of the microwave.", \ + "You start to fix part of the microwave." \ + ) + if (do_after(user,20 * O.toolspeed)) + user.visible_message( \ + "\The [user] fixes the microwave.", \ + "You have fixed the microwave." \ + ) + src.icon_state = "mw" + src.broken = 0 // Fix it! + src.dirty = 0 // just to be sure + src.flags = OPENCONTAINER | NOREACT + else + to_chat(user, "It's broken!") + return 1 + else if(default_deconstruction_screwdriver(user, O)) + return + else if(default_deconstruction_crowbar(user, O)) + return + else if(default_unfasten_wrench(user, O, 10)) + return + + else if(src.dirty==100) // The microwave is all dirty so can't be used! + if(istype(O, /obj/item/weapon/reagent_containers/spray/cleaner) || istype(O, /obj/item/weapon/soap)) // If they're trying to clean it then let them + user.visible_message( \ + "\The [user] starts to clean the microwave.", \ + "You start to clean the microwave." \ + ) + if (do_after(user,20)) + user.visible_message( \ + "\The [user] has cleaned the microwave.", \ + "You have cleaned the microwave." \ + ) + src.dirty = 0 // It's clean! + src.broken = 0 // just to be sure + src.icon_state = "mw" + src.flags = OPENCONTAINER | NOREACT + else //Otherwise bad luck!! + to_chat(user, "It's dirty!") + return 1 + else if(is_type_in_list(O,acceptable_items)) + if (contents.len>=(max_n_of_items + component_parts.len + 1)) //Adds component_parts to the maximum number of items. The 1 is from the circuit + to_chat(user, "This [src] is full of ingredients, you cannot put more.") + return 1 + if(istype(O, /obj/item/stack) && O:get_amount() > 1) // This is bad, but I can't think of how to change it + var/obj/item/stack/S = O + new O.type (src) + S.use(1) + user.visible_message( \ + "\The [user] has added one of [O] to \the [src].", \ + "You add one of [O] to \the [src].") + return + else + // user.remove_from_mob(O) //This just causes problems so far as I can tell. -Pete + user.drop_item() + O.loc = src + user.visible_message( \ + "\The [user] has added \the [O] to \the [src].", \ + "You add \the [O] to \the [src].") + return + else if(istype(O,/obj/item/weapon/reagent_containers/glass) || \ + istype(O,/obj/item/weapon/reagent_containers/food/drinks) || \ + istype(O,/obj/item/weapon/reagent_containers/food/condiment) \ + ) + if (!O.reagents) + return 1 + for (var/datum/reagent/R in O.reagents.reagent_list) + if (!(R.id in acceptable_reagents)) + to_chat(user, "Your [O] contains components unsuitable for cookery.") + return 1 + return + else if(istype(O,/obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = O + to_chat(user, "This is ridiculous. You can not fit \the [G.affecting] in this [src].") + return 1 + else + to_chat(user, "You have no idea what you can cook with this [O].") + ..() + src.updateUsrDialog() + +/obj/machinery/microwave/attack_ai(mob/user as mob) + if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) + attack_hand(user) + +/obj/machinery/microwave/attack_hand(mob/user as mob) + user.set_machine(src) + interact(user) + +/******************* +* Microwave Menu +********************/ + +/obj/machinery/microwave/interact(mob/user as mob) // The microwave Menu + var/dat = "" + if(src.broken > 0) + dat = {"Bzzzzttttt"} + else if(src.operating) + dat = {"Microwaving in progress!
Please wait...!
"} + else if(src.dirty==100) + dat = {"This microwave is dirty!
Please clean it before use!
"} + else + var/list/items_counts = new + var/list/items_measures = new + var/list/items_measures_p = new + for (var/obj/O in ((contents - component_parts) - circuit)) + var/display_name = O.name + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg)) + items_measures[display_name] = "egg" + items_measures_p[display_name] = "eggs" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu)) + items_measures[display_name] = "tofu chunk" + items_measures_p[display_name] = "tofu chunks" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat + items_measures[display_name] = "slab of meat" + items_measures_p[display_name] = "slabs of meat" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket)) + display_name = "Turnovers" + items_measures[display_name] = "turnover" + items_measures_p[display_name] = "turnovers" + if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat)) + items_measures[display_name] = "fillet of meat" + items_measures_p[display_name] = "fillets of meat" + items_counts[display_name]++ + for (var/O in items_counts) + var/N = items_counts[O] + if (!(O in items_measures)) + dat += {"[capitalize(O)]: [N] [lowertext(O)]\s
"} + else + if (N==1) + dat += {"[capitalize(O)]: [N] [items_measures[O]]
"} + else + dat += {"[capitalize(O)]: [N] [items_measures_p[O]]
"} + + for (var/datum/reagent/R in reagents.reagent_list) + var/display_name = R.name + if (R.id == "capsaicin") + display_name = "Hotsauce" + if (R.id == "frostoil") + display_name = "Coldsauce" + dat += {"[display_name]: [R.volume] unit\s
"} + + if (items_counts.len==0 && reagents.reagent_list.len==0) + dat = {"The microwave is empty
"} + else + dat = {"Ingredients:
[dat]"} + dat += {"

\ +
Turn on!
\ +
Eject ingredients!
\ +"} + + to_chat(user, browse("Microwave Controls[dat]", "window=microwave")) + onclose(user, "microwave") + return + + + +/*********************************** +* Microwave Menu Handling/Cooking +************************************/ + +/obj/machinery/microwave/proc/cook() + if(stat & (NOPOWER|BROKEN)) + return + start() + if (reagents.total_volume==0 && !(locate(/obj) in ((contents - component_parts) - circuit))) //dry run + if (!wzhzhzh(10)) + abort() + return + stop() + return + + var/datum/recipe/recipe = select_recipe(available_recipes,src) + var/obj/cooked + if (!recipe) + dirty += 1 + if (prob(max(10,dirty*5))) + if (!wzhzhzh(4)) + abort() + return + muck_start() + wzhzhzh(4) + muck_finish() + cooked = fail() + cooked.loc = src.loc + return + else if (has_extra_item()) + if (!wzhzhzh(4)) + abort() + return + broke() + cooked = fail() + cooked.loc = src.loc + return + else + if (!wzhzhzh(10)) + abort() + return + stop() + cooked = fail() + cooked.loc = src.loc + return + else + var/halftime = round(recipe.time/10/2) + if (!wzhzhzh(halftime)) + abort() + return + if (!wzhzhzh(halftime)) + abort() + cooked = fail() + cooked.loc = src.loc + return + cooked = recipe.make_food(src) + stop() + if(cooked) + cooked.loc = src.loc + return + +/obj/machinery/microwave/proc/wzhzhzh(var/seconds as num) // Whoever named this proc is fucking literally Satan. ~ Z + for (var/i=1 to seconds) + if (stat & (NOPOWER|BROKEN)) + return 0 + use_power(500) + sleep(10) + return 1 + +/obj/machinery/microwave/proc/has_extra_item() + for (var/obj/O in ((contents - component_parts) - circuit)) + if ( \ + !istype(O,/obj/item/weapon/reagent_containers/food) && \ + !istype(O, /obj/item/weapon/grown) \ + ) + return 1 + return 0 + +/obj/machinery/microwave/proc/start() + src.visible_message("The microwave turns on.", "You hear a microwave.") + src.operating = 1 + src.icon_state = "mw1" + src.updateUsrDialog() + +/obj/machinery/microwave/proc/abort() + src.operating = 0 // Turn it off again aferwards + src.icon_state = "mw" + src.updateUsrDialog() + +/obj/machinery/microwave/proc/stop() + playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) + src.operating = 0 // Turn it off again aferwards + src.icon_state = "mw" + src.updateUsrDialog() + +/obj/machinery/microwave/proc/dispose() + for (var/obj/O in ((contents-component_parts)-circuit)) + O.loc = src.loc + if (src.reagents.total_volume) + src.dirty++ + src.reagents.clear_reagents() + usr << "You dispose of the microwave contents." + src.updateUsrDialog() + +/obj/machinery/microwave/proc/muck_start() + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) // Play a splat sound + src.icon_state = "mwbloody1" // Make it look dirty!! + +/obj/machinery/microwave/proc/muck_finish() + playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) + src.visible_message("The microwave gets covered in muck!") + src.dirty = 100 // Make it dirty so it can't be used util cleaned + src.flags = null //So you can't add condiments + src.icon_state = "mwbloody" // Make it look dirty too + src.operating = 0 // Turn it off again aferwards + src.updateUsrDialog() + +/obj/machinery/microwave/proc/broke() + var/datum/effect/effect/system/spark_spread/s = new + s.set_up(2, 1, src) + s.start() + src.icon_state = "mwb" // Make it look all busted up and shit + src.visible_message("The microwave breaks!") //Let them know they're stupid + src.broken = 2 // Make it broken so it can't be used util fixed + src.flags = null //So you can't add condiments + src.operating = 0 // Turn it off again aferwards + src.updateUsrDialog() + +/obj/machinery/microwave/proc/fail() + var/obj/item/weapon/reagent_containers/food/snacks/badrecipe/ffuu = new(src) + var/amount = 0 + for (var/obj/O in (((contents - ffuu) - component_parts) - circuit)) + amount++ + if (O.reagents) + var/id = O.reagents.get_master_reagent_id() + if (id) + amount+=O.reagents.get_reagent_amount(id) + qdel(O) + src.reagents.clear_reagents() + ffuu.reagents.add_reagent("carbon", amount) + ffuu.reagents.add_reagent("toxin", amount/10) + return ffuu + +/obj/machinery/microwave/Topic(href, href_list) + if(..()) + return + + usr.set_machine(src) + if(src.operating) + src.updateUsrDialog() + return + + switch(href_list["action"]) + if ("cook") + cook() + + if ("dispose") + dispose() + return diff --git a/code/modules/food/kitchen/smartfridge.dm b/code/modules/food/kitchen/smartfridge.dm index 005f7749b6..b7c3ac443f 100644 --- a/code/modules/food/kitchen/smartfridge.dm +++ b/code/modules/food/kitchen/smartfridge.dm @@ -10,7 +10,11 @@ idle_power_usage = 5 active_power_usage = 100 flags = NOREACT +<<<<<<< HEAD var/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000. //VOREStation Edit - Non-global +======= + var/global/max_n_of_items = 999 // Sorry but the BYOND infinite loop detector doesn't look things over 1000. +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync var/icon_on = "smartfridge" var/icon_off = "smartfridge-off" var/icon_panel = "smartfridge-panel" diff --git a/code/modules/gamemaster/actions/carp_migration.dm b/code/modules/gamemaster/actions/carp_migration.dm index 7c76a5119c..7bf7d80ee3 100644 --- a/code/modules/gamemaster/actions/carp_migration.dm +++ b/code/modules/gamemaster/actions/carp_migration.dm @@ -52,12 +52,12 @@ while (i <= carp_amount) var/group_size = rand(group_size_min, group_size_max) for (var/j = 1, j <= group_size, j++) - spawned_carp.Add(new /mob/living/simple_animal/hostile/carp(spawn_locations[i])) + spawned_carp.Add(new /mob/living/simple_mob/animal/space/carp/event(spawn_locations[i])) i++ message_admins("[spawned_carp.len] carp spawned by event.") /datum/gm_action/carp_migration/end() - for(var/mob/living/simple_animal/hostile/carp/C in spawned_carp) + for(var/mob/living/simple_mob/animal/space/carp/C in spawned_carp) if(!C.stat) var/turf/T = get_turf(C) if(istype(T, /turf/space)) diff --git a/code/modules/gamemaster/actions/surprise_carp_attack.dm b/code/modules/gamemaster/actions/surprise_carp_attack.dm index e78dd72343..14c4b03e0e 100644 --- a/code/modules/gamemaster/actions/surprise_carp_attack.dm +++ b/code/modules/gamemaster/actions/surprise_carp_attack.dm @@ -47,7 +47,5 @@ spawning_turf = space break if(spawning_turf) - var/mob/living/simple_animal/hostile/carp/C = new(spawning_turf) - C.target_mob = victim - C.stance = STANCE_ATTACK + new /mob/living/simple_mob/animal/space/carp(spawning_turf) number_of_carp-- \ No newline at end of file diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm index cbeff5fb34..f5b6b28e11 100644 --- a/code/modules/games/cards.dm +++ b/code/modules/games/cards.dm @@ -238,7 +238,7 @@ /obj/item/weapon/deck/MouseDrop(mob/user as mob) // Code from Paper bin, so you can still pick up the deck if((user == usr && (!( usr.restrained() ) && (!( usr.stat ) && (usr.contents.Find(src) || in_range(src, usr)))))) - if(!istype(usr, /mob/living/simple_animal)) + if(!istype(usr, /mob/living/simple_mob)) if( !usr.get_active_hand() ) //if active hand is empty var/mob/living/carbon/human/H = user var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] @@ -256,7 +256,7 @@ /obj/item/weapon/deck/verb_pickup(mob/user as mob) // Snowflaked so pick up verb work as intended if((user == usr && (!( usr.restrained() ) && (!( usr.stat ) && (usr.contents.Find(src) || in_range(src, usr)))))) - if(!istype(usr, /mob/living/simple_animal)) + if(!istype(usr, /mob/living/simple_mob)) if( !usr.get_active_hand() ) //if active hand is empty var/mob/living/carbon/human/H = user var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm index e5c9b81d99..845b4701bd 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/code/modules/holodeck/HolodeckControl.dm @@ -177,7 +177,7 @@ for(var/obj/item/weapon/holo/esword/H in linkedholodeck) H.damtype = initial(H.damtype) - for(var/mob/living/simple_animal/hostile/carp/holodeck/C in holographic_mobs) + for(var/mob/living/simple_mob/animal/space/carp/holodeck/C in holographic_mobs) C.set_safety(!safety_disabled) if (last_to_emag) C.friends = list(last_to_emag) @@ -210,7 +210,7 @@ derez(item, 0) if (!safety_disabled) - for(var/mob/living/simple_animal/hostile/carp/holodeck/C in holographic_mobs) + for(var/mob/living/simple_mob/animal/space/carp/holodeck/C in holographic_mobs) if (get_area(C.loc) != linkedholodeck) holographic_mobs -= C C.derez() @@ -306,7 +306,7 @@ for(var/item in holographic_objs) derez(item) - for(var/mob/living/simple_animal/hostile/carp/holodeck/C in holographic_mobs) + for(var/mob/living/simple_mob/animal/space/carp/holodeck/C in holographic_mobs) holographic_mobs -= C C.derez() @@ -340,11 +340,11 @@ T.temperature = 5000 T.hotspot_expose(50000,50000,1) if(L.name=="Holocarp Spawn") - holographic_mobs += new /mob/living/simple_animal/hostile/carp/holodeck(L.loc) + holographic_mobs += new /mob/living/simple_mob/animal/space/carp/holodeck(L.loc) if(L.name=="Holocarp Spawn Random") if (prob(4)) //With 4 spawn points, carp should only appear 15% of the time. - holographic_mobs += new /mob/living/simple_animal/hostile/carp/holodeck(L.loc) + holographic_mobs += new /mob/living/simple_mob/animal/space/carp/holodeck(L.loc) update_projections() diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index 8e6d20eab9..8dd32997a7 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -419,7 +419,7 @@ //Holocarp -/mob/living/simple_animal/hostile/carp/holodeck +/mob/living/simple_mob/animal/space/carp/holodeck icon = 'icons/mob/AI.dmi' icon_state = "holo4" icon_living = "holo4" @@ -429,31 +429,27 @@ meat_amount = 0 meat_type = null -/mob/living/simple_animal/hostile/carp/holodeck/New() +/mob/living/simple_mob/animal/space/carp/holodeck/New() ..() set_light(2) //hologram lighting -/mob/living/simple_animal/hostile/carp/holodeck/proc/set_safety(var/safe) +/mob/living/simple_mob/animal/space/carp/holodeck/proc/set_safety(var/safe) if (safe) faction = "neutral" melee_damage_lower = 0 melee_damage_upper = 0 - environment_smash = 0 - destroy_surroundings = 0 else faction = "carp" melee_damage_lower = initial(melee_damage_lower) melee_damage_upper = initial(melee_damage_upper) - environment_smash = initial(environment_smash) - destroy_surroundings = initial(destroy_surroundings) -/mob/living/simple_animal/hostile/carp/holodeck/gib() +/mob/living/simple_mob/animal/space/carp/holodeck/gib() derez() //holograms can't gib -/mob/living/simple_animal/hostile/carp/holodeck/death() +/mob/living/simple_mob/animal/space/carp/holodeck/death() ..() derez() -/mob/living/simple_animal/hostile/carp/holodeck/proc/derez() +/mob/living/simple_mob/animal/space/carp/holodeck/proc/derez() visible_message("\The [src] fades away!") qdel(src) diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 5c6053aba4..0118088b27 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -104,10 +104,10 @@ return if(!istype(target)) - if(istype(target, /mob/living/simple_animal/mouse)) + if(istype(target, /mob/living/simple_mob/animal/passive/mouse)) new /obj/effect/decal/remains/mouse(get_turf(target)) qdel(target) - else if(istype(target, /mob/living/simple_animal/lizard)) + else if(istype(target, /mob/living/simple_mob/animal/passive/lizard)) new /obj/effect/decal/remains/lizard(get_turf(target)) qdel(target) return diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm index b70a02af4c..eab78597b1 100644 --- a/code/modules/hydroponics/seed_datums.dm +++ b/code/modules/hydroponics/seed_datums.dm @@ -184,7 +184,7 @@ display_name = "killer tomato plant" mutants = null can_self_harvest = 1 - has_mob_product = /mob/living/simple_animal/hostile/tomato + has_mob_product = /mob/living/simple_mob/tomato /datum/seed/tomato/killer/New() ..() diff --git a/code/modules/integrated_electronics/core/assemblies/device.dm b/code/modules/integrated_electronics/core/assemblies/device.dm index 63e0bef562..bcdd0e9bea 100644 --- a/code/modules/integrated_electronics/core/assemblies/device.dm +++ b/code/modules/integrated_electronics/core/assemblies/device.dm @@ -1,84 +1,84 @@ -/obj/item/device/assembly/electronic_assembly - name = "electronic device" - desc = "It's a case for building electronics with. It can be attached to other small devices." - icon_state = "setup_device" - var/opened = 0 - - var/obj/item/device/electronic_assembly/device/EA - -/obj/item/device/assembly/electronic_assembly/New() - EA = new(src) - EA.holder = src - ..() - -/obj/item/device/assembly/electronic_assembly/attackby(obj/item/I as obj, mob/user as mob) - if (I.is_crowbar()) - toggle_open(user) - else if (opened) - EA.attackby(I, user) - else - ..() - -/obj/item/device/assembly/electronic_assembly/proc/toggle_open(mob/user) - playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) - opened = !opened - EA.opened = opened - to_chat(user, "You [opened ? "opened" : "closed"] \the [src].") - secured = 1 - update_icon() - -/obj/item/device/assembly/electronic_assembly/update_icon() - if(EA) - icon_state = initial(icon_state) - else - icon_state = initial(icon_state)+"0" - if(opened) - icon_state = icon_state + "-open" - -/obj/item/device/assembly/electronic_assembly/attack_self(mob/user as mob) - if(EA) - EA.attack_self(user) - -/obj/item/device/assembly/electronic_assembly/pulsed(var/radio = 0) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs - if(EA) - for(var/obj/item/integrated_circuit/built_in/device_input/I in EA.contents) - I.do_work() - return - -/obj/item/device/assembly/electronic_assembly/examine(mob/user) - .=..(user, 1) - if(EA) - for(var/obj/item/integrated_circuit/IC in EA.contents) - IC.external_examine(user) - -/obj/item/device/assembly/electronic_assembly/verb/toggle() - set src in usr - set category = "Object" - set name = "Open/Close Device Assembly" - set desc = "Open or close device assembly!" - - toggle_open(usr) - - -/obj/item/device/electronic_assembly/device - name = "electronic device" - icon_state = "setup_device" - desc = "It's a tiny electronic device with specific use for attaching to other devices." - var/obj/item/device/assembly/electronic_assembly/holder - w_class = ITEMSIZE_TINY - max_components = IC_COMPONENTS_BASE * 3/4 - max_complexity = IC_COMPLEXITY_BASE * 3/4 - - -/obj/item/device/electronic_assembly/device/New() - ..() - var/obj/item/integrated_circuit/built_in/device_input/input = new(src) - var/obj/item/integrated_circuit/built_in/device_output/output = new(src) - input.assembly = src - output.assembly = src - -/obj/item/device/electronic_assembly/device/check_interactivity(mob/user) - if(!CanInteract(user, state = deep_inventory_state)) - return 0 - return 1 - +/obj/item/device/assembly/electronic_assembly + name = "electronic device" + desc = "It's a case for building electronics with. It can be attached to other small devices." + icon_state = "setup_device" + var/opened = 0 + + var/obj/item/device/electronic_assembly/device/EA + +/obj/item/device/assembly/electronic_assembly/New() + EA = new(src) + EA.holder = src + ..() + +/obj/item/device/assembly/electronic_assembly/attackby(obj/item/I as obj, mob/user as mob) + if (I.is_crowbar()) + toggle_open(user) + else if (opened) + EA.attackby(I, user) + else + ..() + +/obj/item/device/assembly/electronic_assembly/proc/toggle_open(mob/user) + playsound(get_turf(src), 'sound/items/Crowbar.ogg', 50, 1) + opened = !opened + EA.opened = opened + to_chat(user, "You [opened ? "opened" : "closed"] \the [src].") + secured = 1 + update_icon() + +/obj/item/device/assembly/electronic_assembly/update_icon() + if(EA) + icon_state = initial(icon_state) + else + icon_state = initial(icon_state)+"0" + if(opened) + icon_state = icon_state + "-open" + +/obj/item/device/assembly/electronic_assembly/attack_self(mob/user as mob) + if(EA) + EA.attack_self(user) + +/obj/item/device/assembly/electronic_assembly/pulsed(var/radio = 0) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs + if(EA) + for(var/obj/item/integrated_circuit/built_in/device_input/I in EA.contents) + I.do_work() + return + +/obj/item/device/assembly/electronic_assembly/examine(mob/user) + .=..(user, 1) + if(EA) + for(var/obj/item/integrated_circuit/IC in EA.contents) + IC.external_examine(user) + +/obj/item/device/assembly/electronic_assembly/verb/toggle() + set src in usr + set category = "Object" + set name = "Open/Close Device Assembly" + set desc = "Open or close device assembly!" + + toggle_open(usr) + + +/obj/item/device/electronic_assembly/device + name = "electronic device" + icon_state = "setup_device" + desc = "It's a tiny electronic device with specific use for attaching to other devices." + var/obj/item/device/assembly/electronic_assembly/holder + w_class = ITEMSIZE_TINY + max_components = IC_COMPONENTS_BASE * 3/4 + max_complexity = IC_COMPLEXITY_BASE * 3/4 + + +/obj/item/device/electronic_assembly/device/New() + ..() + var/obj/item/integrated_circuit/built_in/device_input/input = new(src) + var/obj/item/integrated_circuit/built_in/device_output/output = new(src) + input.assembly = src + output.assembly = src + +/obj/item/device/electronic_assembly/device/check_interactivity(mob/user) + if(!CanInteract(user, state = deep_inventory_state)) + return 0 + return 1 + diff --git a/code/modules/mob/_modifiers/auras.dm b/code/modules/mob/_modifiers/auras.dm new file mode 100644 index 0000000000..fcc023ee2e --- /dev/null +++ b/code/modules/mob/_modifiers/auras.dm @@ -0,0 +1,18 @@ +/* +'Aura' modifiers are semi-permanent, in that they do not have a set duration, but will expire if out of range of the 'source' of the aura. +Note: The source is defined as an argument in New(), and if not specified, it is assumed the holder is the source, +making it not expire ever, which is likely not what you want. +*/ + +/datum/modifier/aura + var/aura_max_distance = 5 // If more than this many tiles away from the source, the modifier expires next tick. + +/datum/modifier/aura/check_if_valid() + if(!origin) + expire() + var/atom/A = origin.resolve() + if(istype(A)) // Make sure we're not null. + if(get_dist(holder, A) > aura_max_distance) + expire() + else + expire() // Source got deleted or something. diff --git a/code/modules/mob/_modifiers/modifiers.dm b/code/modules/mob/_modifiers/modifiers.dm index 4f449b723b..115acb1b3e 100644 --- a/code/modules/mob/_modifiers/modifiers.dm +++ b/code/modules/mob/_modifiers/modifiers.dm @@ -149,6 +149,13 @@ /mob/living/proc/remove_specific_modifier(var/datum/modifier/M, var/silent = FALSE) M.expire(silent) +// Removes one modifier of a type +/mob/living/proc/remove_a_modifier_of_type(var/modifier_type, var/silent = FALSE) + for(var/datum/modifier/M in modifiers) + if(ispath(M.type, modifier_type)) + M.expire(silent) + break + // Removes all modifiers of a type /mob/living/proc/remove_modifiers_of_type(var/modifier_type, var/silent = FALSE) for(var/datum/modifier/M in modifiers) diff --git a/code/modules/mob/_modifiers/modifiers_misc.dm b/code/modules/mob/_modifiers/modifiers_misc.dm index fbe9e35624..af5be943d1 100644 --- a/code/modules/mob/_modifiers/modifiers_misc.dm +++ b/code/modules/mob/_modifiers/modifiers_misc.dm @@ -29,7 +29,7 @@ Berserk is a somewhat rare modifier to obtain freely (and for good reason), howe - Red Slimes will berserk if they go rabid. - Red slime core reactions will berserk slimes that can see the user in addition to making them go rabid. - Red slime core reactions will berserk prometheans that can see the user. -- Bears will berserk when losing a fight. +- Saviks will berserk when losing a fight. - Changelings can evolve a 2 point ability to use a changeling-specific variant of Berserk, that replaces the text with a 'we' variant. Recursive Enhancement allows the changeling to instead used an improved variant that features less exhaustion time and less nutrition drain. - Xenoarch artifacts may have forced berserking as one of their effects. This is especially fun if an artifact that makes hostile mobs is nearby. @@ -180,3 +180,70 @@ the artifact triggers the rage. accuracy = -75 // Aiming requires focus. accuracy_dispersion = 3 // Ditto. evasion = -45 // Too angry to dodge. + + + + +// Ignition, but confined to the modifier system. +// This makes it more predictable and thus, easier to balance. +/datum/modifier/fire + name = "on fire" + desc = "You are on fire! You will be harmed until the fire goes out or you extinguish it with water." + mob_overlay_state = "on_fire" + + on_created_text = "You combust into flames!" + on_expired_text = "The fire starts to fade." + stacks = MODIFIER_STACK_ALLOWED // Multiple instances will hurt a lot. + var/damage_per_tick = 5 + +/datum/modifier/fire/tick() + holder.inflict_heat_damage(damage_per_tick) + + +// Applied when near something very cold. +// Reduces mobility, attack speed. +/datum/modifier/chilled + name = "chilled" + desc = "You feel yourself freezing up. Its hard to move." + mob_overlay_state = "chilled" + + on_created_text = "You feel like you're going to freeze! It's hard to move." + on_expired_text = "You feel somewhat warmer and more mobile now." + stacks = MODIFIER_STACK_EXTEND + + slowdown = 2 + evasion = -40 + attack_speed_percent = 1.4 + disable_duration_percent = 1.2 + + +// Similar to being on fire, except poison tends to be more long term. +// Antitoxins will remove stacks over time. +// Synthetics can't receive this. +/datum/modifier/poisoned + name = "poisoned" + desc = "You have poison inside of you. It will cause harm over a long span of time if not cured." + mob_overlay_state = "poisoned" + + on_created_text = "You feel sick..." + on_expired_text = "You feel a bit better." + stacks = MODIFIER_STACK_ALLOWED // Multiple instances will hurt a lot. + var/damage_per_tick = 1 + +/datum/modifier/poisoned/weak + damage_per_tick = 0.5 + +/datum/modifier/poisoned/strong + damage_per_tick = 2 + +/datum/modifier/poisoned/tick() + if(holder.stat == DEAD) + expire(silent = TRUE) + holder.inflict_poison_damage(damage_per_tick) + +/datum/modifier/poisoned/can_apply(mob/living/L) + if(L.isSynthetic()) + return FALSE + if(L.get_poison_protection() >= 1) + return FALSE + return TRUE \ No newline at end of file diff --git a/code/modules/mob/_modifiers/traits_phobias.dm b/code/modules/mob/_modifiers/traits_phobias.dm index 0fbe099c22..bd30891fbe 100644 --- a/code/modules/mob/_modifiers/traits_phobias.dm +++ b/code/modules/mob/_modifiers/traits_phobias.dm @@ -207,8 +207,8 @@ if(istype(thing, /obj/structure/snowman/spider)) //Snow spiders are also spooky so people can be assholes with those too. fear_amount += 1 - if(istype(thing, /mob/living/simple_animal/hostile/giant_spider)) // Actual giant spiders are the scariest of them all. - var/mob/living/simple_animal/hostile/giant_spider/S = thing + if(istype(thing, /mob/living/simple_mob/animal/giant_spider)) // Actual giant spiders are the scariest of them all. + var/mob/living/simple_mob/animal/giant_spider/S = thing if(S.stat == DEAD) // Dead giant spiders are less scary than alive ones. fear_amount += 4 else @@ -425,14 +425,18 @@ if(istype(thing, /obj/item/clothing/head/collectable/slime)) // Some hats are spooky so people can be assholes with them. fear_amount += 1 - if(istype(thing, /mob/living/simple_animal/slime)) // An actual predatory specimen! - var/mob/living/simple_animal/slime/S = thing + if(istype(thing, /mob/living/simple_mob/slime)) // An actual predatory specimen! + var/mob/living/simple_mob/slime/S = thing if(S.stat == DEAD) // Dead slimes are somewhat less spook. fear_amount += 4 - if(S.is_adult == TRUE) //big boy - fear_amount += 8 + if(istype(S, /mob/living/simple_mob/slime/xenobio)) + var/mob/living/simple_mob/slime/xenobio/X = S + if(X.is_adult == TRUE) //big boy + fear_amount += 8 + else + fear_amount += 6 else - fear_amount += 6 + fear_amount += 10 // It's huge and feral. if(istype(thing, /mob/living/carbon/human)) var/mob/living/carbon/human/S = thing diff --git a/code/modules/mob/_modifiers/unholy.dm b/code/modules/mob/_modifiers/unholy.dm index cc4a23aa40..0eadabc83a 100644 --- a/code/modules/mob/_modifiers/unholy.dm +++ b/code/modules/mob/_modifiers/unholy.dm @@ -71,7 +71,7 @@ /datum/modifier/repair_aura/tick() spawn() - for(var/mob/living/simple_animal/construct/T in view(4,holder)) + for(var/mob/living/simple_mob/construct/T in view(4,holder)) T.adjustBruteLoss(rand(-10,-15)) T.adjustFireLoss(rand(-10,-15)) @@ -110,7 +110,7 @@ spawn() if(isliving(holder)) var/mob/living/L = holder - if(istype(L, /mob/living/simple_animal/construct)) + if(istype(L, /mob/living/simple_mob/construct)) L.adjustBruteLoss(rand(-5,-10)) L.adjustFireLoss(rand(-5,-10)) else diff --git a/code/modules/mob/animations.dm b/code/modules/mob/animations.dm index c5bded9765..9b9ce3957f 100644 --- a/code/modules/mob/animations.dm +++ b/code/modules/mob/animations.dm @@ -180,8 +180,33 @@ note dizziness decrements automatically in the mob's Life() proc. pixel_z = default_pixel_z alpha = initial_alpha -/atom/movable/proc/do_attack_animation(atom/A) +// Similar to attack animations, but in reverse and is longer to act as a telegraph. +/atom/movable/proc/do_windup_animation(atom/A, windup_time) + var/pixel_x_diff = 0 + var/pixel_y_diff = 0 + var/direction = get_dir(src, A) + if(direction & NORTH) + pixel_y_diff = -8 + else if(direction & SOUTH) + pixel_y_diff = 8 + if(direction & EAST) + pixel_x_diff = -8 + else if(direction & WEST) + pixel_x_diff = 8 + + var/default_pixel_x = initial(pixel_x) + var/default_pixel_y = initial(pixel_y) + var/mob/mob = src + if(istype(mob)) + default_pixel_x = mob.default_pixel_x + default_pixel_y = mob.default_pixel_y + + animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = windup_time - 2) + animate(pixel_x = default_pixel_x, pixel_y = default_pixel_y, time = 2) + + +/atom/movable/proc/do_attack_animation(atom/A) var/pixel_x_diff = 0 var/pixel_y_diff = 0 var/direction = get_dir(src, A) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 7345e7e9b1..fa021509fb 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -498,7 +498,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp //find a viable mouse candidate - var/mob/living/simple_animal/mouse/host + var/mob/living/simple_mob/animal/passive/mouse/host var/obj/machinery/atmospherics/unary/vent_pump/vent_found var/list/found_vents = list() for(var/obj/machinery/atmospherics/unary/vent_pump/v in machines) @@ -506,7 +506,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp found_vents.Add(v) if(found_vents.len) vent_found = pick(found_vents) - host = new /mob/living/simple_animal/mouse(vent_found) + host = new /mob/living/simple_mob/animal/passive/mouse(vent_found) else src << "Unable to find any unwelded vents to spawn mice at." diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 0ecdb2fc20..f67f70eb87 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -52,6 +52,13 @@ if(O) O.see_emote(src, message, m_type) +// Shortcuts for above proc +/mob/proc/visible_emote(var/act_desc) + custom_emote(1, act_desc) + +/mob/proc/audible_emote(var/act_desc) + custom_emote(2, act_desc) + /mob/proc/emote_dead(var/message) if(client.prefs.muted & MUTE_DEADCHAT) diff --git a/code/modules/mob/freelook/mask/chunk.dm b/code/modules/mob/freelook/mask/chunk.dm index f98a9b936b..b540a8023b 100644 --- a/code/modules/mob/freelook/mask/chunk.dm +++ b/code/modules/mob/freelook/mask/chunk.dm @@ -25,10 +25,10 @@ /mob/living/silicon/seen_cult_turfs() return list() -/mob/living/simple_animal/seen_cult_turfs() +/mob/living/simple_mob/seen_cult_turfs() return seen_turfs_in_range(src, 1) -/mob/living/simple_animal/shade/narsie/seen_cult_turfs() +/mob/living/simple_mob/construct/shade/seen_cult_turfs() return view(2, src) /proc/seen_turfs_in_range(var/source, var/range) diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 1e862472a2..e7c097c2b3 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -78,6 +78,12 @@ var/turf/source = speaker? get_turf(speaker) : get_turf(src) src.playsound_local(source, speech_sound, sound_vol, 1) +// Done here instead of on_hear_say() since that is NOT called if the mob is clientless (which includes most AI mobs). +/mob/living/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/alt_name = "",var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) + ..() + if(has_AI()) // Won't happen if no ai_holder exists or there's a player inside w/o autopilot active. + ai_holder.on_hear_say(speaker, message) + /mob/proc/on_hear_say(var/message) to_chat(src, message) if(teleop) @@ -158,17 +164,10 @@ if(!(language && (language.flags & INNATE))) // skip understanding checks for INNATE languages if(!say_understands(speaker,language)) - if(istype(speaker,/mob/living/simple_animal)) - var/mob/living/simple_animal/S = speaker - if(S.speak && S.speak.len) - message = pick(S.speak) - else - return + if(language) + message = language.scramble(message) else - if(language) - message = language.scramble(message) - else - message = stars(message) + message = stars(message) if(hard_to_hear) message = stars(message) diff --git a/code/modules/mob/language/outsider.dm b/code/modules/mob/language/outsider.dm index fdd719df89..de78a9c1b5 100644 --- a/code/modules/mob/language/outsider.dm +++ b/code/modules/mob/language/outsider.dm @@ -27,16 +27,16 @@ /datum/language/corticalborer/broadcast(var/mob/living/speaker,var/message,var/speaker_mask) - var/mob/living/simple_animal/borer/B + var/mob/living/simple_mob/animal/borer/B if(istype(speaker,/mob/living/carbon)) var/mob/living/carbon/M = speaker B = M.has_brain_worms() - else if(istype(speaker,/mob/living/simple_animal/borer)) + else if(istype(speaker,/mob/living/simple_mob/animal/borer)) B = speaker if(B) - speaker_mask = B.truename + speaker_mask = B.true_name ..(speaker,message,speaker_mask) /datum/language/vox diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index ba8c0945f0..23008d126a 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -215,7 +215,7 @@ else if(declare_arrests) var/action = arrest_type ? "detaining" : "arresting" - if(istype(target, /mob/living/simple_animal)) + if(istype(target, /mob/living/simple_mob)) action = "fighting" global_announcer.autosay("[src] is [action] a level [threat] [action != "fighting" ? "suspect" : "threat"] [target_name(target)] in [get_area(src)].", "[src]", "Security") UnarmedAttack(target) @@ -269,8 +269,8 @@ C.handcuffed = new /obj/item/weapon/handcuffs(C) C.update_inv_handcuffed() busy = 0 - else if(istype(M, /mob/living/simple_animal)) - var/mob/living/simple_animal/S = M + else if(istype(M, /mob/living/simple_mob)) + var/mob/living/simple_mob/S = M S.Weaken(xeno_stun_strength) S.adjustBruteLoss(xeno_harm_strength) do_attack_animation(M) @@ -286,8 +286,8 @@ /mob/living/bot/secbot/slime/UnarmedAttack(var/mob/living/L, var/proximity) ..() - if(istype(L, /mob/living/simple_animal/slime)) - var/mob/living/simple_animal/slime/S = L + if(istype(L, /mob/living/simple_mob/slime/xenobio)) + var/mob/living/simple_mob/slime/xenobio/S = L S.adjust_discipline(2) diff --git a/code/modules/mob/living/carbon/alien/diona/diona_powers.dm b/code/modules/mob/living/carbon/alien/diona/diona_powers.dm index 585b421c7f..aea0054418 100644 --- a/code/modules/mob/living/carbon/alien/diona/diona_powers.dm +++ b/code/modules/mob/living/carbon/alien/diona/diona_powers.dm @@ -63,5 +63,5 @@ if(istype(M)) for(var/atom/A in M.contents) - if(istype(A,/mob/living/simple_animal/borer) || istype(A,/obj/item/weapon/holder)) + if(istype(A,/mob/living/simple_mob/animal/borer) || istype(A,/obj/item/weapon/holder)) return diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index 7165c36fa8..561dd0c139 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -46,7 +46,7 @@ return 1 if (istype(other, /mob/living/carbon/human)) return 1 - if (istype(other, /mob/living/simple_animal/slime)) + if (istype(other, /mob/living/simple_mob/slime)) return 1 return ..() diff --git a/code/modules/mob/living/carbon/brain/life.dm b/code/modules/mob/living/carbon/brain/life.dm index b631e8ae13..4ab220083d 100644 --- a/code/modules/mob/living/carbon/brain/life.dm +++ b/code/modules/mob/living/carbon/brain/life.dm @@ -76,7 +76,6 @@ if(ingested) ingested.metabolize() if(bloodstr) bloodstr.metabolize() - AdjustConfused(-1) // decrement dizziness counter, clamped to 0 if(resting) dizziness = max(0, dizziness - 5) diff --git a/code/modules/mob/living/carbon/carbon_powers.dm b/code/modules/mob/living/carbon/carbon_powers.dm index f1360504ef..337f0b0ff1 100644 --- a/code/modules/mob/living/carbon/carbon_powers.dm +++ b/code/modules/mob/living/carbon/carbon_powers.dm @@ -5,7 +5,7 @@ set name = "Release Control" set desc = "Release control of your host's body." - var/mob/living/simple_animal/borer/B = has_brain_worms() + var/mob/living/simple_mob/animal/borer/B = has_brain_worms() if(B && B.host_brain) src << "You withdraw your probosci, releasing control of [B.host_brain]" @@ -25,7 +25,7 @@ set name = "Torment host" set desc = "Punish your host with agony." - var/mob/living/simple_animal/borer/B = has_brain_worms() + var/mob/living/simple_mob/animal/borer/B = has_brain_worms() if(!B) return @@ -43,7 +43,7 @@ set name = "Reproduce" set desc = "Spawn several young." - var/mob/living/simple_animal/borer/B = has_brain_worms() + var/mob/living/simple_mob/animal/borer/B = has_brain_worms() if(!B) return @@ -55,7 +55,7 @@ B.has_reproduced = 1 vomit(1) - new /mob/living/simple_animal/borer(get_turf(src)) + new /mob/living/simple_mob/animal/borer(get_turf(src)) else src << "You do not have enough chemicals stored to reproduce." diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 24a5bac586..2244cdac51 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -61,11 +61,11 @@ //Handle brain slugs. var/obj/item/organ/external/Hd = get_organ(BP_HEAD) - var/mob/living/simple_animal/borer/B + var/mob/living/simple_mob/animal/borer/B if(Hd) for(var/I in Hd.implants) - if(istype(I,/mob/living/simple_animal/borer)) + if(istype(I,/mob/living/simple_mob/animal/borer)) B = I if(B) if(!B.ckey && ckey && B.controlling) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index c5f259e0b4..57cb8d0f58 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -135,6 +135,27 @@ emp_act return siemens_coefficient +// Similar to above but is for the mob's overall protection, being the average of all slots. +/mob/living/carbon/human/proc/get_siemens_coefficient_average() + var/siemens_value = 0 + var/total = 0 + for(var/organ_name in organs_by_name) + if(organ_name in organ_rel_size) + var/obj/item/organ/external/organ = organs_by_name[organ_name] + if(organ) + var/weight = organ_rel_size[organ_name] + siemens_value += get_siemens_coefficient_organ(organ) * weight + total += weight + + if(fire_stacks < 0) // Water makes you more conductive. + siemens_value *= 1.5 + + return (siemens_value/max(total, 1)) + +// Returns a number between 0 to 1, with 1 being total protection. +/mob/living/carbon/human/get_shock_protection() + return between(0, 1-get_siemens_coefficient_average(), 1) + // Returns a list of clothing that is currently covering def_zone. /mob/living/carbon/human/proc/get_clothing_list_organ(var/obj/item/organ/external/def_zone, var/type) var/list/results = list() @@ -542,6 +563,20 @@ emp_act return perm +// This is for preventing harm by being covered in water, which only prometheans need to deal with. +// This is not actually used for now since the code for prometheans gets changed a lot. +/mob/living/carbon/human/get_water_protection() + var/protection = ..() // Todo: Replace with species var later. + if(protection == 1) // No point doing permeability checks if it won't matter. + return protection + // Wearing clothing with a low permeability_coefficient can protect from water. + + var/converted_protection = 1 - protection + var/perm = reagent_permeability() + converted_protection *= perm + return 1-converted_protection + + /mob/living/carbon/human/shank_attack(obj/item/W, obj/item/weapon/grab/G, mob/user, hit_zone) if(!..()) diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 2589b1c9da..24a63bf1a4 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -42,6 +42,16 @@ if(cloaker.active) cloaker.deactivate() +/mob/living/carbon/human/is_cloaked() + if(mind && mind.changeling && mind.changeling.cloaked) // Ling camo. + return TRUE + else if(istype(back, /obj/item/weapon/rig)) //Ninja cloak + var/obj/item/weapon/rig/suit = back + for(var/obj/item/rig_module/stealth_field/cloaker in suit.installed_modules) + if(cloaker.active) + return TRUE + return ..() + /mob/living/carbon/human/get_ear_protection() var/sum = 0 if(istype(l_ear, /obj/item/clothing/ears)) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 8a4fe6a18b..e9cbc80d95 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1047,8 +1047,6 @@ sleeping += 1 Paralyse(5) - confused = max(0, confused - 1) - // If you're dirty, your gloves will become dirty, too. if(gloves && germ_level > gloves.germ_level && prob(10)) gloves.germ_level += 1 @@ -1602,7 +1600,7 @@ else if(foundVirus) holder.icon_state = "hudill" else if(has_brain_worms()) - var/mob/living/simple_animal/borer/B = has_brain_worms() + var/mob/living/simple_mob/animal/borer/B = has_brain_worms() if(B.controlling) holder.icon_state = "hudbrainworm" else diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index d42c26880b..7f0e42e599 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -64,7 +64,7 @@ return 1 if (istype(other, /mob/living/carbon/brain)) return 1 - if (istype(other, /mob/living/simple_animal/slime)) + if (istype(other, /mob/living/simple_mob/slime)) return 1 //This is already covered by mob/say_understands() diff --git a/code/modules/mob/living/carbon/human/species/station/prometheans.dm b/code/modules/mob/living/carbon/human/species/station/prometheans.dm index b78d530707..1a24ad9e1b 100644 --- a/code/modules/mob/living/carbon/human/species/station/prometheans.dm +++ b/code/modules/mob/living/carbon/human/species/station/prometheans.dm @@ -116,7 +116,7 @@ var/datum/species/shapeshifter/promethean/prometheans /datum/species/shapeshifter/promethean/equip_survival_gear(var/mob/living/carbon/human/H) var/boxtype = pick(typesof(/obj/item/weapon/storage/toolbox/lunchbox)) var/obj/item/weapon/storage/toolbox/lunchbox/L = new boxtype(get_turf(H)) - var/mob/living/simple_animal/mouse/mouse = new (L) + var/mob/living/simple_mob/animal/passive/mouse/mouse = new (L) var/obj/item/weapon/holder/holder = new (L) mouse.forceMove(holder) holder.sync(mouse) diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index a5da193678..17d6b693d4 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -1,5 +1,6 @@ /mob/living/death() clear_fullscreens() +<<<<<<< HEAD reveal(TRUE) //Silently reveal the mob if they were hidden. //VOREStation Edit - Mob spawner stuff if(source_spawner) @@ -7,6 +8,11 @@ source_spawner = null //VOREStation Edit End . = ..() +======= + + if(ai_holder) + ai_holder.go_sleep() +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync if(nest) //Ew. if(istype(nest, /obj/structure/prop/nest)) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 02131c8cd0..24d56945b8 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -110,6 +110,7 @@ handle_silent() handle_drugged() handle_slurring() + handle_confused() /mob/living/proc/handle_stunned() if(stunned) @@ -146,6 +147,11 @@ AdjustParalysis(-1) return paralysis +/mob/living/proc/handle_confused() + if(confused) + AdjustConfused(-1) + return confused + /mob/living/proc/handle_disabilities() //Eyes if(sdisabilities & BLIND || stat) //blindness from disability or unconsciousness doesn't get better on its own diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 3bcfbe7b60..3a65cb2d2d 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -16,6 +16,8 @@ dsma.blend_mode = BLEND_ADD dsoverlay.appearance = dsma + selected_image = image(icon = 'icons/mob/screen1.dmi', loc = src, icon_state = "centermarker") + /mob/living/Destroy() dsoverlay.loc = null //I'll take my coat with me dsoverlay = null @@ -29,6 +31,7 @@ nest = null if(buckled) buckled.unbuckle_mob(src, TRUE) + qdel(selected_image) return ..() //mob verbs are faster than object verbs. See mob/verb/examine. @@ -550,6 +553,36 @@ default behaviour is: // ++++ROCKDTBEN++++ MOB PROCS //END +// Applies direct "cold" damage while checking protection against the cold. +/mob/living/proc/inflict_cold_damage(amount) + amount *= 1 - get_cold_protection(50) // Within spacesuit protection. + if(amount > 0) + adjustFireLoss(amount) + +// Ditto, but for "heat". +/mob/living/proc/inflict_heat_damage(amount) + amount *= 1 - get_heat_protection(10000) // Within firesuit protection. + if(amount > 0) + adjustFireLoss(amount) + +// and one for electricity because why not +/mob/living/proc/inflict_shock_damage(amount) + electrocute_act(amount, null, 1 - get_shock_protection()) + +// also one for water (most things resist it entirely, except for slimes) +/mob/living/proc/inflict_water_damage(amount) + amount *= 1 - get_water_protection() + if(amount > 0) + adjustToxLoss(amount) + +// one for abstracted away ""poison"" (mostly because simplemobs shouldn't handle reagents) +/mob/living/proc/inflict_poison_damage(amount) + if(isSynthetic()) + return + amount *= 1 - get_poison_protection() + if(amount > 0) + adjustToxLoss(amount) + /mob/proc/get_contents() @@ -663,6 +696,8 @@ default behaviour is: BITSET(hud_updateflag, LIFE_HUD) ExtinguishMob() fire_stacks = 0 + if(ai_holder) // AI gets told to sleep when killed. Since they're not dead anymore, wake it up. + ai_holder.go_wake() /mob/living/proc/rejuvenate() if(reagents) @@ -891,7 +926,7 @@ default behaviour is: // Update whether or not this mob needs to pass emotes to contents. for(var/atom/A in M.contents) - if(istype(A,/mob/living/simple_animal/borer) || istype(A,/obj/item/weapon/holder)) + if(istype(A,/mob/living/simple_mob/animal/borer) || istype(A,/obj/item/weapon/holder)) return else if(istype(H.loc,/obj/item/clothing/accessory/holster)) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 08d35e5de2..23507d88bc 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -92,6 +92,13 @@ /mob/living/proc/getsoak(var/def_zone, var/type) return 0 +// Clicking with an empty hand +/mob/living/attack_hand(mob/living/L) + ..() + if(istype(L) && L.a_intent != I_HELP) + if(ai_holder) // Using disarm, grab, or harm intent is considered a hostile action to the mob's AI. + ai_holder.react_to_attack(L) + /mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone) //Being hit while using a deadman switch @@ -102,6 +109,9 @@ src.visible_message("[src] triggers their deadman's switch!") signaler.signal() + if(ai_holder && P.firer) + ai_holder.react_to_attack(P.firer) + //Armor var/soaked = get_armor_soak(def_zone, P.check_armour, P.armor_penetration) var/absorb = run_armor_check(def_zone, P.check_armour, P.armor_penetration) @@ -267,6 +277,8 @@ var/client/assailant = M.client if(assailant) add_attack_logs(M,src,"Hit by thrown [O.name]") + if(ai_holder) + ai_holder.react_to_attack(O.thrower) // Begin BS12 momentum-transfer code. var/mass = 1.5 @@ -324,12 +336,13 @@ // End BS12 momentum-transfer code. /mob/living/attack_generic(var/mob/user, var/damage, var/attack_message) - if(!damage) return adjustBruteLoss(damage) - add_attack_logs(user,src,"Generic attack (probably animal)", admin_notify = FALSE) //Usually due to simple_animal attacks + add_attack_logs(user,src,"Generic attack (probably animal)", admin_notify = FALSE) //Usually due to simple_mob attacks + if(ai_holder) + ai_holder.react_to_attack(user) src.visible_message("[user] has [attack_message] [src]!") user.do_attack_animation(src) spawn(1) updatehealth() @@ -412,6 +425,15 @@ /mob/living/proc/get_heat_protection() return 0 +/mob/living/proc/get_shock_protection() + return 0 + +/mob/living/proc/get_water_protection() + return 1 // Water won't hurt most things. + +/mob/living/proc/get_poison_protection() + return 0 + //Finds the effective temperature that the mob is burning at. /mob/living/proc/fire_burn_temperature() if (fire_stacks <= 0) @@ -421,6 +443,15 @@ //lower limit of 700 K, same as matches and roughly the temperature of a cool flame. return max(2.25*round(FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE*(fire_stacks/FIRE_MAX_FIRESUIT_STACKS)**2), 700) +// Called when struck by lightning. +/mob/living/proc/lightning_act() + // The actual damage/electrocution is handled by the tesla_zap() that accompanies this. + Paralyse(5) + stuttering += 20 + make_jittery(150) + emp_act(1) + to_chat(src, span("critical", "You've been struck by lightning!")) + /mob/living/proc/reagent_permeability() return 1 return round(FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE*(fire_stacks/FIRE_MAX_FIRESUIT_STACKS)**2) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 9486eb9960..8e3b9fb9a4 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -5,6 +5,8 @@ var/maxHealth = 100 //Maximum health that should be possible. Avoid adjusting this if you can, and instead use modifiers datums. var/health = 100 //A mob's health + var/mob_class = null // A mob's "class", e.g. human, mechanical, animal, etc. Used for certain projectile effects. See __defines/mob.dm for available classes. + var/hud_updateflag = 0 //Damage related vars, NOTE: THESE SHOULD ONLY BE MODIFIED BY PROCS @@ -20,6 +22,7 @@ var/list/atom/hallucinations = list() //A list of hallucinated people that try to attack the mob. See /obj/effect/fake_attacker in hallucinations.dm var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out. + var/base_attack_cooldown = DEFAULT_ATTACK_COOLDOWN var/t_phoron = null var/t_oxygen = null @@ -64,4 +67,5 @@ var/makes_dirt = TRUE //FALSE if the mob shouldn't be making dirt on the ground when it walks var/looking_elsewhere = FALSE //If the mob's view has been relocated to somewhere else, like via a camera or with binocs - \ No newline at end of file + + var/image/selected_image = null // Used for buildmode AI control stuff. \ No newline at end of file diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm index 275e88be08..96d59dc330 100644 --- a/code/modules/mob/living/login.dm +++ b/code/modules/mob/living/login.dm @@ -8,4 +8,9 @@ update_antag_icons(mind) client.screen |= global_hud.darksight client.images |= dsoverlay + + if(ai_holder && !ai_holder.autopilot) + ai_holder.go_sleep() + to_chat(src,"Mob AI disabled while you are controlling the mob.") + return . diff --git a/code/modules/mob/living/logout.dm b/code/modules/mob/living/logout.dm index 577221a03e..fa04b0b317 100644 --- a/code/modules/mob/living/logout.dm +++ b/code/modules/mob/living/logout.dm @@ -1,6 +1,11 @@ /mob/living/Logout() ..() - if (mind) + if (mind) //Per BYOND docs key remains set if the player DCs, becomes null if switching bodies. - if(!key) //key and mind have become seperated. + if(!key) //key and mind have become seperated. mind.active = 0 //This is to stop say, a mind.transfer_to call on a corpse causing a ghost to re-enter its body. + + spawn(15 SECONDS) //15 seconds to get back into the mob before it goes wild + if(src && !src.client) + if(ai_holder) + ai_holder.go_wake() diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index ac78f5aab9..20087b19bb 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -344,7 +344,7 @@ var/grabbed_something = 0 for(var/mob/M in T) - if(istype(M,/mob/living/simple_animal/lizard) || istype(M,/mob/living/simple_animal/mouse)) + if(istype(M,/mob/living/simple_mob/animal/passive/lizard) || istype(M,/mob/living/simple_mob/animal/passive/mouse)) src.loc.visible_message("[src.loc] sucks [M] into its decompiler. There's a horrible crunching noise.","It's a bit of a struggle, but you manage to suck [M] into your decompiler. It makes a series of visceral crunching noises.") new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) qdel(M) diff --git a/code/modules/mob/living/simple_animal/aliens/faithless.dm b/code/modules/mob/living/simple_animal/aliens/faithless.dm index af2e7da48d..3b5e520fcd 100644 --- a/code/modules/mob/living/simple_animal/aliens/faithless.dm +++ b/code/modules/mob/living/simple_animal/aliens/faithless.dm @@ -72,7 +72,6 @@ melee_damage_lower = 13 melee_damage_upper = 28 - /mob/living/simple_animal/hostile/faithless/strong/cult faction = "cult" supernatural = 1 diff --git a/code/modules/mob/living/simple_animal/aliens/hivebot.dm b/code/modules/mob/living/simple_animal/aliens/hivebot.dm index e322198815..6d53b30cfa 100644 --- a/code/modules/mob/living/simple_animal/aliens/hivebot.dm +++ b/code/modules/mob/living/simple_animal/aliens/hivebot.dm @@ -1,6 +1,6 @@ // Hivebots are tuned towards how many default lasers are needed to kill them. // As such, if laser damage is ever changed, you should change this define. -#define LASERS_TO_KILL *30 +#define LASERS_TO_KILL * 40 // Default hivebot is melee, and a bit more meaty, so it can meatshield for their ranged friends. /mob/living/simple_animal/hostile/hivebot @@ -238,3 +238,5 @@ /obj/item/projectile/bullet/hivebot damage = 10 damage_type = BRUTE + +#undef LASERS_TO_KILL \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/animals/cat.dm b/code/modules/mob/living/simple_animal/animals/cat.dm index 8877ea8959..30d23e0105 100644 --- a/code/modules/mob/living/simple_animal/animals/cat.dm +++ b/code/modules/mob/living/simple_animal/animals/cat.dm @@ -1,3 +1,4 @@ +<<<<<<< HEAD //Cat /mob/living/simple_animal/cat name = "cat" @@ -202,3 +203,203 @@ /obj/item/weapon/holder/cat/kitten icon_state = "kitten" w_class = ITEMSIZE_SMALL +======= +//Cat +/mob/living/simple_animal/cat + name = "cat" + desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." + tt_desc = "E Felis silvestris catus" + intelligence_level = SA_ANIMAL + icon_state = "cat2" + item_state = "cat2" + icon_living = "cat2" + icon_dead = "cat2_dead" + icon_rest = "cat2_rest" + + investigates = 1 + specific_targets = 1 //Only targets with Found() + run_at_them = 0 //DOMESTICATED + view_range = 5 + + turns_per_move = 5 + see_in_dark = 6 + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + + min_oxy = 16 //Require atleast 16kPA oxygen + minbodytemp = 223 //Below -50 Degrees Celcius + maxbodytemp = 323 //Above 50 Degrees Celcius + + holder_type = /obj/item/weapon/holder/cat + mob_size = MOB_SMALL + + has_langs = list("Cat") + speak_chance = 1 + speak = list("Meow!","Esp!","Purr!","HSSSSS") + speak_emote = list("purrs", "meows") + emote_hear = list("meows","mews") + emote_see = list("shakes their head", "shivers") + say_maybe_target = list("Meow?","Mew?","Mao?") + say_got_target = list("MEOW!","HSSSS!","REEER!") + + meat_amount = 1 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + var/turns_since_scan = 0 + var/mob/flee_target + +/mob/living/simple_animal/cat/Life() + . = ..() + if(!.) return + + if(prob(2)) //spooky + var/mob/observer/dead/spook = locate() in range(src,5) + if(spook) + var/turf/T = spook.loc + var/list/visible = list() + for(var/obj/O in T.contents) + if(!O.invisibility && O.name) + visible += O + if(visible.len) + var/atom/A = pick(visible) + visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") + + handle_flee_target() + +/mob/living/simple_animal/cat/PunchTarget() + if(ismouse(target_mob)) + var/mob/living/simple_mob/animal/passive/mouse/mouse = target_mob + mouse.splat() + visible_emote(pick("bites \the [mouse]!","toys with \the [mouse].","chomps on \the [mouse]!")) + return mouse + else + ..() + +/mob/living/simple_animal/cat/Found(var/atom/found_atom) + if(ismouse(found_atom) && SA_attackable(found_atom)) + return found_atom + +/mob/living/simple_animal/cat/proc/handle_flee_target() + //see if we should stop fleeing + if (flee_target && !(flee_target in ListTargets(view_range))) + flee_target = null + GiveUpMoving() + + if (flee_target && !stat && !buckled) + if (resting) + lay_down() + if(prob(25)) say("HSSSSS") + stop_automated_movement = 1 + walk_away(src, flee_target, 7, 2) + +/mob/living/simple_animal/cat/react_to_attack(var/atom/A) + if(A == src) return + flee_target = A + turns_since_scan = 5 + +/mob/living/simple_animal/cat/ex_act() + . = ..() + react_to_attack(src.loc) + +//Basic friend AI +/mob/living/simple_animal/cat/fluff + var/mob/living/carbon/human/friend + var/befriend_job = null + +/mob/living/simple_animal/cat/fluff/Life() + . = ..() + if(!. || ai_inactive || !friend) return + + var/friend_dist = get_dist(src,friend) + + if (friend_dist <= 4) + if(stance == STANCE_IDLE) + if(set_follow(friend)) + handle_stance(STANCE_FOLLOW) + + if (friend_dist <= 1) + if (friend.stat >= DEAD || friend.health <= config.health_threshold_softcrit) + if (prob((friend.stat < DEAD)? 50 : 15)) + var/verb = pick("meows", "mews", "mrowls") + audible_emote(pick("[verb] in distress.", "[verb] anxiously.")) + else + if (prob(5)) + visible_emote(pick("nuzzles [friend].", + "brushes against [friend].", + "rubs against [friend].", + "purrs.")) + else if (friend.health <= 50) + if (prob(10)) + var/verb = pick("meows", "mews", "mrowls") + audible_emote("[verb] anxiously.") + +/mob/living/simple_animal/cat/fluff/verb/become_friends() + set name = "Become Friends" + set category = "IC" + set src in view(1) + + if(!friend) + var/mob/living/carbon/human/H = usr + if(istype(H) && (!befriend_job || H.job == befriend_job)) + friend = usr + . = 1 + else if(usr == friend) + . = 1 //already friends, but show success anyways + + if(.) + set_dir(get_dir(src, friend)) + visible_emote(pick("nuzzles [friend].", + "brushes against [friend].", + "rubs against [friend].", + "purrs.")) + else + usr << "[src] ignores you." + return + +//RUNTIME IS ALIVE! SQUEEEEEEEE~ +/mob/living/simple_animal/cat/fluff/Runtime + name = "Runtime" + desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." + tt_desc = "E Felis silvestris medicalis" //a hypoallergenic breed produced by NT for... medical purposes? Sure. + gender = FEMALE + icon_state = "cat" + item_state = "cat" + icon_living = "cat" + icon_dead = "cat_dead" + icon_rest = "cat_rest" + befriend_job = "Chief Medical Officer" + +/mob/living/simple_animal/cat/kitten + name = "kitten" + desc = "D'aaawwww" + icon_state = "kitten" + item_state = "kitten" + icon_living = "kitten" + icon_dead = "kitten_dead" + gender = NEUTER + +// Leaving this here for now. +/obj/item/weapon/holder/cat/fluff/bones + name = "Bones" + desc = "It's Bones! Meow." + gender = MALE + icon_state = "cat3" + +/mob/living/simple_animal/cat/fluff/bones + name = "Bones" + desc = "That's Bones the cat. He's a laid back, black cat. Meow." + gender = MALE + icon_state = "cat3" + item_state = "cat3" + icon_living = "cat3" + icon_dead = "cat3_dead" + icon_rest = "cat3_rest" + holder_type = /obj/item/weapon/holder/cat/fluff/bones + var/friend_name = "Erstatz Vryroxes" + +/mob/living/simple_animal/cat/kitten/New() + gender = pick(MALE, FEMALE) + ..() +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync diff --git a/code/modules/mob/living/simple_animal/animals/corgi.dm b/code/modules/mob/living/simple_animal/animals/corgi.dm index 33bda8b2e0..6cc1f80523 100644 --- a/code/modules/mob/living/simple_animal/animals/corgi.dm +++ b/code/modules/mob/living/simple_animal/animals/corgi.dm @@ -207,6 +207,7 @@ set_dir(i) sleep(1) + //Technically this should be like, its own file or something or a subset of dog but whatever. Not a coder. /mob/living/simple_animal/corgi/tamaskan name = "tamaskan" diff --git a/code/modules/mob/living/simple_animal/animals/mouse.dm b/code/modules/mob/living/simple_animal/animals/mouse.dm index bf5d03c52c..370001a7ca 100644 --- a/code/modules/mob/living/simple_animal/animals/mouse.dm +++ b/code/modules/mob/living/simple_animal/animals/mouse.dm @@ -16,10 +16,10 @@ see_in_dark = 6 universal_understand = 1 - mob_size = MOB_MINISCULE + mob_size = MOB_SMALL pass_flags = PASSTABLE - can_pull_size = ITEMSIZE_TINY - can_pull_mobs = MOB_PULL_NONE +// can_pull_size = ITEMSIZE_TINY +// can_pull_mobs = MOB_PULL_NONE layer = MOB_LAYER density = 0 diff --git a/code/modules/mob/living/simple_animal/animals/spiderbot.dm b/code/modules/mob/living/simple_animal/animals/spiderbot.dm index b76351b976..9145e1923f 100644 --- a/code/modules/mob/living/simple_animal/animals/spiderbot.dm +++ b/code/modules/mob/living/simple_animal/animals/spiderbot.dm @@ -46,7 +46,7 @@ /obj/item/device/radio/borg, /obj/item/weapon/holder, /obj/machinery/camera, - /mob/living/simple_animal/borer, + /mob/living/simple_mob/animal/borer, /obj/item/device/mmi, ) diff --git a/code/modules/mob/living/simple_animal/borer/borer.dm b/code/modules/mob/living/simple_animal/borer/borer.dm index e4add4d749..43edd45602 100644 --- a/code/modules/mob/living/simple_animal/borer/borer.dm +++ b/code/modules/mob/living/simple_animal/borer/borer.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/borer +/mob/living/simple_mob/animal/borer name = "cortical borer" real_name = "cortical borer" desc = "A small, quivering sluglike creature." @@ -35,15 +35,15 @@ can_be_antagged = TRUE -/mob/living/simple_animal/borer/roundstart +/mob/living/simple_mob/animal/borer/roundstart roundstart = 1 -/mob/living/simple_animal/borer/Login() +/mob/living/simple_mob/animal/borer/Login() ..() if(mind) borers.add_antagonist(mind) -/mob/living/simple_animal/borer/New() +/mob/living/simple_mob/animal/borer/New() ..() add_language("Cortical Link") @@ -53,7 +53,7 @@ truename = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]" if(!roundstart) request_player() -/mob/living/simple_animal/borer/Life() +/mob/living/simple_mob/animal/borer/Life() ..() @@ -91,7 +91,7 @@ if(prob(host.brainloss/20)) host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]") -/mob/living/simple_animal/borer/Stat() +/mob/living/simple_mob/animal/borer/Stat() ..() statpanel("Status") @@ -103,7 +103,7 @@ if (client.statpanel == "Status") stat("Chemicals", chemicals) -/mob/living/simple_animal/borer/proc/detatch() +/mob/living/simple_mob/animal/borer/proc/detatch() if(!host || !controlling) return @@ -154,7 +154,7 @@ qdel(host_brain) -/mob/living/simple_animal/borer/proc/leave_host() +/mob/living/simple_mob/animal/borer/proc/leave_host() if(!host) return @@ -172,7 +172,7 @@ return //Procs for grabbing players. -/mob/living/simple_animal/borer/proc/request_player() +/mob/living/simple_mob/animal/borer/proc/request_player() for(var/mob/observer/dead/O in player_list) if(jobban_isbanned(O, "Borer")) continue @@ -180,7 +180,7 @@ if(O.client.prefs.be_special & BE_ALIEN) question(O.client) -/mob/living/simple_animal/borer/proc/question(var/client/C) +/mob/living/simple_mob/animal/borer/proc/question(var/client/C) spawn(0) if(!C) return var/response = alert(C, "A cortical borer needs a player. Are you interested?", "Cortical borer request", "Yes", "No", "Never for this round") @@ -191,7 +191,7 @@ else if (response == "Never for this round") C.prefs.be_special ^= BE_ALIEN -/mob/living/simple_animal/borer/proc/transfer_personality(var/client/candidate) +/mob/living/simple_mob/animal/borer/proc/transfer_personality(var/client/candidate) if(!candidate || !candidate.mob || !candidate.mob.mind) return @@ -209,5 +209,5 @@ your host and your eventual spawn safe and warm." src << "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers." -/mob/living/simple_animal/borer/cannot_use_vents() +/mob/living/simple_mob/animal/borer/cannot_use_vents() return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/borer/borer_captive.dm b/code/modules/mob/living/simple_animal/borer/borer_captive.dm index c34dca00e8..2e5af8be9a 100644 --- a/code/modules/mob/living/simple_animal/borer/borer_captive.dm +++ b/code/modules/mob/living/simple_animal/borer/borer_captive.dm @@ -10,7 +10,7 @@ src << "You cannot speak in IC (muted)." return - if(istype(src.loc,/mob/living/simple_animal/borer)) + if(istype(src.loc,/mob/living/simple_mob/animal/borer)) message = sanitize(message) if (!message) @@ -19,7 +19,7 @@ if (stat == 2) return say_dead(message) - var/mob/living/simple_animal/borer/B = src.loc + var/mob/living/simple_mob/animal/borer/B = src.loc src << "You whisper silently, \"[message]\"" B.host << "The captive mind of [src] whispers, \"[message]\"" @@ -34,8 +34,8 @@ /mob/living/captive_brain/process_resist() //Resisting control by an alien mind. - if(istype(src.loc,/mob/living/simple_animal/borer)) - var/mob/living/simple_animal/borer/B = src.loc + if(istype(src.loc,/mob/living/simple_mob/animal/borer)) + var/mob/living/simple_mob/animal/borer/B = src.loc var/mob/living/captive_brain/H = src H << "You begin doggedly resisting the parasite's control (this will take approximately sixty seconds)." diff --git a/code/modules/mob/living/simple_animal/borer/borer_powers.dm b/code/modules/mob/living/simple_animal/borer/borer_powers.dm index b75da20cc4..c12736a307 100644 --- a/code/modules/mob/living/simple_animal/borer/borer_powers.dm +++ b/code/modules/mob/living/simple_animal/borer/borer_powers.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/borer/verb/release_host() +/mob/living/simple_mob/animal/borer/verb/release_host() set category = "Abilities" set name = "Release Host" set desc = "Slither out of your host." @@ -38,7 +38,7 @@ detatch() leave_host() -/mob/living/simple_animal/borer/verb/infest() +/mob/living/simple_mob/animal/borer/verb/infest() set category = "Abilities" set name = "Infest" set desc = "Infest a suitable humanoid host." @@ -126,7 +126,7 @@ return /* -/mob/living/simple_animal/borer/verb/devour_brain() +/mob/living/simple_mob/animal/borer/verb/devour_brain() set category = "Abilities" set name = "Devour Brain" set desc = "Take permanent control of a dead host." @@ -152,7 +152,7 @@ */ // BRAIN WORM ZOMBIES AAAAH. -/mob/living/simple_animal/borer/proc/replace_brain() +/mob/living/simple_mob/animal/borer/proc/replace_brain() var/mob/living/carbon/human/H = host @@ -198,7 +198,7 @@ if(!H.lastKnownIP) H.lastKnownIP = s2h_ip -/mob/living/simple_animal/borer/verb/secrete_chemicals() +/mob/living/simple_mob/animal/borer/verb/secrete_chemicals() set category = "Abilities" set name = "Secrete Chemicals" set desc = "Push some chemicals into your host's bloodstream." @@ -226,7 +226,7 @@ host.reagents.add_reagent(chem, 10) chemicals -= 50 -/mob/living/simple_animal/borer/verb/dominate_victim() +/mob/living/simple_mob/animal/borer/verb/dominate_victim() set category = "Abilities" set name = "Paralyze Victim" set desc = "Freeze the limbs of a potential host with supernatural fear." @@ -266,7 +266,7 @@ used_dominate = world.time -/mob/living/simple_animal/borer/verb/bond_brain() +/mob/living/simple_mob/animal/borer/verb/bond_brain() set category = "Abilities" set name = "Assume Control" set desc = "Fully connect to the brain of your host." @@ -335,20 +335,3 @@ host.verbs += /mob/living/carbon/proc/spawn_larvae return - -/mob/living/carbon/human/proc/jumpstart() - set category = "Abilities" - set name = "Revive Host" - set desc = "Send a jolt of electricity through your host, reviving them." - - if(stat != 2) - usr << "Your host is already alive." - return - - verbs -= /mob/living/carbon/human/proc/jumpstart - visible_message("With a hideous, rattling moan, [src] shudders back to life!") - - rejuvenate() - restore_blood() - fixblood() - update_canmove() \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/borer/say.dm b/code/modules/mob/living/simple_animal/borer/say.dm index 440dfa7ff7..f4c12a7458 100644 --- a/code/modules/mob/living/simple_animal/borer/say.dm +++ b/code/modules/mob/living/simple_animal/borer/say.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/borer/say(var/message) +/mob/living/simple_mob/animal/borer/say(var/message) message = sanitize(message) message = capitalize(message) diff --git a/code/modules/mob/living/simple_animal/constructs/constructs.dm b/code/modules/mob/living/simple_animal/constructs/constructs.dm deleted file mode 100644 index 26ab3a1260..0000000000 --- a/code/modules/mob/living/simple_animal/constructs/constructs.dm +++ /dev/null @@ -1,488 +0,0 @@ -/mob/living/simple_animal/construct - name = "Construct" - real_name = "Construct" - var/construct_type = "shade" - desc = "" - speak_emote = list("hisses") - emote_hear = list("wails","screeches") - - ui_icons = 'icons/mob/screen1_construct.dmi' - has_hands = 1 - hand_form = "stone manipulators" - - response_help = "thinks better of touching" - response_disarm = "flailed at" - response_harm = "punched" - - intelligence_level = SA_HUMANOID // Player controlled. - - hovering = TRUE - softfall = TRUE //Beings made of Hellmarble and powered by the tears of the damned are not concerned with mortal things such as 'gravity'. - parachuting = TRUE - - icon_dead = "shade_dead" - var/do_glow = 1 - - speed = -1 - a_intent = I_HURT - stop_automated_movement = 1 - - status_flags = CANPUSH - - universal_speak = 0 - universal_understand = 1 - - attack_sound = 'sound/weapons/spiderlunge.ogg' - - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - - minbodytemp = 0 - show_stat_health = 1 - - faction = "cult" - supernatural = 1 - - see_invisible = SEE_INVISIBLE_NOLIGHTING - see_in_dark = 7 - - var/nullblock = 0 - - mob_swap_flags = HUMAN|SIMPLE_ANIMAL|SLIME|MONKEY - mob_push_flags = ALLMOBS - - var/list/construct_spells = list() - - can_be_antagged = TRUE - - taser_kill = 0 // no - - shock_resistance = 0.9 //Electricity isn't very effective on stone, especially that from hell. - - armor = list( - "melee" = 10, - "bullet" = 10, - "laser" = 10, - "energy" = 10, - "bomb" = 10, - "bio" = 100, - "rad" = 100) - -/mob/living/simple_animal/construct/place_spell_in_hand(var/path) - if(!path || !ispath(path)) - return 0 - - //var/obj/item/weapon/spell/S = new path(src) - var/obj/item/weapon/spell/construct/S = new path(src) - - //No hands needed for innate casts. - if(S.cast_methods & CAST_INNATE) - if(S.run_checks()) - S.on_innate_cast(src) - - if(l_hand && r_hand) //Make sure our hands aren't full. - if(istype(r_hand, /obj/item/weapon/spell)) //If they are full, perhaps we can still be useful. - var/obj/item/weapon/spell/r_spell = r_hand - if(r_spell.aspect == ASPECT_CHROMATIC) //Check if we can combine the new spell with one in our hands. - r_spell.on_combine_cast(S, src) - else if(istype(l_hand, /obj/item/weapon/spell)) - var/obj/item/weapon/spell/l_spell = l_hand - if(l_spell.aspect == ASPECT_CHROMATIC) //Check the other hand too. - l_spell.on_combine_cast(S, src) - else //Welp - to_chat(src, "You require a free manipulator to use this power.") - return 0 - - if(S.run_checks()) - put_in_hands(S) - return 1 - else - qdel(S) - return 0 - -/mob/living/simple_animal/construct/cultify() - return - -/mob/living/simple_animal/construct/New() - ..() - name = text("[initial(name)] ([rand(1, 1000)])") - real_name = name - add_language("Cult") - add_language("Occult") - for(var/spell in construct_spells) - src.add_spell(new spell, "const_spell_ready") - updateicon() - -/mob/living/simple_animal/construct/updateicon() - overlays.Cut() - ..() - if(do_glow) - add_glow() - -/mob/living/simple_animal/construct/update_icon() - ..() - if(do_glow) - add_glow() - -/mob/living/simple_animal/construct/death() - new /obj/item/weapon/ectoplasm (src.loc) - ..(null,"collapses in a shattered heap.") - ghostize() - qdel(src) - -/mob/living/simple_animal/construct/attack_generic(var/mob/user) - if(istype(user, /mob/living/simple_animal/construct/builder)) - var/mob/living/simple_animal/construct/builder/B = user - if(health < getMaxHealth()) - var/repair_lower_bound = B.melee_damage_lower * -1 - var/repair_upper_bound = B.melee_damage_upper * -1 - adjustBruteLoss(rand(repair_lower_bound, repair_upper_bound)) - adjustFireLoss(rand(repair_lower_bound, repair_upper_bound)) - user.visible_message("\The [user] mends some of \the [src]'s wounds.") - else - to_chat(user, "\The [src] is undamaged.") - return - return ..() - -/mob/living/simple_animal/construct/examine(mob/user) - ..(user) - var/msg = "*---------*\nThis is \icon[src] \a [src]!\n" - if (src.health < src.getMaxHealth()) - msg += "" - if (src.health >= src.getMaxHealth()/2) - msg += "It looks slightly dented.\n" - else - msg += "It looks severely dented!\n" - msg += "" - msg += "*---------*" - - user << msg - -/mob/living/simple_animal/construct/Process_Spacemove() - return 1 //Constructs levitate, can fall from a shuttle with no harm, and are piloted by either damned spirits or some otherworldly entity. It's not hard to believe. - -/////////////////Juggernaut/////////////// - - - -/mob/living/simple_animal/construct/armoured - name = "Juggernaut" - real_name = "Juggernaut" - construct_type = "juggernaut" - desc = "A possessed suit of armour driven by the will of the restless dead" - icon = 'icons/mob/mob.dmi' - icon_state = "behemoth" - icon_living = "behemoth" - maxHealth = 300 - health = 300 - response_harm = "harmlessly punches" - harm_intent_damage = 0 - melee_damage_lower = 30 - melee_damage_upper = 40 - attack_armor_pen = 60 //Being punched by a living, floating statue. - attacktext = list("smashed their armoured gauntlet into") - friendly = list("pats") - mob_size = MOB_HUGE - speed = 2 //Not super fast, but it might catch up to someone in armor who got punched once or twice. - environment_smash = 2 - attack_sound = 'sound/weapons/heavysmash.ogg' - status_flags = 0 - resistance = 10 - construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser, - /spell/targeted/fortify, - /spell/targeted/construct_advanced/slam - ) - - armor = list( - "melee" = 70, - "bullet" = 30, - "laser" = 30, - "energy" = 30, - "bomb" = 10, - "bio" = 100, - "rad" = 100) - -/mob/living/simple_animal/construct/armoured/Life() - weakened = 0 - ..() - -/mob/living/simple_animal/construct/armoured/bullet_act(var/obj/item/projectile/P) -// if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) //If it's going to be slow, it's probably going to need every reflect it can get. - var/reflectchance = 80 - round(P.damage/3) - if(prob(reflectchance)) - var/damage_mod = rand(2,4) - var/projectile_dam_type = P.damage_type - var/incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3))) - var/armorcheck = run_armor_check(null, P.check_armour) - var/soakedcheck = get_armor_soak(null, P.check_armour) - if(!(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam))) - visible_message("The [P.name] bounces off of [src]'s shell!", \ - "The [P.name] bounces off of [src]'s shell!") - new /obj/item/weapon/material/shard/shrapnel(src.loc) - if(!(P.damage_type == BRUTE || P.damage_type == BURN)) - projectile_dam_type = BRUTE - incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to brute for physical projectiles, though severely decreased. - apply_damage(incoming_damage, projectile_dam_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P) - return -1 //Doesn't reflect non-beams or non-energy projectiles. They just smack and drop with little to no effect. - else - visible_message("The [P.name] gets reflected by [src]'s shell!", \ - "The [P.name] gets reflected by [src]'s shell!") - damage_mod = rand(3,5) - incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3))) - if(!(P.damage_type == BRUTE || P.damage_type == BURN)) - projectile_dam_type = BURN - incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to burn for energy-type projectiles, though severely decreased. - apply_damage(incoming_damage, P.damage_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P) - - // Find a turf near or on the original location to bounce to - if(P.starting) - var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) - var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) - var/turf/curloc = get_turf(src) - - // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = 1 - - return -1 // complete projectile permutation - - return (..(P)) - - - -////////////////////////Wraith///////////////////////////////////////////// - - - -/mob/living/simple_animal/construct/wraith - name = "Wraith" - real_name = "Wraith" - construct_type = "wraith" - desc = "A wicked bladed shell contraption piloted by a bound spirit." - icon = 'icons/mob/mob.dmi' - icon_state = "floating" - icon_living = "floating" - maxHealth = 200 - health = 200 - melee_damage_lower = 25 - melee_damage_upper = 30 - attack_armor_pen = 15 - attack_sharp = 1 - attack_edge = 1 - attacktext = list("slashed") - friendly = list("pinches") - speed = -1 - environment_smash = 1 - attack_sound = 'sound/weapons/rapidslice.ogg' - construct_spells = list(/spell/targeted/ethereal_jaunt/shift, - /spell/targeted/ambush_mode - ) - -/mob/living/simple_animal/construct/wraith/DoPunch(var/atom/A) - . = ..() - if(. && isliving(A)) - var/mob/living/L = A - L.add_modifier(/datum/modifier/deep_wounds, 30 SECONDS) - -/////////////////////////////Artificer///////////////////////// - - - -/mob/living/simple_animal/construct/builder - name = "Artificer" - real_name = "Artificer" - construct_type = "artificer" - desc = "A bulbous construct dedicated to building and maintaining temples to their otherworldly lords." - icon = 'icons/mob/mob.dmi' - icon_state = "artificer" - icon_living = "artificer" - maxHealth = 150 - health = 150 - response_harm = "viciously beaten" - harm_intent_damage = 5 - melee_damage_lower = 15 //It's not the strongest of the bunch, but that doesn't mean it can't hurt you. - melee_damage_upper = 20 - attacktext = list("rammed") - speed = 0 - environment_smash = 2 - attack_sound = 'sound/weapons/rapidslice.ogg' - construct_spells = list(/spell/aoe_turf/conjure/construct/lesser, - /spell/aoe_turf/conjure/wall, - /spell/aoe_turf/conjure/floor, - /spell/aoe_turf/conjure/soulstone, - /spell/aoe_turf/conjure/pylon, - /spell/aoe_turf/conjure/door, - /spell/aoe_turf/conjure/grille, - /spell/targeted/occult_repair_aura, - /spell/targeted/construct_advanced/mend_acolyte - ) - - -/////////////////////////////Behemoth///////////////////////// -/* - * The Behemoth. Admin-allowance only, still try to keep it in some guideline of 'Balanced', even if it means Security has to be fully geared to be so. - */ - -/mob/living/simple_animal/construct/behemoth - name = "Behemoth" - real_name = "Behemoth" - construct_type = "juggernaut" - desc = "The pinnacle of occult technology, Behemoths are nothing shy of both an Immovable Object, and Unstoppable Force." - icon = 'icons/mob/mob.dmi' - icon_state = "behemoth" - icon_living = "behemoth" - maxHealth = 750 - health = 750 - speak_emote = list("rumbles") - response_harm = "harmlessly punched" - harm_intent_damage = 0 - melee_damage_lower = 50 - melee_damage_upper = 50 - attacktext = list("brutally crushed") - friendly = list("pokes") //Anything nice the Behemoth would do would still Kill the Human. Leave it at poke. - speed = 5 - environment_smash = 2 - attack_sound = 'sound/weapons/heavysmash.ogg' - resistance = 10 - icon_scale = 2 - var/energy = 0 - var/max_energy = 1000 - armor = list( - "melee" = 60, - "bullet" = 60, - "laser" = 60, - "energy" = 30, - "bomb" = 10, - "bio" = 100, - "rad" = 100) - construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser, - /spell/targeted/fortify, - /spell/targeted/construct_advanced/slam - ) - -/mob/living/simple_animal/construct/behemoth/bullet_act(var/obj/item/projectile/P) - var/reflectchance = 80 - round(P.damage/3) - if(prob(reflectchance)) - visible_message("The [P.name] gets reflected by [src]'s shell!", \ - "The [P.name] gets reflected by [src]'s shell!") - - // Find a turf near or on the original location to bounce to - if(P.starting) - var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) - var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) - var/turf/curloc = get_turf(src) - - // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = 1 - - return -1 // complete projectile permutation - - return (..(P)) - -////////////////////////Harvester//////////////////////////////// -/* - * Master of Spells and Ranged Abilities. Not as fragile as the Wraith, but nowhere near as maneuverable and deadly in melee. - */ - -/mob/living/simple_animal/construct/harvester - name = "Harvester" - real_name = "Harvester" - construct_type = "harvester" - desc = "A tendril-laden construct piloted by a chained mind." - icon = 'icons/mob/mob.dmi' - icon_state = "harvester" - icon_living = "harvester" - maxHealth = 150 - health = 150 - melee_damage_lower = 20 - melee_damage_upper = 25 - attack_sharp = 1 - attacktext = list("violently stabbed") - friendly = list("caresses") - speed = 0 - environment_smash = 1 - attack_sound = 'sound/weapons/pierce.ogg' - - armor = list( - "melee" = 10, - "bullet" = 20, - "laser" = 20, - "energy" = 20, - "bomb" = 20, - "bio" = 100, - "rad" = 100) - - construct_spells = list( - /spell/aoe_turf/knock/harvester, - /spell/targeted/construct_advanced/inversion_beam, - /spell/targeted/construct_advanced/agonizing_sphere, - /spell/rune_write - ) - -////////////////Glow////////////////// -/mob/living/simple_animal/construct/proc/add_glow() - var/image/eye_glow = image(icon,"glow-[icon_state]") - eye_glow.plane = PLANE_LIGHTING_ABOVE - overlays += eye_glow - set_light(2, -2, l_color = "#FFFFFF") - -/mob/living/simple_animal/construct/proc/remove_glow() - overlays.Cut() - -////////////////HUD////////////////////// - -/mob/living/simple_animal/construct/Life() - . = ..() - if(.) - if(fire) - if(fire_alert) fire.icon_state = "fire1" - else fire.icon_state = "fire0" - if(pullin) - if(pulling) pullin.icon_state = "pull1" - else pullin.icon_state = "pull0" - - if(purged) - if(purge > 0) purged.icon_state = "purge1" - else purged.icon_state = "purge0" - - silence_spells(purge) - -/mob/living/simple_animal/construct/updatehealth() //Special icons. - health = getMaxHealth() - getToxLoss() - getFireLoss() - getBruteLoss() - - //Alive, becoming dead - if((stat < DEAD) && (health <= 0)) - death() - - //Overhealth - if(health > getMaxHealth()) - health = getMaxHealth() - - //Update our hud if we have one - if(healths) - if(stat != DEAD) - var/heal_per = (health / getMaxHealth()) * 100 - switch(heal_per) - if(100 to INFINITY) - healths.icon_state = "[construct_type]_health0" - if(80 to 100) - healths.icon_state = "[construct_type]_health1" - if(60 to 80) - healths.icon_state = "[construct_type]_health2" - if(40 to 60) - healths.icon_state = "[construct_type]_health3" - if(20 to 40) - healths.icon_state = "[construct_type]_health4" - if(0 to 20) - healths.icon_state = "[construct_type]_health5" - else - healths.icon_state = "[construct_type]_health6" - else - healths.icon_state = "[construct_type]_health7" \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/humanoids/head.dm b/code/modules/mob/living/simple_animal/humanoids/head.dm deleted file mode 100644 index 22460f0990..0000000000 --- a/code/modules/mob/living/simple_animal/humanoids/head.dm +++ /dev/null @@ -1,61 +0,0 @@ -//Look Sir, free head! -/mob/living/simple_animal/head - name = "CommandBattle AI" - desc = "A standard borg shell on its chest crude marking saying CommandBattle AI MK4 : Head." - icon_state = "crab" - icon_living = "crab" - icon_dead = "crab_dead" - intelligence_level = SA_ANIMAL - - wander = 0 - stop_automated_movement = 1 - universal_speak = 1 - turns_per_move = 5 - - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "punches" - - speak_chance = 1 - speak_emote = list("clicks") - emote_hear = list("clicks") - emote_see = list("clacks") - - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat - - var/list/insults = list( - "Man you suck", - "You look like the most retarded douche around", - "What's up?, oh wait nevermind you are a fucking asshat", - "you are just overly retarded", - "Whiteman said what?!",) - var/list/comments = list("Man have you seen those furry cats?,I mean who in the right mind would like something like that?", - "They call me abusive,I just like the truth", - "Beeboop, im a robit", - "Gooogooooll, break ya bones", - "Crab say what?", - "Man they say we have space lizards now, man this shit is getting more wack every minute", - "The so called \"improved\" station AI is just bullshit, that thing aint fun for noone", - "The Colony Director is a traitor, he took my power core.", - "Say \"what\" again. Say \"what\" again. I dare you. I double-dare you, motherfucker. Say \"what\" one more goddamn time.", - "Ezekiel 25:17 ,The path of the righteous man is beset on all sides by the iniquities of the selfish and the tyranny of evil men. Blessed is he who in the name of charity and good will shepherds the weak through the valley of darkness, for he is truly his brother's keeper and the finder of lost children. And I will strike down upon thee with great vengeance and furious anger those who attempt to poison and destroy my brothers. And you will know my name is the Lord... when I lay my vengeance upon thee.", - "Did you notice a sign out in front of my house that said \"Dead Nigger Storage\"?") - -/mob/living/simple_animal/head/Life() - . = ..() - if(!. || ai_inactive) return - - for(var/mob/A in viewers(world.view,src)) - if(A.ckey) - say_something(A) - -/mob/living/simple_animal/head/proc/say_something(mob/A) - if(prob(85)) - return - if(prob(30)) - var/msg = pick(insults) - msg = "Hey, [A.name].. [msg]" - src.say(msg) - else - var/msg = pick(comments) - src.say(msg) diff --git a/code/modules/mob/living/simple_animal/humanoids/mechamobs.dm b/code/modules/mob/living/simple_animal/humanoids/mechamobs.dm index 9f8c737894..1bc4b985a4 100644 --- a/code/modules/mob/living/simple_animal/humanoids/mechamobs.dm +++ b/code/modules/mob/living/simple_animal/humanoids/mechamobs.dm @@ -96,7 +96,7 @@ ..() playsound(src,'sound/mecha/mechstep.ogg',40,1) - +// This is a PoI mob, not the normal, floaty drones that hang out around windows /mob/living/simple_animal/hostile/mecha/malf_drone name = "autonomous mechanized drone" desc = "It appears to be an exosuit, piloted by a drone intelligence. It looks scary." diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 71a523c7d0..304fd3e7b2 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -591,12 +591,6 @@ if(act) ..(act, type, desc) -/mob/living/simple_animal/proc/visible_emote(var/act_desc) - custom_emote(1, act_desc) - -/mob/living/simple_animal/proc/audible_emote(var/act_desc) - custom_emote(2, act_desc) - /mob/living/simple_animal/bullet_act(var/obj/item/projectile/Proj) ai_log("bullet_act() I was shot by: [Proj.firer]",2) @@ -710,9 +704,9 @@ //SA vs SA basically /mob/living/simple_animal/attack_generic(var/mob/attacker) - ..() if(attacker) react_to_attack(attacker) + return ..() /mob/living/simple_animal/movement_delay() var/tally = 0 //Incase I need to add stuff other than "speed" later @@ -1280,8 +1274,11 @@ /mob/living/simple_animal/proc/PunchTarget() if(!Adjacent(target_mob)) return - if(!client) - sleep(rand(melee_attack_minDelay, melee_attack_maxDelay)) + if(!canClick()) + return + setClickCooldown(get_attack_speed()) +// if(!client) +// sleep(rand(melee_attack_minDelay, melee_attack_maxDelay)) if(isliving(target_mob)) var/mob/living/L = target_mob @@ -1322,13 +1319,18 @@ //The actual top-level ranged attack proc /mob/living/simple_animal/proc/ShootTarget() + if(!canClick()) + return FALSE + + setClickCooldown(get_attack_speed()) + var/target = target_mob var/tturf = get_turf(target) if((firing_lines && !client) && !CheckFiringLine(tturf)) step_rand(src) face_atom(tturf) - return 0 + return FALSE visible_message("[src] fires at [target]!") if(rapid) @@ -1349,7 +1351,7 @@ if(casingtype) new casingtype - return 1 + return TRUE //Check firing lines for faction_friends (if we're not cooperative, we don't care) /mob/living/simple_animal/proc/CheckFiringLine(var/turf/tturf) @@ -1582,13 +1584,13 @@ return 0 else return armorval - +/* // Force it to target something /mob/living/simple_animal/proc/taunt(var/mob/living/new_target, var/forced = FALSE) if(intelligence_level == SA_HUMANOID && !forced) return set_target(new_target) - +*/ /mob/living/simple_animal/is_sentient() return intelligence_level != SA_PLANT && intelligence_level != SA_ROBOTIC diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 843990c7f6..2b5d44ded9 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -17,8 +17,8 @@ maxHealth = 150 var/maxHealth_adult = 200 - melee_damage_lower = 5 - melee_damage_upper = 25 + melee_damage_lower = 10 + melee_damage_upper = 15 melee_miss_chance = 0 gender = NEUTER diff --git a/code/modules/mob/living/simple_mob/appearance.dm b/code/modules/mob/living/simple_mob/appearance.dm new file mode 100644 index 0000000000..05f758d7c4 --- /dev/null +++ b/code/modules/mob/living/simple_mob/appearance.dm @@ -0,0 +1,76 @@ +/mob/living/simple_mob/update_icon() + . = ..() + cut_overlays() +// var/mutable_appearance/ma = new(src) +// ma.layer = layer +// ma.plane = plane + + add_overlay(modifier_overlay) + + if(!icon_living) // Prevent the mob from turning invisible if icon_living is null. + icon_living = initial(icon_state) + + //Awake and normal + if((stat == CONSCIOUS) && (!icon_rest || !resting || !incapacitated(INCAPACITATION_DISABLED) )) + icon_state = icon_living + + //Dead + else if(stat >= DEAD) + icon_state = icon_dead + + //Resting or KO'd + else if(((stat == UNCONSCIOUS) || resting || incapacitated(INCAPACITATION_DISABLED) ) && icon_rest) + icon_state = icon_rest + + //Backup + else + icon_state = initial(icon_state) + + if(has_hands) + if(r_hand_sprite) + add_overlay(r_hand_sprite) + if(l_hand_sprite) + add_overlay(l_hand_sprite) + + if(has_eye_glow) + if(icon_state != icon_living) + remove_eyes() + else + add_eyes() + +// appearance = ma + + +// If your simple mob's update_icon() call calls overlays.Cut(), this needs to be called after this, or manually apply modifier_overly to overlays. +/mob/living/simple_mob/update_modifier_visuals() + var/image/effects = null + if(modifier_overlay) + cut_overlay(modifier_overlay) + modifier_overlay.cut_overlays() + effects = modifier_overlay + else + effects = new() + + for(var/datum/modifier/M in modifiers) + if(M.mob_overlay_state) + var/image/I = image("icon" = 'icons/mob/modifier_effects.dmi', "icon_state" = M.mob_overlay_state) + I.appearance_flags = RESET_COLOR // So colored mobs don't affect the overlay. + effects.add_overlay(I) + + modifier_overlay = effects + add_overlay(modifier_overlay) + + +/mob/living/simple_mob/proc/add_eyes() + if(!eye_layer) + eye_layer = image(icon, "[icon_state]-eyes") + eye_layer.plane = PLANE_LIGHTING_ABOVE + + add_overlay(eye_layer) + +/mob/living/simple_mob/proc/remove_eyes() + cut_overlay(eye_layer) + + +/mob/living/simple_mob/gib() + ..(icon_gib,1,icon) // we need to specify where the gib animation is stored \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/combat.dm b/code/modules/mob/living/simple_mob/combat.dm new file mode 100644 index 0000000000..e0cf980cc3 --- /dev/null +++ b/code/modules/mob/living/simple_mob/combat.dm @@ -0,0 +1,236 @@ +// Does a melee attack. +/mob/living/simple_mob/proc/attack_target(atom/A) + set waitfor = FALSE // For attack animations. Don't want the AI processor to get held up. + + if(!A.Adjacent(src)) + return FALSE + var/turf/their_T = get_turf(A) + + face_atom(A) + + if(melee_attack_delay) + // their_T.color = "#FF0000" + melee_pre_animation(A) + handle_attack_delay(A, melee_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false. + + // Cooldown testing is done at click code (for players) and interface code (for AI). + setClickCooldown(get_attack_speed()) + + . = do_attack(A, their_T) + + if(melee_attack_delay) + melee_post_animation(A) + // their_T.color = "#FFFFFF" + + + +// This does the actual attack. +// This is a seperate proc for the purposes of attack animations. +// A is the thing getting attacked, T is the turf A is/was on when attack_target was called. +/mob/living/simple_mob/proc/do_attack(atom/A, turf/T) + face_atom(A) + var/missed = FALSE + if(!isturf(A) && !(A in T) ) // Turfs don't contain themselves so checking contents is pointless if we're targeting a turf. + missed = TRUE + else if(!T.AdjacentQuick(src)) + missed = TRUE + + if(missed) // Most likely we have a slow attack and they dodged it or we somehow got moved. + add_attack_logs(src, A, "Animal-attacked (dodged)", admin_notify = FALSE) + playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) + visible_message(span("warning", "\The [src] misses their attack.")) + return FALSE + + var/damage_to_do = rand(melee_damage_lower, melee_damage_upper) + + damage_to_do = apply_bonus_melee_damage(A, damage_to_do) + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.outgoing_melee_damage_percent)) + damage_to_do *= M.outgoing_melee_damage_percent + + if(isliving(A)) // Check defenses. + var/mob/living/L = A + + if(prob(melee_miss_chance)) + add_attack_logs(src, L, "Animal-attacked (miss)", admin_notify = FALSE) + do_attack_animation(src) + playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) + return FALSE // We missed. + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.check_shields(damage = damage_to_do, damage_source = src, attacker = src, def_zone = null, attack_text = "the attack")) + return FALSE // We were blocked. + + if(apply_attack(A, damage_to_do)) + apply_melee_effects(A) + if(attack_sound) + playsound(src, attack_sound, 75, 1) + + return TRUE + +// Generally used to do the regular attack. +// Override for doing special stuff with the direct result of the attack. +/mob/living/simple_mob/proc/apply_attack(atom/A, damage_to_do) + return A.attack_generic(src, damage_to_do, pick(attacktext)) + +// Override for special effects after a successful attack, like injecting poison or stunning the target. +/mob/living/simple_mob/proc/apply_melee_effects(atom/A) + return + +// Override to modify the amount of damage the mob does conditionally. +// This must return the amount of outgoing damage. +// Note that this is done before mob modifiers scale the damage. +/mob/living/simple_mob/proc/apply_bonus_melee_damage(atom/A, damage_amount) + return damage_amount + +//The actual top-level ranged attack proc +/mob/living/simple_mob/proc/shoot_target(atom/A) + set waitfor = FALSE + setClickCooldown(get_attack_speed()) + + face_atom(A) + + if(ranged_attack_delay) + ranged_pre_animation(A) + handle_attack_delay(A, ranged_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false. + + if(needs_reload) + if(reload_count >= reload_max) + try_reload() + return FALSE + + visible_message("\The [src] fires at \the [A]!") + shoot(A, src.loc, src) + if(casingtype) + new casingtype(loc) + + if(ranged_attack_delay) + ranged_post_animation(A) + + return TRUE + + +//Shoot a bullet at someone (idk why user is an argument when src would fit???) +/mob/living/simple_mob/proc/shoot(atom/A, turf/start, mob/living/user, bullet = 0) + if(A == start) + return + + face_atom(A) + + var/obj/item/projectile/P = new projectiletype(user.loc) + if(!P) + return + + // If the projectile has its own sound, use it. + // Otherwise default to the mob's firing sound. + playsound(user, P.fire_sound ? P.fire_sound : projectilesound, 80, 1) + + P.launch(A) + if(needs_reload) + reload_count++ + +// if(distance >= special_attack_min_range && distance <= special_attack_max_range) +// return TRUE + +/mob/living/simple_mob/proc/try_reload() + set waitfor = FALSE + set_AI_busy(TRUE) + + if(do_after(src, reload_time)) + if(reload_sound) + playsound(src.loc, reload_sound, 50, 1) + reload_count = 0 + . = TRUE + else + . = FALSE + set_AI_busy(FALSE) + +// Can we currently do a special attack? +/mob/living/simple_mob/proc/can_special_attack(atom/A) + // Validity check. + if(!istype(A)) + return FALSE + + // Ability check. + if(isnull(special_attack_min_range) || isnull(special_attack_max_range)) + return FALSE + + // Distance check. + var/distance = get_dist(src, A) + if(distance < special_attack_min_range || distance > special_attack_max_range) + return FALSE + + // Cooldown check. + if(!isnull(special_attack_cooldown) && last_special_attack + special_attack_cooldown > world.time) + return FALSE + + // Charge check. + if(!isnull(special_attack_charges) && special_attack_charges <= 0) + return FALSE + + return TRUE + +// Should we do one? Used to make the AI not waste their special attacks. Only checked for AI. Players are free to screw up on their own. +/mob/living/simple_mob/proc/should_special_attack(atom/A) + return TRUE + +// Special attacks, like grenades or blinding spit or whatever. +// Don't override this, override do_special_attack() for your blinding spit/etc. +/mob/living/simple_mob/proc/special_attack_target(atom/A) + face_atom(A) + + if(special_attack_delay) + special_pre_animation(A) + handle_attack_delay(A, special_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false. + + last_special_attack = world.time + if(do_special_attack(A)) + if(special_attack_charges) + special_attack_charges -= 1 + . = TRUE + else + . = FALSE + + if(special_attack_delay) + special_post_animation(A) + +// Override this for the actual special attack. +/mob/living/simple_mob/proc/do_special_attack(atom/A) + return FALSE + +// Sleeps the proc that called it for the correct amount of time. +// Also makes sure the AI doesn't do anything stupid in the middle of the delay. +/mob/living/simple_mob/proc/handle_attack_delay(atom/A, delay_amount) + set_AI_busy(TRUE) + + // Click delay modifiers also affect telegraphing time. + // This means berserked enemies will leave less time to dodge. + var/true_attack_delay = delay_amount + for(var/datum/modifier/M in modifiers) + if(!isnull(M.attack_speed_percent)) + true_attack_delay *= M.attack_speed_percent + + setClickCooldown(true_attack_delay) // Insurance against a really long attack being longer than default click delay. + + sleep(true_attack_delay) + + set_AI_busy(FALSE) + + +// Override these four for special custom animations (like the GOLEM). +/mob/living/simple_mob/proc/melee_pre_animation(atom/A) + do_windup_animation(A, melee_attack_delay) + +/mob/living/simple_mob/proc/melee_post_animation(atom/A) + +/mob/living/simple_mob/proc/ranged_pre_animation(atom/A) + do_windup_animation(A, ranged_attack_delay) // Semi-placeholder. + +/mob/living/simple_mob/proc/ranged_post_animation(atom/A) + +/mob/living/simple_mob/proc/special_pre_animation(atom/A) + do_windup_animation(A, special_attack_delay) // Semi-placeholder. + +/mob/living/simple_mob/proc/special_post_animation(atom/A) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/defense.dm b/code/modules/mob/living/simple_mob/defense.dm new file mode 100644 index 0000000000..af1b2774b3 --- /dev/null +++ b/code/modules/mob/living/simple_mob/defense.dm @@ -0,0 +1,224 @@ +// Hit by a projectile. +/mob/living/simple_mob/bullet_act(var/obj/item/projectile/P) + //Projectiles with bonus SA damage + if(!P.nodamage) + // if(!P.SA_vulnerability || P.SA_vulnerability == intelligence_level) + if(P.SA_vulnerability & mob_class) + P.damage += P.SA_bonus_damage + + . = ..() + + +// When someone clicks us with an empty hand +/mob/living/simple_mob/attack_hand(mob/living/L) + ..() + + switch(L.a_intent) + if(I_HELP) + if(health > 0) + L.visible_message("\The [L] [response_help] \the [src].") + + if(I_DISARM) + L.visible_message("\The [L] [response_disarm] \the [src].") + L.do_attack_animation(src) + //TODO: Push the mob away or something + + if(I_GRAB) + if (L == src) + return + if (!(status_flags & CANPUSH)) + return + if(!incapacitated(INCAPACITATION_ALL) && prob(grab_resist)) + L.visible_message("\The [L] tries to grab \the [src] but fails!") + return + + var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(L, src) + + L.put_in_active_hand(G) + + G.synch() + G.affecting = src + LAssailant = L + + L.visible_message("\The [L] has grabbed [src] passively!") + L.do_attack_animation(src) + + if(I_HURT) + var/armor = run_armor_check(def_zone = null, attack_flag = "melee") + apply_damage(damage = harm_intent_damage, damagetype = BURN, def_zone = null, blocked = armor, blocked = resistance, used_weapon = null, sharp = FALSE, edge = FALSE) + L.visible_message("\The [L] [response_harm] \the [src]!") + L.do_attack_animation(src) + + return + + +// When somoene clicks us with an item in hand +/mob/living/simple_mob/attackby(var/obj/item/O, var/mob/user) + if(istype(O, /obj/item/stack/medical)) + if(stat != DEAD) + // This could be done better. + var/obj/item/stack/medical/MED = O + if(health < getMaxHealth()) + if(MED.amount >= 1) + adjustBruteLoss(-MED.heal_brute) + MED.amount -= 1 + if(MED.amount <= 0) + qdel(MED) + visible_message("\The [user] applies the [MED] on [src].") + else + var/datum/gender/T = gender_datums[src.get_visible_gender()] + to_chat(user, "\The [src] is dead, medical items won't bring [T.him] back to life.") // the gender lookup is somewhat overkill, but it functions identically to the obsolete gender macros and future-proofs this code + if(meat_type && (stat == DEAD)) //if the animal has a meat, and if it is dead. + if(istype(O, /obj/item/weapon/material/knife)) + harvest(user) + + return ..() + + +// Handles the actual harming by a melee weapon. +/mob/living/simple_mob/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone) + effective_force = O.force + + //Animals can't be stunned(?) + if(O.damtype == HALLOSS) + effective_force = 0 + if(supernatural && istype(O,/obj/item/weapon/nullrod)) + effective_force *= 2 + purge = 3 + if(O.force <= resistance) + to_chat(user,"This weapon is ineffective, it does no damage.") + return 2 //??? + + . = ..() + + +// Exploding. +/mob/living/simple_mob/ex_act(severity) + if(!blinded) + flash_eyes() + var/armor = run_armor_check(def_zone = null, attack_flag = "bomb") + var/bombdam = 500 + switch (severity) + if (1.0) + bombdam = 500 + if (2.0) + bombdam = 60 + if (3.0) + bombdam = 30 + + apply_damage(damage = bombdam, damagetype = BRUTE, def_zone = null, blocked = armor, blocked = resistance, used_weapon = null, sharp = FALSE, edge = FALSE) + + if(bombdam > maxHealth) + gib() + +// Cold stuff. +/mob/living/simple_mob/get_cold_protection() + return cold_resist + + +// Fire stuff. Not really exciting at the moment. +/mob/living/simple_mob/handle_fire() + return +/mob/living/simple_mob/update_fire() + return +/mob/living/simple_mob/IgniteMob() + return +/mob/living/simple_mob/ExtinguishMob() + return + +/mob/living/simple_mob/get_heat_protection() + return heat_resist + +// Electricity +/mob/living/simple_mob/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null) + shock_damage *= siemens_coeff + if(shock_damage < 1) + return 0 + + apply_damage(damage = shock_damage, damagetype = BURN, def_zone = null, blocked = null, blocked = resistance, used_weapon = null, sharp = FALSE, edge = FALSE) + playsound(loc, "sparks", 50, 1, -1) + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, loc) + s.start() + +/mob/living/simple_mob/get_shock_protection() + return shock_resist + +// Shot with taser/stunvolver +/mob/living/simple_mob/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null) + if(taser_kill) + var/stunDam = 0 + var/agonyDam = 0 + var/armor = run_armor_check(def_zone = null, attack_flag = "energy") + + if(stun_amount) + stunDam += stun_amount * 0.5 + apply_damage(damage = stunDam, damagetype = BURN, def_zone = null, blocked = armor, blocked = resistance, used_weapon = used_weapon, sharp = FALSE, edge = FALSE) + + if(agony_amount) + agonyDam += agony_amount * 0.5 + apply_damage(damage = agonyDam, damagetype = BURN, def_zone = null, blocked = armor, blocked = resistance, used_weapon = used_weapon, sharp = FALSE, edge = FALSE) + + +// Electromagnetism +/mob/living/simple_mob/emp_act(severity) + ..() // To emp_act() its contents. + if(!isSynthetic()) + return + switch(severity) + if(1) + // adjustFireLoss(rand(15, 25)) + adjustFireLoss(min(60, getMaxHealth()*0.5)) // Weak mobs will always take two direct EMP hits to kill. Stronger ones might take more. + if(2) + adjustFireLoss(min(30, getMaxHealth()*0.25)) + // adjustFireLoss(rand(10, 18)) + if(3) + adjustFireLoss(min(15, getMaxHealth()*0.125)) + // adjustFireLoss(rand(5, 12)) + if(4) + adjustFireLoss(min(7, getMaxHealth()*0.0625)) + // adjustFireLoss(rand(1, 6)) + +// Water +/mob/living/simple_mob/get_water_protection() + return water_resist + +// "Poison" (aka what reagents would do if we wanted to deal with those). +/mob/living/simple_mob/get_poison_protection() + return poison_resist + +// Armor +/mob/living/simple_mob/getarmor(def_zone, attack_flag) + var/armorval = armor[attack_flag] + if(!armorval) + return 0 + else + return armorval + +/mob/living/simple_mob/getsoak(def_zone, attack_flag) + var/armorval = armor_soak[attack_flag] + if(!armorval) + return 0 + else + return armorval + +// Lightning +/mob/living/simple_mob/lightning_act() + ..() + // If a non-player simple_mob was struck, inflict huge damage. + // If the damage is fatal, it is turned to ash. + if(!client) + inflict_shock_damage(200) // Mobs that are very beefy or resistant to shock may survive getting struck. + updatehealth() + if(health <= 0) + visible_message(span("critical", "\The [src] disintegrates into ash!")) + ash() + return // No point deafening something that wont exist. + +// Injections. +/mob/living/simple_mob/can_inject(mob/user, error_msg, target_zone, ignore_thickness) + if(ignore_thickness) + return TRUE + return !thick_armor + diff --git a/code/modules/mob/living/simple_mob/hands.dm b/code/modules/mob/living/simple_mob/hands.dm new file mode 100644 index 0000000000..0e8820fe2e --- /dev/null +++ b/code/modules/mob/living/simple_mob/hands.dm @@ -0,0 +1,143 @@ +// Hand procs for player-controlled SA's +/mob/living/simple_mob/swap_hand() + src.hand = !( src.hand ) + if(hud_used.l_hand_hud_object && hud_used.r_hand_hud_object) + if(hand) //This being 1 means the left hand is in use + hud_used.l_hand_hud_object.icon_state = "l_hand_active" + hud_used.r_hand_hud_object.icon_state = "r_hand_inactive" + else + hud_used.l_hand_hud_object.icon_state = "l_hand_inactive" + hud_used.r_hand_hud_object.icon_state = "r_hand_active" + return + +/mob/living/simple_mob/put_in_hands(var/obj/item/W) // No hands. + if(has_hands) + put_in_active_hand(W) + return 1 + W.forceMove(get_turf(src)) + return 1 + +//Puts the item into our active hand if possible. returns 1 on success. +/mob/living/simple_mob/put_in_active_hand(var/obj/item/W) + if(!has_hands) + return FALSE + return (hand ? put_in_l_hand(W) : put_in_r_hand(W)) + +/mob/living/simple_mob/put_in_l_hand(var/obj/item/W) + if(!..() || l_hand) + return 0 + W.forceMove(src) + l_hand = W + W.equipped(src,slot_l_hand) + W.add_fingerprint(src) + update_inv_l_hand() + return TRUE + +/mob/living/simple_mob/put_in_r_hand(var/obj/item/W) + if(!..() || r_hand) + return 0 + W.forceMove(src) + r_hand = W + W.equipped(src,slot_r_hand) + W.add_fingerprint(src) + update_inv_r_hand() + return TRUE + +/mob/living/simple_mob/update_inv_r_hand() + if(QDESTROYING(src)) + return + + if(r_hand) + r_hand.screen_loc = ui_rhand //TODO + + //determine icon state to use + var/t_state + if(r_hand.item_state_slots && r_hand.item_state_slots[slot_r_hand_str]) + t_state = r_hand.item_state_slots[slot_r_hand_str] + else if(r_hand.item_state) + t_state = r_hand.item_state + else + t_state = r_hand.icon_state + + //determine icon to use + var/icon/t_icon + if(r_hand.item_icons && (slot_r_hand_str in r_hand.item_icons)) + t_icon = r_hand.item_icons[slot_r_hand_str] + else if(r_hand.icon_override) + t_state += "_r" + t_icon = r_hand.icon_override + else + t_icon = INV_R_HAND_DEF_ICON + + //apply color + var/image/standing = image(icon = t_icon, icon_state = t_state) + standing.color = r_hand.color + + r_hand_sprite = standing + + else + r_hand_sprite = null + + update_icon() + +/mob/living/simple_mob/update_inv_l_hand() + if(QDESTROYING(src)) + return + + if(l_hand) + l_hand.screen_loc = ui_lhand //TODO + + //determine icon state to use + var/t_state + if(l_hand.item_state_slots && l_hand.item_state_slots[slot_l_hand_str]) + t_state = l_hand.item_state_slots[slot_l_hand_str] + else if(l_hand.item_state) + t_state = l_hand.item_state + else + t_state = l_hand.icon_state + + //determine icon to use + var/icon/t_icon + if(l_hand.item_icons && (slot_l_hand_str in l_hand.item_icons)) + t_icon = l_hand.item_icons[slot_l_hand_str] + else if(l_hand.icon_override) + t_state += "_l" + t_icon = l_hand.icon_override + else + t_icon = INV_L_HAND_DEF_ICON + + //apply color + var/image/standing = image(icon = t_icon, icon_state = t_state) + standing.color = l_hand.color + + l_hand_sprite = standing + + else + l_hand_sprite = null + + update_icon() + +//Can insert extra huds into the hud holder here. +/mob/living/simple_mob/proc/extra_huds(var/datum/hud/hud,var/icon/ui_style,var/list/hud_elements) + return + +//If they can or cannot use tools/machines/etc +/mob/living/simple_mob/IsAdvancedToolUser() + return has_hands + +/mob/living/simple_mob/proc/IsHumanoidToolUser(var/atom/tool) + if(!humanoid_hands) + var/display_name = null + if(tool) + display_name = tool + else + display_name = "object" + to_chat(src, "Your [hand_form] are not fit for use of \the [display_name].") + return humanoid_hands + +/mob/living/simple_mob/drop_from_inventory(var/obj/item/W, var/atom/target = null) + . = ..(W, target) + if(!target) + target = src.loc + if(.) + W.forceMove(src.loc) diff --git a/code/modules/mob/living/simple_mob/life.dm b/code/modules/mob/living/simple_mob/life.dm new file mode 100644 index 0000000000..bab2aa4129 --- /dev/null +++ b/code/modules/mob/living/simple_mob/life.dm @@ -0,0 +1,160 @@ +/mob/living/simple_mob/Life() + ..() + + //Health + updatehealth() + if(stat >= DEAD) + return FALSE + + handle_stunned() + handle_weakened() + handle_paralysed() + handle_supernatural() + handle_atmos() + + handle_special() + + return TRUE + + +//Should we be dead? +/mob/living/simple_mob/updatehealth() + health = getMaxHealth() - getFireLoss() - getBruteLoss() - getToxLoss() - getOxyLoss() - getCloneLoss() + + //Alive, becoming dead + if((stat < DEAD) && (health <= 0)) + death() + + //Overhealth + if(health > getMaxHealth()) + health = getMaxHealth() + + //Update our hud if we have one + if(healths) + if(stat != DEAD) + var/heal_per = (health / getMaxHealth()) * 100 + switch(heal_per) + if(100 to INFINITY) + healths.icon_state = "health0" + if(80 to 100) + healths.icon_state = "health1" + if(60 to 80) + healths.icon_state = "health2" + if(40 to 60) + healths.icon_state = "health3" + if(20 to 40) + healths.icon_state = "health4" + if(0 to 20) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + + //Updates the nutrition while we're here + if(nutrition_icon) + var/food_per = (nutrition / initial(nutrition)) * 100 + switch(food_per) + if(90 to INFINITY) + nutrition_icon.icon_state = "nutrition0" + if(75 to 90) + nutrition_icon.icon_state = "nutrition1" + if(50 to 75) + nutrition_icon.icon_state = "nutrition2" + if(25 to 50) + nutrition_icon.icon_state = "nutrition3" + if(0 to 25) + nutrition_icon.icon_state = "nutrition4" + +// Override for special bullshit. +/mob/living/simple_mob/proc/handle_special() + return + + +// Handle interacting with and taking damage from atmos +// TODO - Refactor this to use handle_environment() like a good /mob/living +/mob/living/simple_mob/proc/handle_atmos() + var/atmos_unsuitable = 0 + + var/atom/A = src.loc + + if(istype(A,/turf)) + var/turf/T = A + + var/datum/gas_mixture/Environment = T.return_air() + + if(Environment) + + if( abs(Environment.temperature - bodytemperature) > 40 ) + bodytemperature += ((Environment.temperature - bodytemperature) / 5) + + if(min_oxy) + if(Environment.gas["oxygen"] < min_oxy) + atmos_unsuitable = 1 + if(max_oxy) + if(Environment.gas["oxygen"] > max_oxy) + atmos_unsuitable = 1 + if(min_tox) + if(Environment.gas["phoron"] < min_tox) + atmos_unsuitable = 2 + if(max_tox) + if(Environment.gas["phoron"] > max_tox) + atmos_unsuitable = 2 + if(min_n2) + if(Environment.gas["nitrogen"] < min_n2) + atmos_unsuitable = 1 + if(max_n2) + if(Environment.gas["nitrogen"] > max_n2) + atmos_unsuitable = 1 + if(min_co2) + if(Environment.gas["carbon_dioxide"] < min_co2) + atmos_unsuitable = 1 + if(max_co2) + if(Environment.gas["carbon_dioxide"] > max_co2) + atmos_unsuitable = 1 + + //Atmos effect + if(bodytemperature < minbodytemp) + fire_alert = 2 + adjustFireLoss(cold_damage_per_tick) + if(fire) + fire.icon_state = "fire1" + else if(bodytemperature > maxbodytemp) + fire_alert = 1 + adjustFireLoss(heat_damage_per_tick) + if(fire) + fire.icon_state = "fire2" + else + fire_alert = 0 + if(fire) + fire.icon_state = "fire0" + + if(atmos_unsuitable) + adjustOxyLoss(unsuitable_atoms_damage) + if(oxygen) + oxygen.icon_state = "oxy1" + else if(oxygen) + if(oxygen) + oxygen.icon_state = "oxy0" + adjustOxyLoss(-unsuitable_atoms_damage) + + +/mob/living/simple_mob/proc/handle_supernatural() + if(purge) + purge -= 1 + +/mob/living/simple_mob/death(gibbed, deathmessage = "dies!") + density = 0 //We don't block even if we did before + + if(has_eye_glow) + remove_eyes() + + if(loot_list.len) //Drop any loot + for(var/path in loot_list) + if(prob(loot_list[path])) + new path(get_turf(src)) + + spawn(3) //We'll update our icon in a sec + update_icon() + + return ..(gibbed,deathmessage) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/on_click.dm b/code/modules/mob/living/simple_mob/on_click.dm new file mode 100644 index 0000000000..4b824c9580 --- /dev/null +++ b/code/modules/mob/living/simple_mob/on_click.dm @@ -0,0 +1,48 @@ +/* + Animals +*/ +/mob/living/simple_mob/UnarmedAttack(var/atom/A, var/proximity) + if(!(. = ..())) + return + +// setClickCooldown(get_attack_speed()) + + if(has_hands && istype(A,/obj) && a_intent != I_HURT) + var/obj/O = A + return O.attack_hand(src) + + switch(a_intent) + if(I_HELP) + if(isliving(A)) + custom_emote(1,"[pick(friendly)] \the [A]!") + + if(I_HURT) + if(can_special_attack(A) && special_attack_target(A)) + return + + else if(melee_damage_upper == 0 && istype(A,/mob/living)) + custom_emote(1,"[pick(friendly)] \the [A]!") + + else + attack_target(A) + + if(I_GRAB) + if(has_hands) + A.attack_hand(src) + else + attack_target(A) + + if(I_DISARM) + if(has_hands) + A.attack_hand(src) + else + attack_target(A) + +/mob/living/simple_mob/RangedAttack(var/atom/A) +// setClickCooldown(get_attack_speed()) + + if(can_special_attack(A) && special_attack_target(A)) + return + + if(projectiletype) + shoot_target(A) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/simple_hud.dm b/code/modules/mob/living/simple_mob/simple_hud.dm new file mode 100644 index 0000000000..fe851648b4 --- /dev/null +++ b/code/modules/mob/living/simple_mob/simple_hud.dm @@ -0,0 +1,311 @@ +/mob/living/simple_mob/instantiate_hud(var/datum/hud/hud) + if(!client) + return //Why bother. + + var/ui_style = 'icons/mob/screen1_animal.dmi' + if(ui_icons) + ui_style = ui_icons + + var/ui_color = "#ffffff" + var/ui_alpha = 255 + + var/list/adding = list() + var/list/other = list() + var/list/hotkeybuttons = list() + var/list/slot_info = list() + + hud.adding = adding + hud.other = other + hud.hotkeybuttons = hotkeybuttons + + var/list/hud_elements = list() + var/obj/screen/using + var/obj/screen/inventory/inv_box + + var/has_hidden_gear + if(LAZYLEN(hud_gears)) + for(var/gear_slot in hud_gears) + inv_box = new /obj/screen/inventory() + inv_box.icon = ui_style + inv_box.color = ui_color + inv_box.alpha = ui_alpha + + var/list/slot_data = hud_gears[gear_slot] + inv_box.name = gear_slot + inv_box.screen_loc = slot_data["loc"] + inv_box.slot_id = slot_data["slot"] + inv_box.icon_state = slot_data["state"] + slot_info["[inv_box.slot_id]"] = inv_box.screen_loc + + if(slot_data["dir"]) + inv_box.set_dir(slot_data["dir"]) + + if(slot_data["toggle"]) + other += inv_box + has_hidden_gear = 1 + else + adding += inv_box + + if(has_hidden_gear) + using = new /obj/screen() + using.name = "toggle" + using.icon = ui_style + using.icon_state = "other" + using.screen_loc = ui_inventory + using.hud_layerise() + using.color = ui_color + using.alpha = ui_alpha + adding += using + + //Intent Backdrop + using = new /obj/screen() + using.name = "act_intent" + using.icon = ui_style + using.icon_state = "intent_"+a_intent + using.screen_loc = ui_acti + using.color = ui_color + using.alpha = ui_alpha + hud.adding += using + hud.action_intent = using + + hud_elements |= using + + //Small intent quarters + var/icon/ico + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),1,ico.Height()/2,ico.Width()/2,ico.Height()) + using = new /obj/screen() + using.name = I_HELP + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM //These sit on the intent box + hud.adding += using + hud.help_intent = using + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),ico.Width()/2,ico.Height()/2,ico.Width(),ico.Height()) + using = new /obj/screen() + using.name = I_DISARM + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM + hud.adding += using + hud.disarm_intent = using + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),ico.Width()/2,1,ico.Width(),ico.Height()/2) + using = new /obj/screen() + using.name = I_GRAB + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM + hud.adding += using + hud.grab_intent = using + + ico = new(ui_style, "black") + ico.MapColors(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, -1,-1,-1,-1) + ico.DrawBox(rgb(255,255,255,1),1,1,ico.Width()/2,ico.Height()/2) + using = new /obj/screen() + using.name = I_HURT + using.icon = ico + using.screen_loc = ui_acti + using.alpha = ui_alpha + using.layer = LAYER_HUD_ITEM + hud.adding += using + hud.hurt_intent = using + + //Move intent (walk/run) + using = new /obj/screen() + using.name = "mov_intent" + using.icon = ui_style + using.icon_state = (m_intent == "run" ? "running" : "walking") + using.screen_loc = ui_movi + using.color = ui_color + using.alpha = ui_alpha + hud.adding += using + hud.move_intent = using + + //Resist button + using = new /obj/screen() + using.name = "resist" + using.icon = ui_style + using.icon_state = "act_resist" + using.screen_loc = ui_pull_resist + using.color = ui_color + using.alpha = ui_alpha + hud.hotkeybuttons += using + + //Pull button + pullin = new /obj/screen() + pullin.icon = ui_style + pullin.icon_state = "pull0" + pullin.name = "pull" + pullin.screen_loc = ui_pull_resist + hud.hotkeybuttons += pullin + hud_elements |= pullin + + //Health status + healths = new /obj/screen() + healths.icon = ui_style + healths.icon_state = "health0" + healths.name = "health" + healths.screen_loc = ui_health + hud_elements |= healths + + //Oxygen dep icon + oxygen = new /obj/screen() + oxygen.icon = ui_style + oxygen.icon_state = "oxy0" + oxygen.name = "oxygen" + oxygen.screen_loc = ui_oxygen + hud_elements |= oxygen + + //Toxins present icon + toxin = new /obj/screen() + toxin.icon = ui_style + toxin.icon_state = "tox0" + toxin.name = "toxin" + toxin.screen_loc = ui_toxin + hud_elements |= toxin + + //Fire warning + fire = new /obj/screen() + fire.icon = ui_style + fire.icon_state = "fire0" + fire.name = "fire" + fire.screen_loc = ui_fire + hud_elements |= fire + + //Pressure warning + pressure = new /obj/screen() + pressure.icon = ui_style + pressure.icon_state = "pressure0" + pressure.name = "pressure" + pressure.screen_loc = ui_pressure + hud_elements |= pressure + + //Body temp warning + bodytemp = new /obj/screen() + bodytemp.icon = ui_style + bodytemp.icon_state = "temp0" + bodytemp.name = "body temperature" + bodytemp.screen_loc = ui_temp + hud_elements |= bodytemp + + //Nutrition status + nutrition_icon = new /obj/screen() + nutrition_icon.icon = ui_style + nutrition_icon.icon_state = "nutrition0" + nutrition_icon.name = "nutrition" + nutrition_icon.screen_loc = ui_nutrition + hud_elements |= nutrition_icon + + pain = new /obj/screen( null ) + + zone_sel = new /obj/screen/zone_sel( null ) + zone_sel.icon = ui_style + zone_sel.color = ui_color + zone_sel.alpha = ui_alpha + zone_sel.overlays.Cut() + zone_sel.overlays += image('icons/mob/zone_sel.dmi', "[zone_sel.selecting]") + hud_elements |= zone_sel + + //Hand things + if(has_hands) + //Drop button + using = new /obj/screen() + using.name = "drop" + using.icon = ui_style + using.icon_state = "act_drop" + using.screen_loc = ui_drop_throw + using.color = ui_color + using.alpha = ui_alpha + hud.hotkeybuttons += using + + //Equip detail + using = new /obj/screen() + using.name = "equip" + using.icon = ui_style + using.icon_state = "act_equip" + using.screen_loc = ui_equip + using.color = ui_color + using.alpha = ui_alpha + hud.adding += using + + //Hand slots themselves + inv_box = new /obj/screen/inventory/hand() + inv_box.hud = src + inv_box.name = "r_hand" + inv_box.icon = ui_style + inv_box.icon_state = "r_hand_inactive" + if(!hand) //This being 0 or null means the right hand is in use + inv_box.icon_state = "r_hand_active" + inv_box.screen_loc = ui_rhand + inv_box.slot_id = slot_r_hand + inv_box.color = ui_color + inv_box.alpha = ui_alpha + hud.r_hand_hud_object = inv_box + hud.adding += inv_box + slot_info["[slot_r_hand]"] = inv_box.screen_loc + + inv_box = new /obj/screen/inventory/hand() + inv_box.hud = src + inv_box.name = "l_hand" + inv_box.icon = ui_style + inv_box.icon_state = "l_hand_inactive" + if(hand) //This being 1 means the left hand is in use + inv_box.icon_state = "l_hand_active" + inv_box.screen_loc = ui_lhand + inv_box.slot_id = slot_l_hand + inv_box.color = ui_color + inv_box.alpha = ui_alpha + hud.l_hand_hud_object = inv_box + hud.adding += inv_box + slot_info["[slot_l_hand]"] = inv_box.screen_loc + + //Swaphand titlebar + using = new /obj/screen/inventory() + using.name = "hand" + using.icon = ui_style + using.icon_state = "hand1" + using.screen_loc = ui_swaphand1 + using.color = ui_color + using.alpha = ui_alpha + hud.adding += using + + using = new /obj/screen/inventory() + using.name = "hand" + using.icon = ui_style + using.icon_state = "hand2" + using.screen_loc = ui_swaphand2 + using.color = ui_color + using.alpha = ui_alpha + hud.adding += using + + //Throw button + throw_icon = new /obj/screen() + throw_icon.icon = ui_style + throw_icon.icon_state = "act_throw_off" + throw_icon.name = "throw" + throw_icon.screen_loc = ui_drop_throw + throw_icon.color = ui_color + throw_icon.alpha = ui_alpha + hud.hotkeybuttons += throw_icon + hud_elements |= throw_icon + + extra_huds(hud,ui_style,hud_elements) + + client.screen = list() + + client.screen += hud_elements + client.screen += adding + hotkeybuttons + client.screen += client.void + + return diff --git a/code/modules/mob/living/simple_mob/simple_mob.dm b/code/modules/mob/living/simple_mob/simple_mob.dm new file mode 100644 index 0000000000..c1e6b91adb --- /dev/null +++ b/code/modules/mob/living/simple_mob/simple_mob.dm @@ -0,0 +1,290 @@ +// Reorganized and somewhat cleaned up. +// AI code has been made into a datum, inside the AI module folder. + +/mob/living/simple_mob + name = "animal" + desc = "" + icon = 'icons/mob/animal.dmi' + health = 20 + maxHealth = 20 + + // Generally we don't want simple_mobs to get displaced when bumped into due to it trivializing combat with windup attacks. + // Some subtypes allow displacement, like passive animals. + mob_bump_flag = HEAVY + mob_swap_flags = ~HEAVY + mob_push_flags = ~HEAVY + + var/tt_desc = "Uncataloged Life Form" //Tooltip description + + //Settings for played mobs + var/show_stat_health = 1 // Does the percentage health show in the stat panel for the mob + var/has_hands = 0 // Set to 1 to enable the use of hands and the hands hud + var/humanoid_hands = 0 // Can a player in this mob use things like guns or AI cards? + var/hand_form = "hands" // Used in IsHumanoidToolUser. 'Your X are not fit-'. + var/list/hud_gears // Slots to show on the hud (typically none) + var/ui_icons // Icon file path to use for the HUD, otherwise generic icons are used + var/r_hand_sprite // If they have hands, + var/l_hand_sprite // they could use some icons. + var/player_msg // Message to print to players about 'how' to play this mob on login. + + //Mob icon/appearance settings + var/icon_living = "" // The iconstate if we're alive, required + var/icon_dead = "" // The iconstate if we're dead, required + var/icon_gib = "generic_gib" // The iconstate for being gibbed, optional. Defaults to a generic gib animation. + var/icon_rest = null // The iconstate for resting, optional + var/image/modifier_overlay = null // Holds overlays from modifiers. + var/image/eye_layer = null // Holds the eye overlay. + var/has_eye_glow = FALSE // If true, adds an overlay over the lighting plane for [icon_state]-eyes. + attack_icon = 'icons/effects/effects.dmi' //Just the default, played like the weapon attack anim + attack_icon_state = "slash" //Just the default + + //Mob talking settings + universal_speak = 0 // Can all mobs in the entire universe understand this one? + var/has_langs = list(LANGUAGE_GALCOM)// Text name of their language if they speak something other than galcom. They speak the first one. + + //Movement things. + var/movement_cooldown = 5 // Lower is faster. + var/movement_sound = null // If set, will play this sound when it moves on its own will. + var/turn_sound = null // If set, plays the sound when the mob's dir changes in most cases. + var/movement_shake_radius = 0 // If set, moving will shake the camera of all living mobs within this radius slightly. + + //Mob interaction + var/response_help = "tries to help" // If clicked on help intent + var/response_disarm = "tries to disarm" // If clicked on disarm intent + var/response_harm = "tries to hurt" // If clicked on harm intent + var/list/friends = list() // Mobs on this list wont get attacked regardless of faction status. + var/harm_intent_damage = 3 // How much an unarmed harm click does to this mob. + var/meat_amount = 0 // How much meat to drop from this mob when butchered + var/obj/meat_type // The meat object to drop + var/list/loot_list = list() // The list of lootable objects to drop, with "/path = prob%" structure + var/obj/item/weapon/card/id/myid// An ID card if they have one to give them access to stuff. + + //Mob environment settings + var/minbodytemp = 250 // Minimum "okay" temperature in kelvin + var/maxbodytemp = 350 // Maximum of above + var/heat_damage_per_tick = 3 // Amount of damage applied if animal's body temperature is higher than maxbodytemp + var/cold_damage_per_tick = 2 // Same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + var/fire_alert = 0 // 0 = fine, 1 = hot, 2 = cold + + var/min_oxy = 5 // Oxygen in moles, minimum, 0 is 'no minimum' + var/max_oxy = 0 // Oxygen in moles, maximum, 0 is 'no maximum' + var/min_tox = 0 // Phoron min + var/max_tox = 1 // Phoron max + var/min_co2 = 0 // CO2 min + var/max_co2 = 5 // CO2 max + var/min_n2 = 0 // N2 min + var/max_n2 = 0 // N2 max + var/unsuitable_atoms_damage = 2 // This damage is taken when atmos doesn't fit all the requirements above + + //Hostility settings + var/taser_kill = 1 // Is the mob weak to tasers + + //Attack ranged settings + var/projectiletype // The projectiles I shoot + var/projectilesound // The sound I make when I do it + var/casingtype // What to make the hugely laggy casings pile out of + + // Reloading settings, part of ranged code + var/needs_reload = FALSE // If TRUE, mob needs to reload occasionally + var/reload_max = 1 // How many shots the mob gets before it has to reload, will not be used if needs_reload is FALSE + var/reload_count = 0 // A counter to keep track of how many shots the mob has fired so far. Reloads when it hits reload_max. + var/reload_time = 1 SECONDS // How long it takes for a mob to reload. This is to buy a player a bit of time to run or fight. + var/reload_sound = 'sound/weapons/flipblade.ogg' // What sound gets played when the mob successfully reloads. Defaults to the same sound as reloading guns. Can be null. + + //Mob melee settings + var/melee_damage_lower = 2 // Lower bound of randomized melee damage + var/melee_damage_upper = 6 // Upper bound of randomized melee damage + var/list/attacktext = list("attacked") // "You are [attacktext] by the mob!" + var/list/friendly = list("nuzzles") // "The mob [friendly] the person." + var/attack_sound = null // Sound to play when I attack + var/melee_miss_chance = 0 // percent chance to miss a melee attack. + var/attack_armor_type = "melee" // What armor does this check? + var/attack_armor_pen = 0 // How much armor pen this attack has. + var/attack_sharp = FALSE // Is the attack sharp? + var/attack_edge = FALSE // Does the attack have an edge? + + var/melee_attack_delay = null // If set, the mob will do a windup animation and can miss if the target moves out of the way. + var/ranged_attack_delay = null + var/special_attack_delay = null + + //Special attacks +// var/special_attack_prob = 0 // The chance to ATTEMPT a special_attack_target(). If it fails, it will do a regular attack instead. + // This is commented out to ease the AI attack logic by being (a bit more) determanistic. + // You should instead limit special attacks using the below vars instead. + var/special_attack_min_range = null // The minimum distance required for an attempt to be made. + var/special_attack_max_range = null // The maximum for an attempt. + var/special_attack_charges = null // If set, special attacks will work off of a charge system, and won't be usable if all charges are expended. Good for grenades. + var/special_attack_cooldown = null // If set, special attacks will have a cooldown between uses. + var/last_special_attack = null // world.time when a special attack occured last, for cooldown calculations. + + //Damage resistances + var/grab_resist = 0 // Chance for a grab attempt to fail. Note that this is not a true resist and is just a prob() of failure. + var/resistance = 0 // Damage reduction for all types + var/list/armor = list( // Values for normal getarmor() checks + "melee" = 0, + "bullet" = 0, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + var/list/armor_soak = list( // Values for getsoak() checks. + "melee" = 0, + "bullet" = 0, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + // Protection against heat/cold/electric/water effects. + // 0 is no protection, 1 is total protection. Negative numbers increase vulnerability. + var/heat_resist = 0.0 + var/cold_resist = 0.0 + var/shock_resist = 0.0 + var/water_resist = 1.0 + var/poison_resist = 0.0 + var/thick_armor = FALSE // Stops injections and "injections". + var/purge = 0 // Cult stuff. + var/supernatural = FALSE // Ditto. + + +/mob/living/simple_mob/initialize() + verbs -= /mob/verb/observe + health = maxHealth + + for(var/L in has_langs) + languages |= all_languages[L] + if(languages.len) + default_language = languages[1] + + if(has_eye_glow) + add_eyes() + return ..() + + +/mob/living/simple_mob/Destroy() + default_language = null + if(myid) + qdel(myid) + myid = null + + friends.Cut() + languages.Cut() + + if(has_eye_glow) + remove_eyes() + return ..() + +/mob/living/simple_mob/death() + update_icon() + ..() + + +//Client attached +/mob/living/simple_mob/Login() + . = ..() + to_chat(src,"You are \the [src]. [player_msg]") + + +/mob/living/simple_mob/emote(var/act, var/type, var/desc) + if(act) + ..(act, type, desc) + + +/mob/living/simple_mob/SelfMove(turf/n, direct) + var/turf/old_turf = get_turf(src) + var/old_dir = dir + . = ..() + if(. && movement_shake_radius) + for(var/mob/living/L in range(movement_shake_radius, src)) + shake_camera(L, 1, 1) + if(turn_sound && dir != old_dir) + playsound(src, turn_sound, 50, 1) + else if(movement_sound && old_turf != get_turf(src)) // Playing both sounds at the same time generally sounds bad. + playsound(src, movement_sound, 50, 1) +/* +/mob/living/simple_mob/set_dir(new_dir) + if(dir != new_dir) + playsound(src, turn_sound, 50, 1) + return ..() +*/ +/mob/living/simple_mob/movement_delay() + var/tally = 0 //Incase I need to add stuff other than "speed" later + + tally = movement_cooldown + + if(force_max_speed) + return -3 + + for(var/datum/modifier/M in modifiers) + if(!isnull(M.haste) && M.haste == TRUE) + return -3 + if(!isnull(M.slowdown)) + tally += M.slowdown + + // Turf related slowdown + var/turf/T = get_turf(src) + if(T && T.movement_cost && !hovering) // Flying mobs ignore turf-based slowdown. + tally += T.movement_cost + + if(purge)//Purged creatures will move more slowly. The more time before their purge stops, the slower they'll move. + if(tally <= 0) + tally = 1 + tally *= purge + + if(m_intent == "walk") + tally *= 1.5 + + return tally+config.animal_delay + + +/mob/living/simple_mob/Stat() + ..() + if(statpanel("Status") && show_stat_health) + stat(null, "Health: [round((health / getMaxHealth()) * 100)]%") + +/mob/living/simple_mob/lay_down() + ..() + if(resting && icon_rest) + icon_state = icon_rest + else + icon_state = icon_living + update_icon() + + +/mob/living/simple_mob/say(var/message,var/datum/language/language) + var/verb = "says" + if(speak_emote.len) + verb = pick(speak_emote) + + message = sanitize(message) + + ..(message, null, verb) + +/mob/living/simple_mob/get_speech_ending(verb, var/ending) + return verb + + +// Harvest an animal's delicious byproducts +/mob/living/simple_mob/proc/harvest(var/mob/user) + var/actual_meat_amount = max(1,(meat_amount/2)) + if(meat_type && actual_meat_amount>0 && (stat == DEAD)) + for(var/i=0;i[user] chops up \the [src]!") + new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + qdel(src) + else + user.visible_message("[user] butchers \the [src] messily!") + gib() + + +/mob/living/simple_mob/is_sentient() + return mob_class & MOB_CLASS_HUMANOID|MOB_CLASS_ANIMAL|MOB_CLASS_SLIME // Update this if needed. + +/mob/living/simple_mob/get_nametag_desc(mob/user) + return "[tt_desc]" \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/animal.dm b/code/modules/mob/living/simple_mob/subtypes/animal/animal.dm new file mode 100644 index 0000000000..e41d5ae66b --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/animal.dm @@ -0,0 +1,9 @@ +/mob/living/simple_mob/animal + mob_class = MOB_CLASS_ANIMAL + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + response_help = "pets" + response_disarm = "shoos" + response_harm = "hits" + + ai_holder_type = /datum/ai_holder/simple_mob/melee \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm new file mode 100644 index 0000000000..92f4e51c9c --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm @@ -0,0 +1,237 @@ +// Borers are probably still going to be buggy as fuck, this is just bringing their mob defines up to the new system. +// IMO they're a relic of several ages we're long past, their code and their design showing this plainly, but removing them would +// make certain people Unhappy so here we are. They need a complete redesign but thats beyond the scope of the rewrite. + +/mob/living/simple_mob/animal/borer + name = "cortical borer" + desc = "A small, quivering sluglike creature." + icon_state = "brainslug" + item_state = "brainslug" + icon_living = "brainslug" + icon_dead = "brainslug_dead" + + response_help = "pokes" + response_disarm = "prods" + response_harm = "stomps on" + attacktext = list("nipped") + friendly = list("prods") + + status_flags = CANPUSH + pass_flags = PASSTABLE + movement_cooldown = 5 + + universal_understand = TRUE + can_be_antagged = TRUE + + holder_type = /obj/item/weapon/holder/borer + ai_holder_type = null // This is player-controlled, always. + + var/chemicals = 10 // A resource used for reproduction and powers. + var/mob/living/carbon/human/host = null // The humanoid host for the brain worm. + var/true_name = null // String used when speaking among other worms. + var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. + var/controlling = FALSE // Used in human death ceck. + var/docile = FALSE // Sugar can stop borers from acting. + var/has_reproduced = FALSE + var/roundstart = FALSE // If true, spawning won't try to pull a ghost. + var/used_dominate // world.time when the dominate power was last used. + + +/mob/living/simple_mob/animal/borer/roundstart + roundstart = TRUE + +/mob/living/simple_mob/animal/borer/Login() + ..() + if(mind) + borers.add_antagonist(mind) + +/mob/living/simple_mob/animal/borer/initialize() + add_language("Cortical Link") + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + + true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]" + + if(!roundstart) + request_player() + + return ..() + +/mob/living/simple_mob/animal/borer/handle_special() + if(host && !stat && !host.stat) + // Handle docility. + if(host.reagents.has_reagent("sugar") && !docile) + var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility." + var/target = controlling ? host : src + to_chat(target, span("warning", message)) + docile = TRUE + + else if(docile) + var/message = "You shake off your lethargy as the sugar leaves your host's blood." + var/target = controlling ? host : src + to_chat(target, span("notice", message)) + docile = FALSE + + // Chem regen. + if(chemicals < 250) + chemicals++ + + // Control stuff. + if(controlling) + if(docile) + to_chat(host, span("warning", "You are feeling far too docile to continue controlling your host...")) + host.release_control() + return + + if(prob(5)) + host.adjustBrainLoss(0.1) + + if(prob(host.brainloss/20)) + host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]") + +/mob/living/simple_mob/animal/borer/Stat() + ..() + if(client.statpanel == "Status") + statpanel("Status") + if(emergency_shuttle) + var/eta_status = emergency_shuttle.get_status_panel_eta() + if(eta_status) + stat(null, eta_status) + stat("Chemicals", chemicals) + +/mob/living/simple_mob/animal/borer/proc/detatch() + if(!host || !controlling) + return + + if(istype(host, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = host + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + if(head) + head.implants -= src + + controlling = FALSE + + host.remove_language("Cortical Link") + host.verbs -= /mob/living/carbon/proc/release_control + host.verbs -= /mob/living/carbon/proc/punish_host + host.verbs -= /mob/living/carbon/proc/spawn_larvae + + if(host_brain) + // these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed. + // computer_id and IP are not updated magically on their own in offline mobs -walter0o + + // This shit need to die in a phoron fire. + + // host -> self + var/h2s_id = host.computer_id + var/h2s_ip= host.lastKnownIP + host.computer_id = null + host.lastKnownIP = null + + src.ckey = host.ckey + + if(!src.computer_id) + src.computer_id = h2s_id + + if(!host_brain.lastKnownIP) + src.lastKnownIP = h2s_ip + + // brain -> host + var/b2h_id = host_brain.computer_id + var/b2h_ip= host_brain.lastKnownIP + host_brain.computer_id = null + host_brain.lastKnownIP = null + + host.ckey = host_brain.ckey + + if(!host.computer_id) + host.computer_id = b2h_id + + if(!host.lastKnownIP) + host.lastKnownIP = b2h_ip + + qdel(host_brain) + + +/mob/living/simple_mob/animal/borer/proc/leave_host() + if(!host) + return + + if(host.mind) + borers.remove_antagonist(host.mind) + + forceMove(get_turf(host)) + + reset_view(null) + machine = null + + host.reset_view(null) + host.machine = null + host = null + +/mob/living/simple_mob/animal/borer/proc/request_player() + var/datum/ghost_query/Q = new /datum/ghost_query/borer() + var/list/winner = Q.query() // This will sleep the proc for awhile. + if(winner.len) + var/mob/observer/dead/D = winner[1] + transfer_personality(D) + +/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate) + if(!candidate || !candidate.mind) + return + + src.mind = candidate.mind + candidate.mind.current = src + ckey = candidate.ckey + + if(mind) + mind.assigned_role = "Cortical Borer" + mind.special_role = "Cortical Borer" + + to_chat(src, span("notice", "You are a cortical borer! You are a brain slug that worms its way \ + into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \ + your host and your eventual spawn safe and warm.")) + to_chat(src, "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers.") + +/mob/living/simple_mob/animal/borer/cannot_use_vents() + return + +// This is awful but its literally say code. +/mob/living/simple_mob/animal/borer/say(message) + message = sanitize(message) + message = capitalize(message) + + if(!message) + return + + if(stat >= DEAD) + return say_dead(message) + else if(stat) + return + + if(client && client.prefs.muted & MUTE_IC) + to_chat(src, span("danger", "You cannot speak in IC (muted).")) + return + + if(copytext(message, 1, 2) == "*") + return emote(copytext(message, 2)) + + var/datum/language/L = parse_language(message) + if(L && L.flags & HIVEMIND) + L.broadcast(src,trim(copytext(message,3)), src.true_name) + return + + if(!host) + //TODO: have this pick a random mob within 3 tiles to speak for the borer. + to_chat(src, span("warning", "You have no host to speak to.")) + return //No host, no audible speech. + + to_chat(src, "You drop words into [host]'s mind: \"[message]\"") + to_chat(host, "Your own thoughts speak: \"[message]\"") + + for(var/mob/M in player_list) + if(istype(M, /mob/new_player)) + continue + else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + to_chat(M, "[src.true_name] whispers to [host], \"[message]\"") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm new file mode 100644 index 0000000000..f5da079b97 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_captive.dm @@ -0,0 +1,59 @@ +// Straight move from the old location, with the paths corrected. + +/mob/living/captive_brain + name = "host brain" + real_name = "host brain" + universal_understand = 1 + +/mob/living/captive_brain/say(var/message) + + if (src.client) + if(client.prefs.muted & MUTE_IC) + src << "You cannot speak in IC (muted)." + return + + if(istype(src.loc, /mob/living/simple_mob/animal/borer)) + + message = sanitize(message) + if (!message) + return + log_say(message,src) + if (stat == 2) + return say_dead(message) + + var/mob/living/simple_mob/animal/borer/B = src.loc + src << "You whisper silently, \"[message]\"" + B.host << "The captive mind of [src] whispers, \"[message]\"" + + for (var/mob/M in player_list) + if (istype(M, /mob/new_player)) + continue + else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + M << "The captive mind of [src] whispers, \"[message]\"" + +/mob/living/captive_brain/emote(var/message) + return + +/mob/living/captive_brain/process_resist() + //Resisting control by an alien mind. + if(istype(src.loc, /mob/living/simple_mob/animal/borer)) + var/mob/living/simple_mob/animal/borer/B = src.loc + var/mob/living/captive_brain/H = src + + H << "You begin doggedly resisting the parasite's control (this will take approximately sixty seconds)." + B.host << "You feel the captive mind of [src] begin to resist your control." + + spawn(rand(200,250)+B.host.brainloss) + if(!B || !B.controlling) return + + B.host.adjustBrainLoss(rand(0.1,0.5)) + H << "With an immense exertion of will, you regain control of your body!" + B.host << "You feel control of the host brain ripped from your grasp, and retract your probosci before the wild neural impulses can damage you." + B.detatch() + verbs -= /mob/living/carbon/proc/release_control + verbs -= /mob/living/carbon/proc/punish_host + verbs -= /mob/living/carbon/proc/spawn_larvae + + return + + ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm new file mode 100644 index 0000000000..55dac2492a --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer_powers.dm @@ -0,0 +1,354 @@ +/mob/living/simple_mob/animal/borer/verb/release_host() + set category = "Abilities" + set name = "Release Host" + set desc = "Slither out of your host." + + if(!host) + src << "You are not inside a host body." + return + + if(stat) + src << "You cannot leave your host in your current state." + + if(docile) + src << "You are feeling far too docile to do that." + return + + if(!host || !src) return + + src << "You begin disconnecting from [host]'s synapses and prodding at their internal ear canal." + + if(!host.stat) + host << "An odd, uncomfortable pressure begins to build inside your skull, behind your ear..." + + spawn(100) + + if(!host || !src) return + + if(src.stat) + src << "You cannot release your host in your current state." + return + + src << "You wiggle out of [host]'s ear and plop to the ground." + if(host.mind) + if(!host.stat) + host << "Something slimy wiggles out of your ear and plops to the ground!" + host << "As though waking from a dream, you shake off the insidious mind control of the brain worm. Your thoughts are your own again." + + detatch() + leave_host() + +/mob/living/simple_mob/animal/borer/verb/infest() + set category = "Abilities" + set name = "Infest" + set desc = "Infest a suitable humanoid host." + + if(host) + src << "You are already within a host." + return + + if(stat) + src << "You cannot infest a target in your current state." + return + + var/list/choices = list() + for(var/mob/living/carbon/C in view(1,src)) + if(src.Adjacent(C)) + choices += C + + if(!choices.len) + src << "There are no viable hosts within range..." + return + + var/mob/living/carbon/M = input(src,"Who do you wish to infest?") in null|choices + + if(!M || !src) return + + if(!(src.Adjacent(M))) return + + if(M.has_brain_worms()) + src << "You cannot infest someone who is already infested!" + return + + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + + var/obj/item/organ/external/E = H.organs_by_name[BP_HEAD] + if(!E || E.is_stump()) + src << "\The [H] does not have a head!" + + if(!H.should_have_organ("brain")) + src << "\The [H] does not seem to have an ear canal to breach." + return + + if(H.check_head_coverage()) + src << "You cannot get through that host's protective gear." + return + + M << "Something slimy begins probing at the opening of your ear canal..." + src << "You slither up [M] and begin probing at their ear canal..." + + if(!do_after(src,30)) + src << "As [M] moves away, you are dislodged and fall to the ground." + return + + if(!M || !src) return + + if(src.stat) + src << "You cannot infest a target in your current state." + return + + if(M in view(1, src)) + src << "You wiggle into [M]'s ear." + if(!M.stat) + M << "Something disgusting and slimy wiggles into your ear!" + + src.host = M + src.forceMove(M) + + //Update their traitor status. + if(host.mind) + borers.add_antagonist_mind(host.mind, 1, borers.faction_role_text, borers.faction_welcome) + + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/I = H.internal_organs_by_name["brain"] + if(!I) // No brain organ, so the borer moves in and replaces it permanently. + replace_brain() + else + // If they're in normally, implant removal can get them out. + var/obj/item/organ/external/head = H.get_organ(BP_HEAD) + head.implants += src + + return + else + src << "They are no longer in range!" + return + +/* +/mob/living/simple_mob/animal/borer/verb/devour_brain() + set category = "Abilities" + set name = "Devour Brain" + set desc = "Take permanent control of a dead host." + + if(!host) + src << "You are not inside a host body." + return + + if(host.stat != 2) + src << "Your host is still alive." + return + + if(stat) + src << "You cannot do that in your current state." + + if(docile) + src << "You are feeling far too docile to do that." + return + + + src << "It only takes a few moments to render the dead host brain down into a nutrient-rich slurry..." + replace_brain() +*/ + +// BRAIN WORM ZOMBIES AAAAH. +/mob/living/simple_mob/animal/borer/proc/replace_brain() + + var/mob/living/carbon/human/H = host + + if(!istype(host)) + src << "This host does not have a suitable brain." + return + + src << "You settle into the empty brainpan and begin to expand, fusing inextricably with the dead flesh of [H]." + + H.add_language("Cortical Link") + + if(host.stat == 2) + H.verbs |= /mob/living/carbon/human/proc/jumpstart + + H.verbs |= /mob/living/carbon/human/proc/psychic_whisper + H.verbs |= /mob/living/carbon/human/proc/tackle + H.verbs |= /mob/living/carbon/proc/spawn_larvae + + if(H.client) + H.ghostize(0) + + if(src.mind) + src.mind.special_role = "Borer Husk" + src.mind.transfer_to(host) + + H.ChangeToHusk() + + var/obj/item/organ/internal/borer/B = new(H) + H.internal_organs_by_name["brain"] = B + H.internal_organs |= B + + var/obj/item/organ/external/affecting = H.get_organ(BP_HEAD) + affecting.implants -= src + + var/s2h_id = src.computer_id + var/s2h_ip= src.lastKnownIP + src.computer_id = null + src.lastKnownIP = null + + if(!H.computer_id) + H.computer_id = s2h_id + + if(!H.lastKnownIP) + H.lastKnownIP = s2h_ip + +/mob/living/simple_mob/animal/borer/verb/secrete_chemicals() + set category = "Abilities" + set name = "Secrete Chemicals" + set desc = "Push some chemicals into your host's bloodstream." + + if(!host) + src << "You are not inside a host body." + return + + if(stat) + src << "You cannot secrete chemicals in your current state." + + if(docile) + src << "You are feeling far too docile to do that." + return + + if(chemicals < 50) + src << "You don't have enough chemicals!" + + var/chem = input("Select a chemical to secrete.", "Chemicals") as null|anything in list("alkysine","bicaridine","hyperzine","tramadol") + + if(!chem || chemicals < 50 || !host || controlling || !src || stat) //Sanity check. + return + + src << "You squirt a measure of [chem] from your reservoirs into [host]'s bloodstream." + host.reagents.add_reagent(chem, 10) + chemicals -= 50 + +/mob/living/simple_mob/animal/borer/verb/dominate_victim() + set category = "Abilities" + set name = "Paralyze Victim" + set desc = "Freeze the limbs of a potential host with supernatural fear." + + if(world.time - used_dominate < 150) + src << "You cannot use that ability again so soon." + return + + if(host) + src << "You cannot do that from within a host body." + return + + if(src.stat) + src << "You cannot do that in your current state." + return + + var/list/choices = list() + for(var/mob/living/carbon/C in view(3,src)) + if(C.stat != 2) + choices += C + + if(world.time - used_dominate < 150) + src << "You cannot use that ability again so soon." + return + + var/mob/living/carbon/M = input(src,"Who do you wish to dominate?") in null|choices + + if(!M || !src) return + + if(M.has_brain_worms()) + src << "You cannot infest someone who is already infested!" + return + + src << "You focus your psychic lance on [M] and freeze their limbs with a wave of terrible dread." + M << "You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing." + M.Weaken(10) + + used_dominate = world.time + +/mob/living/simple_mob/animal/borer/verb/bond_brain() + set category = "Abilities" + set name = "Assume Control" + set desc = "Fully connect to the brain of your host." + + if(!host) + src << "You are not inside a host body." + return + + if(src.stat) + src << "You cannot do that in your current state." + return + + if(docile) + src << "You are feeling far too docile to do that." + return + + src << "You begin delicately adjusting your connection to the host brain..." + + spawn(100+(host.brainloss*5)) + + if(!host || !src || controlling) + return + else + + src << "You plunge your probosci deep into the cortex of the host brain, interfacing directly with their nervous system." + host << "You feel a strange shifting sensation behind your eyes as an alien consciousness displaces yours." + host.add_language("Cortical Link") + + // host -> brain + var/h2b_id = host.computer_id + var/h2b_ip= host.lastKnownIP + host.computer_id = null + host.lastKnownIP = null + + qdel(host_brain) + host_brain = new(src) + + host_brain.ckey = host.ckey + + host_brain.name = host.name + + if(!host_brain.computer_id) + host_brain.computer_id = h2b_id + + if(!host_brain.lastKnownIP) + host_brain.lastKnownIP = h2b_ip + + // self -> host + var/s2h_id = src.computer_id + var/s2h_ip= src.lastKnownIP + src.computer_id = null + src.lastKnownIP = null + + host.ckey = src.ckey + + if(!host.computer_id) + host.computer_id = s2h_id + + if(!host.lastKnownIP) + host.lastKnownIP = s2h_ip + + controlling = 1 + + host.verbs += /mob/living/carbon/proc/release_control + host.verbs += /mob/living/carbon/proc/punish_host + host.verbs += /mob/living/carbon/proc/spawn_larvae + + return + +/mob/living/carbon/human/proc/jumpstart() + set category = "Abilities" + set name = "Revive Host" + set desc = "Send a jolt of electricity through your host, reviving them." + + if(stat != 2) + usr << "Your host is already alive." + return + + verbs -= /mob/living/carbon/human/proc/jumpstart + visible_message("With a hideous, rattling moan, [src] shudders back to life!") + + rejuvenate() + restore_blood() + fixblood() + update_canmove() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm new file mode 100644 index 0000000000..775f9ae6cb --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm @@ -0,0 +1,156 @@ +GLOBAL_VAR_CONST(MAX_CHICKENS, 50) // How many chickens CAN we have? +GLOBAL_VAR_INIT(chicken_count, 0) // How mant chickens DO we have? + +/mob/living/simple_mob/animal/passive/chicken + name = "chicken" + desc = "Hopefully the eggs are good this season." + tt_desc = "E Gallus gallus" + icon_state = "chicken" + icon_living = "chicken" + icon_dead = "chicken_dead" + + health = 10 + maxHealth = 10 + + pass_flags = PASSTABLE + mob_size = MOB_SMALL + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + attacktext = list("kicked") + + has_langs = list("Bird") + + say_list_type = /datum/say_list/chicken + + meat_amount = 2 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + var/eggsleft = 0 + var/body_color + +/mob/living/simple_mob/animal/passive/chicken/New() + ..() + if(!body_color) + body_color = pick( list("brown","black","white") ) + icon_state = "chicken_[body_color]" + icon_living = "chicken_[body_color]" + icon_dead = "chicken_[body_color]_dead" + pixel_x = rand(-6, 6) + pixel_y = rand(0, 10) + GLOB.chicken_count += 1 + +/mob/living/simple_mob/animal/passive/chicken/Destroy() + ..() + GLOB.chicken_count -= 1 + +/mob/living/simple_mob/animal/passive/chicken/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown)) //feedin' dem chickens + var/obj/item/weapon/reagent_containers/food/snacks/grown/G = O + if(G.seed && G.seed.kitchen_tag == "wheat") + if(!stat && eggsleft < 8) + user.visible_message("[user] feeds [O] to [name]! It clucks happily.","You feed [O] to [name]! It clucks happily.") + user.drop_item() + qdel(O) + eggsleft += rand(1, 4) + else + to_chat(user, "[name] doesn't seem hungry!") + else + to_chat(user, "[name] doesn't seem interested in that.") + else + ..() + +/mob/living/simple_mob/animal/passive/chicken/Life() + . =..() + if(!.) + return + if(!stat && prob(3) && eggsleft > 0) + visible_message("[src] [pick("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.")]") + eggsleft-- + var/obj/item/weapon/reagent_containers/food/snacks/egg/E = new(get_turf(src)) + E.pixel_x = rand(-6,6) + E.pixel_y = rand(-6,6) + if(GLOB.chicken_count < GLOB.MAX_CHICKENS && prob(10)) + processing_objects.Add(E) + + + + + + + +/obj/item/weapon/reagent_containers/food/snacks/egg/var/amount_grown = 0 + +// This only starts normally if there are less than MAX_CHICKENS chickens +/obj/item/weapon/reagent_containers/food/snacks/egg/process() + if(isturf(loc)) + amount_grown += rand(1,2) + if(amount_grown >= 100) + visible_message("[src] hatches with a quiet cracking sound.") + new /mob/living/simple_mob/animal/passive/chick(get_turf(src)) + processing_objects.Remove(src) + qdel(src) + else + processing_objects.Remove(src) + + + + + + + +/mob/living/simple_mob/animal/passive/chick + name = "chick" + desc = "Adorable! They make such a racket though." + tt_desc = "E Gallus gallus" + icon_state = "chick" + icon_living = "chick" + icon_dead = "chick_dead" + icon_gib = "chick_gib" + + health = 1 + maxHealth = 1 + + pass_flags = PASSTABLE | PASSGRILLE + mob_size = MOB_MINISCULE + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + attacktext = list("kicked") + + has_langs = list("Bird") + + say_list_type = /datum/say_list/chick + + meat_amount = 1 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + var/amount_grown = 0 + +/mob/living/simple_mob/animal/passive/chick/New() + ..() + pixel_x = rand(-6, 6) + pixel_y = rand(0, 10) + +/mob/living/simple_mob/animal/passive/chick/Life() + . =..() + if(!.) + return + if(!stat) + amount_grown += rand(1,2) + if(amount_grown >= 100) + new /mob/living/simple_mob/animal/passive/chicken(src.loc) + qdel(src) + +// Say Lists +/datum/say_list/chicken + speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") + emote_hear = list("clucks","croons") + emote_see = list("pecks at the ground","flaps its wings viciously") + +/datum/say_list/chick + speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") + emote_hear = list("cheeps") + emote_see = list("pecks at the ground","flaps its tiny wings") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm new file mode 100644 index 0000000000..b5373c176a --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm @@ -0,0 +1,67 @@ +/mob/living/simple_mob/animal/passive/cow + name = "cow" + desc = "Known for their milk, just don't tip them over." + tt_desc = "E Bos taurus" + icon_state = "cow" + icon_living = "cow" + icon_dead = "cow_dead" + icon_gib = "cow_gib" + + health = 50 + maxHealth = 50 + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + attacktext = list("kicked") + + say_list_type = /datum/say_list/cow + + meat_amount = 6 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + var/datum/reagents/udder = null + +/mob/living/simple_mob/animal/passive/cow/New() + udder = new(50) + udder.my_atom = src + ..() + +/mob/living/simple_mob/animal/passive/cow/attackby(var/obj/item/O as obj, var/mob/user as mob) + var/obj/item/weapon/reagent_containers/glass/G = O + if(stat == CONSCIOUS && istype(G) && G.is_open_container()) + user.visible_message("[user] milks [src] using \the [O].") + var/transfered = udder.trans_id_to(G, "milk", rand(5,10)) + if(G.reagents.total_volume >= G.volume) + to_chat(user, "The [O] is full.") + if(!transfered) + to_chat(user, "The udder is dry. Wait a bit longer...") + else + ..() + +/mob/living/simple_mob/animal/passive/cow/Life() + . = ..() + if(stat == CONSCIOUS) + if(udder && prob(5)) + udder.add_reagent("milk", rand(5, 10)) + +/mob/living/simple_mob/animal/passive/cow/attack_hand(mob/living/carbon/M as mob) + if(!stat && M.a_intent == I_DISARM && icon_state != icon_dead) + M.visible_message("[M] tips over [src].","You tip over [src].") + Weaken(30) + icon_state = icon_dead + spawn(rand(20,50)) + if(!stat && M) + icon_state = icon_living + var/list/responses = list( "[src] looks at you imploringly.", + "[src] looks at you pleadingly", + "[src] looks at you with a resigned expression.", + "[src] seems resigned to its fate.") + to_chat(M, pick(responses)) + else + ..() + +/datum/say_list/cow + speak = list("moo?","moo","MOOOOOO") + emote_hear = list("brays", "moos","moos hauntingly") + emote_see = list("shakes its head") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm new file mode 100644 index 0000000000..7ff82a8160 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm @@ -0,0 +1,81 @@ +/mob/living/simple_mob/animal/goat + name = "goat" + desc = "Not known for their pleasant disposition." + tt_desc = "E Oreamnos americanus" + icon_state = "goat" + icon_living = "goat" + icon_dead = "goat_dead" + + faction = "goat" + + health = 40 + maxHealth = 40 + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + + melee_damage_lower = 1 + melee_damage_upper = 5 + attacktext = list("kicked") + + say_list_type = /datum/say_list/goat + ai_holder_type = /datum/ai_holder/simple_mob/retaliate + + meat_amount = 4 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + var/datum/reagents/udder = null + +/mob/living/simple_mob/animal/goat/New() + udder = new(50) + udder.my_atom = src + ..() + +/mob/living/simple_mob/animal/goat/Life() + . = ..() + if(.) + if(stat == CONSCIOUS) + if(udder && prob(5)) + udder.add_reagent("milk", rand(5, 10)) + + if(locate(/obj/effect/plant) in loc) + var/obj/effect/plant/SV = locate() in loc + SV.die_off(1) + + if(locate(/obj/machinery/portable_atmospherics/hydroponics/soil/invisible) in loc) + var/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/SP = locate() in loc + qdel(SP) + + if(!pulledby) + var/obj/effect/plant/food + food = locate(/obj/effect/plant) in oview(5,loc) + if(food) + var/step = get_step_to(src, food, 0) + Move(step) + +/mob/living/simple_mob/animal/goat/Move() + ..() + if(!stat) + for(var/obj/effect/plant/SV in loc) + SV.die_off(1) + +/mob/living/simple_mob/animal/goat/attackby(var/obj/item/O as obj, var/mob/user as mob) + var/obj/item/weapon/reagent_containers/glass/G = O + if(stat == CONSCIOUS && istype(G) && G.is_open_container()) + user.visible_message("[user] milks [src] using \the [O].") + var/transfered = udder.trans_id_to(G, "milk", rand(5,10)) + if(G.reagents.total_volume >= G.volume) + to_chat(user, "The [O] is full.") + if(!transfered) + to_chat(user, "The udder is dry. Wait a bit longer...") + else + ..() + +/datum/say_list/goat + speak = list("EHEHEHEHEH","eh?") + emote_hear = list("brays") + emote_see = list("shakes its head", "stamps a foot", "glares around") + + // say_got_target doesn't seem to handle emotes, but keeping this here in case someone wants to make it work +// say_got_target = list("[src] gets an evil-looking gleam in their eye.") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm new file mode 100644 index 0000000000..a85463d723 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm @@ -0,0 +1,64 @@ +/* + Spiders come in various types, and are a fairly common enemy both inside and outside the station. + Their attacks can inject reagents, which can cause harm long after the spider is killed. + Thick material will prevent injections, similar to other means of injections. +*/ + +// The base spider, in the 'walking tank' family. +/mob/living/simple_mob/animal/giant_spider + name = "giant spider" + desc = "Furry and brown, it makes you shudder to look at it. This one has deep red eyes." + tt_desc = "Atrax robustus gigantus" + icon_state = "guard" + icon_living = "guard" + icon_dead = "guard_dead" + has_eye_glow = TRUE + + faction = "spiders" + maxHealth = 200 + health = 200 + pass_flags = PASSTABLE + movement_cooldown = 10 + poison_resist = 0.5 + + see_in_dark = 10 + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "punches" + + melee_damage_lower = 18 + melee_damage_upper = 30 + attack_sharp = 1 + attack_edge = 1 + attack_sound = 'sound/weapons/bite.ogg' + + heat_damage_per_tick = 20 + cold_damage_per_tick = 20 + minbodytemp = 175 // So they can all survive Sif without having to be classed under /sif subtype. + + speak_emote = list("chitters") + + meat_amount = 1 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat/spidermeat + + say_list_type = /datum/say_list/spider + + var/poison_type = "spidertoxin" // The reagent that gets injected when it attacks. + var/poison_chance = 10 // Chance for injection to occur. + var/poison_per_bite = 5 // Amount added per injection. + +/mob/living/simple_mob/animal/giant_spider/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.reagents) + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + inject_poison(L, target_zone) + +// Does actual poison injection, after all checks passed. +/mob/living/simple_mob/animal/giant_spider/proc/inject_poison(mob/living/L, target_zone) + if(prob(poison_chance)) + to_chat(L, "You feel a tiny prick.") + L.reagents.add_reagent(poison_type, poison_per_bite) + diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm new file mode 100644 index 0000000000..575e3ad204 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm @@ -0,0 +1,67 @@ +// Carriers are not too dangerous on their own, but they create more spiders when dying. + +/mob/living/simple_mob/animal/giant_spider/carrier + desc = "Furry, beige, and red, it makes you shudder to look at it. This one has luminous green eyes." + icon_state = "carrier" + icon_living = "carrier" + icon_dead = "carrier_dead" + + maxHealth = 100 + health = 100 + + melee_damage_lower = 8 + melee_damage_upper = 25 + + poison_per_bite = 3 + poison_type = "chloralhydrate" + + movement_cooldown = 5 + + player_msg = "Upon dying, you will release a swarm of spiderlings or young hunter spiders.
\ + If a spider emerges, you will be placed in control of it." + + var/spiderling_count = 0 + var/spiderling_type = /obj/effect/spider/spiderling + var/swarmling_type = /mob/living/simple_mob/animal/giant_spider/hunter + var/swarmling_faction = "spiders" + var/swarmling_prob = 10 // Odds that a spiderling will be a swarmling instead. + +/mob/living/simple_mob/animal/giant_spider/carrier/initialize() + spiderling_count = rand(5, 10) + adjust_scale(1.2) + return ..() + +/mob/living/simple_mob/animal/giant_spider/carrier/death() + visible_message(span("warning", "\The [src]'s abdomen splits as it rolls over, spiderlings crawling from the wound.") ) + spawn(1) + var/list/new_spiders = list() + for(var/i = 1 to spiderling_count) + if(prob(swarmling_prob) && src) + var/mob/living/simple_mob/animal/giant_spider/swarmling = new swarmling_type(src.loc) + var/swarm_health = Floor(swarmling.maxHealth * 0.4) + var/swarm_dam_lower = Floor(melee_damage_lower * 0.4) + var/swarm_dam_upper = Floor(melee_damage_upper * 0.4) + swarmling.name = "spiderling" + swarmling.maxHealth = swarm_health + swarmling.health = swarm_health + swarmling.melee_damage_lower = swarm_dam_lower + swarmling.melee_damage_upper = swarm_dam_upper + swarmling.faction = swarmling_faction + swarmling.adjust_scale(0.75) + new_spiders += swarmling + else if(src) + var/obj/effect/spider/spiderling/child = new spiderling_type(src.loc) + child.skitter() + else // We might've gibbed or got deleted. + break + // Transfer our player to their new body, if RNG provided one. + if(new_spiders.len && client) + var/mob/living/simple_mob/animal/giant_spider/new_body = pick(new_spiders) + new_body.key = src.key + return ..() + +/mob/living/simple_mob/animal/giant_spider/carrier/recursive + desc = "Furry, beige, and red, it makes you shudder to look at it. This one has luminous green eyes. \ + You have a distinctly bad feeling about this." + + swarmling_type = /mob/living/simple_mob/animal/giant_spider/carrier/recursive \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/electric.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/electric.dm new file mode 100644 index 0000000000..c3d1f60731 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/electric.dm @@ -0,0 +1,45 @@ +// Electric spiders fire taser-like beams at their enemies. +// TODO: AI + +/mob/living/simple_mob/animal/giant_spider/electric + desc = "Spined and yellow, it makes you shudder to look at it. This one has flickering gold eyes." + icon_state = "spark" + icon_living = "spark" + icon_dead = "spark_dead" + + maxHealth = 210 + health = 210 + + taser_kill = 0 //It -is- the taser. + + base_attack_cooldown = 10 + projectilesound = 'sound/weapons/taser2.ogg' + projectiletype = /obj/item/projectile/beam/stun/electric_spider + + melee_damage_lower = 10 + melee_damage_upper = 25 + + poison_chance = 15 + poison_per_bite = 3 + poison_type = "stimm" + + shock_resist = 0.75 + + player_msg = "You can fire a taser-like ranged attack by clicking on an enemy or tile at a distance." + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/electric_spider + + +/obj/item/projectile/beam/stun/electric_spider + name = "stun beam" + agony = 20 + +// The electric spider's AI. +/datum/ai_holder/simple_mob/ranged/electric_spider + +/datum/ai_holder/simple_mob/ranged/electric_spider/max_range(atom/movable/AM) + if(isliving(AM)) + var/mob/living/L = AM + if(L.incapacitated(INCAPACITATION_DISABLED) || L.stat == UNCONSCIOUS) // If our target is stunned, go in for the kill. + return 1 + return ..() // Do ranged if possible otherwise. \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/frost.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/frost.dm new file mode 100644 index 0000000000..19b85cccd4 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/frost.dm @@ -0,0 +1,20 @@ +// Frost spiders inject cryotoxin, slowing people down (which is very bad if trying to run from spiders). + +/mob/living/simple_mob/animal/giant_spider/frost + desc = "Icy and blue, it makes you shudder to look at it. This one has brilliant blue eyes." + icon_state = "frost" + icon_living = "frost" + icon_dead = "frost_dead" + + maxHealth = 175 + health = 175 + + poison_per_bite = 5 + poison_type = "cryotoxin" + heat_resist = -0.50 + cold_resist = 0.75 + +// Sif variant with a somewhat different desc. +/mob/living/simple_mob/animal/giant_spider/frost/sif + desc = "Icy and blue, it makes you shudder to look at it. This one has brilliant blue eyes. \ + It isn't native to Sif." diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm new file mode 100644 index 0000000000..f3eb1cf81e --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm @@ -0,0 +1,165 @@ +// Hunters are fast, fragile, and possess a leaping attack. +// The leaping attack is somewhat telegraphed and can be dodged if one is paying attention. +// The AI would've followed up after a successful leap with dragging the downed victim away, but the dragging code was too janky. + +/mob/living/simple_mob/animal/giant_spider/hunter + desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes." + icon_state = "hunter" + icon_living = "hunter" + icon_dead = "hunter_dead" + + maxHealth = 120 + health = 120 + + poison_per_bite = 5 + melee_damage_lower = 9 + melee_damage_upper = 15 + + movement_cooldown = 0 // Hunters are FAST. + + ai_holder_type = /datum/ai_holder/simple_mob/melee/hunter_spider + + player_msg = "You are very fast, and can perform a leaping attack by clicking on someone from a short distance away.
\ + If the leap succeeds, the target will be knocked down briefly and you will be on top of them.
\ + Note that there is a short delay before you leap!
\ + In addition, you will do more damage to incapacitated opponents." + + // Leaping is a special attack, so these values determine when leap can happen. + // Leaping won't occur if its on cooldown. + special_attack_min_range = 2 + special_attack_max_range = 4 + special_attack_cooldown = 10 SECONDS + + var/leap_warmup = 1 SECOND // How long the leap telegraphing is. + var/leap_sound = 'sound/weapons/spiderlunge.ogg' + +// Multiplies damage if the victim is stunned in some form, including a successful leap. +/mob/living/simple_mob/animal/giant_spider/hunter/apply_bonus_melee_damage(atom/A, damage_amount) + if(isliving(A)) + var/mob/living/L = A + if(L.incapacitated(INCAPACITATION_DISABLED)) + return damage_amount * 1.5 + return ..() + + +// The actual leaping attack. +/mob/living/simple_mob/animal/giant_spider/hunter/do_special_attack(atom/A) + set waitfor = FALSE + set_AI_busy(TRUE) + + // Telegraph, since getting stunned suddenly feels bad. + do_windup_animation(A, leap_warmup) + sleep(leap_warmup) // For the telegraphing. + + // Do the actual leap. + status_flags |= LEAPING // Lets us pass over everything. + visible_message(span("danger","\The [src] leaps at \the [A]!")) + throw_at(get_step(get_turf(A), get_turf(src)), special_attack_max_range+1, 1, src) + playsound(src, leap_sound, 75, 1) + + sleep(5) // For the throw to complete. It won't hold up the AI ticker due to waitfor being false. + + if(status_flags & LEAPING) + status_flags &= ~LEAPING // Revert special passage ability. + + var/turf/T = get_turf(src) // Where we landed. This might be different than A's turf. + + . = FALSE + + // Now for the stun. + var/mob/living/victim = null + for(var/mob/living/L in T) // So player-controlled spiders only need to click the tile to stun them. + if(L == src) + continue + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.check_shields(damage = 0, damage_source = src, attacker = src, def_zone = null, attack_text = "the leap")) + continue // We were blocked. + + victim = L + break + + if(victim) + victim.Weaken(2) + victim.visible_message(span("danger","\The [src] knocks down \the [victim]!")) + to_chat(victim, span("critical", "\The [src] jumps on you!")) + . = TRUE + + set_AI_busy(FALSE) + +// var/obj/item/weapon/grab/G = new(src, victim) +// put_in_active_hand(G) + +// G.synch() +// G.affecting = victim +// victim.LAssailant = src + +// visible_message("\The [src] seizes \the [victim] aggressively!") +// do_attack_animation(victim) + + +// This AI would've isolated people it stuns with its 'leap' attack, by dragging them away. +/datum/ai_holder/simple_mob/melee/hunter_spider + +/* + +/datum/ai_holder/simple_mob/melee/hunter_spider/post_special_attack(mob/living/L) + drag_away(L) + +// Called after a successful leap. +/datum/ai_holder/simple_mob/melee/hunter_spider/proc/drag_away(mob/living/L) + world << "Doing drag_away attack on [L]" + if(!istype(L)) + world << "Invalid type." + return FALSE + + // If they didn't get stunned, then don't bother. + if(!L.incapacitated(INCAPACITATION_DISABLED)) + world << "Not incapcitated." + return FALSE + + // Grab them. + if(!holder.start_pulling(L)) + world << "Failed to pull." + return FALSE + + holder.visible_message(span("danger","\The [holder] starts to drag \the [L] away!")) + + var/list/allies = list() + var/list/enemies = list() + for(var/mob/living/thing in hearers(vision_range, holder)) + if(thing == holder || thing == L) // Don't count ourselves or the thing we just started pulling. + continue + if(holder.IIsAlly(thing)) + allies += thing + else + enemies += thing + + // First priority: Move our victim to our friends. + if(allies.len) + world << "Going to move to ally" + give_destination(get_turf(pick(allies)), min_distance = 2, combat = TRUE) // This will switch our stance. + + // Second priority: Move our victim away from their friends. + // There's a chance of it derping and pulling towards enemies if there's more than two people. + // Preventing that will likely be both a lot of effort for developers and the CPU. + else if(enemies.len) + world << "Going to move away from enemies" + var/mob/living/hostile = pick(enemies) + var/turf/move_to = get_turf(hostile) + for(var/i = 1 to vision_range) // Move them this many steps away from their friend. + move_to = get_step_away(move_to, L, 7) + if(move_to) + give_destination(move_to, min_distance = 2, combat = TRUE) // This will switch our stance. + + // Third priority: Move our victim SOMEWHERE away from where they were. + else + world << "Going to move away randomly" + var/turf/move_to = get_turf(L) + move_to = get_step(move_to, pick(cardinal)) + for(var/i = 1 to vision_range) // Move them this many steps away from where they were before. + move_to = get_step_away(move_to, L, 7) + if(move_to) + give_destination(move_to, min_distance = 2, combat = TRUE) // This will switch our stance. +*/ diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm new file mode 100644 index 0000000000..1f1ecac678 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm @@ -0,0 +1,103 @@ +// Lurkers are somewhat similar to Hunters, however the big difference is that Lurkers have an imperfect cloak. +// Their AI will try to do hit and run tactics, striking the enemy when its "cloaked", for bonus damage and a stun. +// They keep attacking until the stun ends, then retreat to cloak again and repeat the cycle. +// Hitting the spider before it does its ambush attack will break the cloak and make the spider flee. + +/mob/living/simple_mob/animal/giant_spider/lurker + desc = "Translucent and white, it makes you shudder to look at it. This one has incandescent red eyes." + icon_state = "lurker" + icon_living = "lurker" + icon_dead = "lurker_dead" + + maxHealth = 100 + health = 100 + + poison_per_bite = 5 + + movement_cooldown = 5 + + melee_damage_lower = 10 + melee_damage_upper = 10 + poison_chance = 30 + poison_type = "cryptobiolin" + poison_per_bite = 1 + + player_msg = "You have an imperfect, but automatic stealth. If you attack something while 'hidden', then \ + you will do bonus damage, stun the target, and unstealth for a period of time.
\ + Getting attacked will also break your stealth." + + ai_holder_type = /datum/ai_holder/simple_mob/melee/hit_and_run + + var/cloaked = FALSE + var/cloaked_alpha = 45 // Lower = Harder to see. + var/cloaked_bonus_damage = 30 // This is added on top of the normal melee damage. + var/cloaked_weaken_amount = 3 // How long to stun for. + var/cloak_cooldown = 10 SECONDS // Amount of time needed to re-cloak after losing it. + var/last_uncloak = 0 // world.time + + +/mob/living/simple_mob/animal/giant_spider/lurker/proc/cloak() + if(cloaked) + return + animate(src, alpha = cloaked_alpha, time = 1 SECOND) + cloaked = TRUE + + +/mob/living/simple_mob/animal/giant_spider/lurker/proc/uncloak() + last_uncloak = world.time // This is assigned even if it isn't cloaked already, to 'reset' the timer if the spider is continously getting attacked. + if(!cloaked) + return + animate(src, alpha = initial(alpha), time = 1 SECOND) + cloaked = FALSE + + +// Check if cloaking if possible. +/mob/living/simple_mob/animal/giant_spider/lurker/proc/can_cloak() + if(stat) + return FALSE + if(last_uncloak + cloak_cooldown > world.time) + return FALSE + + return TRUE + + +// Called by things that break cloaks, like Technomancer wards. +/mob/living/simple_mob/animal/giant_spider/lurker/break_cloak() + uncloak() + + +/mob/living/simple_mob/animal/giant_spider/lurker/is_cloaked() + return cloaked + + +// Cloaks the spider automatically, if possible. +/mob/living/simple_mob/animal/giant_spider/lurker/handle_special() + if(!cloaked && can_cloak()) + cloak() + + +// Applies bonus base damage if cloaked. +/mob/living/simple_mob/animal/giant_spider/lurker/apply_bonus_melee_damage(atom/A, damage_amount) + if(cloaked) + return damage_amount + cloaked_bonus_damage + return ..() + +// Applies stun, then uncloaks. +/mob/living/simple_mob/animal/giant_spider/lurker/apply_melee_effects(atom/A) + if(cloaked) + if(isliving(A)) + var/mob/living/L = A + L.Weaken(cloaked_weaken_amount) + to_chat(L, span("danger", "\The [src] ambushes you!")) + playsound(L, 'sound/weapons/spiderlunge.ogg', 75, 1) + uncloak() + ..() // For the poison. + +// Force uncloaking if attacked. +/mob/living/simple_mob/animal/giant_spider/lurker/bullet_act(obj/item/projectile/P) + . = ..() + break_cloak() + +/mob/living/simple_mob/animal/giant_spider/lurker/hit_with_weapon(obj/item/O, mob/living/user, effective_force, hit_zone) + . = ..() + break_cloak() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm new file mode 100644 index 0000000000..62934195dd --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm @@ -0,0 +1,230 @@ +// Nurses, they create webs and eggs. +// They're fragile but their attacks can cause horrifying consequences. +/mob/living/simple_mob/animal/giant_spider/nurse + desc = "Furry and beige, it makes you shudder to look at it. This one has brilliant green eyes." + icon_state = "nurse" + icon_living = "nurse" + icon_dead = "nurse_dead" + + maxHealth = 40 + health = 40 + + movement_cooldown = 5 // A bit faster so that they can inject the eggs easier. + + melee_damage_lower = 5 // Doesn't do a lot of damage, since the goal is to make more spiders with egg attacks. + melee_damage_upper = 10 + poison_per_bite = 5 + poison_type = "stoxin" + + player_msg = "You can spin webs on an adjacent tile, or cocoon an object by clicking on it.
\ + You can also cocoon a dying or dead entity by clicking on them, and you will gain charges for egg-laying.
\ + To lay eggs, click a nearby tile. Laying eggs will deplete a charge." + ai_holder_type = /datum/ai_holder/simple_mob/melee/nurse_spider + + var/fed = 0 // Counter for how many egg laying 'charges' the spider has. + var/egg_inject_chance = 25 // One in four chance to get eggs. + var/egg_type = /obj/effect/spider/eggcluster/small + var/web_type = /obj/effect/spider/stickyweb/dark + + +/mob/living/simple_mob/animal/giant_spider/nurse/inject_poison(mob/living/L, target_zone) + ..() // Inject the stoxin here. + if(ishuman(L) && prob(egg_inject_chance)) + var/mob/living/carbon/human/H = L + var/obj/item/organ/external/O = H.get_organ(target_zone) + if(O) + var/eggcount = 0 + for(var/obj/effect/spider/eggcluster/E in O.implants) + eggcount++ + if(!eggcount) + var/eggs = new egg_type(O, src) + O.implants += eggs + to_chat(H, span("critical", "\The [src] injects something into your [O.name]!") ) // Oh god its laying eggs in me! + +// Webs target in a web if able to. +/mob/living/simple_mob/animal/giant_spider/nurse/attack_target(atom/A) + if(isturf(A)) + if(fed) + return lay_eggs(A) + return web_tile(A) + + if(isliving(A)) + var/mob/living/L = A + if(!L.stat) + return ..() + + if(!istype(A, /atom/movable)) + return + var/atom/movable/AM = A + + if(AM.anchored) + return ..() + + return spin_cocoon(AM) + +/mob/living/simple_mob/animal/giant_spider/nurse/proc/spin_cocoon(atom/movable/AM) + if(!istype(AM)) + return FALSE // We can't cocoon walls sadly. + visible_message(span("notice", "\The [src] begins to secrete a sticky substance around \the [AM].") ) + + // Get our AI to stay still. + set_AI_busy(TRUE) + + if(!do_mob(src, AM, 5 SECONDS)) + set_AI_busy(FALSE) + to_chat(src, span("warning", "You need to stay still to spin a web around \the [AM].")) + return FALSE + + set_AI_busy(FALSE) + + if(!AM) // Make sure it didn't get deleted for whatever reason. + to_chat(src, span("warning", "Whatever you were spinning a web for, its no longer there...")) + return FALSE + + if(!isturf(AM.loc)) + to_chat(src, span("warning", "You can't spin \the [AM] in a web while it is inside \the [AM.loc].")) + return FALSE + + if(!Adjacent(AM)) + to_chat(src, span("warning", "You need to be next to \the [AM] to spin it into a web.")) + return FALSE + + // Finally done with the checks. + var/obj/effect/spider/cocoon/C = new(AM.loc) + var/large_cocoon = FALSE + for(var/mob/living/L in C.loc) + if(istype(L, /mob/living/simple_mob/animal/giant_spider)) // Cannibalism is bad. + continue + fed++ + visible_message(span("warning","\The [src] sticks a proboscis into \the [L], and sucks a viscous substance out.")) + to_chat(src, span("notice", "You've fed upon \the [L], and can now lay [fed] cluster\s of eggs.")) + L.forceMove(C) + large_cocoon = TRUE + break + + // This part's pretty stupid. + for(var/obj/O in C.loc) + if(!O.anchored) + O.forceMove(C) + + // Todo: Put this code on the cocoon object itself? + if(large_cocoon) + C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") + + return TRUE + +/mob/living/simple_mob/animal/giant_spider/nurse/handle_special() + set waitfor = FALSE + if(get_AI_stance() == STANCE_IDLE && !is_AI_busy() && isturf(loc)) + if(fed) + lay_eggs(loc) + else + web_tile(loc) + +/mob/living/simple_mob/animal/giant_spider/nurse/proc/web_tile(turf/T) + if(!istype(T)) + return FALSE + + var/obj/effect/spider/stickyweb/W = locate() in T + if(W) + return FALSE // Already got webs here. + + visible_message(span("notice", "\The [src] begins to secrete a sticky substance.") ) + // Get our AI to stay still. + set_AI_busy(TRUE) + + if(!do_mob(src, T, 5 SECONDS)) + set_AI_busy(FALSE) + to_chat(src, span("warning", "You need to stay still to spin a web on \the [T].")) + return FALSE + + W = locate() in T + if(W) + return FALSE // Spamclick protection. + + set_AI_busy(FALSE) + new web_type(T) + return TRUE + + +/mob/living/simple_mob/animal/giant_spider/nurse/proc/lay_eggs(turf/T) + if(!istype(T)) + return FALSE + + if(!fed) + return FALSE + + var/obj/effect/spider/eggcluster/E = locate() in T + if(E) + return FALSE // Already got eggs here. + + visible_message(span("notice", "\The [src] begins to lay a cluster of eggs.") ) + // Get our AI to stay still. + set_AI_busy(TRUE) + + if(!do_mob(src, T, 5 SECONDS)) + set_AI_busy(FALSE) + to_chat(src, span("warning", "You need to stay still to lay eggs on \the [T].")) + return FALSE + + E = locate() in T + if(E) + return FALSE // Spamclick protection. + + set_AI_busy(FALSE) + new egg_type(T) + fed-- + return TRUE + + +// Variant that 'blocks' light (by being a negative light source). +// This is done to make webbed rooms scary and allow for spiders on the other side of webs to see prey. +/obj/effect/spider/stickyweb/dark + name = "dense web" + desc = "It's sticky, and blocks a lot of light." + light_color = "#FFFFFF" + light_range = 2 + light_power = -3 + +// This is still stupid, but whatever. +/mob/living/simple_mob/animal/giant_spider/nurse/hat + desc = "Furry and beige, it makes you shudder to look at it. This one has brilliant green eyes and a tiny nurse hat." + icon_state = "nursemed" + icon_living = "nursemed" + icon_dead = "nursemed_dead" + + +// The AI for nurse spiders. Wraps things in webs by 'attacking' them. +/datum/ai_holder/simple_mob/melee/nurse_spider + wander = TRUE + base_wander_delay = 8 + cooperative = FALSE // So we don't ask our spider friends to attack things we're webbing. This might also make them stay at the base if their friends find tasty explorers. + +// Get us unachored objects as an option as well. +/datum/ai_holder/simple_mob/melee/nurse_spider/list_targets() + . = ..() + + var/static/alternative_targets = typecacheof(list(/obj/item, /obj/structure)) + + for(var/AT in typecache_filter_list(range(vision_range, holder), alternative_targets)) + var/obj/O = AT + if(can_see(holder, O, vision_range) && !O.anchored) + . += O + +// Select an obj if no mobs are around. +/datum/ai_holder/melee/nurse_spider/pick_target(list/targets) + var/mobs_only = locate(/mob/living) in targets // If a mob is in the list of targets, then ignore objects. + if(mobs_only) + for(var/A in targets) + if(!isliving(A)) + targets -= A + + return ..(targets) + +/datum/ai_holder/simple_mob/melee/nurse_spider/can_attack(atom/movable/the_target) + . = ..() + if(!.) // Parent returned FALSE. + if(istype(the_target, /obj)) + var/obj/O = the_target + if(!O.anchored) + return TRUE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/pepper.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/pepper.dm new file mode 100644 index 0000000000..1c2eaa6dda --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/pepper.dm @@ -0,0 +1,21 @@ +// Pepper spiders inject condensed capsaicin into their victims. + +/mob/living/simple_mob/animal/giant_spider/pepper + desc = "Red and brown, it makes you shudder to look at it. This one has glinting red eyes." + icon_state = "pepper" + icon_living = "pepper" + icon_dead = "pepper_dead" + + maxHealth = 210 + health = 210 + + melee_damage_lower = 8 + melee_damage_upper = 15 + + poison_chance = 20 + poison_per_bite = 5 + poison_type = "condensedcapsaicin_v" + +/mob/living/simple_mob/animal/giant_spider/pepper/initialize() + adjust_scale(1.1) + return ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/phorogenic.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/phorogenic.dm new file mode 100644 index 0000000000..d9508a05d8 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/phorogenic.dm @@ -0,0 +1,56 @@ +// Phorogenic spiders explode when they die. +// You really shouldn't melee them. + +/mob/living/simple_mob/animal/giant_spider/phorogenic + desc = "Crystalline and purple, it makes you shudder to look at it. This one has haunting purple eyes." + icon_state = "phoron" + icon_living = "phoron" + icon_dead = "phoron_dead" + + maxHealth = 225 + health = 225 + taser_kill = FALSE //You will need more than a peashooter to kill the juggernaut. + + melee_damage_lower = 25 + melee_damage_upper = 40 + attack_armor_pen = 15 + + movement_cooldown = 15 + + poison_chance = 30 + poison_per_bite = 0.5 + poison_type = "phoron" + + var/exploded = FALSE + var/explosion_dev_range = 1 + var/explosion_heavy_range = 2 + var/explosion_light_range = 4 + var/explosion_flash_range = 6 // This doesn't do anything iirc. + + var/explosion_delay_lower = 1 SECOND // Lower bound for explosion delay. + var/explosion_delay_upper = 2 SECONDS // Upper bound. + +/mob/living/simple_mob/animal/giant_spider/phorogenic/initialize() + adjust_scale(1.25) + return ..() + +/mob/living/simple_mob/animal/giant_spider/phorogenic/death() + visible_message(span("critical", "\The [src]'s body begins to rupture!")) + var/delay = rand(explosion_delay_lower, explosion_delay_upper) + spawn(0) + // Flash black and red as a warning. + for(var/i = 1 to delay) + if(i % 2 == 0) + color = "#000000" + else + color = "#FF0000" + sleep(1) + + spawn(delay) + // The actual boom. + if(src && !exploded) + visible_message(span("danger", "\The [src]'s body detonates!")) + exploded = TRUE + explosion(src.loc, explosion_dev_range, explosion_heavy_range, explosion_light_range, explosion_flash_range) + return ..() + diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/thermic.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/thermic.dm new file mode 100644 index 0000000000..dc0a345865 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/thermic.dm @@ -0,0 +1,20 @@ +// Thermic spiders inject a special variant of thermite that burns someone from the inside. + +/mob/living/simple_mob/animal/giant_spider/thermic + desc = "Mirage-cloaked and orange, it makes you shudder to look at it. This one has simmering orange eyes." + icon_state = "pit" + icon_living = "pit" + icon_dead = "pit_dead" + + maxHealth = 175 + health = 175 + + melee_damage_lower = 10 + melee_damage_upper = 25 + + heat_resist = 0.75 + cold_resist = -0.50 + + poison_chance = 30 + poison_per_bite = 1 + poison_type = "thermite_v" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/tunneler.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/tunneler.dm new file mode 100644 index 0000000000..82a68d4c1f --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/tunneler.dm @@ -0,0 +1,185 @@ +// Tunnelers have a special ability that allows them to charge at an enemy by tunneling towards them. +// Any mobs inbetween the tunneler's path and the target will be stunned if the tunneler hits them. +// The target will suffer a stun as well, if the tunneler hits them at the end. A successful hit will stop the tunneler. +// If the target moves fast enough, the tunneler can miss, causing it to overshoot. +// If the tunneler hits a solid wall, the tunneler will suffer a stun. + +/mob/living/simple_mob/animal/giant_spider/tunneler + desc = "Sandy and brown, it makes you shudder to look at it. This one has glittering yellow eyes." + icon_state = "tunneler" + icon_living = "tunneler" + icon_dead = "tunneler_dead" + + maxHealth = 120 + health = 120 + + melee_damage_lower = 10 + melee_damage_upper = 10 + + poison_chance = 15 + poison_per_bite = 3 + poison_type = "serotrotium_v" + +// ai_holder_type = /datum/ai_holder/simple_mob/melee/tunneler + + player_msg = "You can perform a tunneling attack by clicking on someone from a distance.
\ + There is a noticable travel delay as you tunnel towards the tile the target was at when you started the tunneling attack.
\ + Any entities inbetween you and the targeted tile will be stunned for a brief period of time.
\ + Whatever is on the targeted tile when you arrive will suffer a potent stun.
\ + If nothing is on the targeted tile, you will overshoot and keep going for a few more tiles.
\ + If you hit a wall or other solid structure during that time, you will suffer a lengthy stun and be vulnerable to more harm." + + // Tunneling is a special attack, similar to the hunter's Leap. + special_attack_min_range = 2 + special_attack_max_range = 6 + special_attack_cooldown = 10 SECONDS + + var/tunnel_warning = 0.5 SECONDS // How long the dig telegraphing is. + var/tunnel_tile_speed = 2 // How long to wait between each tile. Higher numbers result in an easier to dodge tunnel attack. + +/mob/living/simple_mob/animal/giant_spider/tunneler/frequent + special_attack_cooldown = 5 SECONDS + +/mob/living/simple_mob/animal/giant_spider/tunneler/fast + tunnel_tile_speed = 1 + +/mob/living/simple_mob/animal/giant_spider/tunneler/should_special_attack(atom/A) + // Make sure its possible for the spider to reach the target so it doesn't try to go through a window. + var/turf/destination = get_turf(A) + var/turf/starting_turf = get_turf(src) + var/turf/T = starting_turf + for(var/i = 1 to get_dist(starting_turf, destination)) + if(T == destination) + break + + T = get_step(T, get_dir(T, destination)) + if(T.check_density(ignore_mobs = TRUE)) + return FALSE + return T == destination + + +/mob/living/simple_mob/animal/giant_spider/tunneler/do_special_attack(atom/A) + set waitfor = FALSE + set_AI_busy(TRUE) + + // Save where we're gonna go soon. + var/turf/destination = get_turf(A) + var/turf/starting_turf = get_turf(src) + + // Telegraph to give a small window to dodge if really close. + do_windup_animation(A, tunnel_warning) + sleep(tunnel_warning) // For the telegraphing. + + // Do the dig! + visible_message(span("danger","\The [src] tunnels towards \the [A]!")) + submerge() + + if(handle_tunnel(destination) == FALSE) + set_AI_busy(FALSE) + emerge() + return FALSE + + // Did we make it? + if(!(src in destination)) + set_AI_busy(FALSE) + emerge() + return FALSE + + var/overshoot = TRUE + + // Test if something is at destination. + for(var/mob/living/L in destination) + if(L == src) + continue + + visible_message(span("danger","\The [src] erupts from underneath, and hits \the [L]!")) + playsound(L, 'sound/weapons/heavysmash.ogg', 75, 1) + L.Weaken(3) + overshoot = FALSE + + if(!overshoot) // We hit the target, or something, at destination, so we're done. + set_AI_busy(FALSE) + emerge() + return TRUE + + // Otherwise we need to keep going. + to_chat(src, span("warning", "You overshoot your target!")) + playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) + var/dir_to_go = get_dir(starting_turf, destination) + for(var/i = 1 to rand(2, 4)) + destination = get_step(destination, dir_to_go) + + if(handle_tunnel(destination) == FALSE) + set_AI_busy(FALSE) + emerge() + return FALSE + + set_AI_busy(FALSE) + emerge() + return FALSE + + + +// Does the tunnel movement, stuns enemies, etc. +/mob/living/simple_mob/animal/giant_spider/tunneler/proc/handle_tunnel(turf/destination) + var/turf/T = get_turf(src) // Hold our current tile. + + // Regular tunnel loop. + for(var/i = 1 to get_dist(src, destination)) + if(stat) + return FALSE // We died or got knocked out on the way. + if(loc == destination) + break // We somehow got there early. + + // Update T. + T = get_step(src, get_dir(src, destination)) + if(T.check_density(ignore_mobs = TRUE)) + to_chat(src, span("critical", "You hit something really solid!")) + playsound(src, "punch", 75, 1) + Weaken(5) + add_modifier(/datum/modifier/tunneler_vulnerable, 10 SECONDS) + return FALSE // Hit a wall. + + // Stun anyone in our way. + for(var/mob/living/L in T) + playsound(L, 'sound/weapons/heavysmash.ogg', 75, 1) + L.Weaken(2) + + // Get into the tile. + forceMove(T) + + // Visuals and sound. + dig_under_floor(get_turf(src)) + playsound(src, 'sound/effects/break_stone.ogg', 75, 1) + sleep(tunnel_tile_speed) + +// For visuals. +/mob/living/simple_mob/animal/giant_spider/tunneler/proc/submerge() + alpha = 0 + dig_under_floor(get_turf(src)) + new /obj/effect/temporary_effect/tunneler_hole(get_turf(src)) + +// Ditto. +/mob/living/simple_mob/animal/giant_spider/tunneler/proc/emerge() + alpha = 255 + dig_under_floor(get_turf(src)) + new /obj/effect/temporary_effect/tunneler_hole(get_turf(src)) + +/mob/living/simple_mob/animal/giant_spider/tunneler/proc/dig_under_floor(turf/T) + new /obj/item/weapon/ore/glass(T) // This will be rather weird when on station but the alternative is too much work. + +/obj/effect/temporary_effect/tunneler_hole + name = "hole" + desc = "A collapsing tunnel hole." + icon_state = "tunnel_hole" + time_to_die = 1 MINUTE + +/datum/modifier/tunneler_vulnerable + name = "Vulnerable" + desc = "You are vulnerable to more harm than usual." + on_created_text = "You feel vulnerable..." + on_expired_text = "You feel better." + stacks = MODIFIER_STACK_EXTEND + + incoming_damage_percent = 2 + evasion = -100 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/webslinger.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/webslinger.dm new file mode 100644 index 0000000000..0024bc8962 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/webslinger.dm @@ -0,0 +1,37 @@ +/mob/living/simple_mob/animal/giant_spider/webslinger + desc = "Furry and green, it makes you shudder to look at it. This one has brilliant green eyes, and a cloak of web." + tt_desc = "X Brachypelma phorus balisticus" + icon_state = "webslinger" + icon_living = "webslinger" + icon_dead = "webslinger_dead" + maxHealth = 90 + health = 90 + projectilesound = 'sound/weapons/thudswoosh.ogg' + projectiletype = /obj/item/projectile/webball + base_attack_cooldown = 10 + melee_damage_lower = 8 + melee_damage_upper = 15 + poison_per_bite = 2 + poison_type = "psilocybin" + player_msg = "You can fire a ranged attack by clicking on an enemy or tile at a distance." + ai_holder_type = /datum/ai_holder/simple_mob/ranged + +// Check if we should bola, or just shoot the pain ball +/mob/living/simple_mob/animal/giant_spider/webslinger/should_special_attack(atom/A) + if(ismob(A)) + if(ishuman(A)) + var/mob/living/carbon/human/H = A + if(!H.legcuffed) + return TRUE + return FALSE + +// Now we've got a running human in sight, time to throw the bola +/mob/living/simple_mob/animal/giant_spider/webslinger/do_special_attack(atom/A) + set waitfor = FALSE + set_AI_busy(TRUE) + var/obj/item/projectile/bola/B = new /obj/item/projectile/bola(src.loc) + playsound(src, 'sound/weapons/thudswoosh.ogg', 100, 1) + if(!B) + return + B.launch(A) + set_AI_busy(FALSE) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm new file mode 100644 index 0000000000..3b40147794 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm @@ -0,0 +1,25 @@ +//Look Sir, free crabs! +/mob/living/simple_mob/animal/passive/crab + name = "crab" + desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." + tt_desc = "E Cancer bellianus" + faction = "crabs" + + icon_state = "crab" + icon_living = "crab" + icon_dead = "crab_dead" + + mob_size = MOB_SMALL + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stomps" + friendly = "pinches" + + say_list_type = /datum/say_list/crab + +//COFFEE! SQUEEEEEEEEE! +/mob/living/simple_mob/animal/passive/crab/Coffee + name = "Coffee" + real_name = "Coffee" + desc = "It's Coffee, the other pet!" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm new file mode 100644 index 0000000000..bdf8853b4b --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm @@ -0,0 +1,77 @@ +// Different types of fish! They are all subtypes of this tho +/mob/living/simple_mob/animal/passive/fish + name = "fish" + desc = "Its a fishy. No touchy fishy." + icon = 'icons/mob/fish.dmi' + + mob_size = MOB_SMALL + // So fish are actually underwater. + plane = TURF_PLANE + layer = UNDERWATER_LAYER + + // By default they can be in any water turf. Subtypes might restrict to deep/shallow etc + var/global/list/suitable_turf_types = list( + /turf/simulated/floor/beach/water, + /turf/simulated/floor/beach/coastline, + /turf/simulated/floor/holofloor/beach/water, + /turf/simulated/floor/holofloor/beach/coastline, + /turf/simulated/floor/water + ) + +// Makes the AI unable to willingly go on land. +/mob/living/simple_mob/animal/passive/fish/IMove(newloc) + if(is_type_in_list(newloc, suitable_turf_types)) + return ..() // Procede as normal. + return MOVEMENT_FAILED // Don't leave the water! + +// Take damage if we are not in water +/mob/living/simple_mob/animal/passive/fish/handle_breathing() + var/turf/T = get_turf(src) + if(T && !is_type_in_list(T, suitable_turf_types)) + if(prob(50)) + say(pick("Blub", "Glub", "Burble")) + adjustBruteLoss(unsuitable_atoms_damage) + +// Subtypes. +/mob/living/simple_mob/animal/passive/fish/bass + name = "bass" + tt_desc = "E Micropterus notius" + icon_state = "bass-swim" + icon_living = "bass-swim" + icon_dead = "bass-dead" + +/mob/living/simple_mob/animal/passive/fish/trout + name = "trout" + tt_desc = "E Salmo trutta" + icon_state = "trout-swim" + icon_living = "trout-swim" + icon_dead = "trout-dead" + +/mob/living/simple_mob/animal/passive/fish/salmon + name = "salmon" + tt_desc = "E Oncorhynchus nerka" + icon_state = "salmon-swim" + icon_living = "salmon-swim" + icon_dead = "salmon-dead" + +/mob/living/simple_mob/animal/passive/fish/perch + name = "perch" + tt_desc = "E Perca flavescens" + icon_state = "perch-swim" + icon_living = "perch-swim" + icon_dead = "perch-dead" + +/mob/living/simple_mob/animal/passive/fish/pike + name = "pike" + tt_desc = "E Esox aquitanicus" + icon_state = "pike-swim" + icon_living = "pike-swim" + icon_dead = "pike-dead" + +/mob/living/simple_mob/animal/passive/fish/koi + name = "koi" + tt_desc = "E Cyprinus rubrofuscus" + icon_state = "koi-swim" + icon_living = "koi-swim" + icon_dead = "koi-dead" + diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm new file mode 100644 index 0000000000..adb2ea83ca --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm @@ -0,0 +1,24 @@ +/mob/living/simple_mob/animal/passive/lizard + name = "lizard" + desc = "A cute, tiny lizard." + tt_desc = "E Anolis cuvieri" + + icon_state = "lizard" + icon_living = "lizard" + icon_dead = "lizard-dead" + + health = 5 + maxHealth = 5 + mob_size = MOB_MINISCULE + + response_help = "pets" + response_disarm = "shoos" + response_harm = "stomps on" + + attacktext = list("bitten") + melee_damage_lower = 1 + melee_damage_upper = 2 + + speak_emote = list("hisses") + + say_list_type = /datum/say_list/lizard diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm new file mode 100644 index 0000000000..6fc73fc5f9 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/misc.dm @@ -0,0 +1,29 @@ +/mob/living/simple_mob/animal/passive/yithian + name = "yithian" + desc = "A friendly creature vaguely resembling an oversized snail without a shell." + tt_desc = "J Escargot escargot" // a product of Jade, which is a planet that totally exists + + icon_state = "yithian" + icon_living = "yithian" + icon_dead = "yithian_dead" + icon = 'icons/jungle.dmi' + + // Same stats as lizards. + health = 5 + maxHealth = 5 + mob_size = MOB_MINISCULE + +/mob/living/simple_mob/animal/passive/tindalos + name = "tindalos" + desc = "It looks like a large, flightless grasshopper." + tt_desc = "J Locusta bruchus" + + icon_state = "tindalos" + icon_living = "tindalos" + icon_dead = "tindalos_dead" + icon = 'icons/jungle.dmi' + + // Same stats as lizards. + health = 5 + maxHealth = 5 + mob_size = MOB_MINISCULE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm new file mode 100644 index 0000000000..70afdaf910 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm @@ -0,0 +1,115 @@ +/mob/living/simple_mob/animal/passive/mouse + name = "mouse" + real_name = "mouse" + desc = "It's a small rodent." + tt_desc = "E Mus musculus" + icon_state = "mouse_gray" + item_state = "mouse_gray" + icon_living = "mouse_gray" + icon_dead = "mouse_gray_dead" + + maxHealth = 5 + health = 5 + + mob_size = MOB_MINISCULE + pass_flags = PASSTABLE +// can_pull_size = ITEMSIZE_TINY +// can_pull_mobs = MOB_PULL_NONE + layer = MOB_LAYER + density = 0 + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stamps on" + + min_oxy = 16 //Require atleast 16kPA oxygen + minbodytemp = 223 //Below -50 Degrees Celcius + maxbodytemp = 323 //Above 50 Degrees Celcius + + has_langs = list("Mouse") + + holder_type = /obj/item/weapon/holder/mouse + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + say_list_type = /datum/say_list/mouse + + var/body_color //brown, gray and white, leave blank for random + +/mob/living/simple_mob/animal/passive/mouse/New() + ..() + + verbs += /mob/living/proc/ventcrawl + verbs += /mob/living/proc/hide + + if(name == initial(name)) + name = "[name] ([rand(1, 1000)])" + real_name = name + + if(!body_color) + body_color = pick( list("brown","gray","white") ) + icon_state = "mouse_[body_color]" + item_state = "mouse_[body_color]" + icon_living = "mouse_[body_color]" + icon_dead = "mouse_[body_color]_dead" + icon_rest = "mouse_[body_color]_sleep" + desc = "A small [body_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." + +/mob/living/simple_mob/animal/passive/mouse/Crossed(AM as mob|obj) + if( ishuman(AM) ) + if(!stat) + var/mob/M = AM + M.visible_message("\icon[src] Squeek!") + playsound(src, 'sound/effects/mouse_squeak.ogg', 35, 1) + ..() + +/mob/living/simple_mob/animal/passive/mouse/death() + layer = MOB_LAYER + playsound(src, 'sound/effects/mouse_squeak_loud.ogg', 35, 1) + if(client) + client.time_died_as_mouse = world.time + ..() + +/mob/living/simple_mob/animal/passive/mouse/cannot_use_vents() + return + +/mob/living/simple_mob/animal/passive/mouse/proc/splat() + src.health = 0 + src.stat = DEAD + src.icon_dead = "mouse_[body_color]_splat" + src.icon_state = "mouse_[body_color]_splat" + layer = MOB_LAYER + if(client) + client.time_died_as_mouse = world.time + +/* + * Mouse types + */ + +/mob/living/simple_mob/animal/passive/mouse/white + body_color = "white" + icon_state = "mouse_white" + +/mob/living/simple_mob/animal/passive/mouse/gray + body_color = "gray" + icon_state = "mouse_gray" + +/mob/living/simple_mob/animal/passive/mouse/brown + body_color = "brown" + icon_state = "mouse_brown" + +//TOM IS ALIVE! SQUEEEEEEEE~K :) +/mob/living/simple_mob/animal/passive/mouse/brown/Tom + name = "Tom" + desc = "Jerry the cat is not amused." + +/mob/living/simple_mob/animal/passive/mouse/brown/Tom/New() + ..() + // Change my name back, don't want to be named Tom (666) + name = initial(name) + + +// Mouse noises +/datum/say_list/mouse + speak = list("Squeek!","SQUEEK!","Squeek?") + emote_hear = list("squeeks","squeaks","squiks") + emote_see = list("runs in a circle", "shakes", "scritches at something") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm new file mode 100644 index 0000000000..c488d1ed28 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm @@ -0,0 +1,5 @@ +// Passive mobs can't attack things, and will run away instead. +// They can also be displaced by all mobs. +/mob/living/simple_mob/animal/passive + ai_holder_type = /datum/ai_holder/simple_mob/passive + mob_bump_flag = 0 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm new file mode 100644 index 0000000000..7fcd020f8e --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/penguin.dm @@ -0,0 +1,27 @@ +/mob/living/simple_mob/animal/passive/penguin + name = "penguin" + desc = "An ungainly, waddling, cute, and VERY well-dressed bird." + tt_desc = "Aptenodytes forsteri" + icon_state = "penguin" + icon_living = "penguin" + icon_dead = "penguin_dead" + + maxHealth = 20 + health = 20 + minbodytemp = 175 // Same as Sif mobs. + + response_help = "pets" + response_disarm = "pushes aside" + response_harm = "hits" + + harm_intent_damage = 5 + melee_damage_lower = 10 + melee_damage_upper = 15 + attacktext = list("pecked") + + has_langs = list("Bird") + +/mob/living/simple_mob/animal/passive/penguin/tux + name = "Tux" + desc = "A penguin that has been known to associate with gnus." + speak_emote = list("interjects") diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm new file mode 100644 index 0000000000..5e77a57a3c --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm @@ -0,0 +1,93 @@ +// Base bird type. + +/mob/living/simple_mob/animal/passive/bird + name = "bird" + desc = "A domesticated bird. Tweet tweet!" + player_msg = "You are able to fly." + + icon = 'icons/mob/birds.dmi' + icon_state = "parrot" + item_state = null + icon_rest = "parrot-held" + icon_dead = "parrot-dead" + + pass_flags = PASSTABLE + + health = 30 + maxHealth = 30 + melee_damage_lower = 3 + melee_damage_upper = 3 + + movement_cooldown = 0 + hovering = TRUE // Birds can fly. + softfall = TRUE + parachuting = TRUE + + attacktext = list("claws", "pecks") + speak_emote = list("chirps", "caws") + has_langs = list("Bird") + response_help = "pets" + response_disarm = "gently moves aside" + response_harm = "swats" + + say_list_type = /datum/say_list/bird + holder_type = /obj/item/weapon/holder/bird + +/datum/say_list/bird + speak = list("Chirp!","Caw!","Screech!","Squawk!") + emote_hear = list("chirps","caws") + emote_see = list("shakes their head", "ruffles their feathers") + +/obj/item/weapon/holder/bird + name = "bird" + desc = "It's a bird!" + icon_state = null + item_icons = null + w_class = ITEMSIZE_SMALL + +/obj/item/weapon/holder/bird/sync(var/mob/living/simple_mob/SM) + ..() + icon_state = SM.icon_rest // Looks better if the bird isn't flapping constantly in the UI. + +// Subtypes for birbs. + +/mob/living/simple_mob/animal/passive/bird/black_bird + name = "common blackbird" + desc = "A species of bird, both the males and females are known to be territorial on their breeding grounds." + icon_state = "commonblackbird" + icon_dead = "commonblackbird-dead" + tt_desc = "E Turdus merula" + icon_scale = 0.5 + +/mob/living/simple_mob/animal/passive/bird/azure_tit + name = "azure tit" + desc = "A species of bird, colored blue and white." + icon_state = "azuretit" + icon_dead = "azuretit-dead" + tt_desc = "E Cyanistes cyanus" + icon_scale = 0.5 + +/mob/living/simple_mob/animal/passive/bird/european_robin + name = "european robin" + desc = "A species of bird, they have been studied for their sense of magnetoreception." + icon_state = "europeanrobin" + icon_dead = "europeanrobin-dead" + tt_desc = "E Erithacus rubecula" + icon_scale = 0.5 + +/mob/living/simple_mob/animal/passive/bird/goldcrest + name = "goldcrest" + desc = "A species of bird, they were once called 'king of the birds' in ancient human folklore, for their golden crest. \ + Today, their scientific name still elude towards this, with regulus, meaning petty king." + icon_state = "goldcrest" + icon_dead = "goldcrest-dead" + tt_desc = "E Regulus regulus" + icon_scale = 0.5 + +/mob/living/simple_mob/animal/passive/bird/ringneck_dove + name = "ringneck dove" + desc = "A species of bird. They are also known as the barbary dove, and have a distinct ring-like shape around the back of their neck." + icon_state = "ringneckdove" + icon_dead = "ringneckdove-dead" + tt_desc = "E Streptopelia risoria" // This is actually disputed IRL but since we can't tell the future it'll stay the same for 500+ years. + icon_scale = 0.5 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm new file mode 100644 index 0000000000..6ded938dff --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat.dm @@ -0,0 +1,136 @@ +/mob/living/simple_mob/animal/passive/cat + name = "cat" + desc = "A domesticated, feline pet. Has a tendency to adopt crewmembers." + tt_desc = "E Felis silvestris catus" + icon_state = "cat2" + item_state = "cat2" + icon_living = "cat2" + icon_dead = "cat2_dead" + icon_rest = "cat2_rest" + + movement_cooldown = 0.5 SECONDS + + see_in_dark = 6 // Not sure if this actually works. + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + + holder_type = /obj/item/weapon/holder/cat + mob_size = MOB_SMALL + + has_langs = list("Cat") + + var/mob/living/friend = null // Our best pal, who we'll follow. Meow. + +/mob/living/simple_mob/animal/passive/cat/handle_special() + if(!stat && prob(2)) // spooky + var/mob/observer/dead/spook = locate() in range(src, 5) + if(spook) + var/turf/T = get_turf(spook) + var/list/visible = list() + for(var/obj/O in T.contents) + if(!O.invisibility && O.name) + visible += O + if(visible.len) + var/atom/A = pick(visible) + visible_emote("suddenly stops and stares at something unseen[istype(A) ? " near [A]":""].") + +// Instakills mice. +/mob/living/simple_mob/animal/passive/cat/apply_melee_effects(var/atom/A) + if(ismouse(A)) + var/mob/living/simple_mob/animal/passive/mouse/mouse = A + if(mouse.getMaxHealth() < 20) // In case a badmin makes giant mice or something. + mouse.splat() + visible_emote(pick("bites \the [mouse]!", "toys with \the [mouse].", "chomps on \the [mouse]!")) + else + ..() + +/mob/living/simple_mob/animal/passive/cat/IIsAlly(mob/living/L) + if(L == friend) // Always be pals with our special friend. + return TRUE + + . = ..() + + if(.) // We're pals, but they might be a dirty mouse... + if(ismouse(L)) + return FALSE // Cats and mice can never get along. + +/mob/living/simple_mob/animal/passive/cat/verb/become_friends() + set name = "Become Friends" + set category = "IC" + set src in view(1) + + var/mob/living/L = usr + if(!istype(L)) + return // Fuck off ghosts. + + if(friend) + if(friend == usr) + to_chat(L, span("notice", "\The [src] is already your friend! Meow!")) + return + else + to_chat(L, span("warning", "\The [src] ignores you.")) + return + + friend = L + face_atom(L) + to_chat(L, span("notice", "\The [src] is now your friend! Meow.")) + visible_emote(pick("nuzzles [friend].", "brushes against [friend].", "rubs against [friend].", "purrs.")) + + if(has_AI()) + var/datum/ai_holder/AI = ai_holder + AI.set_follow(friend) + + +//RUNTIME IS ALIVE! SQUEEEEEEEE~ +/mob/living/simple_mob/animal/passive/cat/runtime + name = "Runtime" + desc = "Her fur has the look and feel of velvet, and her tail quivers occasionally." + tt_desc = "E Felis silvestris medicalis" // a hypoallergenic breed produced by NT for... medical purposes? Sure. + gender = FEMALE + icon_state = "cat" + item_state = "cat" + icon_living = "cat" + icon_dead = "cat_dead" + icon_rest = "cat_rest" + +/mob/living/simple_mob/animal/passive/cat/kitten + name = "kitten" + desc = "D'aaawwww" + icon_state = "kitten" + item_state = "kitten" + icon_living = "kitten" + icon_dead = "kitten_dead" + gender = NEUTER + +/mob/living/simple_mob/animal/passive/cat/kitten/initialize() + if(gender == NEUTER) + gender = pick(MALE, FEMALE) + return ..() + +// Leaving this here for now. +/obj/item/weapon/holder/cat/fluff/bones + name = "Bones" + desc = "It's Bones! Meow." + gender = MALE + icon_state = "cat3" + +/mob/living/simple_mob/animal/passive/cat/bones + name = "Bones" + desc = "That's Bones the cat. He's a laid back, black cat. Meow." + gender = MALE + icon_state = "cat3" + item_state = "cat3" + icon_living = "cat3" + icon_dead = "cat3_dead" + icon_rest = "cat3_rest" + holder_type = /obj/item/weapon/holder/cat/fluff/bones + + +/datum/say_list/cat + speak = list("Meow!","Esp!","Purr!","HSSSSS") + emote_hear = list("meows","mews") + emote_see = list("shakes their head", "shivers") + say_maybe_target = list("Meow?","Mew?","Mao?") + say_got_target = list("MEOW!","HSSSS!","REEER!") + diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm new file mode 100644 index 0000000000..fbfafc426a --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/dog.dm @@ -0,0 +1,234 @@ +/mob/living/simple_mob/animal/passive/dog + name = "dog" + real_name = "dog" + desc = "It's a dog." + tt_desc = "E Canis lupus familiaris" + icon_state = "corgi" + icon_living = "corgi" + icon_dead = "corgi_dead" + + health = 20 + maxHealth = 20 + + response_help = "pets" + response_disarm = "bops" + response_harm = "kicks" + + mob_size = MOB_SMALL + + has_langs = list("Dog") + + say_list_type = /datum/say_list/dog + + meat_amount = 3 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/corgi + + var/obj/item/inventory_head + var/obj/item/inventory_back + + +/mob/living/simple_mob/animal/passive/dog/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/weapon/newspaper)) + if(!stat) + for(var/mob/M in viewers(user, null)) + if ((M.client && !( M.blinded ))) + M.show_message("[user] baps [name] on the nose with the rolled up [O]") + spawn(0) + for(var/i in list(1,2,4,8,4,2,1,2)) + set_dir(i) + sleep(1) + else + ..() + +/mob/living/simple_mob/animal/passive/dog/regenerate_icons() + overlays = list() + + if(inventory_head) + var/head_icon_state = inventory_head.icon_state + if(health <= 0) + head_icon_state += "2" + + var/icon/head_icon = image('icons/mob/corgi_head.dmi',head_icon_state) + if(head_icon) + overlays += head_icon + + if(inventory_back) + var/back_icon_state = inventory_back.icon_state + if(health <= 0) + back_icon_state += "2" + + var/icon/back_icon = image('icons/mob/corgi_back.dmi',back_icon_state) + if(back_icon) + overlays += back_icon + + return + + + + +/obj/item/weapon/reagent_containers/food/snacks/meat/corgi + name = "corgi meat" + desc = "Tastes like... well, you know..." + + + + +/datum/say_list/dog + speak = list("YAP", "Woof!", "Bark!", "AUUUUUU") + emote_hear = list("barks", "woofs", "yaps","pants") + emote_see = list("shakes its head", "shivers") + +// This exists so not every type of dog has to be a subtype of corgi, and in case we get more dog sprites +/mob/living/simple_mob/animal/passive/dog/corgi + name = "corgi" + real_name = "corgi" + desc = "It's a corgi." + tt_desc = "E Canis lupus familiaris" + icon_state = "corgi" + icon_living = "corgi" + icon_dead = "corgi_dead" + +/mob/living/simple_mob/animal/passive/dog/corgi/puppy + name = "corgi puppy" + real_name = "corgi" + desc = "It's a corgi puppy." + icon_state = "puppy" + icon_living = "puppy" + icon_dead = "puppy_dead" + +//pupplies cannot wear anything. +/mob/living/simple_mob/animal/passive/dog/corgi/puppy/Topic(href, href_list) + if(href_list["remove_inv"] || href_list["add_inv"]) + usr << "You can't fit this on [src]" + return + ..() + +/mob/living/simple_mob/animal/passive/dog/corgi/puppy/Bockscar + name = "Bockscar" + real_name = "Bockscar" + +//IAN! SQUEEEEEEEEE~ +/mob/living/simple_mob/animal/passive/dog/corgi/Ian + name = "Ian" + real_name = "Ian" //Intended to hold the name without altering it. + gender = MALE + desc = "It's a corgi." + var/turns_since_scan = 0 + var/obj/movement_target + +/mob/living/simple_mob/animal/passive/dog/corgi/Ian/Life() + ..() + + //Not replacing with SA FollowTarget mechanics because Ian behaves... very... specifically. + + //Feeding, chasing food, FOOOOODDDD + if(!stat && !resting && !buckled) + turns_since_scan++ + if(turns_since_scan > 5) + turns_since_scan = 0 + if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) + movement_target = null + if( !movement_target || !(movement_target.loc in oview(src, 3)) ) + movement_target = null + for(var/obj/item/weapon/reagent_containers/food/snacks/S in oview(src,3)) + if(isturf(S.loc) || ishuman(S.loc)) + movement_target = S + break + if(movement_target) + step_to(src,movement_target,1) + sleep(3) + step_to(src,movement_target,1) + sleep(3) + step_to(src,movement_target,1) + + if(movement_target) //Not redundant due to sleeps, Item can be gone in 6 decisecomds + if (movement_target.loc.x < src.x) + set_dir(WEST) + else if (movement_target.loc.x > src.x) + set_dir(EAST) + else if (movement_target.loc.y < src.y) + set_dir(SOUTH) + else if (movement_target.loc.y > src.y) + set_dir(NORTH) + else + set_dir(SOUTH) + + if(isturf(movement_target.loc) ) + UnarmedAttack(movement_target) + else if(ishuman(movement_target.loc) && prob(20)) + visible_emote("stares at the [movement_target] that [movement_target.loc] has with sad puppy eyes.") + + if(prob(1)) + visible_emote(pick("dances around","chases their tail")) + spawn(0) + for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2)) + set_dir(i) + sleep(1) + +//LISA! SQUEEEEEEEEE~ +/mob/living/simple_mob/animal/passive/dog/corgi/Lisa + name = "Lisa" + real_name = "Lisa" + gender = FEMALE + desc = "It's a corgi with a cute pink bow." + icon_state = "lisa" + icon_living = "lisa" + icon_dead = "lisa_dead" + response_help = "pets" + response_disarm = "bops" + response_harm = "kicks" + var/turns_since_scan = 0 + var/puppies = 0 + +//Lisa already has a cute bow! +/mob/living/simple_mob/animal/passive/dog/corgi/Lisa/Topic(href, href_list) + if(href_list["remove_inv"] || href_list["add_inv"]) + to_chat(usr, "[src] already has a cute bow!") + return + ..() + +/mob/living/simple_mob/animal/passive/dog/corgi/Lisa/Life() + ..() + + if(!stat && !resting && !buckled) + turns_since_scan++ + if(turns_since_scan > 15) + turns_since_scan = 0 + var/alone = TRUE + var/ian = FALSE + for(var/mob/M in oviewers(7, src)) + if(istype(M, /mob/living/simple_mob/animal/passive/dog/corgi/Ian)) + if(M.client) + alone = FALSE + break + else + ian = M + else + alone = FALSE + break + if(alone && ian && puppies < 4) + if(near_camera(src) || near_camera(ian)) + return + new /mob/living/simple_mob/animal/passive/dog/corgi/puppy(loc) + + if(prob(1)) + visible_emote(pick("dances around","chases her tail")) + spawn(0) + for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2)) + set_dir(i) + sleep(1) + +// Tamaskans +/mob/living/simple_mob/animal/passive/dog/tamaskan + name = "tamaskan" + real_name = "tamaskan" + desc = "It's a tamaskan." + icon_state = "tamaskan" + icon_living = "tamaskan" + icon_dead = "tamaskan_dead" + +/mob/living/simple_mob/animal/passive/dog/tamaskan/Spice + name = "Spice" + real_name = "Spice" //Intended to hold the name without altering it. + gender = FEMALE + desc = "It's a tamaskan, the name Spice can be found on its collar." \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm new file mode 100644 index 0000000000..bedd95e3ce --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm @@ -0,0 +1,252 @@ +// Parrots can talk, and may repeat things it hears. +/mob/living/simple_mob/animal/passive/bird/parrot + name = "parrot" + description_info = "You can give it a headset by clicking on it with a headset. \ + To remove it, click the bird while on grab intent." + has_langs = list("Galactic Common", "Bird") + + ai_holder_type = /datum/ai_holder/simple_mob/passive/parrot + + // A headset, so that talking parrots can yell at the crew over comms. + // If set to a type, on initialize it will be instantiated into that type. + var/obj/item/device/radio/headset/my_headset = null + +// Say list +/datum/say_list/bird/poly + speak = list( + "Poly wanna cracker!", + "Check the singulo, you chucklefucks!", + "Wire the solars, you lazy bums!", + "WHO TOOK THE DAMN HARDSUITS?", + "OH GOD ITS FREE CALL THE SHUTTLE", + "Danger! Crystal hyperstructure instability!", + "CRYSTAL DELAMINATION IMMINENT.", + "Tweet tweet, I'm a Teshari.", + "Chitters.", + "Meteors have been detected on a collision course with the station!" + ) + +// Lets the AI use headsets. +// Player-controlled parrots will need to do it manually. +/mob/living/simple_mob/animal/passive/bird/parrot/ISay(message) + if(my_headset && prob(50)) + var/list/keys = list() + for(var/channel in my_headset.channels) + var/key = get_radio_key_from_channel(channel) + if(key) + keys += key + if(keys.len) + var/key_used = pick(keys) + return say("[key_used] [message]") + return say(message) + +// Ugly saycode so parrots can use their headsets. +/mob/living/simple_mob/animal/passive/bird/parrot/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name) + ..() + if(message_mode) + if(my_headset && istype(my_headset, /obj/item/device/radio)) + my_headset.talk_into(src, message, message_mode, verb, speaking) + used_radios += my_headset + +// Clicked on while holding an object. +/mob/living/simple_mob/animal/passive/bird/parrot/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/device/radio/headset)) + give_headset(I, user) + return + return ..() + +// Clicked on by empty hand. +/mob/living/simple_mob/animal/passive/bird/parrot/attack_hand(mob/living/L) + if(L.a_intent == I_GRAB && my_headset) + remove_headset(L) + else + ..() + + +/mob/living/simple_mob/animal/passive/bird/parrot/proc/give_headset(obj/item/device/radio/headset/new_headset, mob/living/user) + if(!istype(new_headset)) + to_chat(user, span("warning", "\The [new_headset] isn't a headset.")) + return + if(my_headset) + to_chat(user, span("warning", "\The [src] is already wearing \a [my_headset].")) + return + else + user.drop_item(new_headset) + my_headset = new_headset + new_headset.forceMove(src) + to_chat(user, span("warning", "You place \a [new_headset] on \the [src]. You monster.")) + to_chat(src, span("notice", "\The [user] gives you \a [new_headset]. You should put it to good use immediately.")) + return + +/mob/living/simple_mob/animal/passive/bird/parrot/proc/remove_headset(mob/living/user) + if(!my_headset) + to_chat(user, "\The [src] doesn't have a headset to remove, thankfully.") + else + ISay("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + my_headset.forceMove(get_turf(src)) + user.put_in_hands(my_headset) + to_chat(user, span("notice", "You take away \the [src]'s [my_headset.name]. Finally.")) + to_chat(src, span("warning", "\The [user] takes your [my_headset.name] away! How cruel!")) + my_headset = null + +/mob/living/simple_mob/animal/passive/bird/parrot/examine(mob/user) + ..() + if(my_headset) + to_chat(user, "It is wearing \a [my_headset].") + +/mob/living/simple_mob/animal/passive/bird/parrot/initialize() + if(my_headset) + my_headset = new my_headset(src) + return ..() + +// Subtypes. + +// Best Bird +/mob/living/simple_mob/animal/passive/bird/parrot/poly + name = "Poly" + desc = "It's a parrot. An expert on quantum cracker theory." + icon_state = "poly" + icon_rest = "poly-held" + icon_dead = "poly-dead" + tt_desc = "E Ara macao" + my_headset = /obj/item/device/radio/headset/headset_eng + say_list_type = /datum/say_list/bird/poly + +// Best Bird with best headset. +/mob/living/simple_mob/animal/passive/bird/parrot/poly/ultimate + my_headset = /obj/item/device/radio/headset/omni + +/mob/living/simple_mob/animal/passive/bird/parrot/kea + name = "kea" + desc = "A species of parrot. On Earth, they are unique among other parrots for residing in alpine climates. \ + They are known to be intelligent and curious, which has made some consider them a pest." + icon_state = "kea" + icon_rest = "kea-held" + icon_dead = "kea-dead" + tt_desc = "E Nestor notabilis" + +/mob/living/simple_mob/animal/passive/bird/parrot/eclectus + name = "eclectus" + desc = "A species of parrot, this species features extreme sexual dimorphism in their plumage's colors. \ + A male eclectus has emerald green plumage, where as a female eclectus has red and purple plumage." + icon_state = "eclectus" + icon_rest = "eclectus-held" + icon_dead = "eclectus-dead" + tt_desc = "E Eclectus roratus" + +/mob/living/simple_mob/animal/passive/bird/parrot/eclectus/initialize() + gender = pick(MALE, FEMALE) + if(gender == FEMALE) + icon_state = "eclectusf" + icon_rest = "eclectusf-held" + icon_dead = "eclectusf-dead" + return ..() + +/mob/living/simple_mob/animal/passive/bird/parrot/grey_parrot + name = "grey parrot" + desc = "A species of parrot. This one is predominantly grey, but has red tail feathers." + icon_state = "agrey" + icon_rest = "agrey-held" + icon_dead = "agrey-dead" + tt_desc = "E Psittacus erithacus" + +/mob/living/simple_mob/animal/passive/bird/parrot/black_headed_caique + name = "black-headed caique" + desc = "A species of parrot, these birds have a distinct black color on their heads, distinguishing them from their relative Caiques." + icon_state = "bcaique" + icon_rest = "bcaique-held" + icon_dead = "bcaique-dead" + tt_desc = "E Pionites melanocephalus" + +/mob/living/simple_mob/animal/passive/bird/parrot/white_caique + name = "white-bellied caique" + desc = "A species of parrot, they are also known as the Green-Thighed Parrot." + icon_state = "wcaique" + icon_rest = "wcaique-held" + icon_dead = "wcaique-dead" + tt_desc = "E Pionites leucogaster" + +/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar + name = "budgerigar" + desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ + This one is has its natural colors of green and yellow." + icon_state = "gbudge" + icon_rest = "gbudge-held" + icon_dead = "gbudge-dead" + tt_desc = "E Melopsittacus undulatus" + +/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/blue + icon_state = "bbudge" + icon_rest = "bbudge-held" + icon_dead = "bbudge-dead" + desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ + This one has a mutation which altered its color to be blue instead of green and yellow." + +/mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen + icon_state = "bgbudge" + icon_rest = "bgbudge-held" + icon_dead = "bgbudge-dead" + desc = "A species of parrot, they are also known as the common parakeet, or in some circles, the budgie. \ + This one has a mutation which altered its color to be a mix of blue and green." + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel + name = "cockatiel" + desc = "A species of parrot. This one has a highly visible crest." + icon_state = "tiel" + icon_rest = "tiel-held" + icon_dead = "tiel-dead" + tt_desc = "E Nymphicus hollandicus" + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/white + icon_state = "wtiel" + icon_rest = "wtiel-held" + icon_dead = "wtiel-dead" + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish + icon_state = "luttiel" + icon_rest = "luttiel-held" + icon_dead = "luttiel-dead" + +/mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey + icon_state = "blutiel" // idk why this is blu. + icon_rest = "blutiel-held" + icon_dead = "blutiel-dead" + +// This actually might be the yellow-crested cockatoo but idk. +/mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo + name = "sulphur-crested cockatoo" + desc = "A species of parrot. This one has an expressive yellow crest. Their underwing and tail feathers are also yellow." + icon_state = "too" + icon_rest = "too-held" + icon_dead = "too-dead" + tt_desc = "E Cacatua galerita" + +// This was originally called 'hooded_too', which might not mean the unbrella cockatoo but idk. +/mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo + name = "white cockatoo" + desc = "A species of parrot. This one is also known as the Umbrella Cockatoo, due to the semicircular shape of its crest." + icon_state = "utoo" + icon_rest = "utoo-held" + icon_dead = "utoo-dead" + tt_desc = "E Cacatua alba" + +/mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo + name = "pink cockatoo" + desc = "A species of parrot. This one is also known as Major Mitchell's cockatoo, \ + in honor of a human surveyor and explorer who existed before humans fully explored their home planet." + icon_state = "mtoo" + icon_rest = "mtoo-held" + icon_dead = "mtoo-dead" + tt_desc = "E Lophochroa leadbeateri" + + +// AI +/datum/ai_holder/simple_mob/passive/parrot + speak_chance = 2 + base_wander_delay = 8 + +/datum/ai_holder/simple_mob/passive/parrot/on_hear_say(mob/living/speaker, message) + if(holder.stat || !holder.say_list || !message || speaker == holder) + return + var/datum/say_list/S = holder.say_list + S.speak |= message \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm new file mode 100644 index 0000000000..fdd9f66ae5 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm @@ -0,0 +1,35 @@ +// Diyaabs are rather weak, but tend to exist in large numbers. +// They cooperate with other diyaabs, in order to swarm whoever decides to pick on the little fluffy critter. +// A cleaving weapon like an axe will make short work of the pack. + +/mob/living/simple_mob/animal/sif/diyaab + name = "diyaab" + desc = "A small pack animal. Although omnivorous, it will hunt meat on occasion." + tt_desc = "S Choeros hirtus" //diyaab and shantak are technically reletives! + + faction = "diyaab" + + icon_state = "diyaab" + icon_living = "diyaab" + icon_dead = "diyaab_dead" + icon = 'icons/jungle.dmi' + + maxHealth = 25 + health = 25 + + movement_cooldown = 0 + + melee_damage_lower = 2 + melee_damage_upper = 6 + base_attack_cooldown = 1 SECOND + attack_sharp = 1 //Bleeds, but it shouldn't rip off a limb? + attacktext = list("gouged") + + say_list_type = /datum/say_list/diyaab + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative + +/datum/say_list/diyaab + speak = list("Awrr?", "Aowrl!", "Worrl.") + emote_see = list("sniffs the air cautiously","looks around") + emote_hear = list("snuffles") + diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm new file mode 100644 index 0000000000..3b7f279653 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm @@ -0,0 +1,102 @@ +/* + Hooligan Crabs are called so because they are rather curious and tend to follow people, + whether the people want them to or not, and sometimes causing vandalism by accident. + They're pretty strong and have strong melee armor, but won't attack first. + They unknowingly play a role in keeping the shoreline fairly safe, by killing whatever would attack other people. + + They also have a slow, but very strong attack that is telegraphed. If it hits, it will briefly stun whatever got hit + and inflict a very large chunk of damage. If the thing was already stunned, the crab will 'throw' them away, to + hopefully prevent chainstuns forever. +*/ + +/mob/living/simple_mob/animal/sif/hooligan_crab + name = "hooligan crab" + desc = "A large, hard-shelled crustacean. This one is mostly grey. \ + You probably shouldn't mess with it." + icon_state = "sif_crab" + icon_living = "sif_crab" + icon_dead = "sif_crab_dead" + icon_scale = 1.5 + + faction = "crabs" + + maxHealth = 200 + health = 200 + movement_cooldown = 10 + movement_sound = 'sound/weapons/heavysmash.ogg' + movement_shake_radius = 5 + + taser_kill = FALSE + armor = list( + "melee" = 40, + "bullet" = 20, + "laser" = 10, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + armor_soak = list( + "melee" = 10, + "bullet" = 5, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + mob_size = MOB_LARGE + + melee_damage_lower = 22 + melee_damage_upper = 35 + attack_armor_pen = 35 + attack_sharp = TRUE + attack_edge = TRUE + melee_attack_delay = 1 SECOND + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + friendly = "pinches" + attacktext = list("clawed", "pinched", "crushed") + speak_emote = list("clicks") + + ai_holder_type = /datum/ai_holder/simple_mob/melee/hooligan + say_list_type = /datum/say_list/crab + + var/weaken_amount = 2 // Be careful with this number. High values will equal a permastun. + +// Stuns the thing that got hit briefly. +/mob/living/simple_mob/animal/sif/hooligan_crab/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + var/was_stunned = L.incapacitated(INCAPACITATION_DISABLED) + L.Weaken(weaken_amount) + + playsound(L, 'sound/effects/break_stone.ogg', 75, 1) + if(was_stunned) // Try to prevent chain-stuns by having them thrown. + var/throwdir = get_dir(src, L) + L.throw_at(get_edge_target_turf(L, throwdir), 5, 1, src) + visible_message(span("danger", "\The [src] hurls \the [L] away!")) + else + visible_message(span("danger", "\The [src] crushes \the [L]!")) + +// The AI for hooligan crabs. Follows people for awhile. +/datum/ai_holder/simple_mob/melee/hooligan + hostile = FALSE + retaliate = TRUE + returns_home = TRUE + max_home_distance = 12 + var/random_follow = TRUE // Turn off if you want to bus with crabs. + +/datum/ai_holder/simple_mob/melee/hooligan/handle_stance_strategical() + ..() + if(random_follow && stance == STANCE_IDLE && !leader) + if(prob(10)) + for(var/mob/living/L in hearers(holder)) + if(!istype(L, holder)) // Don't follow other hooligan crabs. + holder.visible_message("\The [holder] starts to follow \the [L].") + set_follow(L, rand(20 SECONDS, 40 SECONDS)) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm new file mode 100644 index 0000000000..e8d56d6bb4 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm @@ -0,0 +1,48 @@ +// Saviks are dangerous, angry creatures that hit hard, and will berserk if losing a fight. + +/mob/living/simple_mob/animal/sif/savik + name = "savik" + tt_desc = "S Pistris tellus" //landshark + player_msg = "You have the ability to berserk at will, which will grant strong physical bonuses for \ + a short period of time, however it will tire you and you will be much weaker for awhile after it expires." + + faction = "savik" + + icon_state = "savik" + icon_living = "savik" + icon_dead = "savik_dead" + icon = 'icons/jungle.dmi' + + maxHealth = 125 + health = 125 + + movement_cooldown = 0.5 SECONDS + + melee_damage_lower = 15 + melee_damage_upper = 35 + attack_armor_pen = 15 + attack_sharp = TRUE + attack_edge = TRUE + melee_attack_delay = 1 SECOND + attacktext = list("mauled") + + say_list_type = /datum/say_list/savik + +/datum/say_list/savik + speak = list("Hruuugh!","Hrunnph") + emote_see = list("paws the ground","shakes its mane","stomps") + emote_hear = list("snuffles") + +/mob/living/simple_mob/animal/sif/savik/handle_special() + if((get_AI_stance() in list(STANCE_APPROACH, STANCE_FIGHT)) && !is_AI_busy() && isturf(loc)) + if(health <= (maxHealth * 0.5)) // At half health, and fighting someone currently. + berserk() + + +// So players can use it too. +/mob/living/simple_mob/animal/sif/savik/verb/berserk() + set name = "Berserk" + set desc = "Enrage and become vastly stronger for a period of time, however you will be weaker afterwards." + set category = "Abilities" + + add_modifier(/datum/modifier/berserk, 30 SECONDS) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm new file mode 100644 index 0000000000..0148a39954 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm @@ -0,0 +1,70 @@ +// Shantaks are essentially sif wolves. + +/mob/living/simple_mob/animal/sif/shantak + name = "shantak" + desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ + Don't be fooled by its beauty though." + tt_desc = "S Choeros shantak" + + faction = "shantak" + + icon_state = "shantak" + icon_living = "shantak" + icon_dead = "shantak_dead" + icon = 'icons/jungle.dmi' + + maxHealth = 75 + + movement_cooldown = 5 + + melee_damage_lower = 6 + melee_damage_upper = 14 + base_attack_cooldown = 1 SECOND + melee_attack_delay = 0.5 SECONDS + attack_armor_pen = 5 + attack_sharp = TRUE + attack_edge = TRUE + attacktext = list("gouged") + + say_list_type = /datum/say_list/shantak + +/datum/say_list/shantak + speak = list("Shuhn.","Shrunnph?","Shunpf.") + emote_see = list("scratches the ground", "shakes out its mane", "clinks gently as it moves") + + +// The pack leader. +// Will command other shantaks to follow it. +/mob/living/simple_mob/animal/sif/shantak/leader + name = "big shantak" + desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \ + This one seems bigger than the others, and has a commanding presence." + icon_scale = 1.5 + maxHealth = 125 + player_msg = "You have the ability to command other shantaks to follow you." + +/mob/living/simple_mob/animal/sif/shantak/leader/verb/rally_pack() + set name = "Rally Pack" + set desc = "Commands your fellow packmembers to follow you, the leader." + set category = "Abilities" + + for(var/mob/living/simple_mob/animal/sif/shantak/S in hearers(7, src)) + if(istype(S, /mob/living/simple_mob/animal/sif/shantak/leader)) // Leaders won't follow other leaders. Also avoids trying to follow ourselves. + continue + if(!S.ai_holder) + continue + if(S.faction != src.faction) + continue + var/datum/ai_holder/AI = S.ai_holder + AI.set_follow(src) + +// Variant that automatically commands nearby allies to follow it when created. +// Suggested to spawn last so it can rally up all the shantaks easily before hunting for tasty explorers. +/mob/living/simple_mob/animal/sif/shantak/leader/autofollow/initialize() + rally_pack() + return ..() + + +// These ones only retaliate. Used for a PoI. +/mob/living/simple_mob/animal/sif/shantak/retaliate + ai_holder_type = /datum/ai_holder/simple_mob/retaliate \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/sif.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/sif.dm new file mode 100644 index 0000000000..8d19324cf2 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/sif.dm @@ -0,0 +1,5 @@ +// Mobs intended to be on Sif. As such, they won't die to the cold. +/mob/living/simple_mob/animal/sif + minbodytemp = 175 + cold_resist = 0.75 + heat_resist = -0.5 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm new file mode 100644 index 0000000000..7de14dc6e7 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm @@ -0,0 +1,118 @@ +/mob/living/simple_mob/animal/space/alien + name = "alien hunter" + desc = "Hiss!" + icon = 'icons/mob/alien.dmi' + icon_state = "alienh_running" + icon_living = "alienh_running" + icon_dead = "alien_l" + icon_gib = "syndicate_gib" + icon_rest = "alienh_sleep" + + faction = "xeno" + + mob_class = MOB_CLASS_ABERRATION + + response_help = "pokes" + response_disarm = "shoves" + response_harm = "hits" + + maxHealth = 100 + health = 100 + + harm_intent_damage = 5 + melee_damage_lower = 25 + melee_damage_upper = 25 + attack_sharp = TRUE + attack_edge = TRUE + + attacktext = list("slashed") + attack_sound = 'sound/weapons/bladeslice.ogg' + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat + +/mob/living/simple_mob/animal/space/alien/drone + name = "alien drone" + icon_state = "aliend_running" + icon_living = "aliend_running" + icon_dead = "aliend_l" + icon_rest = "aliend_sleep" + health = 60 + melee_damage_lower = 15 + melee_damage_upper = 15 + +/mob/living/simple_mob/animal/space/alien/sentinel + name = "alien sentinel" + icon_state = "aliens_running" + icon_living = "aliens_running" + icon_dead = "aliens_l" + icon_rest = "aliens_sleep" + health = 120 + melee_damage_lower = 15 + melee_damage_upper = 15 + projectiletype = /obj/item/projectile/energy/neurotoxin/toxic + projectilesound = 'sound/weapons/pierce.ogg' + +/mob/living/simple_mob/animal/space/alien/sentinel/praetorian + name = "alien praetorian" + icon = 'icons/mob/64x64.dmi' + icon_state = "prat_s" + icon_living = "prat_s" + icon_dead = "prat_dead" + icon_rest = "prat_sleep" + maxHealth = 200 + health = 200 + + pixel_x = -16 + old_x = -16 + meat_amount = 5 + +/mob/living/simple_mob/animal/space/alien/queen + name = "alien queen" + icon_state = "alienq_running" + icon_living = "alienq_running" + icon_dead = "alienq_l" + icon_rest = "alienq_sleep" + health = 250 + maxHealth = 250 + melee_damage_lower = 15 + melee_damage_upper = 15 + projectiletype = /obj/item/projectile/energy/neurotoxin/toxic + projectilesound = 'sound/weapons/pierce.ogg' + + + movement_cooldown = 8 + +/mob/living/simple_mob/animal/space/alien/queen/empress + name = "alien empress" + icon = 'icons/mob/64x64.dmi' + icon_state = "queen_s" + icon_living = "queen_s" + icon_dead = "queen_dead" + icon_rest = "queen_sleep" + maxHealth = 400 + health = 400 + meat_amount = 5 + + pixel_x = -16 + old_x = -16 + +/mob/living/simple_mob/animal/space/alien/queen/empress/mother + name = "alien mother" + icon = 'icons/mob/96x96.dmi' + icon_state = "empress_s" + icon_living = "empress_s" + icon_dead = "empress_dead" + icon_rest = "empress_rest" + maxHealth = 600 + health = 600 + meat_amount = 10 + melee_damage_lower = 15 + melee_damage_upper = 25 + + pixel_x = -32 + old_x = -32 + +/mob/living/simple_mob/animal/space/alien/death() + ..() + visible_message("[src] lets out a waning guttural screech, green blood bubbling from its maw...") + playsound(src, 'sound/voice/hiss6.ogg', 100, 1) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm new file mode 100644 index 0000000000..5244fd9b98 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm @@ -0,0 +1,52 @@ +/mob/living/simple_mob/animal/space/bats + name = "space bat swarm" + desc = "A swarm of cute little blood sucking bats that looks pretty upset." + tt_desc = "N Bestia gregaria" //Nispean swarm bats, because of course Nisp has swarm bats + icon = 'icons/mob/bats.dmi' + icon_state = "bat" + icon_living = "bat" + icon_dead = "bat_dead" + icon_gib = "bat_dead" + + faction = "scarybat" + + maxHealth = 20 + health = 20 + + attacktext = list("bites") + attack_sound = 'sound/weapons/bite.ogg' + + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + + harm_intent_damage = 10 + + melee_damage_lower = 5 + melee_damage_upper = 5 + attack_sharp = TRUE + + ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive + + has_langs = list("Mouse") + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + + say_list_type = /datum/say_list/mouse // Close enough + + var/scare_chance = 15 + +/mob/living/simple_mob/animal/space/bats/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(scare_chance)) + L.Stun(1) + L.visible_message("\the [src] scares \the [L]!") + +// Spookiest of bats +/mob/living/simple_mob/animal/space/bats/cult + faction = "cult" + supernatural = TRUE + +/mob/living/simple_mob/animal/space/bats/cult/cultify() + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm new file mode 100644 index 0000000000..e4199373bd --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm @@ -0,0 +1,46 @@ +/mob/living/simple_mob/animal/space/bear + name = "space bear" + desc = "A product of Space Russia?" + tt_desc = "U Ursinae aetherius" //...bearspace? Maybe. + icon_state = "bear" + icon_living = "bear" + icon_dead = "bear_dead" + icon_gib = "bear_gib" + + faction = "russian" + + maxHealth = 125 + health = 125 + + movement_cooldown = 0.5 SECONDS + + melee_damage_lower = 15 + melee_damage_upper = 35 + attack_armor_pen = 15 + attack_sharp = TRUE + attack_edge = TRUE + melee_attack_delay = 1 SECOND + attacktext = list("mauled") + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/bearmeat + + say_list_type = /datum/say_list/bear + +/datum/say_list/bear + speak = list("RAWR!","Rawr!","GRR!","Growl!") + emote_see = list("stares ferociously", "stomps") + emote_hear = list("rawrs","grumbles","grawls", "growls", "roars") + +// Is it time to be mad? +/mob/living/simple_mob/animal/space/bear/handle_special() + if((get_AI_stance() in list(STANCE_APPROACH, STANCE_FIGHT)) && !is_AI_busy() && isturf(loc)) + if(health <= (maxHealth * 0.5)) // At half health, and fighting someone currently. + berserk() + +// So players can use it too. +/mob/living/simple_mob/animal/space/bear/verb/berserk() + set name = "Berserk" + set desc = "Enrage and become vastly stronger for a period of time, however you will be weaker afterwards." + set category = "Abilities" + + add_modifier(/datum/modifier/berserk, 30 SECONDS) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm new file mode 100644 index 0000000000..e1327f033b --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm @@ -0,0 +1,128 @@ +// Space carp show up as a random event to wreck hapless people in space or near windows. +// They generally fit the archetype of 'fast but fragile'. +// This is compensated by being in groups (usually). +/mob/living/simple_mob/animal/space/carp + name = "space carp" + desc = "A ferocious, fang-bearing creature that resembles a fish." + icon_state = "carp" + icon_living = "carp" + icon_dead = "carp_dead" + icon_gib = "carp_gib" + + faction = "carp" + maxHealth = 25 + health = 25 + movement_cooldown = 0 // Carp go fast + hovering = TRUE + + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + + melee_damage_lower = 7 // About 14 DPS. + melee_damage_upper = 7 + base_attack_cooldown = 10 // One attack a second. + attack_sharp = TRUE + attack_sound = 'sound/weapons/bite.ogg' + attacktext = list("bitten") + + meat_amount = 1 + meat_type = /obj/item/weapon/reagent_containers/food/snacks/carpmeat + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + var/knockdown_chance = 15 + +/mob/living/simple_mob/animal/space/carp/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(knockdown_chance)) + L.Weaken(3) + L.visible_message(span("danger", "\The [src] knocks down \the [L]!")) + +// Subtypes. + +// Won't wander away. +/mob/living/simple_mob/animal/space/carp/event + ai_holder_type = /datum/ai_holder/simple_mob/event + + +/mob/living/simple_mob/animal/space/carp/large + name = "elder carp" + desc = "An older, more matured carp. Few survive to this age due to their aggressiveness." + icon = 'icons/mob/64x32.dmi' + icon_state = "shark" + icon_living = "shark" + icon_dead = "shark_dead" + + maxHealth = 50 + health = 50 + movement_cooldown = 5 // Slower than the younger carp. + mob_size = MOB_LARGE + + pixel_x = -16 + default_pixel_x = -16 + + meat_amount = 3 + + +/mob/living/simple_mob/animal/space/carp/large/huge + name = "great white carp" + desc = "A very rare breed of carp- and a very aggressive one." + icon = 'icons/mob/64x64.dmi' + icon_dead = "megacarp_dead" + icon_living = "megacarp" + icon_state = "megacarp" + + maxHealth = 230 + health = 230 + movement_cooldown = 10 + + melee_damage_lower = 15 // About 20 DPS. + melee_damage_upper = 25 + + pixel_y = -16 + default_pixel_y = -16 + + meat_amount = 10 + + +/mob/living/simple_mob/animal/space/carp/holographic + name = "holographic carp" + desc = "An obviously holographic, but still ferocious looking carp." + // Might be worth using a filter similar to AI holograms in the future. + icon = 'icons/mob/AI.dmi' + icon_state = "holo4" + icon_living = "holo4" + icon_dead = "holo4" + alpha = 127 + icon_gib = null + meat_amount = 0 + meat_type = null + + mob_class = MOB_CLASS_PHOTONIC // Xeno-taser won't work on this as its not a 'real' carp. + +/mob/living/simple_mob/animal/space/carp/holographic/initialize() + set_light(2) // Hologram lighting. + return ..() + +// Presumably the holodeck emag code requires this. +// Pass TRUE to make safe. Pass FALSE to make unsafe. +/mob/living/simple_mob/animal/space/carp/holographic/proc/set_safety(safe) + if(!isnull(get_AI_stance())) // Will return null if lacking an AI holder or a player is controlling it w/o autopilot var. + ai_holder.hostile = !safe // Inverted so safe = TRUE means hostility = FALSE. + ai_holder.forget_everything() // Reset state so it'll stop chewing on its target. + +// Called on death. +/mob/living/simple_mob/animal/space/carp/holographic/proc/derez() + visible_message(span("notice", "\The [src] fades away!")) + qdel(src) + +/mob/living/simple_mob/animal/space/carp/holographic/gib() + derez() // Holograms can't gib. + +/mob/living/simple_mob/animal/space/carp/holographic/death() + ..() + derez() + + diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm new file mode 100644 index 0000000000..43d159a9f0 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm @@ -0,0 +1,44 @@ +/mob/living/simple_mob/animal/space/goose + name = "goose" + desc = "It looks pretty angry!" + tt_desc = "E Branta canadensis" //that iconstate is just a regular goose + icon_state = "goose" + icon_living = "goose" + icon_dead = "goose_dead" + + faction = "geese" + + maxHealth = 30 + health = 30 + + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + + harm_intent_damage = 5 + melee_damage_lower = 5 //they're meant to be annoying, not threatening. + melee_damage_upper = 5 //unless there's like a dozen of them, then you're screwed. + attacktext = list("pecked") + attack_sound = 'sound/weapons/bite.ogg' + + has_langs = list("Bird") + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + +/datum/say_list/goose + speak = list("HONK!") + emote_hear = list("honks loudly!") + say_maybe_target = list("Honk?") + say_got_target = list("HONK!!!") + +/mob/living/simple_mob/animal/space/goose/handle_special() + if((get_AI_stance() in list(STANCE_APPROACH, STANCE_FIGHT)) && !is_AI_busy() && isturf(loc)) + if(health <= (maxHealth * 0.5)) // At half health, and fighting someone currently. + berserk() + +/mob/living/simple_mob/animal/space/goose/verb/berserk() + set name = "Berserk" + set desc = "Enrage and become vastly stronger for a period of time, however you will be weaker afterwards." + set category = "Abilities" + + add_modifier(/datum/modifier/berserk, 30 SECONDS) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm new file mode 100644 index 0000000000..b7f77e6410 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/space.dm @@ -0,0 +1,15 @@ +// 'Space' mobs don't care about atmos (like carp) +/mob/living/simple_mob/animal/space + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + +// They can also, you know, move around, in space +/mob/living/simple_mob/animal/space/Process_Spacemove(var/check_drift = 0) + return TRUE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm new file mode 100644 index 0000000000..2d700646ae --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm @@ -0,0 +1,62 @@ +// Blob simple_mobs generally get made from the blob random event. +// They're considered slimes for the purposes of attack bonuses from certain weapons. + +// Do not spawn, this is a base type. +/mob/living/simple_mob/blob + icon = 'icons/mob/blob.dmi' + pass_flags = PASSBLOB | PASSTABLE + faction = "blob" + + heat_damage_per_tick = 0 + cold_damage_per_tick = 0 + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + taser_kill = FALSE + + var/mob/observer/blob/overmind = null + var/obj/structure/blob/factory/factory = null + + mob_class = MOB_CLASS_SLIME + ai_holder_type = /datum/ai_holder/simple_mob/melee + +/mob/living/simple_mob/blob/speech_bubble_appearance() + return "slime" + +/mob/living/simple_mob/blob/update_icons() + if(overmind) + color = overmind.blob_type.complementary_color + else + color = null + ..() + +/mob/living/simple_mob/blob/Destroy() + if(overmind) + overmind.blob_mobs -= src + return ..() + +/mob/living/simple_mob/blob/blob_act(obj/structure/blob/B) + if(!overmind && B.overmind) + overmind = B.overmind + update_icon() + + if(stat != DEAD && health < maxHealth) + adjustBruteLoss(-maxHealth*0.0125) + adjustFireLoss(-maxHealth*0.0125) + +/mob/living/simple_mob/blob/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /obj/structure/blob)) // Don't block blobs from expanding onto a tile occupied by a blob mob. + return TRUE + return ..() + +/mob/living/simple_mob/blob/Process_Spacemove() + for(var/obj/structure/blob/B in range(1, src)) + return TRUE + return ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm b/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm new file mode 100644 index 0000000000..fd91b00b33 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/blob/spore.dm @@ -0,0 +1,148 @@ +// Spores are made from blob factories. +// They are very weak and expendable, but can overwhelm when a lot of them are together. +// When attacking, spores will hit harder if near other friendly spores. +// Some blobs can infest dead non-robotic mobs, making them into Not Zombies. + +/mob/living/simple_mob/blob/spore + name = "blob spore" + desc = "A floating, fragile spore." + + icon_state = "blobpod" + icon_living = "blobpod" + glow_range = 3 + glow_intensity = 5 + layer = ABOVE_MOB_LAYER // Over the blob. + + health = 30 + maxHealth = 30 + melee_damage_lower = 2 + melee_damage_upper = 4 + movement_cooldown = 0 + hovering = TRUE + + attacktext = list("slams into") + attack_sound = 'sound/effects/slime_squish.ogg' + say_list_type = /datum/say_list/spore + + var/mob/living/carbon/human/infested = null // The human this thing is totally not making into a zombie. + var/can_infest = FALSE + var/is_infesting = FALSE + +/datum/say_list/spore + emote_see = list("sways", "inflates briefly") + +/datum/say_list/infested + emote_see = list("shambles around", "twitches", "stares") + + +/mob/living/simple_mob/blob/spore/infesting + name = "infesting blob spore" + can_infest = TRUE + +/mob/living/simple_mob/blob/spore/weak + name = "fragile blob spore" + health = 15 + maxHealth = 15 + melee_damage_lower = 1 + melee_damage_upper = 2 + +/mob/living/simple_mob/blob/spore/initialize(mapload, var/obj/structure/blob/factory/my_factory) + if(istype(my_factory)) + factory = my_factory + factory.spores += src + return ..() + +/mob/living/simple_mob/blob/spore/Destroy() + if(factory) + factory.spores -= src + factory = null + if(infested) + infested.forceMove(get_turf(src)) + visible_message(span("warning", "\The [infested] falls to the ground as the blob spore bursts.")) + infested = null + return ..() + +/mob/living/simple_mob/blob/spore/death(gibbed, deathmessage = "bursts!") + if(overmind) + overmind.blob_type.on_spore_death(src) + ..(gibbed, deathmessage) + qdel(src) + +/mob/living/simple_mob/blob/spore/update_icons() + ..() // This will cut our overlays. + + if(overmind) + color = overmind.blob_type.complementary_color + glow_color = color + glow_toggle = TRUE + else + color = null + glow_color = null + glow_toggle = FALSE + + if(is_infesting) + icon = infested.icon + copy_overlays(infested) + // overlays = infested.overlays + var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head") + if(overmind) + blob_head_overlay.color = overmind.blob_type.complementary_color + color = initial(color)//looks better. + // overlays += blob_head_overlay + add_overlay(blob_head_overlay, TRUE) + +/mob/living/simple_mob/blob/spore/handle_special() + ..() + if(can_infest && !is_infesting && isturf(loc)) + for(var/mob/living/carbon/human/H in view(src,1)) + if(H.stat != DEAD) // We want zombies. + continue + if(H.isSynthetic()) // Not philosophical zombies. + continue + infest(H) + break + + if(factory && z != factory.z) // This is to prevent spores getting lost in space and making the factory useless. + qdel(src) + +/mob/living/simple_mob/blob/spore/proc/infest(mob/living/carbon/human/H) + is_infesting = TRUE + if(H.wear_suit) + var/obj/item/clothing/suit/A = H.wear_suit + if(A.armor && A.armor["melee"]) + maxHealth += A.armor["melee"] //That zombie's got armor, I want armor! + + maxHealth += 40 + health = maxHealth + name = "Infested [H.real_name]" // Not using the Z word. + desc = "A parasitic organism attached to a deceased body, controlling it directly as if it were a puppet." + melee_damage_lower += 8 // 10 total. + melee_damage_upper += 11 // 15 total. + attacktext = list("claws") + + H.forceMove(src) + infested = H + + say_list = new /datum/say_list/infested() + + update_icons() + visible_message(span("warning", "The corpse of [H.name] suddenly rises!")) + +/mob/living/simple_mob/blob/spore/GetIdCard() + if(infested) // If we've infested someone, use their ID. + return infested.GetIdCard() + +/mob/living/simple_mob/blob/spore/apply_bonus_melee_damage(A, damage_to_do) + var/helpers = 0 + for(var/mob/living/simple_mob/blob/spore/S in view(1, src)) + if(S == src) // Don't count ourselves. + continue + if(!IIsAlly(S)) // Only friendly spores make us stronger. + continue + // Friendly spores contribute 1/4th of their averaged attack power to our attack. + damage_to_do += ((S.melee_damage_lower + S.melee_damage_upper) / 2) / 4 + helpers++ + + if(helpers) + to_chat(src, span("notice", "Your attack is assisted by [helpers] other spore\s.")) + return damage_to_do \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm new file mode 100644 index 0000000000..b6f9520698 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm @@ -0,0 +1,29 @@ +/mob/living/simple_mob/humanoid/clown + clown + name = "clown" + desc = "A denizen of clown planet" + tt_desc = "E Homo sapiens corydon" //this is an actual clown, as opposed to someone dressed up as one + icon_state = "clown" + icon_living = "clown" + icon_dead = "clown_dead" + icon_gib = "clown_gib" + + faction = "clown" + + loot_list = list(/obj/item/weapon/bikehorn = 100) + + response_help = "pokes" + response_disarm = "gently pushes aside" + response_harm = "hits" + + harm_intent_damage = 8 + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = list("attacked") + attack_sound = 'sound/items/bikehorn.ogg' + + say_list_type = /datum/say_list/clown + +/datum/say_list/clown + speak = list("HONK", "Honk!", "Welcome to clown planet!") + emote_see = list("honks") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid.dm new file mode 100644 index 0000000000..6e17e2e1a0 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid.dm @@ -0,0 +1,26 @@ +/mob/living/simple_mob/humanoid + mob_class = MOB_CLASS_HUMANOID + + // Generic humanoid mob tolerances + min_oxy = 5 + max_oxy = 0 + min_tox = 0 + max_tox = 1 + min_co2 = 0 + max_co2 = 5 + min_n2 = 0 + max_n2 = 0 + unsuitable_atoms_damage = 15 + + health = 150 // Point of human crit, as of commenting + maxHealth = 150 + + // Most humans leave a corpse + var/corpse = null + +/mob/living/simple_mob/humanoid/death() + ..() + if(corpse) + new corpse (src.loc) + qdel(src) + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm new file mode 100644 index 0000000000..57d5986e80 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm @@ -0,0 +1,278 @@ +/////////////////////////////// +// Merc Mobs Go Here +/////////////////////////////// + +// Probably shouldn't use this directly, there are a bunch of sub-classes that are more complete. +/mob/living/simple_mob/humanoid/merc + name = "mercenary" + desc = "A tough looking heavily-armed individual." + tt_desc = "E Homo sapiens" + icon_state = "syndicate" + icon_living = "syndicate" + icon_dead = "syndicate_dead" + icon_gib = "syndicate_gib" + + faction = "syndicate" + movement_cooldown = 4 + + status_flags = 0 + + response_help = "pokes" + response_disarm = "shoves" + response_harm = "hits" + + harm_intent_damage = 5 + melee_damage_lower = 15 //Tac Knife damage + melee_damage_upper = 15 + attack_sharp = 1 + attack_edge = 1 + attacktext = list("slashed", "stabbed") + armor = list(melee = 40, bullet = 30, laser = 30, energy = 10, bomb = 10, bio = 100, rad = 100) // Same armor values as the vest they drop, plus simple mob immunities + + corpse = /obj/effect/landmark/mobcorpse/syndicatesoldier + loot_list = list(/obj/item/weapon/material/knife/tacknife = 100) // Might as well give it the knife + + ai_holder_type = /datum/ai_holder/simple_mob/merc + say_list_type = /datum/say_list/merc + + // Grenade special attack vars + var/grenade_type = /obj/item/weapon/grenade/concussion + special_attack_cooldown = 45 SECONDS + special_attack_min_range = 2 + special_attack_max_range = 7 + +//////////////////////////////// +// Grenade Attack +//////////////////////////////// + +// Any merc can use this, just set special_attack_charges to a positive value + +// Check if we should bother with the grenade +/mob/living/simple_mob/humanoid/merc/should_special_attack(atom/A) + var/mob_count = 0 // Are there enough mobs to consider grenading? + var/turf/T = get_turf(A) + for(var/mob/M in range(T, 2)) + if(M.faction == faction) // Don't grenade our friends + return FALSE + if(M in oview(src, special_attack_max_range)) // And lets check if we can actually see at least two people before we throw a grenade + if(!M.stat) // Dead things don't warrant a grenade + mob_count ++ + if(mob_count < 2) + return FALSE + else + return TRUE + +// Yes? Throw the grenade +/mob/living/simple_mob/humanoid/merc/do_special_attack(atom/A) + set waitfor = FALSE + set_AI_busy(TRUE) + + var/obj/item/weapon/grenade/G = new grenade_type(get_turf(src)) + if(istype(G)) + G.throw_at(A, G.throw_range, G.throw_speed, src) + G.attack_self(src) + special_attack_charges = max(special_attack_charges-1, 0) + + set_AI_busy(FALSE) + + +//////////////////////////////// +// Merc AI Types +//////////////////////////////// +/datum/ai_holder/simple_mob/merc + threaten = TRUE + returns_home = TRUE // Stay close to the base... + wander = TRUE // ... but "patrol" a little. + +/datum/ai_holder/simple_mob/merc/ranged + pointblank = TRUE // They get close? Just shoot 'em! + firing_lanes = TRUE // But not your buddies! + conserve_ammo = TRUE // And don't go wasting bullets! + + +//////////////////////////////// +// Melee +//////////////////////////////// +/mob/living/simple_mob/humanoid/merc/melee // Defined in case we add non-sword-and-board mercs + loot_list = list(/obj/item/weapon/material/knife/tacknife = 100) + +// Sword and Shield Merc +/mob/living/simple_mob/humanoid/merc/melee/sword + icon_state = "syndicatemelee" + icon_living = "syndicatemelee" + + melee_damage_lower = 30 + melee_damage_upper = 30 + attack_armor_pen = 50 + attack_sharp = 1 + attack_edge = 1 + attacktext = list("slashed") + + loot_list = list(/obj/item/weapon/melee/energy/sword/red = 100, /obj/item/weapon/shield/energy = 100) + +// They have a shield, so they try to block +/mob/living/simple_mob/humanoid/merc/melee/sword/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(O.force) + if(prob(20)) + visible_message("\The [src] blocks \the [O] with its shield!") + if(user) + ai_holder.react_to_attack(user) + return + else + ..() + else + to_chat(user, "This weapon is ineffective, it does no damage.") + visible_message("\The [user] gently taps [src] with \the [O].") + +/mob/living/simple_mob/humanoid/merc/melee/sword/bullet_act(var/obj/item/projectile/Proj) + if(!Proj) return + if(prob(35)) + visible_message("[src] blocks [Proj] with its shield!") + if(Proj.firer) + ai_holder.react_to_attack(Proj.firer) + return + else + ..() + + +//////////////////////////////// +// Ranged +//////////////////////////////// + +// Base Ranged Merc, so we don't have to redefine a million vars for every subtype. Uses a pistol. +/mob/living/simple_mob/humanoid/merc/ranged + icon_state = "syndicateranged" + icon_living = "syndicateranged" + projectiletype = /obj/item/projectile/bullet/pistol/medium +// casingtype = /obj/item/ammo_casing/spent //Makes infinite stacks of bullets when put in PoIs. + projectilesound = 'sound/weapons/Gunshot_light.ogg' + loot_list = list(/obj/item/weapon/gun/projectile/colt = 100) + + needs_reload = TRUE + reload_max = 7 // Not the best default, but it fits the pistol + ai_holder_type = /datum/ai_holder/simple_mob/merc/ranged + +// C20r SMG +/mob/living/simple_mob/humanoid/merc/ranged/smg + icon_state = "syndicateranged_smg" + icon_living = "syndicateranged_smg" + + loot_list = list(/obj/item/weapon/gun/projectile/automatic/c20r = 100) + + base_attack_cooldown = 5 // Two attacks a second or so. + reload_max = 20 + +// Laser Rifle +/mob/living/simple_mob/humanoid/merc/ranged/laser + icon_state = "syndicateranged_laser" + icon_living = "syndicateranged_laser" + projectiletype = /obj/item/projectile/beam/midlaser + projectilesound = 'sound/weapons/Laser.ogg' + + loot_list = list(/obj/item/weapon/gun/energy/laser = 100) + + reload_max = 10 + +// Ion Rifle +/mob/living/simple_mob/humanoid/merc/ranged/ionrifle + icon_state = "syndicateranged_ionrifle" + icon_living = "syndicateranged_ionrifle" + projectiletype = /obj/item/projectile/ion + projectilesound = 'sound/weapons/Laser.ogg' + + loot_list = list(/obj/item/weapon/gun/energy/ionrifle = 100) + + reload_max = 10 + +// Grenadier, Basically a miniboss +/mob/living/simple_mob/humanoid/merc/ranged/grenadier + icon_state = "syndicateranged_shotgun" + icon_living = "syndicateranged_shotgun" + projectiletype = /obj/item/projectile/bullet/pellet/shotgun // Buckshot + projectilesound = 'sound/weapons/gunshot/shotgun.ogg' + + loot_list = list(/obj/item/weapon/gun/projectile/shotgun/pump = 100) + + reload_max = 4 + reload_time = 1.5 SECONDS // It's a shotgun, it takes a moment + + special_attack_charges = 5 + + +//////////////////////////////// +// Space Mercs +//////////////////////////////// + +// Sword Space Merc +/mob/living/simple_mob/humanoid/merc/melee/sword/space + name = "syndicate commando" + icon_state = "syndicatemeleespace" + icon_living = "syndicatemeleespace" + + movement_cooldown = 0 + + armor = list(melee = 60, bullet = 50, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 100) // Same armor as their voidsuit + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + corpse = /obj/effect/landmark/mobcorpse/syndicatecommando + +/mob/living/simple_mob/humanoid/merc/melee/sword/space/Process_Spacemove(var/check_drift = 0) + return + +// Ranged Space Merc +/mob/living/simple_mob/humanoid/merc/ranged/space + name = "syndicate sommando" + icon_state = "syndicaterangedpsace" + icon_living = "syndicaterangedpsace" + + movement_cooldown = 0 + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + corpse = /obj/effect/landmark/mobcorpse/syndicatecommando + +/mob/living/simple_mob/humanoid/merc/ranged/space/Process_Spacemove(var/check_drift = 0) + return + +//////////////////////////////// +// PoI Mercs +//////////////////////////////// + +// None of these drop weapons, until we have a better way to balance them +/mob/living/simple_mob/humanoid/merc/melee/poi + loot_list = list() + +/mob/living/simple_mob/humanoid/merc/melee/sword/poi + loot_list = list() + +/mob/living/simple_mob/humanoid/merc/ranged/poi + loot_list = list() + +/mob/living/simple_mob/humanoid/merc/ranged/smg/poi + loot_list = list() + +/mob/living/simple_mob/humanoid/merc/ranged/laser/poi + loot_list = list() + +/mob/living/simple_mob/humanoid/merc/ranged/ionrifle + loot_list = list() + +/mob/living/simple_mob/humanoid/merc/ranged/grenadier/poi + loot_list = list() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm new file mode 100644 index 0000000000..cc16cebedc --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm @@ -0,0 +1,40 @@ +/mob/living/simple_mob/humanoid/pirate + name = "Pirate" + desc = "Does what he wants cause a pirate is free." + tt_desc = "E Homo sapiens" + icon_state = "piratemelee" + icon_living = "piratemelee" + icon_dead = "piratemelee_dead" + + faction = "pirate" + + response_help = "pushes" + response_disarm = "shoves" + response_harm = "hits" + + harm_intent_damage = 5 + melee_damage_lower = 30 + melee_damage_upper = 30 + attack_armor_pen = 50 + attack_sharp = 1 + attack_edge = 1 + + attacktext = list("slashed") + attack_sound = 'sound/weapons/bladeslice.ogg' + + loot_list = list(/obj/item/weapon/melee/energy/sword/pirate = 100) + + corpse = /obj/effect/landmark/mobcorpse/pirate + +/mob/living/simple_mob/humanoid/pirate/ranged + name = "Pirate Gunner" + icon_state = "pirateranged" + icon_living = "pirateranged" + icon_dead = "piratemelee_dead" + + projectiletype = /obj/item/projectile/beam + projectilesound = 'sound/weapons/laser.ogg' + + loot_list = list(/obj/item/weapon/gun/energy/laser = 100) + + corpse = /obj/effect/landmark/mobcorpse/pirate/ranged \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm new file mode 100644 index 0000000000..4a0bd00261 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm @@ -0,0 +1,35 @@ +/mob/living/simple_mob/humanoid/russian + name = "russian" + desc = "For the Motherland!" + tt_desc = "E Homo sapiens" + icon_state = "russianmelee" + icon_living = "russianmelee" + icon_dead = "russianmelee_dead" + icon_gib = "syndicate_gib" + + faction = "russian" + + response_help = "pokes" + response_disarm = "shoves" + response_harm = "hits" + + harm_intent_damage = 5 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = list("punched") + + loot_list = list(/obj/item/weapon/material/knife = 100) + + corpse = /obj/effect/landmark/mobcorpse/russian + +/mob/living/simple_mob/humanoid/russian/ranged + icon_state = "russianranged" + icon_living = "russianranged" + + projectiletype = /obj/item/projectile/bullet + casingtype = /obj/item/ammo_casing/spent + projectilesound = 'sound/weapons/Gunshot.ogg' + + loot_list = list(/obj/item/weapon/gun/projectile/revolver/mateba = 100) + + corpse = /obj/effect/landmark/mobcorpse/russian/ranged \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm new file mode 100644 index 0000000000..60cc13fb2b --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm @@ -0,0 +1,101 @@ +// Illusion type mobs pretend to be other things visually, and generally cannot be harmed as they're not 'real'. + +/mob/living/simple_mob/illusion + name = "illusion" + desc = "If you can read me, the game broke. Please report this to a coder." + + resistance = 1000 // Holograms are tough. + heat_resist = 1 + cold_resist = 1 + shock_resist = 1 + poison_resist = 1 + + movement_cooldown = 0 + mob_bump_flag = 0 // If the illusion can't be swapped it will be obvious. + + response_help = "pushes a hand through" + response_disarm = "tried to disarm" + response_harm = "tried to punch" + + mob_class = MOB_CLASS_ILLUSION + + ai_holder_type = /datum/ai_holder/simple_mob/inert/astar // Gets controlled manually by technomancers/admins, with AI pathfinding assistance. + + var/atom/movable/copying = null // The thing we're trying to look like. + var/realistic = FALSE // If true, things like bullets and weapons will hit it, to be a bit more convincing from a distance. + +/mob/living/simple_mob/illusion/update_icon() // We don't want the appearance changing AT ALL unless by copy_appearance(). + return + +/mob/living/simple_mob/illusion/proc/copy_appearance(atom/movable/thing_to_copy) + if(!thing_to_copy) + return FALSE + appearance = thing_to_copy.appearance + copying = thing_to_copy + density = thing_to_copy.density // So you can't bump into objects that aren't supposed to be dense. + return TRUE + +// Because we can't perfectly duplicate some examine() output, we directly examine the AM it is copying. It's messy but +// this is to prevent easy checks from the opposing force. +/mob/living/simple_mob/illusion/examine(mob/user) + if(copying) + copying.examine(user) + return + ..() + +/mob/living/simple_mob/illusion/bullet_act(obj/item/projectile/P) + if(!P) + return + + if(realistic) + return ..() + + return PROJECTILE_FORCE_MISS + +/mob/living/simple_mob/illusion/attack_hand(mob/living/carbon/human/M) + if(!realistic) + playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message(span("warning", "\The [M]'s hand goes through \the [src]!")) + return + else + switch(M.a_intent) + if(I_HELP) + var/datum/gender/T = gender_datums[src.get_visible_gender()] + M.visible_message( + span("notice", "\The [M] hugs [src] to make [T.him] feel better!"), \ + span("notice", "You hug [src] to make [T.him] feel better!") + ) // slightly redundant as at the moment most mobs still use the normal gender var, but it works and future-proofs it + playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + + if(I_DISARM) + playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message(span("danger", "\The [M] attempted to disarm [src]!")) + M.do_attack_animation(src) + + if(I_GRAB) + ..() + + if(I_HURT) + adjustBruteLoss(harm_intent_damage) + M.visible_message(span("danger", "\The [M] [response_harm] \the [src]")) + M.do_attack_animation(src) + +/mob/living/simple_mob/illusion/hit_with_weapon(obj/item/I, mob/living/user, effective_force, hit_zone) + if(realistic) + return ..() + + playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message(span("warning", "\The [user]'s [I] goes through \the [src]!")) + return FALSE + +/mob/living/simple_mob/illusion/ex_act() + return + +// Try to have the same tooltip, or else it becomes really obvious which one is fake. +/mob/living/simple_mob/illusion/get_nametag_name(mob/user) + if(copying) + return copying.get_nametag_name(user) + +/mob/living/simple_mob/illusion/get_nametag_desc(mob/user) + if(copying) + return copying.get_nametag_desc(user) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm new file mode 100644 index 0000000000..8731a06764 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm @@ -0,0 +1,77 @@ +/* + Combat drones have a rapid ranged attack, and have a projectile shield. + They are rather slow, but attempt to 'kite' its target. + A solid hit with an EMP grenade will kill the shield instantly. +*/ + +/mob/living/simple_mob/mechanical/combat_drone + name = "combat drone" + desc = "An automated combat drone armed with state of the art weaponry and shielding." + icon_state = "drone" + icon_living = "drone" + icon_dead = "drone_dead" + has_eye_glow = TRUE + + faction = "malf_drone" + + maxHealth = 50 // Shield has 150 for total of 200. + health = 50 + movement_cooldown = 5 + hovering = TRUE + + base_attack_cooldown = 5 + projectiletype = /obj/item/projectile/beam/drone + projectilesound = 'sound/weapons/laser3.ogg' + + response_help = "pokes" + response_disarm = "gently pushes aside" + response_harm = "hits" + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening + say_list_type = /datum/say_list/malf_drone + + var/datum/effect/effect/system/ion_trail_follow/ion_trail = null + var/obj/item/shield_projector/shields = null + +/mob/living/simple_mob/mechanical/combat_drone/initialize() + ion_trail = new + ion_trail.set_up(src) + ion_trail.start() + + shields = new /obj/item/shield_projector/rectangle/automatic/drone(src) + return ..() + +/mob/living/simple_mob/mechanical/combat_drone/Destroy() + QDEL_NULL(ion_trail) + QDEL_NULL(shields) + return ..() + +/mob/living/simple_mob/mechanical/combat_drone/death() + ..(null,"suddenly breaks apart.") + qdel(src) + +/mob/living/simple_mob/mechanical/combat_drone/Process_Spacemove(var/check_drift = 0) + return TRUE + +/obj/item/projectile/beam/drone + damage = 10 + +/obj/item/shield_projector/rectangle/automatic/drone + shield_health = 150 + max_shield_health = 150 + shield_regen_delay = 10 SECONDS + shield_regen_amount = 10 + size_x = 1 + size_y = 1 + +// A slightly easier drone, for POIs. +// Difference is that it should not be faster than you. +/mob/living/simple_mob/mechanical/combat_drone/lesser + desc = "An automated combat drone with an aged apperance." + movement_cooldown = 10 + + +// This one is the type spawned by the random event. +// It won't wander away from its spawn point +/mob/living/simple_mob/mechanical/combat_drone/event + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/threatening/event diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm new file mode 100644 index 0000000000..cffa9cb134 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm @@ -0,0 +1,152 @@ +// The GOLEM is a spell-flinging synthetic. + +/mob/living/simple_mob/mechanical/technomancer_golem + name = "unknown synthetic" + desc = "A rather unusual looking synthetic." + icon = 'icons/mob/mob.dmi' + icon_state = "golem" + health = 300 + maxHealth = 300 + + faction = "golem" + + response_help = "pets" + response_disarm = "pushes away" + response_harm = "punches" + harm_intent_damage = 3 + friendly = "hugs" + + melee_damage_lower = 30 // It has a built in esword. + melee_damage_upper = 30 + attack_sound = 'sound/weapons/blade1.ogg' + attacktext = list("slashed") + melee_attack_delay = 0.5 SECONDS // Even has custom attack animations. + ranged_attack_delay = 0.5 SECONDS + special_attack_delay = 1 SECOND + + special_attack_min_range = 0 + special_attack_max_range = 7 + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + var/obj/item/weapon/technomancer_core/golem/core = null + var/obj/item/weapon/spell/active_spell = null // Shield and ranged spells + var/mob/living/master = null + var/casting = FALSE // Used to ensure the correct animation is played. Testing if a spell exists won't always work as some spells delete themselves upon use. + + var/list/known_spells = list( + "beam" = /obj/item/weapon/spell/projectile/beam, + "chain lightning" = /obj/item/weapon/spell/projectile/chain_lightning, + "force missile" = /obj/item/weapon/spell/projectile/force_missile, + "ionic bolt" = /obj/item/weapon/spell/projectile/ionic_bolt, + "lightning" = /obj/item/weapon/spell/projectile/lightning, + "blink" = /obj/item/weapon/spell/blink, + "dispel" = /obj/item/weapon/spell/dispel, + "oxygenate" = /obj/item/weapon/spell/oxygenate, + "mend life" = /obj/item/weapon/spell/modifier/mend_life, + "mend synthetic" = /obj/item/weapon/spell/modifier/mend_synthetic, + "mend organs" = /obj/item/weapon/spell/mend_organs, + "purify" = /obj/item/weapon/spell/modifier/purify, + "resurrect" = /obj/item/weapon/spell/resurrect, + "passwall" = /obj/item/weapon/spell/passwall, + "repel missiles" = /obj/item/weapon/spell/modifier/repel_missiles, + "corona" = /obj/item/weapon/spell/modifier/corona, + "haste" = /obj/item/weapon/spell/modifier/haste + ) + +/mob/living/simple_mob/mechanical/technomancer_golem/initialize() + core = new(src) + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/Destroy() + qdel(core) + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/unref_spell() + active_spell = null + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/death() + ..() + visible_message("\The [src] disintegrates!") + new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + qdel(src) + +/mob/living/simple_mob/mechanical/technomancer_golem/place_spell_in_hand(var/path) + if(!path || !ispath(path)) + return FALSE + if(active_spell) + qdel(active_spell) + + active_spell = new path(src) + +/mob/living/simple_mob/mechanical/technomancer_golem/verb/test_giving_spells() + var/choice = input(usr, "What spell?", "Give spell") as null|anything in known_spells + if(choice) + place_spell_in_hand(known_spells[choice]) + else + qdel(active_spell) + +/mob/living/simple_mob/mechanical/technomancer_golem/get_technomancer_core() + return core + +/mob/living/simple_mob/mechanical/technomancer_golem/can_special_attack(atom/A) + if(active_spell) // Don't bother checking everything else if no spell is ready. + return ..() + return FALSE + +/mob/living/simple_mob/mechanical/technomancer_golem/should_special_attack(atom/A) + return instability < 50 // Don't kill ourselves by casting everything. + + +/mob/living/simple_mob/mechanical/technomancer_golem/do_special_attack(atom/A) + var/proximity = Adjacent(A) + if(active_spell) + if(proximity && active_spell.cast_methods & CAST_MELEE) // Use melee method if available and close enough. + return active_spell.on_melee_cast(A, src) + else if(active_spell.cast_methods & CAST_RANGED) // Otherwise use ranged if possible. Will also work for point-blank range. + return active_spell.on_ranged_cast(A, src) + return ..() + +/mob/living/simple_mob/mechanical/technomancer_golem/melee_pre_animation(atom/A) + if(active_spell && active_spell.cast_methods & CAST_MELEE|CAST_RANGED) // If they're trying to melee-cast a spell, use the special animation instead. + special_pre_animation(A) + return + + flick("golem_pre_melee", src) // To force the animation to restart. + icon_living = "golem_pre_melee" // The animation will hold after this point until melee_post_animation() gets called. + icon_state = "golem_pre_melee" + setClickCooldown(2) + +/mob/living/simple_mob/mechanical/technomancer_golem/melee_post_animation(atom/A) + if(casting) // Some spells delete themselves when used, so we use a different variable set earlier instead. + special_post_animation(A) + return + + flick("golem_post_melee", src) + icon_living = "golem" + icon_state = "golem" + setClickCooldown(6) + +/mob/living/simple_mob/mechanical/technomancer_golem/ranged_pre_animation(atom/A) + flick("golem_pre_ranged", src) + icon_living = "golem_pre_ranged" + icon_state = "golem_pre_ranged" + setClickCooldown(5) + +/mob/living/simple_mob/mechanical/technomancer_golem/ranged_post_animation(atom/A) + flick("golem_post_ranged", src) + icon_living = "golem" + icon_state = "golem" + setClickCooldown(5) + +/mob/living/simple_mob/mechanical/technomancer_golem/special_pre_animation(atom/A) + casting = TRUE + ranged_pre_animation(A) // Both have the same animation. + +/mob/living/simple_mob/mechanical/technomancer_golem/special_post_animation(atom/A) + casting = FALSE + ranged_post_animation(A) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm new file mode 100644 index 0000000000..fe43c856f9 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm @@ -0,0 +1,54 @@ +// Hivebots are tuned towards how many default lasers are needed to kill them. +// As such, if laser damage is ever changed, you should change this define. +#define LASERS_TO_KILL * 40 + +/mob/living/simple_mob/mechanical/hivebot + name = "hivebot" + desc = "A robot. It appears to be somewhat resilient, but lacks a true weapon." + icon = 'icons/mob/hivebot.dmi' + icon_state = "basic" + icon_living = "basic" + + faction = "hivebot" + + maxHealth = 3 LASERS_TO_KILL + health = 3 LASERS_TO_KILL + water_resist = 0.5 + movement_sound = 'sound/effects/servostep.ogg' + + attacktext = list("clawed") + projectilesound = 'sound/weapons/Gunshot.ogg' + + ai_holder_type = /datum/ai_holder/simple_mob/hivebot + say_list_type = /datum/say_list/hivebot + + +/mob/living/simple_mob/mechanical/hivebot/death() + ..() + visible_message(span("warning","\The [src] blows apart!")) + new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(3, 1, src) + s.start() + qdel(src) + +// The hivebot's default projectile. +/obj/item/projectile/bullet/hivebot + damage = 10 + damage_type = BRUTE + sharp = FALSE + edge = FALSE + +/mob/living/simple_mob/mechanical/hivebot/swarm + name = "swarm hivebot" + desc = "A robot. It looks fragile and weak." + maxHealth = 1 LASERS_TO_KILL + health = 1 LASERS_TO_KILL + melee_damage_lower = 8 + melee_damage_upper = 8 + +/datum/ai_holder/simple_mob/hivebot + pointblank = TRUE + conserve_ammo = TRUE + firing_lanes = TRUE + can_flee = FALSE // Fearless dumb machines. \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm new file mode 100644 index 0000000000..5e4d877751 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm @@ -0,0 +1,147 @@ +// These hivebots are intended for general damage causing, at range. + +/mob/living/simple_mob/mechanical/hivebot/ranged_damage + maxHealth = 2 LASERS_TO_KILL // 60 health + health = 2 LASERS_TO_KILL + projectiletype = /obj/item/projectile/bullet/hivebot + +// The regular ranged hivebot, that fires somewhat weak projectiles. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/basic + name = "ranged hivebot" + desc = "A robot with a makeshift integrated ballistic weapon." + + +// This one shoots quickly, and is considerably more dangerous. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/rapid + name = "rapid hivebot" + desc = "A robot with a crude but deadly integrated rifle." + base_attack_cooldown = 5 // Two attacks a second or so. + player_msg = "You have a rapid fire attack." + + +// Shoots deadly lasers. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/laser + name = "laser hivebot" + desc = "A robot with a photonic weapon integrated into itself." + projectiletype = /obj/item/projectile/beam/blue + projectilesound = 'sound/weapons/Laser.ogg' + player_msg = "You have a laser attack." + + +// Shoots EMPs, to screw over other robots. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/ion + name = "ionic hivebot" + desc = "A robot with an electromagnetic pulse projector." + icon_state = "yellow" + icon_living = "yellow" + + projectiletype = /obj/item/projectile/ion + projectilesound = 'sound/weapons/Laser.ogg' + player_msg = "You have a ranged ion attack, which is very strong against other synthetics.
\ + Be careful to not hit yourself or your team, as it will affect you as well." + +// Beefy and ranged. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong + name = "strong hivebot" + desc = "A robot with a crude ballistic weapon and strong armor." + maxHealth = 4 LASERS_TO_KILL // 120 health. + health = 4 LASERS_TO_KILL + melee_damage_lower = 15 + melee_damage_upper = 15 + +// Also beefy, but tries to stay at their 'home', ideal for base defense. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/strong/guard + name = "guard hivebot" + desc = "A robot that seems to be guarding something." +// ai_holder_type = todo + + +// Inflicts a damage-over-time modifier on things it hits. +// It is able to stack with repeated attacks. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/dot + name = "ember hivebot" + desc = "A robot that appears to utilize fire to cook their enemies." + icon_state = "red" + icon_living = "red" + + projectiletype = /obj/item/projectile/fire + heat_resist = 1 + player_msg = "Your attacks inflict a damage over time effect, that will \ + harm your target slowly. The effect stacks with further attacks.
\ + You are also immune to fire." + +/obj/item/projectile/fire + name = "ember" + icon = 'icons/effects/effects.dmi' + icon_state = "explosion_particle" + modifier_type_to_apply = /datum/modifier/fire + modifier_duration = 6 SECONDS // About 15 damage per stack, as Life() ticks every two seconds. + damage = 0 + nodamage = TRUE + + +// Close to mid-ranged shooter that arcs over other things, ideal if allies are in front of it. +// Difference from siege hivebots is that siege hivebots have limited charges for their attacks, are very long range, and \ +// the projectiles have an AoE component, where as backline hivebots do not. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/backline + name = "backline hivebot" + desc = "A robot that can fire short-ranged projectiles over their allies." + projectiletype = /obj/item/projectile/arc/blue_energy + projectilesound = 'sound/weapons/Laser.ogg' + player_msg = "Your attacks are short-ranged, but can arc over obstructions such as allies \ + or barriers." + +/obj/item/projectile/arc/blue_energy + name = "energy missile" + icon_state = "force_missile" + damage = 15 // A bit stronger since arcing projectiles are much easier to avoid than traditional ones. + damage_type = BURN + +// Very long ranged hivebot that rains down hell. +// Their projectiles arc, meaning they go over everything until it hits the ground. +// This means they're somewhat easier to avoid, but go over most defenses (like allies, or barriers), +// and tend to do more harm than a regular projectile, due to being AoE. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege + name = "siege engine hivebot" + desc = "A large robot capable of delivering long range bombardment." + projectiletype = /obj/item/projectile/arc/test + icon_scale = 2 + icon_state = "red" + icon_living = "red" + + player_msg = "You are capable of firing very long range bombardment attacks.
\ + To use, click on a tile or enemy at a long range. Note that the projectile arcs in the air, \ + so it will fly over everything inbetween you and the target.
\ + The bombardment is most effective when attacking a static structure, as it cannot avoid your fire." + +// Fires EMP blasts. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/emp + name = "ionic artillery hivebot" + desc = "A large robot capable of annihilating electronics from a long distance." + projectiletype = /obj/item/projectile/arc/emp_blast + +/obj/item/projectile/arc/emp_blast + name = "emp blast" + icon_state = "bluespace" + +/obj/item/projectile/arc/emp_blast/on_impact(turf/T) + empulse(T, 2, 4, 7, 10) // Normal EMP grenade. + return ..() + +/obj/item/projectile/arc/emp_blast/weak/on_impact(turf/T) + empulse(T, 1, 2, 3, 4) // Sec EMP grenade. + return ..() + + +// Fires shots that irradiate the tile hit. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation + name = "desolator hivebot" + desc = "A large robot capable of irradiating a large area from afar." + projectiletype = /obj/item/projectile/arc/radioactive + + +// Essentially a long ranged frag grenade. +/mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/fragmentation + name = "anti-personnel artillery hivebot" + desc = "A large robot capable of delivering fragmentation shells to rip apart their fleshy enemies." + projectiletype = /obj/item/projectile/arc/fragmentation \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm new file mode 100644 index 0000000000..360686d591 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm @@ -0,0 +1,87 @@ +// These hivebots help their team in various ways, and can be very powerful with allies, but are otherwise very weak when alone. + +/mob/living/simple_mob/mechanical/hivebot/support + icon_state = "white" + icon_living = "white" + attacktext = list("prodded") + movement_cooldown = 5 + melee_damage_lower = 2 + melee_damage_upper = 2 + + +// This hivebot supplies a general buff to nearby hivebots that improve their performance. +// Note that the commander itself does not receive the buff. +/mob/living/simple_mob/mechanical/hivebot/support/commander + name = "commander hivebot" + desc = "A robot that appears to be directing the others." + maxHealth = 5 LASERS_TO_KILL // 150 health + health = 5 LASERS_TO_KILL + player_msg = "You increase the performance of other hivebots near you passively.
\ + You are otherwise very weak offensively." + +/mob/living/simple_mob/mechanical/hivebot/support/commander/handle_special() + for(var/mob/living/L in range(4, src)) + if(L == src) + continue // Don't buff ourselves. + if(IIsAlly(L) && L.isSynthetic()) // Don't buff enemies. + L.add_modifier(/datum/modifier/aura/hivebot_commander_buff, null, src) + +// Modifier added to friendly hivebots nearby. +// Boosts most stats by 30%. +// The boost is lost if the commander is too far away or dies. +/datum/modifier/aura/hivebot_commander_buff + name = "Strategicals" + on_created_text = "Signal established with commander. Optimizating combat performance..." + on_expired_text = "Lost signal to commander. Optimization halting." + stacks = MODIFIER_STACK_FORBID + aura_max_distance = 4 + mob_overlay_state = "signal_blue" + + disable_duration_percent = 0.7 + outgoing_melee_damage_percent = 1.3 + attack_speed_percent = 1.3 + accuracy = 30 + slowdown = -1 + evasion = 30 + +// Variant that automatically commands nearby allies to follow it when created. +// Useful to avoid having to manually set follow to a lot of hivebots that are gonna die in the next minute anyways. +/mob/living/simple_mob/mechanical/hivebot/support/commander/autofollow/initialize() + for(var/mob/living/L in hearers(7, src)) + if(!L.ai_holder) + continue + if(L.faction != src.faction) + continue + var/datum/ai_holder/AI = L.ai_holder + AI.set_follow(src) + return ..() + + +// This hivebot adds charges to nearby allied hivebots that use the charge system for their special attacks. +// A charge is given to a nearby ally every so often. +// Charges cannot exceed the initial starting amount. +/mob/living/simple_mob/mechanical/hivebot/support/logistics + name = "logistics hivebot" + desc = "A robot that resupplies their allies." + maxHealth = 3 LASERS_TO_KILL // 90 health + health = 3 LASERS_TO_KILL + player_msg = "You passively restore 'charges' to allies with special abilities who are \ + limited to using them a specific number of times." + var/resupply_range = 5 + var/resupply_cooldown = 4 SECONDS + var/last_resupply = null + +/mob/living/simple_mob/mechanical/hivebot/support/logistics/handle_special() + if(last_resupply + resupply_cooldown > world.time) + return // On cooldown. + + for(var/mob/living/simple_mob/SM in hearers(resupply_range, src)) + if(SM == src) + continue // We don't use charges buuuuut in case that changes in the future... + if(IIsAlly(SM)) // Don't resupply enemies. + if(!isnull(SM.special_attack_charges) && SM.special_attack_charges < initial(SM.special_attack_charges)) + SM.special_attack_charges += 1 + to_chat(SM, span("notice", "\The [src] has resupplied you, and you can use your special ability one additional time.")) + to_chat(src, span("notice", "You have resupplied \the [SM].")) + last_resupply = world.time + break // Only one resupply per pulse. diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm new file mode 100644 index 0000000000..007f190c04 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm @@ -0,0 +1,166 @@ +// These hivebots are harder to kill than normal, and are meant to protect their squad by +// distracting their enemies. This is done by being seen as very threatening. +// Their melee attacks weaken whatever they hit. + +/mob/living/simple_mob/mechanical/hivebot/tank + attacktext = list("prodded") + projectiletype = null // To force the AI to melee. + movement_cooldown = 10 + melee_damage_lower = 3 + melee_damage_upper = 3 + attack_sound = 'sound/weapons/Egloves.ogg' + +// All tank hivebots apply a modifier to their target, and force them to attack them if they're AI controlled. +/mob/living/simple_mob/mechanical/hivebot/tank/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + L.taunt(src, TRUE) + L.add_modifier(/datum/modifier/hivebot_weaken, 3 SECONDS) + +// Modifier applied to whatever a tank hivebot hits, intended to make the target do even less damage. +/datum/modifier/hivebot_weaken + name = "Shocked" + desc = "You feel less able to exert yourself after being prodded." + on_created_text = "You feel weak..." + on_expired_text = "You feel better." + stacks = MODIFIER_STACK_EXTEND + mob_overlay_state = "electricity" + + attack_speed_percent = 0.6 + outgoing_melee_damage_percent = 0.7 + accuracy = -40 + accuracy_dispersion = 1 + slowdown = 1 + evasion = -20 + +// This one is tanky by having a massive amount of health. +/mob/living/simple_mob/mechanical/hivebot/tank/meatshield + name = "bulky hivebot" + desc = "A large robot." + maxHealth = 10 LASERS_TO_KILL // 300 health + health = 10 LASERS_TO_KILL + icon_scale = 2 + player_msg = "You have a very large amount of health." + + +// This one is tanky by having armor. +/mob/living/simple_mob/mechanical/hivebot/tank/armored + name = "armored hivebot" + desc = "A robot clad in heavy armor." + maxHealth = 5 LASERS_TO_KILL // 150 health. + health = 5 LASERS_TO_KILL + icon_scale = 1.5 + player_msg = "You are heavily armored." + // Note that armor effectively makes lasers do about 9 damage instead of 30, + // so it has an effective health of ~16.6 LASERS_TO_KILL if regular lasers are used. + // Xrays will do much better against this. + armor = list( + "melee" = 40, + "bullet" = 40, + "laser" = 40, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100 + ) + armor_soak = list( + "melee" = 15, + "bullet" = 10, + "laser" = 15, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + +/mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_melee + name = "riot hivebot" + desc = "A robot specialized in close quarters combat." + player_msg = "You are heavily armored against close quarters combat." + armor = list( + "melee" = 70, + "bullet" = 0, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + armor_soak = list( + "melee" = 20, + "bullet" = 0, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + +/mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_bullet + name = "bulletproof hivebot" + desc = "A robot specialized in ballistic defense." + player_msg = "You are heavily armored against ballistic weapons." + armor = list( + "melee" = 0, + "bullet" = 70, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + armor_soak = list( + "melee" = 0, + "bullet" = 20, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + +/mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser + name = "ablative hivebot" + desc = "A robot specialized in photonic defense." + player_msg = "You are heavily armored against laser weapons." + armor = list( + "melee" = 0, + "bullet" = 0, + "laser" = 70, + "energy" = 0, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + armor_soak = list( + "melee" = 0, + "bullet" = 0, + "laser" = 20, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + var/reflect_chance = 40 // Same as regular ablative. + +// Ablative Hivebots can reflect lasers just like humans. +/mob/living/simple_mob/mechanical/hivebot/tank/armored/anti_laser/bullet_act(obj/item/projectile/P) + if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) + var/reflect_prob = reflect_chance - round(P.damage/3) + if(prob(reflect_prob)) + visible_message(span("danger", "The [P.name] gets reflected by [src]'s armor!"), \ + span("userdanger", "The [P.name] gets reflected by [src]'s armor!")) + + // Find a turf near or on the original location to bounce to + if(P.starting) + var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/turf/curloc = get_turf(src) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, src) + P.reflected = 1 + + return -1 // complete projectile permutation + + return (..(P)) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm new file mode 100644 index 0000000000..246b0fa1c6 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm @@ -0,0 +1,261 @@ +// Stronger than a regular Dark Gygax, this one has three special attacks, based on intents. +// First special attack launches three arcing rockets at the current target. +// Second special attack fires a projectile that creates a short-lived microsingularity that pulls in everything nearby. Magboots can protect from this. +// Third special attack creates a dangerous electric field that causes escalating electric damage, before emitting a tesla shock and blinding anyone looking at the mecha. +// The AI will choose one every ten seconds. +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced + name = "advanced dark gygax" + desc = "An experimental exosuit that utilizes advanced materials to allow for greater protection while still being lightweight and fast. \ + It also is armed with an array of next-generation weaponry." + icon_state = "darkgygax_adv" + wreckage = /obj/structure/loot_pile/mecha/gygax/dark/adv + icon_scale = 1.5 + movement_shake_radius = 14 + + maxHealth = 450 + deflect_chance = 25 + has_repair_droid = TRUE + armor = list( + "melee" = 50, + "bullet" = 50, + "laser" = 50, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100 + ) + + special_attack_min_range = 1 + special_attack_max_range = 7 + special_attack_cooldown = 10 SECONDS + projectiletype = /obj/item/projectile/force_missile + projectilesound = 'sound/weapons/wave.ogg' + var/obj/effect/overlay/energy_ball/energy_ball = null + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/Destroy() + if(energy_ball) + energy_ball.stop_orbit() + qdel(energy_ball) + return ..() + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/do_special_attack(atom/A) + . = TRUE // So we don't fire a laser as well. + switch(a_intent) + if(I_DISARM) // Side gun + electric_defense(A) + if(I_HURT) // Rockets + launch_rockets(A) + if(I_GRAB) // Micro-singulo + launch_microsingularity(A) + +#define ELECTRIC_ZAP_POWER 20000 + +// Charges a tesla shot, while emitting a dangerous electric field. The exosuit is immune to electric damage while this is ongoing. +// It also briefly blinds anyone looking directly at the mech without flash protection. +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/electric_defense(atom/target) + set waitfor = FALSE + + // Temporary immunity to shock to avoid killing themselves with their own attack. + var/old_shock_resist = shock_resist + shock_resist = 1 + + // Make the energy ball. This is purely visual since the tesla ball is hyper-deadly. + energy_ball = new(loc) + energy_ball.adjust_scale(0.5) + energy_ball.orbit(src, 32, TRUE, 1 SECOND) + + visible_message(span("warning", "\The [src] creates \an [energy_ball] around itself!")) + + playsound(src.loc, 'sound/effects/lightning_chargeup.ogg', 100, 1, extrarange = 30) + + // Shock nearby things that aren't ourselves. + for(var/i = 1 to 10) + energy_ball.adjust_scale(0.5 + (i/10)) + energy_ball.set_light(i/2, i/2, "#0000FF") + for(var/thing in range(3, src)) + // This is stupid because mechs are stupid and not mobs. + if(isliving(thing)) + var/mob/living/L = thing + + if(L == src) + continue + if(L.stat) + continue // Otherwise it can get pretty laggy if there's loads of corpses around. + L.inflict_shock_damage(i * 2) + if(L && L.has_AI()) // Some mobs delete themselves when dying. + L.ai_holder.react_to_attack(src) + + else if(istype(thing, /obj/mecha)) + var/obj/mecha/M = thing + M.take_damage(i * 2, "energy") // Mechs don't have a concept for siemens so energy armor check is the best alternative. + + sleep(1 SECOND) + + // Shoot a tesla bolt, and flashes people who are looking at the mecha without sufficent eye protection. + visible_message(span("warning", "\The [energy_ball] explodes in a flash of light, sending a shock everywhere!")) + playsound(src.loc, 'sound/effects/lightningbolt.ogg', 100, 1, extrarange = 30) + tesla_zap(src.loc, 5, ELECTRIC_ZAP_POWER, FALSE) + for(var/mob/living/L in viewers(src)) + if(L == src) + continue + var/dir_towards_us = get_dir(L, src) + if(L.dir && L.dir & dir_towards_us) + to_chat(L, span("danger", "The flash of light blinds you briefly.")) + L.flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = TRUE) + + // Get rid of our energy ball. + energy_ball.stop_orbit() + qdel(energy_ball) + + sleep(1 SECOND) + // Resist resistance to old value. + shock_resist = old_shock_resist // Not using initial() in case the value gets modified by an admin or something. + +#undef ELECTRIC_ZAP_POWER + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_rockets(atom/target) + set waitfor = FALSE + + // Telegraph our next move. + Beam(target, icon_state = "sat_beam", time = 3.5 SECONDS, maxdistance = INFINITY) + visible_message(span("warning", "\The [src] deploys a missile rack!")) + playsound(src, 'sound/effects/turret/move1.wav', 50, 1) + sleep(0.5 SECONDS) + + for(var/i = 1 to 3) + if(target) // Might get deleted in the meantime. + var/turf/T = get_turf(target) + if(T) + visible_message(span("warning", "\The [src] fires a rocket into the air!")) + playsound(src, 'sound/weapons/rpg.ogg', 70, 1) + face_atom(T) + var/obj/item/projectile/arc/explosive_rocket/rocket = new(loc) + rocket.launch(T) + sleep(1 SECOND) + + visible_message(span("warning", "\The [src] retracts the missile rack.")) + playsound(src, 'sound/effects/turret/move2.wav', 50, 1) + +// Arcing rocket projectile that produces a weak explosion when it lands. +// Shouldn't punch holes in the floor, but will still hurt. +/obj/item/projectile/arc/explosive_rocket + name = "rocket" + icon_state = "mortar" + +/obj/item/projectile/arc/explosive_rocket/on_impact(turf/T) + new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently. + explosion(T, 0, 0, 2, adminlog = FALSE) + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced/proc/launch_microsingularity(atom/target) + var/turf/T = get_turf(target) + visible_message(span("warning", "\The [src] fires an energetic sphere into the air!")) + playsound(src, 'sound/weapons/Laser.ogg', 50, 1) + face_atom(T) + var/obj/item/projectile/arc/microsingulo/sphere = new(loc) + sphere.launch(T) + +/obj/item/projectile/arc/microsingulo + name = "micro singularity" + icon_state = "bluespace" + +/obj/item/projectile/arc/microsingulo/on_impact(turf/T) + new /obj/effect/temporary_effect/pulse/microsingulo(T) + + +/obj/effect/temporary_effect/pulse/microsingulo + name = "micro singularity" + desc = "It's sucking everything in!" + icon = 'icons/obj/objects.dmi' + icon_state = "bhole3" + light_range = 4 + light_power = 5 + light_color = "#2ECCFA" + pulses_remaining = 10 + pulse_delay = 0.5 SECONDS + var/pull_radius = 3 + var/pull_strength = STAGE_THREE + +/obj/effect/temporary_effect/pulse/microsingulo/on_pulse() + for(var/atom/A in range(pull_radius, src)) + A.singularity_pull(src, pull_strength) + + +// The Advanced Dark Gygax's AI. +// The mob has three special attacks, based on the current intent. +// This AI choose the appropiate intent for the situation, and tries to ensure it doesn't kill itself by firing missiles at its feet. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax + conserve_ammo = TRUE // Might help avoid 'I shoot the wall forever' cheese. + var/closest_desired_distance = 1 // Otherwise run up to them to be able to potentially shock or punch them. + + var/electric_defense_radius = 3 // How big to assume electric defense's area is. + var/microsingulo_radius = 3 // Same but for microsingulo pull. + var/rocket_explosive_radius = 2 // Explosion radius for the rockets. + + var/electric_defense_threshold = 2 // How many non-targeted people are needed in close proximity before electric defense is viable. + var/microsingulo_threshold = 2 // Similar to above, but uses an area around the target. + +// Used to control the mob's positioning based on which special attack it has done. +// Note that the intent will not change again until the next special attack is about to happen. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/on_engagement(atom/A) + // Make the AI backpeddle if using an AoE special attack. + var/list/risky_intents = list(I_GRAB, I_HURT) // Mini-singulo and missiles. + if(holder.a_intent in risky_intents) + var/closest_distance = 1 + switch(holder.a_intent) // Plus one just in case. + if(I_HURT) + closest_distance = rocket_explosive_radius + 1 + if(I_GRAB) + closest_distance = microsingulo_radius + 1 + + if(get_dist(holder, A) <= closest_distance) + holder.IMove(get_step_away(holder, A, closest_distance)) + + // Otherwise get up close and personal. + else if(get_dist(holder, A) > closest_desired_distance) + holder.IMove(get_step_towards(holder, A)) + +// Changes the mob's intent, which controls which special attack is used. +// I_DISARM causes Electric Defense, I_GRAB causes Micro-Singularity, and I_HURT causes Missile Barrage. +/datum/ai_holder/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A) + if(isliving(A)) + var/mob/living/target = A + + // If we're surrounded, Electric Defense will quickly fix that. + var/tally = 0 + var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets. + for(var/atom/movable/AM in potential_targets) + if(get_dist(holder, AM) > electric_defense_radius) + continue + if(!can_attack(AM)) + continue + tally++ + + // Should we shock them? + if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius) + holder.a_intent = I_DISARM + return + + // Otherwise they're a fair distance away and we're not getting mobbed up close. + // See if we should use missiles or microsingulo. + tally = 0 // Let's recycle the var. + for(var/atom/movable/AM in potential_targets) + if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder. + continue + if(!can_attack(AM)) + continue + if(AM.anchored) // Microsingulo doesn't do anything to anchored things. + tally-- + else + tally++ + + // Lots of people means minisingulo would be more useful. + if(tally >= microsingulo_threshold) + holder.a_intent = I_GRAB + else // Otherwise use rockets. + holder.a_intent = I_HURT + + else + if(get_dist(holder, A) >= rocket_explosive_radius + 1) + holder.a_intent = I_HURT // Fire rockets if it's an obj/turf. + else + holder.a_intent = I_DISARM // Electricity might not work but it's safe up close. diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm new file mode 100644 index 0000000000..7a2d8abb19 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/combat_mecha.dm @@ -0,0 +1,38 @@ +// Base type for the 'combat' mechas like gygax/durand/maulers/etc. +// They generally are walking tanks, and their melee attack knocks back and stuns, like the real deal. + +/mob/living/simple_mob/mechanical/mecha/combat + name = "combat mecha" + desc = "An even bigger stompy mech!!" + + movement_cooldown = 10 + melee_damage_lower = 30 + melee_damage_upper = 30 + melee_attack_delay = 1 SECOND + attacktext = list("punched", "slammed", "uppercutted", "pummeled") + + armor = list( + "melee" = 30, + "bullet" = 30, + "laser" = 15, + "energy" = 0, + "bomb" = 20, + "bio" = 100, + "rad" = 100 + ) + + var/weaken_amount = 2 // Be careful with this number. High values can equal a permastun. + +// Melee hits knock back by one tile (or more if already stunned to help prevent permastuns). +/mob/living/simple_mob/mechanical/mecha/combat/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.mob_size <= MOB_MEDIUM) + visible_message(span("danger", "\The [src] sends \the [L] flying with their mechanized fist!")) + playsound(src, "punch", 50, 1) + L.Weaken(weaken_amount) + var/throw_dir = get_dir(src, L) + var/throw_dist = L.incapacitated(INCAPACITATION_DISABLED) ? 4 : 1 + L.throw_at(get_edge_target_turf(L, throw_dir), throw_dist, 1, src) + else + to_chat(L, span("warning", "\The [src] punches you with incredible force, but you remain in place.")) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm new file mode 100644 index 0000000000..c20d623c53 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/durand.dm @@ -0,0 +1,53 @@ +// Durands are slow, tanky, beefy, and hit really hard. +// They can also root themselves to become even tankier. +// The AI doesn't do this currently. + +/mob/living/simple_mob/mechanical/mecha/combat/durand + name = "durand" + desc = "An aging combat exosuit utilized by many corporations. It was originally developed to fight in the First Contact War." + icon_state = "durand" + movement_cooldown = 10 + wreckage = /obj/structure/loot_pile/mecha/durand + + maxHealth = 400 + deflect_chance = 20 + armor = list( + "melee" = 50, + "bullet" = 35, + "laser" = 15, + "energy" = 10, + "bomb" = 20, + "bio" = 100, + "rad" = 100 + ) + melee_damage_lower = 40 + melee_damage_upper = 40 + base_attack_cooldown = 2 SECONDS + projectiletype = /obj/item/projectile/beam/heavylaser + + var/defense_mode = FALSE + var/defense_deflect = 35 + +/mob/living/simple_mob/mechanical/mecha/combat/durand/proc/set_defense_mode(new_mode) + defense_mode = new_mode + deflect_chance = defense_mode ? defense_deflect : initial(deflect_chance) + to_chat(src, span("notice", "You [defense_mode ? "en" : "dis"]able defense mode.")) + +/mob/living/simple_mob/mechanical/mecha/combat/durand/SelfMove(turf/n, direct) + if(defense_mode) + to_chat(src, span("warning", "You are in defense mode, you cannot move.")) + return FALSE + return ..() + +// So players can toggle it too. +/mob/living/simple_mob/mechanical/mecha/combat/durand/verb/toggle_defense_mode() + set name = "Toggle Defense Mode" + set desc = "Toggles a special mode which makes you immobile and much more resilient." + set category = "Abilities" + + set_defense_mode(!defense_mode) + +// Variant that starts in defense mode, perhaps for PoIs. +/mob/living/simple_mob/mechanical/mecha/combat/durand/defensive/initialize() + set_defense_mode(TRUE) + return ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm new file mode 100644 index 0000000000..4e0cc0cb3d --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/gygax.dm @@ -0,0 +1,57 @@ +// Gygaxes are tough but also fast. +// Their AI, unlike most, will advance towards their target instead of remaining in place. + +/mob/living/simple_mob/mechanical/mecha/combat/gygax + name = "gygax" + desc = "A lightweight, security exosuit. Popular among private and corporate security." + icon_state = "gygax" + movement_cooldown = 0 + wreckage = /obj/structure/loot_pile/mecha/gygax + + maxHealth = 300 + armor = list( + "melee" = 25, + "bullet" = 20, + "laser" = 30, + "energy" = 15, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + projectiletype = /obj/item/projectile/beam/midlaser + + ai_holder_type = /datum/ai_holder/simple_mob/intentional/adv_dark_gygax + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. + + +// A stronger variant. +/mob/living/simple_mob/mechanical/mecha/combat/gygax/dark + name = "dark gygax" + desc = "A significantly upgraded Gygax security mech, often utilized by corporate asset protection teams and \ + PMCs." + icon_state = "darkgygax" + wreckage = /obj/structure/loot_pile/mecha/gygax/dark + + maxHealth = 400 + deflect_chance = 25 + has_repair_droid = TRUE + armor = list( + "melee" = 40, + "bullet" = 40, + "laser" = 50, + "energy" = 35, + "bomb" = 20, + "bio" = 100, + "rad" = 100 + ) + +/mob/living/simple_mob/mechanical/mecha/combat/gygax/medgax + name = "medgax" + desc = "An unorthodox fusion of the Gygax and Odysseus exosuits, this one is fast, sturdy, and carries a wide array of \ + potent chemicals and delivery mechanisms. The doctor is in!" + icon_state = "medgax" + wreckage = /obj/structure/loot_pile/mecha/gygax/medgax + diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm new file mode 100644 index 0000000000..29f102b9b9 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/hoverpod.dm @@ -0,0 +1,27 @@ +// Ranged, and capable of flight. +/mob/living/simple_mob/mechanical/mecha/hoverpod + name = "hover pod" + desc = "Stubby and round, this space-capable craft is an ancient favorite. It has a jury-rigged welder-laser." + icon_state = "engineering_pod" + movement_sound = 'sound/machines/hiss.ogg' + wreckage = /obj/structure/loot_pile/mecha/hoverpod + + maxHealth = 150 + hovering = TRUE // Can fly. + + projectiletype = /obj/item/projectile/beam + base_attack_cooldown = 2 SECONDS + + var/datum/effect/effect/system/ion_trail_follow/ion_trail + +/mob/living/simple_mob/mechanical/mecha/hoverpod/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged + +/mob/living/simple_mob/mechanical/mecha/hoverpod/initialize() + ion_trail = new /datum/effect/effect/system/ion_trail_follow() + ion_trail.set_up(src) + ion_trail.start() + return ..() + +/mob/living/simple_mob/mechanical/mecha/hoverpod/Process_Spacemove(var/check_drift = 0) + return TRUE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm new file mode 100644 index 0000000000..b4fa89c005 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/marauder.dm @@ -0,0 +1,44 @@ +// Marauders are even tougher than Durands. + +/mob/living/simple_mob/mechanical/mecha/combat/marauder + name = "marauder" + desc = "A heavy-duty, combat exosuit, developed after the Durand model. This is rarely found among civilian populations." + icon_state = "marauder" + movement_cooldown = 5 + wreckage = /obj/structure/loot_pile/mecha/marauder + + maxHealth = 500 + deflect_chance = 25 + sight = SEE_SELF | SEE_MOBS + armor = list( + "melee" = 50, + "bullet" = 55, + "laser" = 40, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100 + ) + melee_damage_lower = 45 + melee_damage_upper = 45 + base_attack_cooldown = 2 SECONDS + projectiletype = /obj/item/projectile/beam/heavylaser + + +// Slightly stronger, used to allow comdoms to frontline without dying instantly, I guess. +/mob/living/simple_mob/mechanical/mecha/combat/marauder/seraph + name = "seraph" + desc = "A heavy-duty, combat/command exosuit. This one is specialized towards housing important commanders such as high-ranking \ + military personnel. It's stronger than the regular Marauder model, but not by much." + icon_state = "seraph" + wreckage = /obj/structure/loot_pile/mecha/marauder/seraph + health = 550 + melee_damage_lower = 55 // The real version hits this hard apparently. Ouch. + melee_damage_upper = 55 + + +/mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler + name = "mauler" + desc = "A heavy duty, combat exosuit that is based off of the Marauder model." + icon_state = "mauler" + wreckage = /obj/structure/loot_pile/mecha/marauder/mauler diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm new file mode 100644 index 0000000000..7aa24bdf74 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm @@ -0,0 +1,141 @@ +// Mecha simple_mobs are essentially fake mechs. Generally tough and scary to fight. +// By default, they're automatically piloted by some kind of drone AI. They can be set to be "piloted" instead with a var. +// Tries to be as similar to the real deal as possible. + +/mob/living/simple_mob/mechanical/mecha + name = "mecha" + desc = "A big stompy mech!" + icon = 'icons/mecha/mecha.dmi' + + faction = "syndicate" + movement_cooldown = 5 + movement_sound = "mechstep" // This gets fed into playsound(), which can also take strings as a 'group' of sound files. + turn_sound = 'sound/mecha/mechturn.ogg' + maxHealth = 300 + mob_size = MOB_LARGE + + // Very close to the base 'damage_absorption' var on the base mecha class. + armor = list( + "melee" = 20, + "bullet" = 10, + "laser" = 0, + "energy" = 0, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + response_help = "taps on" + response_disarm = "knocks on" + response_harm = "uselessly hits" + harm_intent_damage = 0 + + ai_holder_type = /datum/ai_holder/simple_mob/melee + say_list_type = /datum/say_list/malf_drone + + var/datum/effect/effect/system/spark_spread/sparks + var/wreckage = /obj/effect/decal/mecha_wreckage/gygax/dark + var/pilot_type = null // Set to spawn a pilot when destroyed. Setting this also makes the mecha vulnerable to things that affect sentient minds. + var/deflect_chance = 10 // Chance to outright stop an attack, just like a normal exosuit. + var/has_repair_droid = FALSE // If true, heals 2 damage every tick and gets a repair droid overlay. + + +/mob/living/simple_mob/mechanical/mecha/initialize() + sparks = new (src) + sparks.set_up(3, 1, src) + sparks.attach(src) + + if(!pilot_type) + name = "autonomous [initial(name)]" + desc = "[initial(desc)] It appears to be piloted by a drone intelligence." + else + say_list_type = /datum/say_list/merc + + if(has_repair_droid) + update_icon() + + return ..() + +/mob/living/simple_mob/mechanical/mecha/Destroy() + qdel(sparks) + return ..() + +/mob/living/simple_mob/mechanical/mecha/death() + ..(0,"explodes!") // Do everything else first. + + // Make the exploding more convincing with an actual explosion and some sparks. + sparks.start() + explosion(get_turf(src), 0, 0, 1, 3) + + // 'Eject' our pilot, if one exists. + if(pilot_type) + var/mob/living/L = new pilot_type(loc) + L.faction = src.faction + + new wreckage(loc) // Leave some wreckage. + + qdel(src) // Then delete us since we don't actually have a body. + +/mob/living/simple_mob/mechanical/mecha/handle_special() + if(has_repair_droid) + adjustBruteLoss(-2) + adjustFireLoss(-2) + adjustToxLoss(-2) + adjustOxyLoss(-2) + adjustCloneLoss(-2) + ..() + +/mob/living/simple_mob/mechanical/mecha/update_icon() + ..() // Cuts everything else, so do that first. + if(has_repair_droid) + add_overlay(image(icon = 'icons/mecha/mecha_equipment.dmi', icon_state = "repair_droid")) + +/mob/living/simple_mob/mechanical/mecha/bullet_act() + . = ..() + sparks.start() + +/mob/living/simple_mob/mechanical/mecha/speech_bubble_appearance() + return pilot_type ? "" : ..() + +// Piloted mechs are controlled by (presumably) something humanoid so they are vulnerable to certain things. +/mob/living/simple_mob/mechanical/mecha/is_sentient() + return pilot_type ? TRUE : FALSE + +/* +// Real mechs can't turn and run at the same time. This tries to simulate that. +// Commented out because the AI can't handle it sadly. +/mob/living/simple_mob/mechanical/mecha/SelfMove(turf/n, direct) + if(direct != dir) + set_dir(direct) + return FALSE // We didn't actually move, and returning FALSE means the mob can try to actually move almost immediately and not have to wait the full movement cooldown. + return ..() +*/ + +/mob/living/simple_mob/mechanical/mecha/bullet_act(obj/item/projectile/P) + if(prob(deflect_chance)) + visible_message(span("warning", "\The [P] is deflected by \the [src]'s armor!")) + deflect_sprite() + return 0 + return ..() + +/mob/living/simple_mob/mechanical/mecha/proc/deflect_sprite() + var/image/deflect_image = image('icons/effects/effects.dmi', "deflect_static") + add_overlay(deflect_image) + sleep(1 SECOND) + cut_overlay(deflect_image) + qdel(deflect_image) +// flick_overlay_view(deflect_image, src, duration = 1 SECOND, gc_after = TRUE) + +/mob/living/simple_mob/mechanical/mecha/attackby(obj/item/I, mob/user) + if(prob(deflect_chance)) + visible_message(span("warning", "\The [user]'s [I] bounces off \the [src]'s armor!")) + deflect_sprite() + user.setClickCooldown(user.get_attack_speed(I)) + return + ..() + +/mob/living/simple_mob/mechanical/mecha/ex_act(severity) + if(prob(deflect_chance)) + severity++ // This somewhat misleadingly makes it less severe. + deflect_sprite() + ..(severity) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm new file mode 100644 index 0000000000..245bd54193 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm @@ -0,0 +1,71 @@ +// Shoots syringe-darts at enemies, which applies a stacking poison modifier that hurts over time. +// They also do this in melee. +// Fortunately they're quite fragile and don't fire that fast. + +/mob/living/simple_mob/mechanical/mecha/odysseus + name = "odysseus" + desc = "These exosuits are developed and produced by Vey-Med. This one has a syringe gun." + icon_state = "odysseus" + wreckage = /obj/structure/loot_pile/mecha/odysseus + + maxHealth = 120 + movement_cooldown = 0 + turn_sound = 'sound/mecha/mechmove01.ogg' + + melee_damage_lower = 5 + melee_damage_upper = 5 + base_attack_cooldown = 2 SECONDS + attacktext = list("injected") + projectiletype = /obj/item/projectile/fake_syringe/poison + projectilesound = 'sound/weapons/empty.ogg' // Just like the syringe gun. + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/kiting/no_moonwalk + +/mob/living/simple_mob/mechanical/mecha/odysseus/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. + + +// Resprite of the regular one, perhaps for merc PoIs. +/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus + icon_state = "murdysseus" + wreckage = /obj/structure/loot_pile/mecha/odysseus/murdysseus + +/mob/living/simple_mob/mechanical/mecha/odysseus/murdysseus/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged + + +/mob/living/simple_mob/mechanical/mecha/odysseus/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + + var/target_zone = pick(BP_TORSO,BP_TORSO,BP_TORSO,BP_L_LEG,BP_R_LEG,BP_L_ARM,BP_R_ARM,BP_HEAD) + if(L.can_inject(src, null, target_zone)) + to_chat(L, span("warning", "You feel a tiny prick.")) + if(L.get_poison_protection() < 1) + L.add_modifier(/datum/modifier/poisoned, 30 SECONDS) + L.inflict_poison_damage(5) + + +// Fake syringe that tests if target can be injected before applying damage/modifiers/etc. +/obj/item/projectile/fake_syringe + name = "syringe" + icon_state = "syringe" + damage = 5 // Getting hit with a launched syringe probably hurts, and makes it at least slightly relevant against synthetics. + var/piercing = FALSE // If true, ignores thick material. + +/obj/item/projectile/fake_syringe/on_hit(atom/target, blocked = 0, def_zone = null) + if(isliving(target)) + var/mob/living/L = target + if(!L.can_inject(null, null, def_zone, piercing)) + return FALSE + to_chat(L, span("warning", "You feel a tiny prick.")) + return ..() // This will add the modifier and return the correct value. + + +// Fake syringe, which inflicts a long lasting modifier that slowly kills them. +/obj/item/projectile/fake_syringe/poison + modifier_type_to_apply = /datum/modifier/poisoned + modifier_duration = 1 MINUTE // About 30 damage per stack over a minute. + + + diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm new file mode 100644 index 0000000000..ebbe80ecf4 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/phazon.dm @@ -0,0 +1,22 @@ +// Phazons are weird. + +/mob/living/simple_mob/mechanical/mecha/combat/phazon + name = "phazon" + desc = "An extremly enigmatic exosuit." + icon_state = "phazon" + movement_cooldown = 5 + wreckage = /obj/structure/loot_pile/mecha/phazon + + maxHealth = 200 + deflect_chance = 30 + armor = list( + "melee" = 30, + "bullet" = 30, + "laser" = 30, + "energy" = 30, + "bomb" = 30, + "bio" = 100, + "rad" = 100 + ) + projectiletype = /obj/item/projectile/energy/declone + diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm new file mode 100644 index 0000000000..8372363ea0 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm @@ -0,0 +1,64 @@ +// Beefy, but somewhat slow. +// Melee attack is to bore you with its big drill, which has a lot of armor penetration and strikes rapidly. + +/mob/living/simple_mob/mechanical/mecha/ripley + name = "\improper APLU ripley" + desc = "Autonomous Power Loader Unit. The workhorse of the exosuit world. This one has big drill." + icon_state = "ripley" + wreckage = /obj/structure/loot_pile/mecha/ripley + + maxHealth = 200 + + melee_damage_lower = 10 + melee_damage_upper = 10 + base_attack_cooldown = 5 // About 20 DPS. + attack_armor_pen = 50 + attack_sharp = TRUE + attack_sound = 'sound/mecha/mechdrill.ogg' + attacktext = list("drilled", "bored", "pierced") + +/mob/living/simple_mob/mechanical/mecha/ripley/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged // Carries a pistol. + +/mob/living/simple_mob/mechanical/mecha/ripley/red_flames + icon_state = "ripley_flames_red" + +/mob/living/simple_mob/mechanical/mecha/ripley/blue_flames + icon_state = "ripley_flames_blue" + + +// Immune to heat damage, resistant to lasers, and somewhat beefier. Still tries to melee you. +/mob/living/simple_mob/mechanical/mecha/ripley/firefighter + name = "\improper APLU firefighter" + desc = "A standard APLU chassis, refitted with additional thermal protection and cistern. This one has a big drill." + icon_state = "firefighter" + wreckage = /obj/structure/loot_pile/mecha/ripley/firefighter + + maxHealth = 250 + heat_resist = 1 + armor = list( + "melee" = 0, + "bullet" = 20, + "laser" = 50, + "energy" = 0, + "bomb" = 50, + "bio" = 100, + "rad" = 100 + ) + +/mob/living/simple_mob/mechanical/mecha/ripley/firefighter/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged + +// Mostly a joke mob, like the real DEATH-RIPLEY. +/mob/living/simple_mob/mechanical/mecha/ripley/deathripley + name = "\improper DEATH-RIPLEY" + desc = "OH SHIT RUN!!! IT HAS A KILL CLAMP!" + icon_state = "deathripley" + wreckage = /obj/structure/loot_pile/mecha/deathripley + + melee_damage_lower = 0 + melee_damage_upper = 0 + friendly = list("utterly obliterates", "furiously destroys", "permanently removes", "unflichingly decimates", "brutally murders", "absolutely demolishes", "completely annihilates") + +/mob/living/simple_mob/mechanical/mecha/ripley/deathripley/manned + pilot_type = /mob/living/simple_mob/humanoid/merc/ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm new file mode 100644 index 0000000000..f61212ba75 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm @@ -0,0 +1,25 @@ +// Mechanical mobs don't care about the atmosphere and cannot be hurt by tasers. +// They're also immune to poisons as they're entirely metal, however this also makes most of them vulnerable to shocks. +// They can also be hurt by EMP. + +/mob/living/simple_mob/mechanical + mob_class = MOB_CLASS_SYNTHETIC + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + taser_kill = FALSE + poison_resist = 1.0 + shock_resist = -0.5 + +/mob/living/simple_mob/mechanical/isSynthetic() + return TRUE + +/mob/living/simple_mob/mechanical/speech_bubble_appearance() + return faction != "neutral" ? "synthetic_evil" : "machine" \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm new file mode 100644 index 0000000000..09d778547b --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm @@ -0,0 +1,51 @@ +/* + Viscerators are fragile and don't hit very hard, but fast, evasive, and rarely come alone. + They also tend to dodge while in melee range. + A weapon that can cleave is very effective against them. +*/ + +/mob/living/simple_mob/mechanical/viscerator + name = "viscerator" + desc = "A small, twin-bladed machine capable of inflicting very deadly lacerations." + icon = 'icons/mob/critter.dmi' + icon_state = "viscerator_attack" + icon_living = "viscerator_attack" + hovering = TRUE // Won't trigger landmines. + + faction = "syndicate" + maxHealth = 15 + health = 15 + movement_cooldown = 0 + + pass_flags = PASSTABLE + mob_swap_flags = 0 + mob_push_flags = 0 + + melee_damage_lower = 4 // Approx 8 DPS. + melee_damage_upper = 4 + base_attack_cooldown = 5 // Two attacks a second or so. + attack_sharp = 1 + attack_edge = 1 + attack_sound = 'sound/weapons/bladeslice.ogg' + attacktext = list("cut", "sliced") + + ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive + +/mob/living/simple_mob/mechanical/viscerator/death() + ..(null,"is smashed into pieces!") + qdel(src) + +// Variant that is always loyal to mercenary antagonists. +// Used for a special grenade, to ensure they don't attack the wrong thing. +/mob/living/simple_mob/mechanical/viscerator/mercenary/IIsAlly(mob/living/L) + . = ..() + if(!.) // Not friendly, see if they're a baddie first. + if(L.mind && mercs.is_antagonist(L.mind)) + return TRUE + +// Similar to above but for raiders. +/mob/living/simple_mob/mechanical/viscerator/raider/IIsAlly(mob/living/L) + . = ..() + if(!.) // Not friendly, see if they're a baddie first. + if(L.mind && raiders.is_antagonist(L.mind)) + return TRUE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm new file mode 100644 index 0000000000..153bc9c478 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm @@ -0,0 +1,106 @@ +/* + 'Monitor' wards are drones that yell at their creator if they see someone besides them that they are hostile to. + They can also force an invisible entity to uncloak if the invisible mob is hostile to the ward. + If AI controlled, they will also warn their faction if they see a hostile entity, acting as floating cameras. +*/ + +/mob/living/simple_mob/mechanical/ward/monitor + desc = "It's a little flying drone. This one seems to be watching you..." + icon_state = "ward" + glow_color = "#00FF00" + see_invisible = SEE_INVISIBLE_LEVEL_TWO + + has_eye_glow = TRUE + glow_range = 3 + glow_intensity = 3 + glow_toggle = TRUE + + player_msg = "You will automatically alert your owner (if one exists) of enemies you see nearby.
\ + You can also see invisible entities, and will automatically uncloak nearby invisible or hidden enemies." + + ai_holder_type = /datum/ai_holder/simple_mob/monitor + + var/list/seen_mobs = list() + var/view_range = 5 + +// For PoIs. +/mob/living/simple_mob/mechanical/ward/monitor/syndicate + faction = "syndicate" + +/mob/living/simple_mob/mechanical/ward/monitor/crew + faction = "neutral" + +/mob/living/simple_mob/mechanical/ward/monitor/death() + if(owner) + to_chat(owner, span("warning", "Your [src.name] inside [get_area(src)] was destroyed!")) + ..() + +/mob/living/simple_mob/mechanical/ward/monitor/handle_special() + detect_mobs() + +/mob/living/simple_mob/mechanical/ward/monitor/update_icon() + if(seen_mobs.len) + icon_living = "ward_spotted" + glow_color = "#FF0000" + else + icon_living = "ward" + glow_color = "#00FF00" + handle_light() // Update the light immediately. + ..() + +/mob/living/simple_mob/mechanical/ward/monitor/proc/detect_mobs() + var/last_seen_mobs_len = seen_mobs.len + var/list/mobs_nearby = hearers(view_range, src) + var/list/newly_seen_mobs = list() + for(var/mob/living/L in mobs_nearby) + if(L == src) // Don't detect ourselves. + continue + + if(L.stat) // Dead mobs aren't concerning. + continue + + if(src.IIsAlly(L)) + continue + + // Decloak them . + if(L.is_cloaked()) + Beam(L, icon_state = "solar_beam", time = 5) + playsound(L, 'sound/effects/EMPulse.ogg', 75, 1) + L.break_cloak() + + to_chat(L, span("danger", "\The [src] disrupts your cloak!")) + if(owner) + to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] uncloaked \the [L].")) + + // Warn the owner when it sees a new mob. + if(!(L in seen_mobs)) + seen_mobs += L + newly_seen_mobs += L + + if(newly_seen_mobs.len && owner) // Yell at our owner if someone new shows up. + to_chat(owner, span("notice", "Your [src.name] at [get_area(src)] detected [english_list(newly_seen_mobs)].")) + + // Now get rid of old mobs that left vision. + for(var/thing in seen_mobs) + if(!(thing in mobs_nearby)) + seen_mobs -= thing + + // Check if we need to update icon. + if(seen_mobs.len != last_seen_mobs_len) + update_icon() + + +// Can't attack but calls for help. Used by the monitor and spotter wards. +// Special attacks are not blocked since they might be used for things besides attacking, and can be conditional. +/datum/ai_holder/simple_mob/monitor + hostile = TRUE // Required to call for help. + cooperative = TRUE + stand_ground = TRUE // So it doesn't run up to the thing it sees. + wander = FALSE + can_flee = FALSE + +/datum/ai_holder/simple_mob/monitor/melee_attack(atom/A) + return FALSE + +/datum/ai_holder/simple_mob/monitor/ranged_attack(atom/A) + return FALSE \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm new file mode 100644 index 0000000000..0e897599ac --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm @@ -0,0 +1,42 @@ +/* + Wards are a specific type of mechanical simplemob that generally fill a support role for their faction or for a specific mob. + Generally they are helpless by themselves and are fragile, but can do very useful things if protected. This makes them a high priority target. +*/ + +/mob/living/simple_mob/mechanical/ward + name = "ward" + desc = "A small floating machine. This one seems rather useless..." + icon = 'icons/mob/critter.dmi' + icon_state = "ward" + icon_living = "ward" + hovering = TRUE // Won't trigger landmines. + response_help = "pets" + response_disarm = "swats away" + response_harm = "punches" + faction = "wards" // Needed as most human mobs are in neutral faction. The owner is generally except from any ward hostility regardless. + + maxHealth = 15 + health = 15 + movement_cooldown = 0 + hovering = TRUE + + mob_bump_flag = 0 + + melee_damage_lower = 0 + melee_damage_upper = 0 + + ai_holder_type = null + var/mob/living/owner = null // The mob that made the ward, if any. Used to ensure the ward does not interfere with its creator. + +/mob/living/simple_mob/mechanical/ward/death() + ..(null,"is smashed into pieces!") + qdel(src) + +/mob/living/simple_mob/mechanical/ward/Destroy() + owner = null + return ..() + +/mob/living/simple_mob/mechanical/ward/IIsAlly(mob/living/L) + if(owner == L) + return TRUE + return ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm new file mode 100644 index 0000000000..cd3b074a40 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm @@ -0,0 +1,163 @@ +//////////////////////////// +// Base Construct +//////////////////////////// + +/mob/living/simple_mob/construct + name = "Construct" + real_name = "Construct" + desc = "" + tt_desc = "Error" + + icon_living = "shade" + icon_dead = "shade_dead" + + mob_class = MOB_CLASS_DEMONIC + + ui_icons = 'icons/mob/screen1_construct.dmi' + has_hands = 1 + hand_form = "stone manipulators" + + response_help = "thinks better of touching" + response_disarm = "flailed at" + response_harm = "punched" + + hovering = TRUE + softfall = TRUE //Beings made of Hellmarble and powered by the tears of the damned are not concerned with mortal things such as 'gravity'. + parachuting = TRUE + + has_langs = list(LANGUAGE_GALCOM, LANGUAGE_CULT, LANGUAGE_OCCULT) + + has_eye_glow = TRUE + + taser_kill = FALSE + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + shock_resist = 0.1 //Electricity isn't very effective on stone, especially that from hell. + poison_resist = 1.0 + + armor = list( + "melee" = 10, + "bullet" = 10, + "laser" = 10, + "energy" = 10, + "bomb" = 10, + "bio" = 100, + "rad" = 100) + + can_be_antagged = TRUE + faction = "cult" + + supernatural = TRUE + + var/construct_type = "shade" + var/list/construct_spells = list() +// var/do_glow = TRUE + +/mob/living/simple_mob/construct/place_spell_in_hand(var/path) + if(!path || !ispath(path)) + return 0 + + //var/obj/item/weapon/spell/S = new path(src) + var/obj/item/weapon/spell/construct/S = new path(src) + + //No hands needed for innate casts. + if(S.cast_methods & CAST_INNATE) + if(S.run_checks()) + S.on_innate_cast(src) + + if(l_hand && r_hand) //Make sure our hands aren't full. + if(istype(r_hand, /obj/item/weapon/spell)) //If they are full, perhaps we can still be useful. + var/obj/item/weapon/spell/r_spell = r_hand + if(r_spell.aspect == ASPECT_CHROMATIC) //Check if we can combine the new spell with one in our hands. + r_spell.on_combine_cast(S, src) + else if(istype(l_hand, /obj/item/weapon/spell)) + var/obj/item/weapon/spell/l_spell = l_hand + if(l_spell.aspect == ASPECT_CHROMATIC) //Check the other hand too. + l_spell.on_combine_cast(S, src) + else //Welp + to_chat(src, "You require a free manipulator to use this power.") + return 0 + + if(S.run_checks()) + put_in_hands(S) + return 1 + else + qdel(S) + return 0 + +/mob/living/simple_mob/construct/cultify() + return + +/mob/living/simple_mob/construct/New() + ..() + name = text("[initial(name)] ([rand(1, 1000)])") + real_name = name + for(var/spell in construct_spells) + src.add_spell(new spell, "const_spell_ready") + updateicon() + +/* +/mob/living/simple_mob/construct/update_icon() + ..() + if(do_glow) + add_glow() +*/ + +/mob/living/simple_mob/construct/death() + new /obj/item/weapon/ectoplasm (src.loc) + ..(null,"collapses in a shattered heap.") + ghostize() + qdel(src) + +/mob/living/simple_mob/construct/attack_generic(var/mob/user) + if(istype(user, /mob/living/simple_mob/construct/artificer)) + var/mob/living/simple_mob/construct/artificer/A = user + if(health < getMaxHealth()) + var/repair_lower_bound = A.melee_damage_lower * -1 + var/repair_upper_bound = A.melee_damage_upper * -1 + adjustBruteLoss(rand(repair_lower_bound, repair_upper_bound)) + adjustFireLoss(rand(repair_lower_bound, repair_upper_bound)) + user.visible_message("\The [user] mends some of \the [src]'s wounds.") + else + to_chat(user, "\The [src] is undamaged.") + return + return ..() + +/mob/living/simple_mob/construct/examine(mob/user) + ..(user) + var/msg = "*---------*\nThis is \icon[src] \a [src]!\n" + if (src.health < src.getMaxHealth()) + msg += "" + if (src.health >= src.getMaxHealth()/2) + msg += "It looks slightly dented.\n" + else + msg += "It looks severely dented!\n" + msg += "" + msg += "*---------*" + + user << msg + +//Constructs levitate, can fall from a shuttle with no harm, and are piloted by either damned spirits or some otherworldly entity. Let 'em float in space. +/mob/living/simple_mob/construct/Process_Spacemove() + return 1 + +/* +// Glowing Procs +/mob/living/simple_mob/construct/proc/add_glow() + var/image/eye_glow = image(icon,"glow-[icon_state]") + eye_glow.plane = PLANE_LIGHTING_ABOVE + overlays += eye_glow + set_light(2, -2, l_color = "#FFFFFF") + +/mob/living/simple_mob/construct/proc/remove_glow() + overlays.Cut() +*/ \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/artificer.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/artificer.dm new file mode 100644 index 0000000000..69d0da0251 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/artificer.dm @@ -0,0 +1,30 @@ +//////////////////////////// +// Artificer +//////////////////////////// + +/mob/living/simple_mob/construct/artificer + name = "Artificer" + real_name = "Artificer" + construct_type = "artificer" + desc = "A bulbous construct dedicated to building and maintaining temples to their otherworldly lords." + icon = 'icons/mob/mob.dmi' + icon_state = "artificer" + icon_living = "artificer" + maxHealth = 150 + health = 150 + response_harm = "viciously beaten" + harm_intent_damage = 5 + melee_damage_lower = 15 //It's not the strongest of the bunch, but that doesn't mean it can't hurt you. + melee_damage_upper = 20 + attacktext = list("rammed") + attack_sound = 'sound/weapons/rapidslice.ogg' + construct_spells = list(/spell/aoe_turf/conjure/construct/lesser, + /spell/aoe_turf/conjure/wall, + /spell/aoe_turf/conjure/floor, + /spell/aoe_turf/conjure/soulstone, + /spell/aoe_turf/conjure/pylon, + /spell/aoe_turf/conjure/door, + /spell/aoe_turf/conjure/grille, + /spell/targeted/occult_repair_aura, + /spell/targeted/construct_advanced/mend_acolyte + ) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/harvester.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/harvester.dm new file mode 100644 index 0000000000..9328d9cffd --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/harvester.dm @@ -0,0 +1,40 @@ +//////////////////////////// +// Harvester +//////////////////////////// + +/mob/living/simple_mob/construct/harvester + name = "Harvester" + real_name = "Harvester" + construct_type = "harvester" + desc = "A tendril-laden construct piloted by a chained mind." + icon = 'icons/mob/mob.dmi' + icon_state = "harvester" + icon_living = "harvester" + maxHealth = 150 + health = 150 + melee_damage_lower = 20 + melee_damage_upper = 25 + attack_sharp = 1 + attacktext = list("violently stabbed") + friendly = list("caresses") + movement_cooldown = 0 + + // environment_smash = 1 // Whatever this gets renamed to, Harvesters need to break things + + attack_sound = 'sound/weapons/pierce.ogg' + + armor = list( + "melee" = 10, + "bullet" = 20, + "laser" = 20, + "energy" = 20, + "bomb" = 20, + "bio" = 100, + "rad" = 100) + + construct_spells = list( + /spell/aoe_turf/knock/harvester, + /spell/targeted/construct_advanced/inversion_beam, + /spell/targeted/construct_advanced/agonizing_sphere, + /spell/rune_write + ) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm new file mode 100644 index 0000000000..31dfa4d34a --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm @@ -0,0 +1,143 @@ +//////////////////////////// +// Juggernaut +//////////////////////////// + +/mob/living/simple_mob/construct/juggernaut + name = "Juggernaut" + real_name = "Juggernaut" + construct_type = "juggernaut" + desc = "A possessed suit of armour driven by the will of the restless dead" + icon = 'icons/mob/mob.dmi' + icon_state = "behemoth" + icon_living = "behemoth" + maxHealth = 300 + health = 300 + response_harm = "harmlessly punches" + harm_intent_damage = 0 + melee_damage_lower = 30 + melee_damage_upper = 40 + attack_armor_pen = 60 //Being punched by a living, floating statue. + attacktext = list("smashed their armoured gauntlet into") + friendly = list("pats") + mob_size = MOB_HUGE + + + movement_cooldown = 6 //Not super fast, but it might catch up to someone in armor who got punched once or twice. + +// environment_smash = 2 // Whatever this gets renamed to, Juggernauts need to break things + + + attack_sound = 'sound/weapons/heavysmash.ogg' + status_flags = 0 + resistance = 10 + construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser, + /spell/targeted/fortify, + /spell/targeted/construct_advanced/slam + ) + + armor = list( + "melee" = 70, + "bullet" = 30, + "laser" = 30, + "energy" = 30, + "bomb" = 10, + "bio" = 100, + "rad" = 100) + +/mob/living/simple_mob/construct/juggernaut/Life() + weakened = 0 + ..() + +/mob/living/simple_mob/construct/juggernaut/bullet_act(var/obj/item/projectile/P) + var/reflectchance = 80 - round(P.damage/3) + if(prob(reflectchance)) + var/damage_mod = rand(2,4) + var/projectile_dam_type = P.damage_type + var/incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3))) + var/armorcheck = run_armor_check(null, P.check_armour) + var/soakedcheck = get_armor_soak(null, P.check_armour) + if(!(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam))) + visible_message("The [P.name] bounces off of [src]'s shell!", \ + "The [P.name] bounces off of [src]'s shell!") + new /obj/item/weapon/material/shard/shrapnel(src.loc) + if(!(P.damage_type == BRUTE || P.damage_type == BURN)) + projectile_dam_type = BRUTE + incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to brute for physical projectiles, though severely decreased. + apply_damage(incoming_damage, projectile_dam_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P) + return -1 //Doesn't reflect non-beams or non-energy projectiles. They just smack and drop with little to no effect. + else + visible_message("The [P.name] gets reflected by [src]'s shell!", \ + "The [P.name] gets reflected by [src]'s shell!") + damage_mod = rand(3,5) + incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3))) + if(!(P.damage_type == BRUTE || P.damage_type == BURN)) + projectile_dam_type = BURN + incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to burn for energy-type projectiles, though severely decreased. + apply_damage(incoming_damage, P.damage_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P) + + // Find a turf near or on the original location to bounce to + if(P.starting) + var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/turf/curloc = get_turf(src) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, src) + P.reflected = 1 + + return -1 // complete projectile permutation + + return (..(P)) + +/* + * The Behemoth. Admin-allowance only, still try to keep it in some guideline of 'Balanced', even if it means Security has to be fully geared to be so. + */ + +/mob/living/simple_mob/construct/juggernaut/behemoth + name = "Behemoth" + real_name = "Behemoth" + desc = "The pinnacle of occult technology, Behemoths are nothing shy of both an Immovable Object, and Unstoppable Force." + maxHealth = 750 + health = 750 + speak_emote = list("rumbles") + melee_damage_lower = 50 + melee_damage_upper = 50 + attacktext = list("brutally crushed") + friendly = list("pokes") //Anything nice the Behemoth would do would still Kill the Human. Leave it at poke. + attack_sound = 'sound/weapons/heavysmash.ogg' + resistance = 10 + icon_scale = 2 + var/energy = 0 + var/max_energy = 1000 + armor = list( + "melee" = 60, + "bullet" = 60, + "laser" = 60, + "energy" = 30, + "bomb" = 10, + "bio" = 100, + "rad" = 100) + construct_spells = list(/spell/aoe_turf/conjure/forcewall/lesser, + /spell/targeted/fortify, + /spell/targeted/construct_advanced/slam + ) + +/mob/living/simple_mob/construct/juggernaut/behemoth/bullet_act(var/obj/item/projectile/P) + var/reflectchance = 80 - round(P.damage/3) + if(prob(reflectchance)) + visible_message("The [P.name] gets reflected by [src]'s shell!", \ + "The [P.name] gets reflected by [src]'s shell!") + + // Find a turf near or on the original location to bounce to + if(P.starting) + var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/turf/curloc = get_turf(src) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, src) + P.reflected = 1 + + return -1 // complete projectile permutation + + return (..(P)) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/shade.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/shade.dm new file mode 100644 index 0000000000..1a56abcdcd --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/shade.dm @@ -0,0 +1,48 @@ +//////////////////////////// +// Shade +//////////////////////////// + +/mob/living/simple_mob/construct/shade + name = "Shade" + real_name = "Shade" + desc = "A bound spirit" + icon = 'icons/mob/mob.dmi' + icon_state = "shade" + icon_living = "shade" + icon_dead = "shade_dead" + + response_help = "puts their hand through" + response_disarm = "flails at" + response_harm = "punches" + + melee_damage_lower = 5 + melee_damage_upper = 15 + attack_armor_pen = 100 //It's a ghost/horror from beyond, I ain't gotta explain 100 AP + attacktext = list("drained the life from") + + minbodytemp = 0 + maxbodytemp = 4000 + min_oxy = 0 + max_co2 = 0 + max_tox = 0 + + universal_speak = 1 + + loot_list = list(/obj/item/weapon/ectoplasm = 100) + +/mob/living/simple_mob/construct/shade/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, /obj/item/device/soulstone)) + var/obj/item/device/soulstone/S = O; + S.transfer_soul("SHADE", src, user) + return + ..() + +/mob/living/simple_mob/construct/shade/death() + ..() + for(var/mob/M in viewers(src, null)) + if((M.client && !( M.blinded ))) + M.show_message("[src] lets out a contented sigh as their form unwinds.") + + ghostize() + qdel(src) + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/wraith.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/wraith.dm new file mode 100644 index 0000000000..13580bc845 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/wraith.dm @@ -0,0 +1,33 @@ +//////////////////////////// +// Wraith +//////////////////////////// + +/mob/living/simple_mob/construct/wraith + name = "Wraith" + real_name = "Wraith" + construct_type = "wraith" + desc = "A wicked bladed shell contraption piloted by a bound spirit." + icon = 'icons/mob/mob.dmi' + icon_state = "floating" + icon_living = "floating" + maxHealth = 200 + health = 200 + melee_damage_lower = 25 + melee_damage_upper = 30 + attack_armor_pen = 15 + attack_sharp = 1 + attack_edge = 1 + attacktext = list("slashed") + friendly = list("pinches") + movement_cooldown = 0 + attack_sound = 'sound/weapons/rapidslice.ogg' + construct_spells = list(/spell/targeted/ethereal_jaunt/shift, + /spell/targeted/ambush_mode + ) + +// environment_smash = 1 // Whatever this gets renamed to, Wraiths need to break things + +/mob/living/simple_mob/construct/wraith/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + L.add_modifier(/datum/modifier/deep_wounds, 30 SECONDS) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm b/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm new file mode 100644 index 0000000000..3cc9df4fa3 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm @@ -0,0 +1,68 @@ +/mob/living/simple_mob/creature + name = "creature" + desc = "A sanity-destroying otherthing." + icon = 'icons/mob/critter.dmi' + icon_state = "otherthing" + icon_living = "otherthing" + icon_dead = "otherthing-dead" + + mob_class = MOB_CLASS_ABERRATION + + faction = "creature" + + maxHealth = 40 + health = 40 + + harm_intent_damage = 8 + + melee_damage_lower = 8 + melee_damage_upper = 15 + attack_armor_pen = 5 //It's a horror from beyond, I ain't gotta explain 5 AP + attack_sharp = 1 + attack_edge = 1 + + attacktext = list("chomped") + attack_sound = 'sound/weapons/bite.ogg' + + speak_emote = list("gibbers") + + ai_holder_type = /datum/ai_holder/simple_mob/melee + +// Strong Variant +/mob/living/simple_mob/creature/strong + maxHealth = 160 + health = 160 + + harm_intent_damage = 5 + melee_damage_lower = 13 + melee_damage_upper = 25 + +// Cult Variant +/mob/living/simple_mob/creature/cult + mob_class = MOB_CLASS_DEMONIC + + faction = "cult" + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + + supernatural = TRUE + +/mob/living/simple_mob/creature/cult/cultify() + return + +// Strong Cult Variant +/mob/living/simple_mob/creature/cult/strong + maxHealth = 160 + health = 160 + + harm_intent_damage = 5 + melee_damage_lower = 13 + melee_damage_upper = 25 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm b/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm new file mode 100644 index 0000000000..1c4ac8876c --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm @@ -0,0 +1,77 @@ +/mob/living/simple_mob/faithless + name = "Faithless" + desc = "The Wish Granter's faith in humanity, incarnate" + icon_state = "faithless" + icon_living = "faithless" + icon_dead = "faithless_dead" + + faction = "faithless" + + mob_class = MOB_CLASS_DEMONIC + + maxHealth = 50 + health = 50 + + response_help = "passes through" + response_disarm = "shoves" + response_harm = "hits" + + harm_intent_damage = 10 + + melee_damage_lower = 10 + melee_damage_upper = 18 + attack_armor_pen = 5 //It's a horror from beyond, I ain't gotta explain 5 AP + + attacktext = list("gripped") + attack_sound = 'sound/hallucinations/growl1.ogg' + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + + taser_kill = FALSE + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + +/mob/living/simple_mob/faithless/Process_Spacemove(var/check_drift = 0) + return 1 + +/mob/living/simple_mob/faithless/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(12)) + L.Weaken(3) + L.visible_message("\the [src] knocks down \the [L]!") + +// Strong Variant +/mob/living/simple_mob/faithless/strong + maxHealth = 100 + health = 100 + + harm_intent_damage = 5 + melee_damage_lower = 13 + melee_damage_upper = 28 + +// Cult Variant +/mob/living/simple_mob/faithless/cult + faction = "cult" + supernatural = TRUE + +/mob/living/simple_mob/faithless/cult/cultify() + return + +// Strong Cult Variant +/mob/living/simple_mob/faithless/cult/strong + maxHealth = 100 + health = 100 + + harm_intent_damage = 5 + melee_damage_lower = 13 + melee_damage_upper = 28 \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm b/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm new file mode 100644 index 0000000000..79e5c4d349 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm @@ -0,0 +1,27 @@ +/mob/living/simple_mob/tomato + name = "tomato" + desc = "It's a horrifyingly enormous beef tomato, and it's packing extra beef!" + tt_desc = "X Solanum abominable" + icon_state = "tomato" + icon_living = "tomato" + icon_dead = "tomato_dead" + + mob_class = MOB_CLASS_PLANT + + faction = "plants" + maxHealth = 15 + health = 15 + poison_resist = 1.0 + + response_help = "prods" + response_disarm = "pushes aside" + response_harm = "smacks" + + harm_intent_damage = 5 + melee_damage_upper = 15 + melee_damage_lower = 10 + attacktext = list("mauled") + + ai_holder_type = /datum/ai_holder/simple_mob/melee + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/tomatomeat diff --git a/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm b/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm new file mode 100644 index 0000000000..30c891088c --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm @@ -0,0 +1,43 @@ +/mob/living/simple_mob/animal/space/tree + name = "pine tree" + desc = "A pissed off tree-like alien. It seems annoyed with the festivities..." + tt_desc = "X Festivus tyrannus" + icon = 'icons/obj/flora/pinetrees.dmi' + icon_state = "pine_1" + icon_living = "pine_1" + icon_dead = "pine_1" + icon_gib = "pine_1" + + mob_class = MOB_CLASS_PLANT + + faction = "plants" + maxHealth = 250 + health = 250 + poison_resist = 1.0 + + response_help = "brushes" + response_disarm = "pushes" + response_harm = "hits" + + harm_intent_damage = 5 + melee_damage_lower = 8 + melee_damage_upper = 12 + attacktext = list("bitten") + attack_sound = 'sound/weapons/bite.ogg' + + meat_type = /obj/item/weapon/reagent_containers/food/snacks/xenomeat + + pixel_x = -16 + +/mob/living/simple_mob/animal/space/tree/apply_melee_effects(var/atom/A) + if(isliving(A)) + var/mob/living/L = A + if(prob(15)) + L.Weaken(3) + L.visible_message(span("danger", "\The [src] knocks down \the [L]!")) + +/mob/living/simple_mob/animal/space/tree/death() + ..(null,"is hacked into pieces!") + playsound(loc, 'sound/effects/woodcutting.ogg', 100, 1) + new /obj/item/stack/material/wood(loc) + qdel(src) \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm new file mode 100644 index 0000000000..8986cbcbe3 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm @@ -0,0 +1,94 @@ +// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists. + +/mob/living/simple_mob/slime/feral + name = "feral slime" + desc = "The result of slimes escaping containment from some xenobiology lab. \ + Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \ + creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species." + description_info = "Note that processing this large slime will give six cores." + + cores = 6 // Xenobio will love getting their hands on these. + + icon_state = "slime adult" + icon_living = "slime adult" + icon_dead = "slime adult dead" + glow_range = 5 + glow_intensity = 4 + icon_scale = 2 // Twice as big as the xenobio variant. + pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help. + default_pixel_y = -10 // To prevent resetting above var. + + maxHealth = 300 + movement_cooldown = 10 + melee_attack_delay = 0.5 SECONDS + + ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank + + +// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't). +/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount) + taunt(user, TRUE) + + +// *********** +// *Dark Blue* +// *********** + +// Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration. +// They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs. + +/mob/living/simple_mob/slime/feral/dark_blue + name = "dark blue feral slime" + color = "#2398FF" + glow_toggle = TRUE + slime_color = "dark blue" + coretype = /obj/item/slime_extract/dark_blue + cold_resist = 1 // Complete immunity. + minbodytemp = 0 + cold_damage_per_tick = 0 + + projectiletype = /obj/item/projectile/icicle + base_attack_cooldown = 2 SECONDS + ranged_attack_delay = 1 SECOND + + player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.
\ + You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.
\ + Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer." + +/obj/item/projectile/icicle + name = "icicle" + icon_state = "ice_2" + damage = 40 + damage_type = BRUTE + check_armour = "melee" + armor_penetration = 30 + step_delay = 2 // Make it a bit easier to dodge since its not a bullet. + icon_scale = 2 // It hits like a truck. + sharp = TRUE + +/obj/item/projectile/icicle/on_impact(atom/A) + playsound(get_turf(A), "shatter", 70, 1) + return ..() + +/obj/item/projectile/icicle/get_structure_damage() + return damage / 2 // They're really deadly against mobs, but less effective against solid things. + +/mob/living/simple_mob/slime/feral/dark_blue/handle_special() + if(stat != DEAD) + cold_aura() + ..() + +/mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura() + for(var/mob/living/L in view(3, src)) + if(L == src) + continue + chill(L) + +/mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L) + L.inflict_cold_damage(10) + if(L.get_cold_protection() < 1) + L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src) + + if(L.has_AI()) // Other AIs should react to hostile auras. + L.ai_holder.react_to_attack(src) + diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm new file mode 100644 index 0000000000..dd756d267f --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm @@ -0,0 +1,216 @@ +// The top-level slime defines. Xenobio slimes and feral slimes will inherit from this. + +/mob/living/simple_mob/slime + name = "slime" + desc = "It's a slime." + tt_desc = "A Macrolimbus vulgaris" + icon = 'icons/mob/slime2.dmi' + icon_state = "slime baby" + icon_living = "slime baby" + icon_dead = "slime baby dead" + var/shiny = FALSE // If true, will add a 'shiny' overlay. + var/icon_state_override = null // Used for special slime appearances like the rainbow slime. + color = "#CACACA" + glow_range = 3 + glow_intensity = 2 + gender = NEUTER + + faction = "slime" // Note that slimes are hostile to other slimes of different color regardless of faction (unless Unified). + maxHealth = 150 + movement_cooldown = 0 + pass_flags = PASSTABLE + makes_dirt = FALSE // Goop + mob_class = MOB_CLASS_SLIME + + response_help = "pets" + + // Atmos stuff. + minbodytemp = T0C-30 + heat_damage_per_tick = 0 + cold_damage_per_tick = 40 + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + unsuitable_atoms_damage = 0 + shock_resist = 0.5 // Slimes are resistant to electricity, and it actually charges them. + taser_kill = FALSE + water_resist = 0 // Slimes are very weak to water. + + melee_damage_lower = 10 + melee_damage_upper = 15 + base_attack_cooldown = 10 // One attack a second. + attack_sound = 'sound/weapons/bite.ogg' + attacktext = list("glomped") + speak_emote = list("chirps") + friendly = list("pokes") + + ai_holder_type = /datum/ai_holder/simple_mob/melee + say_list_type = /datum/say_list/slime + + var/cores = 1 // How many cores you get when placed in a Processor. + var/obj/item/clothing/head/hat = null // The hat the slime may be wearing. + var/slime_color = "grey" // Used for updating the name and for slime color-ism. + var/unity = FALSE // If true, slimes will consider other colors as their own. Other slimes will see this slime as the same color as well. + var/coretype = /obj/item/slime_extract/grey // What core is inside the slime, and what you get from the processor. + var/reagent_injected = null // Some slimes inject reagents on attack. This tells the game what reagent to use. + var/injection_amount = 5 // This determines how much. + var/mood = ":3" // Icon to use to display 'mood', as an overlay. + + can_enter_vent_with = list(/obj/item/clothing/head) + +/datum/say_list/slime + speak = list("Blorp...", "Blop...") + emote_see = list("bounces", "jiggles", "sways") + emote_hear = list("squishes") + +/mob/living/simple_mob/slime/initialize() + verbs += /mob/living/proc/ventcrawl + update_mood() + glow_color = color + handle_light() + update_icon() + return ..() + +/mob/living/simple_mob/slime/Destroy() + if(hat) + drop_hat() + return ..() + +/mob/living/simple_mob/slime/death() + // Make dead slimes stop glowing. + glow_toggle = FALSE + handle_light() + ..() + +/mob/living/simple_mob/slime/revive() + // Make revived slimes resume glowing. + glow_toggle = initial(glow_toggle) + handle_light() + ..() + +/mob/living/simple_mob/slime/update_icon() + ..() // Do the regular stuff first. + + if(stat != DEAD) + // General slime shine. + var/image/I = image(icon, src, "slime light") + I.appearance_flags = RESET_COLOR + add_overlay(I) + + // 'Shiny' overlay, for gemstone-slimes. + if(shiny) + I = image(icon, src, "slime shiny") + I.appearance_flags = RESET_COLOR + add_overlay(I) + + // Mood overlay. + I = image(icon, src, "aslime-[mood]") + I.appearance_flags = RESET_COLOR + add_overlay(I) + + // Hat simulator. + if(hat) + var/hat_state = hat.item_state ? hat.item_state : hat.icon_state + var/image/I = image('icons/mob/head.dmi', src, hat_state) + I.pixel_y = -7 // Slimes are small. + I.appearance_flags = RESET_COLOR + add_overlay(I) + +// Controls the 'mood' overlay. Overrided in subtypes for specific behaviour. +/mob/living/simple_mob/slime/proc/update_mood() + mood = "feral" // This is to avoid another override in the /feral subtype. + +/mob/living/simple_mob/slime/proc/unify() + unity = TRUE + +// Interface override, because slimes are supposed to attack other slimes of different color regardless of faction +// (unless Unified, of course). +/mob/living/simple_mob/slime/IIsAlly(mob/living/L) + . = ..() + if(.) // Need to do an additional check if its another slime. + if(istype(L, /mob/living/simple_mob/slime)) + var/mob/living/simple_mob/slime/S = L + if(S.slime_color != src.slime_color) + if(S.unity || src.unity) + return TRUE + return FALSE + // The other stuff was already checked in parent proc, and the . variable will implicitly return the correct value. + +// Slimes regenerate passively. +/mob/living/simple_mob/slime/handle_special() + adjustOxyLoss(-1) + adjustToxLoss(-1) + adjustFireLoss(-1) + adjustCloneLoss(-1) + adjustBruteLoss(-1) + +// Clicked on by empty hand. +/mob/living/simple_mob/slime/attack_hand(mob/living/L) + if(L.a_intent == I_GRAB && hat) + remove_hat(L) + else + ..() + +// Clicked on while holding an object. +/mob/living/simple_mob/slime/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/clothing/head)) // Handle hat simulator. + give_hat(I, user) + return + + // Otherwise they're probably fighting the slime. + if(prob(25)) + visible_message(span("warning", "\The [user]'s [I] passes right through \the [src]!")) + user.setClickCooldown(user.get_attack_speed(I)) + return + ..() + +// Called when hit with an active slimebaton (or xeno taser). +// Subtypes react differently. +/mob/living/simple_mob/slime/proc/slimebatoned(mob/living/user, amount) + return + +// Hat simulator +/mob/living/simple_mob/slime/proc/give_hat(var/obj/item/clothing/head/new_hat, var/mob/living/user) + if(!istype(new_hat)) + to_chat(user, span("warning", "\The [new_hat] isn't a hat.")) + return + if(hat) + to_chat(user, span("warning", "\The [src] is already wearing \a [hat].")) + return + else + user.drop_item(new_hat) + hat = new_hat + new_hat.forceMove(src) + to_chat(user, span("notice", "You place \a [new_hat] on \the [src]. How adorable!")) + update_icon() + return + +/mob/living/simple_mob/slime/proc/remove_hat(var/mob/living/user) + if(!hat) + to_chat(user, "\The [src] doesn't have a hat to remove.") + else + hat.forceMove(get_turf(src)) + user.put_in_hands(hat) + to_chat(user, "You take away \the [src]'s [hat.name]. How mean.") + hat = null + update_icon() + +/mob/living/simple_mob/slime/proc/drop_hat() + if(!hat) + return + hat.forceMove(get_turf(src)) + hat = null + update_icon() + +/mob/living/simple_mob/slime/speech_bubble_appearance() + return "slime" + +/mob/living/simple_mob/slime/proc/squish() + playsound(src.loc, 'sound/effects/slime_squish.ogg', 50, 0) + visible_message("\The [src] squishes!") \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm new file mode 100644 index 0000000000..2cda9bfc7e --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm @@ -0,0 +1,76 @@ +// Code for slimes attacking other things. + +// Slime attacks change based on intent. +/mob/living/simple_mob/slime/xenobio/apply_attack(mob/living/L, damage_to_do) + if(istype(L)) + switch(a_intent) + if(I_HELP) // This shouldn't happen but just in case. + return FALSE + + if(I_DISARM) + var/stun_power = between(0, power_charge + rand(0, 3), 10) + + if(ishuman(L)) + var/mob/living/carbon/human/H = L + stun_power *= max(H.species.siemens_coefficient, 0) + + if(prob(stun_power * 10)) // Try an electric shock. + power_charge = max(0, power_charge - 3) + L.visible_message( + span("danger", "\The [src] has shocked \the [L]!"), + span("danger", "\The [src] has shocked you!") + ) + playsound(src, 'sound/weapons/Egloves.ogg', 75, 1) + L.Weaken(4) + L.Stun(4) + do_attack_animation(L) + if(L.buckled) + L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you. + L.stuttering = max(L.stuttering, stun_power) + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, L) + s.start() + + if(prob(stun_power * 10) && stun_power >= 8) + L.adjustFireLoss(power_charge * rand(1, 2)) + return FALSE + + else if(prob(20)) // Try to do a regular disarm attack. + L.visible_message( + span("danger", "\The [src] has pounced at \the [L]!"), + span("danger", "\The [src] has pounced at you!") + ) + playsound(src, 'sound/weapons/thudswoosh.ogg', 75, 1) + L.Weaken(2) + do_attack_animation(L) + if(L.buckled) + L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you. + return FALSE + + else // Failed to do anything this time. + L.visible_message( + span("warning", "\The [src] has tried to pounce at \the [L]!"), + span("warning", "\The [src] has tried to pounce at you!") + ) + playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) + do_attack_animation(L) + return FALSE + + if(I_GRAB) + start_consuming(L) + return FALSE + + if(I_HURT) + return ..() // Regular stuff. + else + return ..() // Do the regular stuff if we're hitting a window/mech/etc. + +/mob/living/simple_mob/slime/xenobio/apply_melee_effects(mob/living/L) + if(istype(L) && a_intent == I_HURT) + // Pump them full of toxins, if able. + if(L.reagents && L.can_inject() && reagent_injected) + L.reagents.add_reagent(reagent_injected, injection_amount) + + // Feed off of their flesh, if able. + consume(L, 5) diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm new file mode 100644 index 0000000000..b997dde35d --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm @@ -0,0 +1,169 @@ +// Handles hunger, starvation, growth, and eatting humans. + +// Might be best to make this a /mob/living proc and override. +/mob/living/simple_mob/slime/xenobio/proc/adjust_nutrition(input) + nutrition = between(0, nutrition + input, get_max_nutrition()) + + if(input > 0) + // Gain around one level per 50 nutrition. + if(prob(input * 2)) + power_charge = min(power_charge++, 10) + if(power_charge == 10) + adjustToxLoss(-10) + + // Heal 1 point of damage per 5 nutrition coming in. + adjustBruteLoss(-input * 0.2) + adjustFireLoss(-input * 0.2) + adjustToxLoss(-input * 0.2) + adjustOxyLoss(-input * 0.2) + adjustCloneLoss(-input * 0.2) + + +/mob/living/simple_mob/slime/xenobio/proc/get_max_nutrition() // Can't go above it + return is_adult ? 1200 : 1000 + +/mob/living/simple_mob/slime/xenobio/proc/get_grow_nutrition() // Above it we grow, below it we can eat + return is_adult ? 1000 : 800 + +/mob/living/simple_mob/slime/xenobio/proc/get_hunger_nutrition() // Below it we will always eat + return is_adult ? 600 : 500 + +/mob/living/simple_mob/slime/xenobio/proc/get_starve_nutrition() // Below it we will eat before everything else + return is_adult ? 300 : 200 + +// Called by Life(). +/mob/living/simple_mob/slime/xenobio/proc/handle_nutrition() + if(harmless) + return + + if(prob(15)) + adjust_nutrition(is_adult ? -2 : -1) // Adult slimes get hungry faster. + + if(nutrition <= get_starve_nutrition()) + handle_starvation() + + else if(nutrition >= get_grow_nutrition() && amount_grown < 10) + adjust_nutrition(-20) + amount_grown = between(0, amount_grown + 1, 10) + +// Called if above proc happens while below a nutrition threshold. +/mob/living/simple_mob/slime/xenobio/proc/handle_starvation() + if(nutrition < get_starve_nutrition() && !client) // if a slime is starving, it starts losing its friends + if(friends.len && prob(1)) + var/mob/nofriend = pick(friends) + if(nofriend) + friends -= nofriend + say("[nofriend]... food now...") + + if(nutrition <= 0) + adjustToxLoss(rand(1,3)) + if(client && prob(5)) + to_chat(src, span("danger", "You are starving!")) + + +/mob/living/simple_mob/slime/xenobio/proc/handle_consumption() + if(victim && !stat) + if(istype(victim) && consume(victim, 20)) + if(prob(25)) + to_chat(src, span("notice", "You continue absorbing \the [victim].")) + + else + var/list/feedback = list( + "This subject is incompatable", + "This subject does not have a life energy", + "This subject is empty", + "I am not satisfied", + "I can not feed from this subject", + "I do not feel nourished", + "This subject is not food" + ) + to_chat(src, span("warning", "[pick(feedback)]...")) + stop_consumption() + + if(victim) + victim.updatehealth() + + else + stop_consumption() + +/mob/living/simple_mob/slime/xenobio/proc/start_consuming(mob/living/L) + if(!can_consume(L)) + return + if(!Adjacent(L)) + return + + step_towards(src, L) // Get on top of them to feed. + if(loc != L.loc) + return + + if(L.buckle_mob(src, forced = TRUE)) + victim = L + update_icon() + set_AI_busy(TRUE) // Don't want the AI to interfere with eatting. + victim.visible_message( + span("danger", "\The [src] latches onto \the [victim]!"), + span("critical", "\The [src] latches onto you!") + ) + +/mob/living/simple_mob/slime/xenobio/proc/stop_consumption(mob/living/L) + if(!victim) + return + victim.unbuckle_mob() + victim.visible_message( + span("notice", "\The [src] slides off of [victim]!"), + span("notice", "\The [src] slides off of you!") + ) + victim = null + update_icon() + set_AI_busy(FALSE) // Resume normal operations. + +/mob/living/simple_mob/slime/xenobio/proc/can_consume(mob/living/L) + if(!L || !istype(L)) + to_chat(src, "This subject is incomparable...") + return FALSE + if(harmless) + to_chat(src, "I am pacified... I cannot eat...") + return FALSE + if(L.mob_class & MOB_CLASS_SLIME) + to_chat(src, "I cannot feed on other slimes...") + return FALSE + if(L.isSynthetic()) + to_chat(src, "This subject is not biological...") + return FALSE + if(L.getarmor(null, "bio") >= 75) + to_chat(src, "I cannot reach this subject's biological matter...") + return FALSE + if(!Adjacent(L)) + to_chat(src, "This subject is too far away...") + return FALSE + if(L.getCloneLoss() >= L.getMaxHealth() * 1.5) + to_chat(src, "This subject does not have an edible life energy...") + return FALSE + if(L.has_buckled_mobs()) + for(var/A in L.buckled_mobs) + if(istype(A, /mob/living/simple_mob/slime/xenobio)) + if(A != src) + to_chat(src, "\The [A] is already feeding on this subject...") + return FALSE + return TRUE + +// This does the actual damage, as well as give nutrition and heals. +// Assuming no bio armor, calling consume(10) will result in; +// 6 clone damage to victim +// 4 tox damage to victim. +// 25 nutrition for the slime. +// 2 points of damage healed on the slime (as a result of the nutrition). +// 50% of giving +1 charge to the slime (same as above). +/mob/living/simple_mob/slime/xenobio/proc/consume(mob/living/victim, amount) + if(can_consume(victim)) + var/armor_modifier = abs((victim.getarmor(null, "bio") / 100) - 1) + var/damage_done = amount * armor_modifier + if(damage_done > 0) + victim.adjustCloneLoss(damage_done * 0.6) + victim.adjustToxLoss(damage_done * 0.4) + adjust_nutrition(damage_done * 5) + Beam(victim, icon_state = "slime_consume", time = 8) + to_chat(src, span("notice", "You absorb some biomaterial from \the [victim].")) + to_chat(victim, span("danger", "\The [src] consumes some of your flesh!")) + return TRUE + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm new file mode 100644 index 0000000000..084f4633b2 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm @@ -0,0 +1,54 @@ +// Contains code for slimes getting attacked, beat, touched, etc, and reacting to that. + +// Clicked on by empty hand. +// Handles trying to wrestle a slime off of someone being eatten. +/mob/living/simple_mob/slime/xenobio/attack_hand(mob/living/L) + if(victim) // Are we eating someone? + var/fail_odds = 30 + if(victim == L) // Harder to get the slime off if it's you that is being eatten. + fail_odds = 60 + + if(prob(fail_odds)) + visible_message(span("warning", "\The [L] attempts to wrestle \the [name] off!")) + playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + + else + visible_message(span("warning", "\The [L] manages to wrestle \the [name] off!")) + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + + if(prob(40)) + adjust_discipline(1) // Do this here so that it will be justified discipline. + stop_consumption() + step_away(src, L) + + else + ..() + +// Handles the actual harming by a melee weapon. +/mob/living/simple_mob/slime/xenobio/hit_with_weapon(obj/item/I, mob/living/user, effective_force, hit_zone) + ..() // Apply damage and etc. + if(!stat && effective_force > 0) + if(!is_justified_to_discipline()) // Wow, buddy, why am I getting attacked?? + adjust_discipline(1) // This builds resentment due to being unjustified. + + if(user in friends) // Friend attacking us for no reason. + if(prob(25)) + friends -= user + say("[user]... not friend...") + + else // We're actually being bad. + var/prob_to_back_down = round(effective_force) + if(is_adult) + prob_to_back_down /= 2 + if(prob(prob_to_back_down)) + adjust_discipline(2) // Justified. + +// Shocked grilles don't hurt slimes, and in fact give them charge. +/mob/living/simple_mob/slime/xenobio/electrocute_act(shock_damage, obj/source, siemens_coeff = 1.0, def_zone = null) + power_charge = between(0, power_charge + round(shock_damage / 10), 10) + to_chat(src, span("notice", "\The [source] shocks you, and it charges you.")) + +// Getting slimebatoned/xenotased. +/mob/living/simple_mob/slime/xenobio/slimebatoned(mob/living/user, amount) + Weaken(amount) + adjust_discipline(round(amount/2)) diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm new file mode 100644 index 0000000000..5360dab7b3 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm @@ -0,0 +1,23 @@ +// Handles the subjugation of slimes by force. +// Mostly a way for things to talk to the AI indirectly. + +/mob/living/simple_mob/slime/xenobio/proc/adjust_discipline(amount, silent) + if(amount > 0) + to_chat(src, span("warning", "You've been disciplined!")) + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.adjust_discipline(amount, silent) + + +/mob/living/simple_mob/slime/xenobio/proc/is_justified_to_discipline() + if(victim) // Punish if eating someone that isn't a monkey. + if(ishuman(victim)) + var/mob/living/carbon/human/H = victim + if(istype(H.species, /datum/species/monkey)) + return FALSE + return TRUE + + else if(has_AI()) // Now for thoughtcrimes. + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + return AI.is_justified_to_discipline() // Will return true if targeting a non-monkey. + return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm new file mode 100644 index 0000000000..03cd14eb3a --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm @@ -0,0 +1,784 @@ +// Here are where all the other colors of slime live. +// They will generally fight each other if not Unified, meaning the xenobiologist has to seperate them. + +// Tier 1. + +/mob/living/simple_mob/slime/xenobio/purple + desc = "This slime is rather toxic to handle, as it is poisonous." + color = "#CC23FF" + slime_color = "purple" + coretype = /obj/item/slime_extract/purple + reagent_injected = "toxin" + + description_info = "This slime spreads a toxin when it attacks. A biosuit or other thick armor can protect from the toxic attack." + player_msg = "You inject a harmful toxin when attacking." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/orange + desc = "This slime is known to be flammable and can ignite enemies." + color = "#FFA723" + slime_color = "orange" + coretype = /obj/item/slime_extract/orange + melee_damage_lower = 5 + melee_damage_upper = 5 + heat_resist = 1 + + description_info = "The slime is immune to burning attacks, and attacks from this slime will burn you, and can ignite you. \ + A firesuit can protect from the burning attacks of this slime." + player_msg = "You inflict burning attacks, which causes additional damage, makes the target more flammable, and has a chance to ignite them.
\ + You are also immune to burning attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/orange/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.inflict_heat_damage(is_adult ? 10 : 5) + to_chat(src, span("span", "You burn \the [L].")) + to_chat(L, span("danger", "You've been burned by \the [src]!")) + L.adjust_fire_stacks(1) + if(prob(12)) + L.IgniteMob() + +/mob/living/simple_mob/slime/xenobio/blue + desc = "This slime produces 'cryotoxin' and uses it against their foes. Very deadly to other slimes." + color = "#19FFFF" + slime_color = "blue" + coretype = /obj/item/slime_extract/blue + reagent_injected = "cryotoxin" + cold_resist = 0.50 // Not as strong as dark blue, which has immunity. + + description_info = "The slime is resistant to the cold, and attacks from this slime can inject cryotoxin into you. \ + A biosuit or other thick armor can protect from the injection." + player_msg = "You inject cryotoxin on attack, which causes them to get very cold, slowing them down and harming them over time.
\ + You are also resistant to cold attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio + ) + + +/mob/living/simple_mob/slime/xenobio/metal + desc = "This slime is a lot more resilient than the others, due to having a metamorphic metallic and sloped surface." + color = "#5F5F5F" + slime_color = "metal" + shiny = TRUE + coretype = /obj/item/slime_extract/metal + + description_info = "This slime is a lot more durable and tough to damage than the others. It also seems to provoke others to attack it over others." + player_msg = "You are more resilient and armored than more slimes. Your attacks will also encourage less intelligent enemies to focus on you." + + maxHealth = 250 + maxHealth_adult = 350 + + // The sloped armor. + // It's resistant to most weapons (but a spraybottle still kills it rather fast). + armor = list( + "melee" = 25, + "bullet" = 25, + "laser" = 25, + "energy" = 50, + "bomb" = 80, + "bio" = 100, + "rad" = 100 + ) + + armor_soak = list( + "melee" = 5, + "bullet" = 5, + "laser" = 5, + "energy" = 0, + "bomb" = 0, + "bio" = 0, + "rad" = 0 + ) + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio + ) + +/mob/living/simple_mob/slime/xenobio/metal/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.taunt(src, TRUE) // We're the party tank now. + +// Tier 2 + +/mob/living/simple_mob/slime/xenobio/yellow + desc = "This slime is very conductive, and is known to use electricity as a means of defense moreso than usual for slimes." + color = "#FFF423" + slime_color = "yellow" + coretype = /obj/item/slime_extract/yellow + melee_damage_lower = 5 + melee_damage_upper = 5 + shock_resist = 1 + + projectiletype = /obj/item/projectile/beam/lightning/slime + projectilesound = 'sound/effects/lightningbolt.ogg' + glow_toggle = TRUE + + description_info = "In addition to being immune to electrical shocks, this slime will fire ranged lightning attacks at \ + enemies if they are at range, inflict shocks upon entities they attack, and generate electricity for their stun \ + attack faster than usual. Insulative or reflective armor can protect from these attacks." + player_msg = "You have a ranged electric attack. You also shock enemies you attack, and your electric stun attack charges passively.
\ + You are also immune to shocking attacks." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/orange + ) + +/mob/living/simple_mob/slime/xenobio/yellow/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + L.inflict_shock_damage(is_adult ? 10 : 5) + to_chat(src, span("span", "You shock \the [L].")) + to_chat(L, span("danger", "You've been shocked by \the [src]!")) + +/mob/living/simple_mob/slime/xenobio/yellow/handle_special() + if(stat == CONSCIOUS) + if(prob(25)) + power_charge = between(0, power_charge + 1, 10) + ..() + +/obj/item/projectile/beam/lightning/slime + power = 10 + fire_sound = 'sound/effects/lightningbolt.ogg' + + +/mob/living/simple_mob/slime/xenobio/dark_purple + desc = "This slime produces ever-coveted phoron. Risky to handle but very much worth it." + color = "#660088" + slime_color = "dark purple" + coretype = /obj/item/slime_extract/dark_purple + reagent_injected = "phoron" + + description_info = "This slime applies phoron to enemies it attacks. A biosuit or other thick armor can protect from the toxic attack. \ + If hit with a burning attack, it will erupt in flames." + player_msg = "You inject phoron into enemies you attack.
\ + You will erupt into flames if harmed by fire!" + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/orange, + /mob/living/simple_mob/slime/xenobio/ruby, + /mob/living/simple_mob/slime/xenobio/ruby + ) + +/mob/living/simple_mob/slime/xenobio/dark_purple/proc/ignite() + visible_message(span("critical", "\The [src] erupts in an inferno!")) + for(var/turf/simulated/target_turf in view(2, src)) + target_turf.assume_gas("phoron", 30, 1500+T0C) + spawn(0) + target_turf.hotspot_expose(1500+T0C, 400) + qdel(src) + +/mob/living/simple_mob/slime/xenobio/dark_purple/ex_act(severity) + log_and_message_admins("[src] ignited due to a chain reaction with an explosion.") + ignite() + +/mob/living/simple_mob/slime/xenobio/dark_purple/fire_act(datum/gas_mixture/air, temperature, volume) + log_and_message_admins("[src] ignited due to exposure to fire.") + ignite() + +/mob/living/simple_mob/slime/xenobio/dark_purple/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") + ignite() + else + ..() + +/mob/living/simple_mob/slime/xenobio/dark_purple/attackby(var/obj/item/weapon/W, var/mob/user) + if(istype(W) && W.force && W.damtype == BURN) + log_and_message_admins("[src] ignited due to being hit with a burning weapon ([W]) by [key_name(user)].") + ignite() + else + ..() + + + +/mob/living/simple_mob/slime/xenobio/dark_blue + desc = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It tends to kill other slimes rather quickly." + color = "#2398FF" + glow_toggle = TRUE + slime_color = "dark blue" + coretype = /obj/item/slime_extract/dark_blue + melee_damage_lower = 5 + melee_damage_upper = 5 + cold_resist = 1 + + description_info = "This slime is immune to the cold, however water will still kill it. Its presense, as well as its attacks, will \ + also cause you additional harm from the cold. A winter coat or other cold-resistant clothing can protect from this." + player_msg = "You are immune to the cold, inflict additional cold damage on attack, and cause nearby entities to suffer from coldness." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/cerulean, + /mob/living/simple_mob/slime/xenobio/cerulean + ) + + minbodytemp = 0 + cold_damage_per_tick = 0 + +/mob/living/simple_mob/slime/xenobio/dark_blue/handle_special() + if(stat != DEAD) + cold_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/dark_blue/proc/cold_aura() + for(var/mob/living/L in view(2, src)) + if(L == src) + continue + chill(L) + + var/turf/T = get_turf(src) + var/datum/gas_mixture/env = T.return_air() + if(env) + env.add_thermal_energy(-10 * 1000) + +/mob/living/simple_mob/slime/xenobio/dark_blue/apply_melee_effects(atom/A) + ..() + if(isliving(A)) + var/mob/living/L = A + chill(L) + to_chat(src, span("span", "You chill \the [L].")) + to_chat(L, span("danger", "You've been chilled by \the [src]!")) + + +/mob/living/simple_mob/slime/xenobio/dark_blue/proc/chill(mob/living/L) + L.inflict_cold_damage(is_adult ? 10 : 5) + if(L.get_cold_protection() < 1 && L.has_AI()) // Harmful auras will make the AI react to its bearer. + L.ai_holder.react_to_attack(src) + + +/mob/living/simple_mob/slime/xenobio/silver + desc = "This slime is shiny, and can deflect lasers or other energy weapons directed at it." + color = "#AAAAAA" + slime_color = "silver" + coretype = /obj/item/slime_extract/silver + shiny = TRUE + + description_info = "Tasers, including the slime version, are ineffective against this slime. The slimebation still works." + player_msg = "You automatically reflect lasers, beams, and tasers that hit you." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/amber, + /mob/living/simple_mob/slime/xenobio/amber + ) + +/mob/living/simple_mob/slime/xenobio/silver/bullet_act(var/obj/item/projectile/P, var/def_zone) + if(istype(P,/obj/item/projectile/beam) || istype(P, /obj/item/projectile/energy)) + visible_message(span("danger", "\The [src] reflects \the [P]!")) + + // Find a turf near or on the original location to bounce to + var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(src) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, src) + P.reflected = TRUE + return PROJECTILE_CONTINUE // complete projectile permutation + else + ..() + + +// Tier 3 + +/mob/living/simple_mob/slime/xenobio/bluespace + desc = "Trapping this slime in a cell is generally futile, as it can teleport at will." + color = null + slime_color = "bluespace" + icon_state_override = "bluespace" + coretype = /obj/item/slime_extract/bluespace + + description_info = "This slime will teleport to attack something if it is within a range of seven tiles. The teleport has a cooldown of five seconds." + player_msg = "You can teleport at will to a specific tile by clicking on it at range. This has a five second cooldown." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/bluespace, + /mob/living/simple_mob/slime/xenobio/yellow, + /mob/living/simple_mob/slime/xenobio/yellow + ) + + special_attack_min_range = 3 + special_attack_max_range = 7 + special_attack_cooldown = 5 SECONDS + +/mob/living/simple_mob/slime/xenobio/bluespace/do_special_attack(atom/A) + // Teleport attack. + if(!A) + to_chat(src, span("warning", "There's nothing to teleport to.")) + return FALSE + + var/list/nearby_things = range(1, A) + var/list/valid_turfs = list() + + // All this work to just go to a non-dense tile. + for(var/turf/potential_turf in nearby_things) + var/valid_turf = TRUE + if(potential_turf.density) + continue + for(var/atom/movable/AM in potential_turf) + if(AM.density) + valid_turf = FALSE + if(valid_turf) + valid_turfs.Add(potential_turf) + + var/turf/T = get_turf(src) + var/turf/target_turf = pick(valid_turfs) + + if(!target_turf) + to_chat(src, span("warning", "There wasn't an unoccupied spot to teleport to.")) + return FALSE + + var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread + s1.set_up(5, 1, T) + var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread + s2.set_up(5, 1, target_turf) + + + T.visible_message(span("notice", "\The [src] vanishes!")) + s1.start() + + forceMove(target_turf) + playsound(target_turf, 'sound/effects/phasein.ogg', 50, 1) + to_chat(src, span("notice", "You teleport to \the [target_turf].")) + + target_turf.visible_message(span("warning", "\The [src] appears!")) + s2.start() + + if(Adjacent(A)) + attack_target(A) + + +/mob/living/simple_mob/slime/xenobio/ruby + desc = "This slime has great physical strength." + color = "#FF3333" + slime_color = "ruby" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/ruby + + description_info = "This slime is unnaturally stronger, allowing it to hit much harder, take less damage, and be stunned for less time. \ + Their glomp attacks also send the victim flying." + player_msg = "Your attacks knock back the target a fair distance.
\ + You also hit harder, take less damage, and stuns affect you for less time." + + melee_attack_delay = 1 SECOND + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/dark_purple, + /mob/living/simple_mob/slime/xenobio/ruby, + /mob/living/simple_mob/slime/xenobio/ruby + ) + +/mob/living/simple_mob/slime/xenobio/ruby/initialize() + add_modifier(/datum/modifier/slime_strength, null, src) // Slime is always swole. + return ..() + +/mob/living/simple_mob/slime/xenobio/ruby/apply_melee_effects(atom/A) + ..() + + if(isliving(A) && a_intent == I_HURT) + var/mob/living/L = A + if(L.mob_size <= MOB_MEDIUM) + visible_message(span("danger", "\The [src] sends \the [L] flying with the impact!")) + playsound(src, "punch", 50, 1) + L.Weaken(1) + var/throwdir = get_dir(src, L) + L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src) + else + to_chat(L, span("warning", "\The [src] hits you with incredible force, but you remain in place.")) + + +/mob/living/simple_mob/slime/xenobio/amber + desc = "This slime seems to be an expert in the culinary arts, as they create their own food to share with others. \ + They would probably be very important to other slimes, if the other colors didn't try to kill them." + color = "#FFBB00" + slime_color = "amber" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/amber + + description_info = "This slime feeds nearby entities passively while it is alive. This can cause uncontrollable \ + slime growth and reproduction if not kept in check. The amber slime cannot feed itself, but can be fed by other amber slimes." + player_msg = "You passively provide nutrition to nearby entities." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/silver, + /mob/living/simple_mob/slime/xenobio/amber, + /mob/living/simple_mob/slime/xenobio/amber + ) + +/mob/living/simple_mob/slime/xenobio/amber/handle_special() + if(stat != DEAD) + feed_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/amber/proc/feed_aura() + for(var/mob/living/L in view(2, src)) + if(L == src) // Don't feed themselves, or it is impossible to stop infinite slimes without killing all of the ambers. + continue + if(istype(L, /mob/living/simple_mob/slime/xenobio)) + var/mob/living/simple_mob/slime/xenobio/X = L + X.adjust_nutrition(rand(15, 25)) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.isSynthetic()) + continue + H.nutrition = between(0, H.nutrition + rand(15, 25), 800) + +/mob/living/simple_mob/slime/xenobio/cerulean + desc = "This slime is generally superior in a wide range of attributes, compared to the common slime. The jack of all trades, but master of none." + color = "#4F7EAA" + slime_color = "cerulean" + coretype = /obj/item/slime_extract/cerulean + + // Less than the specialized slimes, but higher than the rest. + maxHealth = 200 + maxHealth_adult = 250 + + melee_damage_lower = 10 + melee_damage_upper = 30 + + movement_cooldown = 0 // This actually isn't any faster due to AI limitations that hopefully the timer subsystem can fix in the future. + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/dark_blue, + /mob/living/simple_mob/slime/xenobio/cerulean, + /mob/living/simple_mob/slime/xenobio/cerulean + ) + +// Tier 4 + +/mob/living/simple_mob/slime/xenobio/red + desc = "This slime is full of energy, and very aggressive. 'The red ones go faster.' seems to apply here." + color = "#FF3333" + slime_color = "red" + coretype = /obj/item/slime_extract/red + movement_cooldown = 0 // See above. + + description_info = "This slime is faster than the others. Attempting to discipline this slime will always cause it to go rabid and berserk." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/orange + ) + + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/red // Will enrage if disciplined. + + +/mob/living/simple_mob/slime/xenobio/green + desc = "This slime is radioactive." + color = "#14FF20" + slime_color = "green" + coretype = /obj/item/slime_extract/green + glow_toggle = TRUE + reagent_injected = "radium" + var/rads = 25 + + description_info = "This slime will irradiate anything nearby passively, and will inject radium on attack. \ + A radsuit or other thick and radiation-hardened armor can protect from this. It will only radiate while alive." + player_msg = "You passively irradiate your surroundings.
\ + You also inject radium on attack." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/purple, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/emerald, + /mob/living/simple_mob/slime/xenobio/emerald + ) + +/mob/living/simple_mob/slime/xenobio/green/handle_special() + if(stat != DEAD) + irradiate() + ..() + +/mob/living/simple_mob/slime/xenobio/green/proc/irradiate() + radiation_repository.radiate(src, rads) + + + +/mob/living/simple_mob/slime/xenobio/pink + desc = "This slime has regenerative properties." + color = "#FF0080" + slime_color = "pink" + coretype = /obj/item/slime_extract/pink + glow_toggle = TRUE + + description_info = "This slime will passively heal nearby entities within two tiles, including itself. It will only do this while alive." + player_msg = "You passively heal yourself and nearby allies." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/pink + ) + +/mob/living/simple_mob/slime/xenobio/pink/handle_special() + if(stat != DEAD) + heal_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/pink/proc/heal_aura() + for(var/mob/living/L in view(src, 2)) + if(L.stat == DEAD || !IIsAlly(L)) + continue + L.add_modifier(/datum/modifier/aura/slime_heal, null, src) + +/datum/modifier/aura/slime_heal + name = "slime mending" + desc = "You feel somewhat gooy." + mob_overlay_state = "pink_sparkles" + stacks = MODIFIER_STACK_FORBID + aura_max_distance = 2 + + on_created_text = "Twinkling spores of goo surround you. It makes you feel healthier." + on_expired_text = "The spores of goo have faded, although you feel much healthier than before." + +/datum/modifier/aura/slime_heal/tick() + if(holder.stat == DEAD) + expire() + + if(ishuman(holder)) // Robolimbs need this code sadly. + var/mob/living/carbon/human/H = holder + for(var/obj/item/organ/external/E in H.organs) + var/obj/item/organ/external/O = E + O.heal_damage(2, 2, 0, 1) + else + holder.adjustBruteLoss(-2) + holder.adjustFireLoss(-2) + + holder.adjustToxLoss(-2) + holder.adjustOxyLoss(-2) + holder.adjustCloneLoss(-1) + + +/mob/living/simple_mob/slime/xenobio/gold + desc = "This slime absorbs energy, and cannot be stunned by normal means." + color = "#EEAA00" + shiny = TRUE + slime_color = "gold" + coretype = /obj/item/slime_extract/gold + description_info = "This slime is immune to the slimebaton and taser, and will actually charge the slime, however it will still discipline the slime." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/sapphire + ) + +/mob/living/simple_mob/slime/xenobio/gold/slimebatoned(mob/living/user, amount) + power_charge = between(0, power_charge + amount, 10) + +/mob/living/simple_mob/slime/xenobio/gold/get_description_interaction() // So it doesn't say to use a baton on them. + return list() + + +// Tier 5 + +/mob/living/simple_mob/slime/xenobio/oil + desc = "This slime is explosive and volatile. Smoking near it is probably a bad idea." + color = "#333333" + slime_color = "oil" + shiny = TRUE + coretype = /obj/item/slime_extract/oil + + description_info = "If this slime suffers damage from a fire or heat based source, or if it is caught inside \ + an explosion, it will explode. Oil slimes will also suicide-bomb themselves when fighting something that is not a monkey or slime." + player_msg = "You will explode if struck by a burning attack, or if you hit an enemy with a melee attack that is not a monkey or another slime." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/oil, + /mob/living/simple_mob/slime/xenobio/red, + /mob/living/simple_mob/slime/xenobio/red + ) + +/mob/living/simple_mob/slime/xenobio/oil/proc/explode() + if(stat != DEAD) + explosion(src.loc, 0, 2, 4) // A bit weaker since the suicide charger tended to gib the poor sod being targeted. + if(src) // Delete ourselves if the explosion didn't do it. + qdel(src) + +/mob/living/simple_mob/slime/xenobio/oil/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(ishuman(L)) + var/mob/living/carbon/human/H = A + if(istype(H.species, /datum/species/monkey)) + return ..()// Don't blow up when just eatting monkeys. + + else if(isslime(L)) + return ..() + + // Otherwise blow ourselves up. + say(pick("Sacrifice...!", "Sssss...", "Boom...!")) + set_AI_busy(TRUE) + sleep(2 SECONDS) + log_and_message_admins("[src] has suicide-bombed themselves while trying to kill \the [L].") + explode() + + return ..() + +/mob/living/simple_mob/slime/xenobio/oil/ex_act(severity) + log_and_message_admins("[src] exploded due to a chain reaction with another explosion.") + explode() + +/mob/living/simple_mob/slime/xenobio/oil/fire_act(datum/gas_mixture/air, temperature, volume) + log_and_message_admins("[src] exploded due to exposure to fire.") + explode() + +/mob/living/simple_mob/slime/xenobio/oil/bullet_act(obj/item/projectile/P, def_zone) + if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") + explode() + else + ..() + +/mob/living/simple_mob/slime/xenobio/oil/attackby(obj/item/weapon/W, mob/living/user) + if(istype(W) && W.force && W.damtype == BURN) + log_and_message_admins("[src] exploded due to being hit with a burning weapon ([W]) by [key_name(user)].") + explode() + else + ..() + + +/mob/living/simple_mob/slime/xenobio/sapphire + desc = "This slime seems a bit brighter than the rest, both figuratively and literally." + color = "#2398FF" + slime_color = "sapphire" + shiny = TRUE + glow_toggle = TRUE + coretype = /obj/item/slime_extract/sapphire + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/sapphire + + description_info = "This slime uses more robust tactics when fighting and won't hold back, so it is dangerous to be alone \ + with one if hostile, and especially dangerous if they outnumber you." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/sapphire, + /mob/living/simple_mob/slime/xenobio/gold, + /mob/living/simple_mob/slime/xenobio/gold + ) + + +/mob/living/simple_mob/slime/xenobio/emerald + desc = "This slime is faster than usual, even more so than the red slimes." + color = "#22FF22" + shiny = TRUE + glow_toggle = TRUE + slime_color = "emerald" + coretype = /obj/item/slime_extract/emerald + + description_info = "This slime will make everything around it, and itself, faster for a few seconds, if close by." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/green, + /mob/living/simple_mob/slime/xenobio/emerald, + /mob/living/simple_mob/slime/xenobio/emerald + ) + +/mob/living/simple_mob/slime/xenobio/emerald/handle_special() + if(stat != DEAD) + zoom_aura() + ..() + +/mob/living/simple_mob/slime/xenobio/emerald/proc/zoom_aura() + for(var/mob/living/L in view(src, 2)) + if(L.stat == DEAD || !IIsAlly(L)) + continue + L.add_modifier(/datum/modifier/technomancer/haste, 5 SECONDS, src) + + +/mob/living/simple_mob/slime/xenobio/light_pink + desc = "This slime seems a lot more peaceful than the others." + color = "#FF8888" + slime_color = "light pink" + coretype = /obj/item/slime_extract/light_pink + + description_info = "This slime is effectively always disciplined initially." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio/pink, + /mob/living/simple_mob/slime/xenobio/light_pink, + /mob/living/simple_mob/slime/xenobio/light_pink + ) + + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/light_pink + +// Special +/mob/living/simple_mob/slime/xenobio/rainbow + desc = "This slime changes colors constantly." + color = null // Uses a special icon_state. + slime_color = "rainbow" + coretype = /obj/item/slime_extract/rainbow + icon_state_override = "rainbow" + unity = TRUE + + description_info = "This slime is considered to be the same color as all other slime colors at the same time for the purposes of \ + other slimes being friendly to them, and therefore will never be harmed by another slime. \ + Attacking this slime will provoke the wrath of all slimes within range." + player_msg = "You are considered to be the same color as every slime, \ + meaning that you are considered an ally to all slimes." + + slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow, + /mob/living/simple_mob/slime/xenobio/rainbow + ) + +/mob/living/simple_mob/slime/xenobio/rainbow/initialize() + unify() + return ..() + +// The RD's pet slime. +/mob/living/simple_mob/slime/xenobio/rainbow/kendrick + name = "Kendrick" + desc = "The Research Director's pet slime. It shifts colors constantly." + rainbow_core_candidate = FALSE + // Doing pacify() in initialize() won't actually pacify the AI due to the ai_holder not existing due to parent initialize() not being called yet. + // Instead lets just give them an ai_holder that does that for us. + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime/passive + +/mob/living/simple_mob/slime/xenobio/rainbow/kendrick/initialize() + pacify() // So the physical mob also gets made harmless. + return ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm new file mode 100644 index 0000000000..f7f594f139 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm @@ -0,0 +1,284 @@ +// These slimes have the mechanics xenobiologists care about, such as reproduction, mutating into new colors, and being able to submit through fear. + +/mob/living/simple_mob/slime/xenobio + desc = "The most basic of slimes. The grey slime has no remarkable qualities, however it remains one of the most useful colors for scientists." + layer = MOB_LAYER + 1 // Need them on top of other mobs or it looks weird when consuming something. + ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime // This should never be changed for xenobio slimes. + var/is_adult = FALSE // Slimes turn into adults when fed enough. Adult slimes are somewhat stronger, and can reproduce if fed enough. + var/maxHealth_adult = 200 + var/power_charge = 0 // Disarm attacks can shock someone if high/lucky enough. + var/mob/living/victim = null // the person the slime is currently feeding on + var/rainbow_core_candidate = TRUE // If false, rainbow cores cannot make this type randomly. + var/mutation_chance = 25 // Odds of spawning as a new color when reproducing. Can be modified by certain xenobio products. Carried across generations of slimes. + var/list/slime_mutation = list( + /mob/living/simple_mob/slime/xenobio/orange, + /mob/living/simple_mob/slime/xenobio/metal, + /mob/living/simple_mob/slime/xenobio/blue, + /mob/living/simple_mob/slime/xenobio/purple + ) + var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces + var/number = 0 // This is used to make the slime semi-unique for indentification. + var/harmless = FALSE // Set to true when pacified. Makes the slime harmless, not get hungry, and not be able to grow/reproduce. + +/mob/living/simple_mob/slime/xenobio/initialize() + ASSERT(ispath(ai_holder_type, /datum/ai_holder/simple_mob/xenobio_slime)) + number = rand(1, 1000) + update_name() + return ..() + +/mob/living/simple_mob/slime/xenobio/Destroy() + if(victim) + stop_consumption() // Unbuckle us from our victim. + return ..() + +/mob/living/simple_mob/slime/xenobio/update_icon() + icon_living = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"][victim ? " eating" : ""]" + icon_dead = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"] dead" + icon_rest = icon_dead + ..() // This will apply the correct icon_state and do the other overlay-related things. + + +/mob/living/simple_mob/slime/xenobio/handle_special() + if(stat != DEAD) + handle_nutrition() + + if(victim) + handle_consumption() + + handle_stuttering() // ?? + + ..() + +/mob/living/simple_mob/slime/xenobio/examine(mob/user) + ..() + if(hat) + to_chat(user, "It is wearing \a [hat].") + + if(stat == DEAD) + to_chat(user, "It appears to be dead.") + else if(incapacitated(INCAPACITATION_DISABLED)) + to_chat(user, "It appears to be incapacitated.") + else if(harmless) + to_chat(user, "It appears to have been pacified.") + else + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + if(AI.rabid) + to_chat(user, "It seems very, very angry and upset.") + else if(AI.obedience >= 5) + to_chat(user, "It looks rather obedient.") + else if(AI.discipline) + to_chat(user, "It has been subjugated by force, at least for now.") + +/mob/living/simple_mob/slime/xenobio/proc/make_adult() + if(is_adult) + return + + is_adult = TRUE + melee_damage_lower = round(melee_damage_lower * 2) // 20 + melee_damage_upper = round(melee_damage_upper * 2) // 30 + maxHealth = maxHealth_adult + amount_grown = 0 + update_icon() + update_name() + +/mob/living/simple_mob/slime/xenobio/proc/update_name() + if(harmless) // Docile slimes are generally named, so we shouldn't mess with it. + return + name = "[slime_color] [is_adult ? "adult" : "baby"] [initial(name)] ([number])" + real_name = name + +/mob/living/simple_mob/slime/xenobio/update_mood() + var/old_mood = mood + if(incapacitated(INCAPACITATION_DISABLED)) + mood = "sad" + else if(harmless) + mood = ":33" + else if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + if(AI.rabid) + mood = "angry" + else if(AI.target) + mood = "mischevous" + else if(AI.discipline) + mood = "pout" + else + mood = ":3" + else + mood = ":3" + + if(old_mood != mood) + update_icon() + +/mob/living/simple_mob/slime/xenobio/proc/enrage() + if(harmless) + return + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.enrage() + +/mob/living/simple_mob/slime/xenobio/proc/pacify() + harmless = TRUE + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + AI.pacify() + + faction = "neutral" + + // If for whatever reason the mob AI (or player) decides to try to attack something anyways. + melee_damage_upper = 0 + melee_damage_lower = 0 + + update_mood() + + +// These are verbs so that player slimes can evolve/split. +/mob/living/simple_mob/slime/xenobio/verb/evolve() + set category = "Slime" + set desc = "This will let you evolve from baby to adult slime." + + if(stat) + to_chat(src, span("warning", "I must be conscious to do this...")) + return + + if(harmless) + to_chat(src, span("warning", "I have been pacified. I cannot evolve...")) + return + + if(!is_adult) + if(amount_grown >= 10) + make_adult() + else + to_chat(src, span("warning", "I am not ready to evolve yet...")) + else + to_chat(src, span("warning", "I have already evolved...")) + + +/mob/living/simple_mob/slime/xenobio/verb/reproduce() + set category = "Slime" + set desc = "This will make you split into four new slimes." + + if(stat) + to_chat(src, span("warning", "I must be conscious to do this...")) + return + + if(harmless) + to_chat(src, span("warning", "I have been pacified. I cannot reproduce...")) + return + + if(is_adult) + if(amount_grown >= 10) + // Check if there's enough 'room' to split. + var/list/nearby_things = orange(1, src) + var/free_tiles = 0 + for(var/turf/T in nearby_things) + var/free = TRUE + if(T.density) // No walls. + continue + for(var/atom/movable/AM in T) + if(AM.density) + free = FALSE + break + + if(free) + free_tiles++ + + if(free_tiles < 3) // Three free tiles are needed, as four slimes are made and the 4th tile is from the center tile that the current slime occupies. + to_chat(src, span("warning", "It is too cramped here to reproduce...")) + return + + var/list/babies = list() + for(var/i = 1 to 4) + babies.Add(make_new_slime()) + + var/mob/living/simple_mob/slime/new_slime = pick(babies) + new_slime.universal_speak = universal_speak + if(src.mind) + src.mind.transfer_to(new_slime) + else + new_slime.key = src.key + qdel(src) + else + to_chat(src, span("warning", "I am not ready to reproduce yet...")) + else + to_chat(src, span("warning", "I have not evolved enough to reproduce yet...")) + +// Used when reproducing or dying. +/mob/living/simple_mob/slime/xenobio/proc/make_new_slime(var/desired_type) + var/t = src.type + if(desired_type) + t = desired_type + if(prob(mutation_chance / 10)) + t = /mob/living/simple_mob/slime/xenobio/rainbow + else if(prob(mutation_chance) && slime_mutation.len) + t = slime_mutation[rand(1, slime_mutation.len)] + var/mob/living/simple_mob/slime/xenobio/baby = new t(loc) + + // Handle 'inheriting' from parent slime. + baby.mutation_chance = mutation_chance + baby.power_charge = round(power_charge / 4) + + pass_on_data(baby) // Transfer the AI stuff slowly, sadly. + + if(!istype(baby, /mob/living/simple_mob/slime/xenobio/rainbow)) + baby.unity = unity + baby.faction = faction + baby.friends = friends.Copy() + + step_away(baby, src) + return baby + +/mob/living/simple_mob/slime/xenobio/proc/pass_on_data(mob/living/simple_mob/slime/xenobio/baby) + // This is superdumb but the AI datum won't exist until the new slime's initialize() finishes. + var/new_discipline = 0 + var/new_obedience = 0 + var/new_resentment = 0 + var/new_rabid = FALSE + + // First, get this slime's AI values since they are likely to be deleted in a moment. + if(src && src.has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/our_AI = ai_holder + new_discipline = max(our_AI.discipline - 1, 0) + new_obedience = max(our_AI.obedience - 1, 0) + new_resentment = max(our_AI.resentment - 1, 0) + new_rabid = our_AI.rabid + + spawn(2) // Race conditions are fun, but with the first two letters capitalized. + if(istype(baby) && baby.has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/their_AI = baby.ai_holder + + if(!istype(baby, /mob/living/simple_mob/slime/xenobio/light_pink)) + their_AI.discipline = new_discipline + their_AI.obedience = new_obedience + + their_AI.resentment = new_resentment + + their_AI.rabid = new_rabid + +/mob/living/simple_mob/slime/xenobio/get_description_interaction() + var/list/results = list() + + if(!stat) + results += "[desc_panel_image("slimebaton")]to stun the slime, if it's being bad." + + results += ..() + + return results + +/mob/living/simple_mob/slime/xenobio/get_description_info() + var/list/lines = list() + var/intro_line = "Slimes are generally the test subjects of Xenobiology, with different colors having different properties. \ + They can be extremely dangerous if not handled properly." + lines.Add(intro_line) + lines.Add(null) // To pad the line breaks. + + var/list/rewards = list() + for(var/potential_color in slime_mutation) + var/mob/living/simple_mob/slime/S = potential_color + rewards.Add(initial(S.slime_color)) + var/reward_line = "This color of slime can mutate into [english_list(rewards)] colors, when it reproduces. It will do so when it has eatten enough." + lines.Add(reward_line) + lines.Add(null) + + lines.Add(description_info) + return lines.Join("\n") \ No newline at end of file diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index c7c0c9a253..d41314a69b 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -743,13 +743,12 @@ /mob/proc/facedir(var/ndir) - if(!canface() || (client && (client.moving || (world.time < client.move_delay)))) + if(!canface() || (client && (client.moving || (world.time < move_delay)))) return 0 set_dir(ndir) if(buckled && buckled.buckle_movable) buckled.set_dir(ndir) - if(client) - client.move_delay += movement_delay() + move_delay += movement_delay() return 1 @@ -1002,7 +1001,7 @@ mob/proc/yank_out_object() /mob/proc/has_brain_worms() for(var/I in contents) - if(istype(I,/mob/living/simple_animal/borer)) + if(istype(I,/mob/living/simple_mob/animal/borer)) return I return 0 diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 32902d7d48..9852c3f723 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -7,6 +7,8 @@ var/datum/mind/mind var/stat = 0 //Whether a mob is alive or dead. TODO: Move this to living - Nodrak + var/move_delay = null // For movement speed delays. + var/next_move = null // For click delay, despite the misleading name. //Not in use yet var/obj/effect/organstructure/organStructure = null @@ -62,7 +64,6 @@ var/sdisabilities = 0 //Carbon var/disabilities = 0 //Carbon var/atom/movable/pulling = null - var/next_move = null var/transforming = null //Carbon var/other = 0.0 var/eye_blind = null //Carbon diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 186487d0b9..6c31f54f8e 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -54,6 +54,9 @@ proc/isdeaf(A) /mob/proc/break_cloak() return +/mob/proc/is_cloaked() + return FALSE + proc/hasorgans(A) // Fucking really?? return ishuman(A) @@ -534,19 +537,17 @@ proc/is_blind(A) return threatcount -/mob/living/simple_animal/assess_perp(var/obj/access_obj, var/check_access, var/auth_weapons, var/check_records, var/check_arrest) +/mob/living/simple_mob/assess_perp(var/obj/access_obj, var/check_access, var/auth_weapons, var/check_records, var/check_arrest) var/threatcount = ..() if(. == SAFE_PERP) return SAFE_PERP - if(!istype(src, /mob/living/simple_animal/retaliate/goat)) - if(hostile) - if(faction != "neutral") // Otherwise Runtime gets killed. - threatcount += 4 + if(has_AI() && ai_holder.hostile && faction != "neutral") // Otherwise Runtime gets killed. + threatcount += 4 return threatcount // Beepsky will (try to) only beat 'bad' slimes. -/mob/living/simple_animal/slime/assess_perp(var/obj/access_obj, var/check_access, var/auth_weapons, var/check_records, var/check_arrest) +/mob/living/simple_mob/slime/xenobio/assess_perp(var/obj/access_obj, var/check_access, var/auth_weapons, var/check_records, var/check_arrest) var/threatcount = 0 if(stat == DEAD) @@ -565,8 +566,10 @@ proc/is_blind(A) if(victim) threatcount += 4 */ - if(rabid) - threatcount = 10 + if(has_AI()) + var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder + if(AI.rabid) + threatcount = 10 return threatcount diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 6b65c81c84..8848ed4aec 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -11,8 +11,12 @@ return /mob/proc/setMoveCooldown(var/timeout) - if(client) - client.move_delay = max(world.time + timeout, client.move_delay) + move_delay = max(world.time + timeout, move_delay) + +/mob/proc/check_move_cooldown() + if(world.time < src.move_delay) + return FALSE // Need to wait more. + return TRUE /client/North() ..() @@ -199,7 +203,8 @@ if(moving) return 0 - if(world.time < move_delay) return + if(!mob.check_move_cooldown()) + return if(locate(/obj/effect/stop/, mob.loc)) for(var/obj/effect/stop/S in mob.loc) @@ -271,21 +276,21 @@ src << "You're pinned to a wall by [mob.pinned[1]]!" return 0 - move_delay = world.time//set move delay + mob.move_delay = world.time//set move delay switch(mob.m_intent) if("run") if(mob.drowsyness > 0) - move_delay += 6 - move_delay += config.run_speed + mob.move_delay += 6 + mob.move_delay += config.run_speed if("walk") - move_delay += config.walk_speed - move_delay += mob.movement_delay() + mob.move_delay += config.walk_speed + mob.move_delay += mob.movement_delay() if(istype(mob.buckled))// VOREStation Removal - , /obj/vehicle)) //manually set move_delay for vehicles so we don't inherit any mob movement penalties //specific vehicle move delays are set in code\modules\vehicles\vehicle.dm - move_delay = world.time + mob.move_delay = world.time //drunk driving if(mob.confused && prob(20)) //vehicles tend to keep moving in the same direction direct = turn(direct, pick(90, -90)) @@ -314,14 +319,14 @@ if(prob(50)) direct = turn(direct, pick(90, -90)) if("walk") if(prob(25)) direct = turn(direct, pick(90, -90)) - move_delay += 2 + mob.move_delay += 2 return mob.buckled.relaymove(mob,direct) //We are now going to move moving = 1 //Something with pulling things if(locate(/obj/item/weapon/grab, mob)) - move_delay = max(move_delay, world.time + 7) + mob.move_delay = max(mob.move_delay, world.time + 7) var/list/L = mob.ret_grab() if(istype(L, /list)) if(L.len == 2) @@ -572,4 +577,4 @@ /client/verb/moveleft() set name = ".moveleft" set instant = 1 - Move(get_step(mob, WEST), WEST) \ No newline at end of file + Move(get_step(mob, WEST), WEST) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index dcd95c20de..3b3477b833 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -227,7 +227,7 @@ for(var/t in organs) //this really should not be necessary qdel(t) - var/mob/living/simple_animal/corgi/new_corgi = new /mob/living/simple_animal/corgi (loc) + var/mob/living/simple_mob/animal/passive/dog/corgi/new_corgi = new /mob/living/simple_mob/animal/passive/dog/corgi (loc) new_corgi.a_intent = I_HURT new_corgi.key = key @@ -237,7 +237,7 @@ /mob/living/carbon/human/Animalize() - var/list/mobtypes = typesof(/mob/living/simple_animal) + var/list/mobtypes = typesof(/mob/living/simple_mob) var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes if(!safe_animal(mobpath)) @@ -271,7 +271,7 @@ /mob/proc/Animalize() - var/list/mobtypes = typesof(/mob/living/simple_animal) + var/list/mobtypes = typesof(/mob/living/simple_mob) var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes if(!safe_animal(mobpath)) @@ -297,39 +297,29 @@ if(!MP) return 0 //Sanity, this should never happen. +/* if(ispath(MP, /mob/living/simple_animal/space_worm)) return 0 //Unfinished. Very buggy, they seem to just spawn additional space worms everywhere and eating your own tail results in new worms spawning. - - if(ispath(MP, /mob/living/simple_animal/construct/behemoth)) - return 0 //I think this may have been an unfinished WiP or something. These constructs should really have their own class simple_animal/construct/subtype - - if(ispath(MP, /mob/living/simple_animal/construct/armoured)) - return 0 //Verbs do not appear for players. These constructs should really have their own class simple_animal/construct/subtype - - if(ispath(MP, /mob/living/simple_animal/construct/wraith)) - return 0 //Verbs do not appear for players. These constructs should really have their own class simple_animal/construct/subtype - - if(ispath(MP, /mob/living/simple_animal/construct/builder)) - return 0 //Verbs do not appear for players. These constructs should really have their own class simple_animal/construct/subtype +*/ //Good mobs! - if(ispath(MP, /mob/living/simple_animal/cat)) + if(ispath(MP, /mob/living/simple_mob/animal/passive/cat)) return 1 - if(ispath(MP, /mob/living/simple_animal/corgi)) + if(ispath(MP, /mob/living/simple_mob/animal/passive/dog)) return 1 - if(ispath(MP, /mob/living/simple_animal/crab)) + if(ispath(MP, /mob/living/simple_mob/animal/passive/crab)) return 1 - if(ispath(MP, /mob/living/simple_animal/hostile/carp)) + if(ispath(MP, /mob/living/simple_mob/animal/space/carp)) return 1 - if(ispath(MP, /mob/living/simple_animal/shade)) + if(ispath(MP, /mob/living/simple_mob/construct)) return 1 - if(ispath(MP, /mob/living/simple_animal/hostile/tomato)) + if(ispath(MP, /mob/living/simple_mob/tomato)) return 1 - if(ispath(MP, /mob/living/simple_animal/mouse)) + if(ispath(MP, /mob/living/simple_mob/animal/passive/mouse)) return 1 //It is impossible to pull up the player panel for mice (Fixed! - Nodrak) - if(ispath(MP, /mob/living/simple_animal/hostile/bear)) + if(ispath(MP, /mob/living/simple_mob/animal/space/bear)) return 1 //Bears will auto-attack mobs, even if they're player controlled (Fixed! - Nodrak) - if(ispath(MP, /mob/living/simple_animal/parrot)) + if(ispath(MP, /mob/living/simple_mob/animal/passive/bird/parrot)) return 1 //Parrots are no longer unfinished! -Nodrak //Not in here? Must be untested! diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 06840f65fe..6f3cbb9bc8 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -83,11 +83,13 @@ /mob/proc/can_overcome_gravity() return FALSE -/mob/living/carbon/human/can_overcome_gravity() - return species && species.can_overcome_gravity(src) +/mob/living/can_overcome_gravity() + return hovering -/mob/living/simple_animal/construct/can_overcome_gravity() - return 1 //They care not for standard physics. +/mob/living/carbon/human/can_overcome_gravity() + . = ..() + if(!.) + return species && species.can_overcome_gravity(src) /mob/observer/zMove(direction) var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src) @@ -117,36 +119,44 @@ return ..() /mob/observer/can_ztravel() - return 1 + return TRUE -/mob/living/simple_animal/construct/can_ztravel() - return 1 +/mob/living/can_ztravel() + if(incapacitated()) + return FALSE + return hovering /mob/living/carbon/human/can_ztravel() if(incapacitated()) - return 0 + return FALSE + + if(hovering) + return TRUE if(flying) //VOREStation Edit. Allows movement up/down with wings. return 1 //VOREStation Edit if(Process_Spacemove()) - return 1 + return TRUE if(Check_Shoegrip()) //scaling hull with magboots for(var/turf/simulated/T in trange(1,src)) if(T.density) - return 1 + return TRUE /mob/living/silicon/robot/can_ztravel() if(incapacitated() || is_dead()) - return 0 + return FALSE + + if(hovering) + return TRUE if(Process_Spacemove()) //Checks for active jetpack - return 1 + return FALSE for(var/turf/simulated/T in trange(1,src)) //Robots get "magboots" if(T.density) - return 1 + return TRUE // TODO - Leshana Experimental @@ -262,19 +272,15 @@ if((locate(/obj/structure/disposalpipe/up) in below) || locate(/obj/machinery/atmospherics/pipe/zpipe/up in below)) return FALSE +/mob/living/can_fall() + if(hovering) + return FALSE + return ..() + /mob/living/carbon/human/can_fall() if(..()) return species.can_fall(src) -/mob/living/simple_animal/parrot/can_fall() // Poly can fly. - return FALSE - -/mob/living/simple_animal/hostile/carp/can_fall() // So can carp apparently. - return FALSE - -/mob/living/simple_animal/construct/can_fall() //As do Constructs. - return FALSE - // Check if this atom prevents things standing on it from falling. Return TRUE to allow the fall. /obj/proc/CanFallThru(atom/movable/mover as mob|obj, turf/target as turf) if(!isturf(mover.loc)) // VORESTATION EDIT. We clearly didn't have enough backup checks. diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm index cbf7623267..18f8523907 100644 --- a/code/modules/multiz/turf.dm +++ b/code/modules/multiz/turf.dm @@ -149,3 +149,12 @@ /turf/simulated/open/is_space() var/turf/below = GetBelow(src) return !below || below.is_space() +<<<<<<< HEAD +======= + +/turf/simulated/open/is_safe_to_enter(mob/living/L) + if(L.can_fall()) + if(!locate(/obj/structure/stairs) in GetBelow(src)) + return FALSE + return ..() +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index 5f8a9dd0d7..2003abe04f 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -107,7 +107,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) if(name == initial(name)) name = "\the [owner.real_name]'s [initial(name)]" - var/mob/living/simple_animal/borer/borer = owner.has_brain_worms() + var/mob/living/simple_mob/animal/borer/borer = owner.has_brain_worms() if(borer) borer.detatch() //Should remove borer if the brain is removed - RR diff --git a/code/modules/organs/misc.dm b/code/modules/organs/misc.dm index 862c96a96f..30515e746b 100644 --- a/code/modules/organs/misc.dm +++ b/code/modules/organs/misc.dm @@ -34,7 +34,7 @@ ..() - var/mob/living/simple_animal/borer/B = owner.has_brain_worms() + var/mob/living/simple_mob/animal/borer/B = owner.has_brain_worms() if(B) B.leave_host() B.ckey = owner.ckey diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index 4a81179c72..4a24500fd3 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -19,7 +19,7 @@ /obj/item/weapon/paper_bin/MouseDrop(mob/user as mob) if((user == usr && (!( usr.restrained() ) && (!( usr.stat ) && (usr.contents.Find(src) || in_range(src, usr)))))) - if(!istype(usr, /mob/living/simple_animal)) + if(!istype(usr, /mob/living/simple_mob)) if( !usr.get_active_hand() ) //if active hand is empty var/mob/living/carbon/human/H = user var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 535debad21..9e95e67a6d 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -33,7 +33,6 @@ var/global/photo_count = 0 var/icon/img //Big photo image var/scribble //Scribble on the back. var/icon/tiny - var/cursed = 0 var/photo_size = 3 /obj/item/weapon/photo/New() @@ -237,10 +236,6 @@ var/global/photo_count = 0 else mob_detail += "You can also see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." - for(var/mob/living/simple_animal/hostile/statue/S in the_turf) - if(S) - mob_detail += "You can see \a [S] on the photo. Its stare makes you feel uneasy." //"That which holds the image of an angel, becomes itself an angel." - return mob_detail /obj/item/device/camera/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) @@ -282,20 +277,7 @@ var/global/photo_count = 0 y_c-- x_c = x_c - size - - - var/obj/item/weapon/photo/p = createpicture(target, user, turfs, mobs, flag) - if(findtext(mobs, "Its stare makes you feel uneasy")) - p.cursed = 1 - user.visible_message("Something starts to slowly manifest from the picture!") - spawn(150) - var/turf/T = get_turf(p) - var/mob/living/simple_animal/hostile/statue/S = new /mob/living/simple_animal/hostile/statue/(T) - S.banishable = 1//At least you can get rid of those bastards - T.visible_message("The photo turns into \a [S]!") - qdel(p) - printpicture(user, p) @@ -339,16 +321,6 @@ var/global/photo_count = 0 p.pixel_y = pixel_y p.photo_size = photo_size p.scribble = scribble - p.cursed = cursed - if(p.cursed) - var/turf/T = get_turf(p) - T.visible_message("Something starts to slowly manifest from the picture!") - spawn(150) - T = get_turf(p) //second time, because the photo could've moved - var/mob/living/simple_animal/hostile/statue/S = new /mob/living/simple_animal/hostile/statue/(T) - S.banishable = 1//At least you can get rid of those bastards - T.visible_message("The photo turns into \a [S]!") - qdel(p) if(copy_id) p.id = id diff --git a/code/modules/planet/sif.dm b/code/modules/planet/sif.dm index b8c99636f6..371130511a 100644 --- a/code/modules/planet/sif.dm +++ b/code/modules/planet/sif.dm @@ -446,4 +446,4 @@ var/datum/planet/sif/planet_sif = null observed_message = "Everything is red. Something really wrong is going on." transition_messages = list( "The sky turns blood red!" - ) \ No newline at end of file + ) diff --git a/code/modules/power/singularity/act.dm b/code/modules/power/singularity/act.dm index 5998906c66..a5d7dc3651 100644 --- a/code/modules/power/singularity/act.dm +++ b/code/modules/power/singularity/act.dm @@ -115,6 +115,15 @@ ChangeTurf(get_base_turf_by_area(src)) return 2 +/turf/simulated/floor/singularity_pull(S, current_size) + if(flooring && current_size >= STAGE_THREE) + if(prob(current_size / 2)) + var/leave_tile = TRUE + if(broken || burnt || flooring.flags & TURF_IS_FRAGILE) + leave_tile = FALSE + playsound(src, 'sound/items/crowbar.ogg', 50, 1) + make_plating(leave_tile) + /turf/simulated/wall/singularity_pull(S, current_size) if(!reinf_material) diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 9274bb85a6..fd548bdb81 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -185,7 +185,6 @@ /obj/machinery/atmospherics, /obj/machinery/power/emitter, /obj/machinery/field_generator, - /mob/living/simple_animal, /obj/machinery/door/blast, /obj/machinery/particle_accelerator/control_box, /obj/structure/particle_accelerator/fuel_chamber, @@ -290,7 +289,7 @@ else if(closest_mob) var/shock_damage = Clamp(round(power/400), 10, 90) + rand(-5, 5) - closest_mob.electrocute_act(shock_damage, source, 1, ran_zone()) + closest_mob.electrocute_act(shock_damage, source, 1 - closest_mob.get_shock_protection(), ran_zone()) log_game("TESLA([source.x],[source.y],[source.z]) Shocked [key_name(closest_mob)] for [shock_damage]dmg.") message_admins("Tesla zapped [key_name_admin(closest_mob)]!") if(issilicon(closest_mob)) diff --git a/code/modules/power/tesla/tesla_act.dm b/code/modules/power/tesla/tesla_act.dm index 8e617fca86..01b58f8a80 100644 --- a/code/modules/power/tesla/tesla_act.dm +++ b/code/modules/power/tesla/tesla_act.dm @@ -60,6 +60,9 @@ ..() //extend the zap explode() +/obj/mecha/tesla_act(power) + ..() + take_damage(power / 200, "energy") // A surface lightning strike will do 100 damage. diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 40dbaf0650..cb8ca6cdf2 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -173,7 +173,7 @@ if(!user.IsAdvancedToolUser()) return 0 if(isanimal(user)) - var/mob/living/simple_animal/S = user + var/mob/living/simple_mob/S = user if(!S.IsHumanoidToolUser(src)) return 0 @@ -526,7 +526,8 @@ return 2 //just assume we can shoot through glass and stuff. No big deal, the player can just choose to not target someone //on the other side of a window if it makes a difference. Or if they run behind a window, too bad. - return check_trajectory(target, user) + if(check_trajectory(target, user)) + return 1 // Magic numbers are fun. //called if there was no projectile to shoot /obj/item/weapon/gun/proc/handle_click_empty(mob/user) diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index f1e20b169b..7bd20cdcef 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -155,13 +155,13 @@ else src.visible_message("*fizzle*") playsound(src.loc, 'sound/effects/sparks1.ogg', 100, 1) - +/* /obj/item/weapon/gun/energy/staff/animate name = "staff of animation" desc = "An artifact that spits bolts of life force, which causes objects which are hit by it to animate and come to life! This magic doesn't affect machines." projectile_type = /obj/item/projectile/animate charge_cost = 240 - +*/ obj/item/weapon/gun/energy/staff/focus name = "mental focus" desc = "An artifact that channels the will of the user into destructive bolts of force. If you aren't careful with it, you might poke someone's brain out." diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 4566577a94..8b113299b3 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -59,6 +59,8 @@ var/drowsy = 0 var/agony = 0 var/reflected = 0 // This should be set to 1 if reflected by any means, to prevent infinite reflections. + var/modifier_type_to_apply = null // If set, will apply a modifier to mobs that are hit by this projectile. + var/modifier_duration = null // How long the above modifier should last for. Leave null to be permanent. embed_chance = 0 //Base chance for a projectile to embed @@ -86,6 +88,8 @@ // if(isanimal(target)) return 0 var/mob/living/L = target L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) // add in AGONY! + if(modifier_type_to_apply) + L.add_modifier(modifier_type_to_apply, modifier_duration) return 1 //called when the projectile stops flying because it collided with something @@ -421,6 +425,7 @@ yo = null xo = null var/result = 0 //To pass the message back to the gun. + var/atom/movable/result_ref = null // The thing that got hit that made the check return true. /obj/item/projectile/test/Bump(atom/A as mob|obj|turf|area) if(A == firer) @@ -439,6 +444,7 @@ if(!S.anchored) return if(istype(A, /mob/living) || istype(A, /obj/mecha) || istype(A, /obj/vehicle)) + result_ref = A result = 2 //We hit someone, return 1! return result = 1 @@ -459,7 +465,8 @@ /obj/item/projectile/test/process(var/turf/targloc) while(src) //Loop on through! if(result) - return (result - 1) + return result_ref + // return (result - 1) if((!( targloc ) || loc == targloc)) targloc = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) //Finding the target turf at map edge @@ -470,11 +477,13 @@ var/mob/living/M = locate() in get_turf(src) if(istype(M)) //If there is someting living... - return 1 //Return 1 + result_ref = M + return result_ref //Return 1 else M = locate() in get_step(src,targloc) if(istype(M)) - return 1 + result_ref = M + return result_ref //Helper proc to check if you can hit them or not. /proc/check_trajectory(atom/target as mob|obj, atom/firer as mob|obj, var/pass_flags=PASSTABLE|PASSGLASS|PASSGRILLE, flags=null) diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/change.dm index 0beffe46a3..df802f4bb5 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/change.dm @@ -54,7 +54,7 @@ Robot.mmi = new /obj/item/device/mmi(new_mob) Robot.mmi.transfer_identity(M) //Does not transfer key/client. if("slime") - new_mob = new /mob/living/simple_animal/slime(M.loc) + new_mob = new /mob/living/simple_mob/slime/xenobio(M.loc) new_mob.universal_speak = 1 else var/mob/living/carbon/human/H diff --git a/code/modules/random_map/drop/droppod.dm b/code/modules/random_map/drop/droppod.dm index da346d2eb7..c7afde1d7f 100644 --- a/code/modules/random_map/drop/droppod.dm +++ b/code/modules/random_map/drop/droppod.dm @@ -15,7 +15,7 @@ floor_type = /turf/simulated/floor/reinforced var/list/supplied_drop_types = list() var/door_type = /obj/structure/droppod_door - var/drop_type = /mob/living/simple_animal/parrot + var/drop_type = /mob/living/simple_mob/animal/passive/bird/parrot var/auto_open_doors var/placement_explosion_dev = 1 diff --git a/code/modules/random_map/noise/desert.dm b/code/modules/random_map/noise/desert.dm index 1c7ca229a4..0264d2a781 100644 --- a/code/modules/random_map/noise/desert.dm +++ b/code/modules/random_map/noise/desert.dm @@ -27,7 +27,7 @@ var/grass_path = pick(typesof(/obj/structure/flora/grass)-/obj/structure/flora/grass) new grass_path(T) if(prob(5)) - var/mob_type = pick(list(/mob/living/simple_animal/lizard, /mob/living/simple_animal/mouse)) + var/mob_type = pick(list(/mob/living/simple_mob/animal/passive/lizard, /mob/living/simple_mob/animal/passive/mouse)) new mob_type(T) if(5 to 6) if(prob(20)) diff --git a/code/modules/random_map/noise/tundra.dm b/code/modules/random_map/noise/tundra.dm index 83b7435e40..6cb8114298 100644 --- a/code/modules/random_map/noise/tundra.dm +++ b/code/modules/random_map/noise/tundra.dm @@ -46,13 +46,13 @@ switch(val) if(2) if(prob(5)) - new /mob/living/simple_animal/crab(T) + new /mob/living/simple_mob/animal/passive/crab(T) if(6) if(prob(60)) var/grass_path = pick(typesof(/obj/structure/flora/grass)-/obj/structure/flora/grass) new grass_path(T) if(prob(5)) - var/mob_type = pick(list(/mob/living/simple_animal/lizard, /mob/living/simple_animal/mouse)) + var/mob_type = pick(list(/mob/living/simple_mob/animal/passive/lizard, /mob/living/simple_mob/animal/passive/mouse)) new mob_type(T) if(7) if(prob(60)) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm index a32f83f794..cbe7dd9c5b 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm @@ -169,8 +169,8 @@ /datum/reagent/water/touch_mob(var/mob/living/L, var/amount) if(istype(L)) // First, kill slimes. - if(istype(L, /mob/living/simple_animal/slime)) - var/mob/living/simple_animal/slime/S = L + if(istype(L, /mob/living/simple_mob/slime)) + var/mob/living/simple_mob/slime/S = L S.adjustToxLoss(15 * amount) S.visible_message("[S]'s flesh sizzles where the water touches it!", "Your flesh burns in the water!") diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm index a7bcd87892..f66d91dd42 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm @@ -305,8 +305,6 @@ M.bodytemperature = max(M.bodytemperature - 10 * TEMPERATURE_DAMAGE_COEFFICIENT, 215) if(prob(1)) M.emote("shiver") - if(istype(M, /mob/living/simple_animal/slime)) - M.bodytemperature = max(M.bodytemperature - rand(10,20), 0) holder.remove_reagent("capsaicin", 5) /datum/reagent/frostoil/cryotoxin //A longer lasting version of frost oil. @@ -346,8 +344,6 @@ M.apply_effect(2, AGONY, 0) if(prob(5)) M.visible_message("[M] [pick("dry heaves!","coughs!","splutters!")]", "You feel like your insides are burning!") - if(istype(M, /mob/living/simple_animal/slime)) - M.bodytemperature += rand(10, 25) holder.remove_reagent("frostoil", 5) /datum/reagent/condensedcapsaicin @@ -492,8 +488,6 @@ M.apply_effect(4, AGONY, 0) if(prob(5)) M.visible_message("[M] [pick("dry heaves!","coughs!","splutters!")]", "You feel like your insides are burning!") - if(istype(M, /mob/living/simple_animal/slime)) - M.bodytemperature += rand(15, 30) holder.remove_reagent("frostoil", 5) /* Drinks */ diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm index 6b705f4f54..9cf58bbfb8 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm @@ -106,6 +106,8 @@ M.drowsyness = max(0, M.drowsyness - 6 * removed * chem_effective) M.hallucination = max(0, M.hallucination - 9 * removed * chem_effective) M.adjustToxLoss(-4 * removed * chem_effective) + if(prob(10)) + M.remove_a_modifier_of_type(/datum/modifier/poisoned) /datum/reagent/carthatoline name = "Carthatoline" @@ -121,6 +123,8 @@ if(M.getToxLoss() && prob(10)) M.vomit(1) M.adjustToxLoss(-8 * removed) + if(prob(30)) + M.remove_a_modifier_of_type(/datum/modifier/poisoned) if(ishuman(M)) var/mob/living/carbon/human/H = M var/obj/item/organ/internal/liver/L = H.internal_organs_by_name[O_LIVER] diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm index 53ce37b3ef..ff753a5b37 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm @@ -360,7 +360,7 @@ S.dirt = 0 T.clean_blood() - for(var/mob/living/simple_animal/slime/M in T) + for(var/mob/living/simple_mob/slime/M in T) M.adjustToxLoss(rand(5, 10)) /datum/reagent/space_cleaner/affect_touch(var/mob/living/carbon/M, var/alien, var/removed) diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 737dbdcdad..1b70001c28 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -37,8 +37,8 @@ /obj/machinery/iv_drip, /obj/machinery/disease2/incubator, /obj/machinery/disposal, - /mob/living/simple_animal/cow, - /mob/living/simple_animal/retaliate/goat, + /mob/living/simple_mob/animal/passive/cow, + /mob/living/simple_mob/animal/goat, /obj/machinery/computer/centrifuge, /obj/machinery/sleeper, /obj/machinery/smartfridge/, diff --git a/code/modules/shieldgen/directional_shield.dm b/code/modules/shieldgen/directional_shield.dm index 519edcea07..6a0aae0005 100644 --- a/code/modules/shieldgen/directional_shield.dm +++ b/code/modules/shieldgen/directional_shield.dm @@ -88,6 +88,8 @@ but allow those projectiles to leave the shield from the inside. Blocking too many damaging projectiles will cause the shield to fail." icon = 'icons/obj/device.dmi' icon_state = "signmaker_sec" + light_range = 4 + light_power = 4 var/active = FALSE // If it's on. var/shield_health = 400 // How much damage the shield blocks before breaking. This is a shared health pool for all shields attached to this projector. var/max_shield_health = 400 // Ditto. This is fairly high, but shields are really big, you can't miss them, and laser carbines pump out so much hurt. @@ -170,6 +172,8 @@ var/new_color = rgb(new_r, new_g, new_b) + set_light(light_range, light_power, new_color) + // Now deploy the new color to all the shields. for(var/obj/effect/directional_shield/S in active_shields) S.update_color(new_color) diff --git a/code/modules/shieldgen/energy_field.dm b/code/modules/shieldgen/energy_field.dm index 023d5fee95..1c5b8d2965 100644 --- a/code/modules/shieldgen/energy_field.dm +++ b/code/modules/shieldgen/energy_field.dm @@ -53,6 +53,12 @@ user.setClickCooldown(user.get_attack_speed(W)) ..() +/obj/effect/energy_field/attack_generic(mob/user, damage) + if(damage) + adjust_strength(-damage / 20) + user.do_attack_animation(src) + user.setClickCooldown(user.get_attack_speed()) + /obj/effect/energy_field/attack_hand(var/mob/living/user) impact_effect(3) // Harmless, but still produces the 'impact' effect. ..() diff --git a/code/modules/shuttles/shuttle.dm b/code/modules/shuttles/shuttle.dm index 080f21a524..c8f74aff81 100644 --- a/code/modules/shuttles/shuttle.dm +++ b/code/modules/shuttles/shuttle.dm @@ -208,6 +208,7 @@ for(var/turf/T in dstturfs) var/turf/D = locate(T.x, throwy - 1, T.z) +<<<<<<< HEAD for(var/I in T) if(istype(I,/mob/living)) var/mob/living/L = I @@ -215,6 +216,16 @@ else if(istype(I,/obj)) var/obj/O = I O.forceMove(D) +======= + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + + for(var/mob/living/carbon/bug in destination) + bug.gib() + + for(var/mob/living/simple_mob/pest in destination) + pest.gib() +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync origin.move_contents_to(destination, direction=direction) diff --git a/code/modules/spells/aoe_turf/summons.dm b/code/modules/spells/aoe_turf/summons.dm index e8769d8537..94548a362f 100644 --- a/code/modules/spells/aoe_turf/summons.dm +++ b/code/modules/spells/aoe_turf/summons.dm @@ -20,7 +20,7 @@ invocation_type = SpI_SHOUT range = 1 - summon_type = list(/mob/living/simple_animal/hostile/carp) + summon_type = list(/mob/living/simple_mob/animal/space/carp) hud_state = "wiz_carp" @@ -36,6 +36,10 @@ summon_amt = 10 range = 3 +<<<<<<< HEAD summon_type = list(/mob/living/simple_animal/hostile/creature/vore) // Vorestation Edit +======= + summon_type = list(/mob/living/simple_mob/creature) +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync hud_state = "wiz_creature" \ No newline at end of file diff --git a/code/modules/spells/spell_code.dm b/code/modules/spells/spell_code.dm index afd9bfdfa3..c1af923f72 100644 --- a/code/modules/spells/spell_code.dm +++ b/code/modules/spells/spell_code.dm @@ -198,10 +198,10 @@ var/list/spells = typesof(/spell) //needed for the badmin verb for now if(findNullRod(T)) return 0 - if(istype(user, /mob/living/simple_animal) && holder == user) - var/mob/living/simple_animal/SA = user - if(SA.purge) - SA << "The nullrod's power interferes with your own!" + if(istype(user, /mob/living/simple_mob) && holder == user) + var/mob/living/simple_mob/SM = user + if(SM.purge) + SM << "The nullrod's power interferes with your own!" return 0 if(!src.check_charge(skipcharge, user)) //sees if we can cast based on charges alone diff --git a/code/modules/spells/spellbook.dm b/code/modules/spells/spellbook.dm index 515453f6a3..1f41ce9e3d 100644 --- a/code/modules/spells/spellbook.dm +++ b/code/modules/spells/spellbook.dm @@ -221,11 +221,6 @@ new /obj/item/clothing/head/helmet/space/void/wizard(get_turf(H)) temp = "You have purchased a suit of wizard armor." max_uses-- - if("staffanimation") - feedback_add_details("wizard_spell_learned","SA") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells - new /obj/item/weapon/gun/energy/staff/animate(get_turf(H)) - temp = "You have purchased a staff of animation." - max_uses-- if("scrying") feedback_add_details("wizard_spell_learned","SO") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells new /obj/item/weapon/scrying(get_turf(H)) diff --git a/code/modules/surgery/implant.dm b/code/modules/surgery/implant.dm index ae0e7610a7..58a322febd 100644 --- a/code/modules/surgery/implant.dm +++ b/code/modules/surgery/implant.dm @@ -207,8 +207,8 @@ BITSET(target.hud_updateflag, IMPLOYAL_HUD) //Handle possessive brain borers. - if(istype(obj,/mob/living/simple_animal/borer)) - var/mob/living/simple_animal/borer/worm = obj + if(istype(obj,/mob/living/simple_mob/animal/borer)) + var/mob/living/simple_mob/animal/borer/worm = obj if(worm.controlling) target.release_control() worm.detatch() diff --git a/code/modules/tension/tension.dm b/code/modules/tension/tension.dm index 913f1d2dff..82151a7e78 100644 --- a/code/modules/tension/tension.dm +++ b/code/modules/tension/tension.dm @@ -9,35 +9,42 @@ /atom/movable/proc/guess_threat_level(var/mob/living/threatened) return 0 -/mob/living/simple_animal +/mob/living/simple_mob var/threat_level = null // Set this if you want an explicit danger rating. -/mob/living/simple_animal/guess_threat_level(var/mob/living/threatened) +/mob/living/simple_mob/guess_threat_level(var/mob/living/threatened) if(threat_level) // If they have a predefined number, use it. return threat_level // Otherwise we need to guess how scary this thing is. var/threat_guess = 0 // First lets consider their attack ability. + var/will_point_blank = FALSE + if(has_AI()) + will_point_blank = ai_holder.pointblank + var/potential_damage = 0 - if(!ranged) //Melee damage. + if(!projectiletype || ( get_dist(src, threatened >= 1) && !will_point_blank ) ) // Melee damage. potential_damage = (melee_damage_lower + melee_damage_upper) / 2 + + // Treat potential_damage as estimated DPS. If the enemy attacks twice as fast as usual, it will double the number. + potential_damage *= 1 SECOND / (base_attack_cooldown + melee_attack_delay) else - if(projectiletype) - var/obj/item/projectile/P = new projectiletype(src) - if(P.nodamage || P.taser_effect) // Tasers are somewhat less scary. - potential_damage = P.agony / 2 - else - potential_damage = P.damage - if(P.damage_type == HALLOSS) // Not sure if any projectiles do this, but can't be too safe. - potential_damage /= 2 - // Rubber bullets, I guess. - potential_damage += P.agony / 2 + var/obj/item/projectile/P = new projectiletype(src) + if(P.nodamage || P.taser_effect) // Tasers are somewhat less scary. + potential_damage = P.agony / 2 + else + potential_damage = P.damage + if(P.damage_type == HALLOSS) // Not sure if any projectiles do this, but can't be too safe. + potential_damage /= 2 + // Rubber bullets, I guess. + potential_damage += P.agony / 2 + qdel(P) - if(rapid) // This makes them shoot three times per cycle. - potential_damage *= 3 + potential_damage *= 1 SECOND / (base_attack_cooldown + ranged_attack_delay) + + // Special attacks are ultra-specific to the mob so a generic threat assessment isn't really possible. - qdel(P) threat_guess += potential_damage // Then consider their defense. @@ -50,11 +57,12 @@ return 0 -/mob/living/simple_animal/get_threat(var/mob/living/threatened) +/mob/living/simple_mob/get_threat(var/mob/living/threatened) . = ..() - if(!hostile) - return 0 // Can't hurt anyone. + if(has_AI()) + if(!ai_holder.hostile) + return 0 // Can't hurt anyone. if(incapacitated(INCAPACITATION_DISABLED)) return 0 // Can't currently hurt you if it's stunned. @@ -86,7 +94,7 @@ // Handle ability to harm. // Being five tiles away from some spiders is a lot less scary than being in melee range of five spiders at once. - if(!ranged) + if(!projectiletype) threat /= max(get_dist(src, threatened), 1) return threat diff --git a/code/modules/ventcrawl/ventcrawl.dm b/code/modules/ventcrawl/ventcrawl.dm index 1a4271050d..14d9a21dd7 100644 --- a/code/modules/ventcrawl/ventcrawl.dm +++ b/code/modules/ventcrawl/ventcrawl.dm @@ -9,8 +9,12 @@ var/list/ventcrawl_machinery = list( /obj/item/device/radio/borg, /obj/item/weapon/holder, /obj/machinery/camera, +<<<<<<< HEAD /mob/living/simple_animal/borer, /obj/belly, //VOREStation Edit, +======= + /mob/living/simple_mob/animal/borer, +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync /obj/screen ) @@ -38,7 +42,7 @@ var/list/ventcrawl_machinery = list( add_ventcrawl(loc) client.screen += global_hud.centermarker -/mob/living/simple_animal/slime/can_ventcrawl() +/mob/living/simple_mob/slime/xenobio/can_ventcrawl() if(victim) to_chat(src, "You cannot ventcrawl while feeding.") return FALSE @@ -70,11 +74,6 @@ var/list/ventcrawl_machinery = list( return 1 return ..() -/mob/living/simple_animal/spiderbot/is_allowed_vent_crawl_item(var/obj/item/carried_item) - if(carried_item == held_item) - return 1 - return ..() - /mob/living/proc/ventcrawl_carry() for(var/atom/A in contents) if(!is_allowed_vent_crawl_item(A)) diff --git a/code/modules/xenoarcheaology/artifacts/autocloner.dm b/code/modules/xenoarcheaology/artifacts/autocloner.dm index 535eee1559..d0568b2080 100644 --- a/code/modules/xenoarcheaology/artifacts/autocloner.dm +++ b/code/modules/xenoarcheaology/artifacts/autocloner.dm @@ -21,6 +21,7 @@ //33% chance to spawn nasties if(prob(33)) +<<<<<<< HEAD spawn_type = pick(\ /mob/living/simple_animal/hostile/giant_spider/nurse,\ /mob/living/simple_animal/hostile/alien,\ @@ -28,18 +29,25 @@ /mob/living/simple_animal/hostile/carp,\ /mob/living/simple_animal/hostile/creature\ ) // Vorestation Edits +======= + spawn_type = pick( + /mob/living/simple_mob/animal/giant_spider/nurse, + /mob/living/simple_mob/animal/space/alien, + /mob/living/simple_mob/animal/space/bear, + /mob/living/simple_mob/creature, + /mob/living/simple_mob/slime/xenobio) +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync else spawn_type = pick(\ - /mob/living/simple_animal/cat, - /mob/living/simple_animal/corgi, - /mob/living/simple_animal/corgi/puppy, - /mob/living/simple_animal/chicken, - /mob/living/simple_animal/cow, - /mob/living/simple_animal/parrot, - /mob/living/simple_animal/slime, - /mob/living/simple_animal/crab, - /mob/living/simple_animal/mouse, - /mob/living/simple_animal/retaliate/goat) + /mob/living/simple_mob/animal/passive/cat, + /mob/living/simple_mob/animal/passive/dog/corgi, + /mob/living/simple_mob/animal/passive/dog/corgi/puppy, + /mob/living/simple_mob/animal/passive/chicken, + /mob/living/simple_mob/animal/passive/cow, + /mob/living/simple_mob/animal/passive/bird/parrot, + /mob/living/simple_mob/animal/passive/crab, + /mob/living/simple_mob/animal/passive/mouse, + /mob/living/simple_mob/animal/goat) //todo: how the hell is the asteroid permanently powered? /obj/machinery/auto_cloner/process() diff --git a/code/modules/xenoarcheaology/artifacts/replicator.dm b/code/modules/xenoarcheaology/artifacts/replicator.dm index f5575721e5..7e27528aeb 100644 --- a/code/modules/xenoarcheaology/artifacts/replicator.dm +++ b/code/modules/xenoarcheaology/artifacts/replicator.dm @@ -26,9 +26,14 @@ /obj/item/roller, /obj/structure/closet/crate, /obj/structure/closet/acloset, +<<<<<<< HEAD /mob/living/simple_animal/hostile/mimic/crate, /mob/living/simple_animal/hostile/viscerator, /mob/living/simple_animal/hostile/hivebot, +======= + /mob/living/simple_mob/mechanical/viscerator, + /mob/living/simple_mob/mechanical/hivebot, +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync /obj/item/device/analyzer, /obj/item/device/camera, /obj/item/device/flash, diff --git a/code/modules/xenoarcheaology/finds/special.dm b/code/modules/xenoarcheaology/finds/special.dm index 8f3d7e4a79..d467af7a4b 100644 --- a/code/modules/xenoarcheaology/finds/special.dm +++ b/code/modules/xenoarcheaology/finds/special.dm @@ -87,7 +87,11 @@ if(charges >= 3) if(prob(5)) charges -= 1 +<<<<<<< HEAD var/spawn_type = pick(/mob/living/simple_animal/hostile/creature/vore) // Vorestation Edit +======= + var/spawn_type = pick(/mob/living/simple_mob/creature) +>>>>>>> 3155d58... Merge pull request #5735 from Neerti/hopefully_last_master_sync new spawn_type(pick(view(1,src))) playsound(src.loc, pick('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg'), 50, 1, -3) diff --git a/code/modules/xenobio/items/extracts.dm b/code/modules/xenobio/items/extracts.dm index 6d472b5a09..397ca52c33 100644 --- a/code/modules/xenobio/items/extracts.dm +++ b/code/modules/xenobio/items/extracts.dm @@ -77,7 +77,7 @@ /datum/chemical_reaction/slime/grey_new_slime/on_reaction(var/datum/reagents/holder) holder.my_atom.visible_message("Infused with phoron, the core begins to quiver and grow, and soon a new baby slime emerges from it!") - new /mob/living/simple_animal/slime(get_turf(holder.my_atom)) + new /mob/living/simple_mob/slime/xenobio(get_turf(holder.my_atom)) ..() /datum/chemical_reaction/slime/grey_monkey @@ -495,16 +495,16 @@ if(!(their_turf in Z.contents)) // Not in the same zone. continue - if(istype(L, /mob/living/simple_animal/slime)) - var/mob/living/simple_animal/slime/S = L - if(S.cold_damage_per_tick <= 0) // Immune to cold. + if(istype(L, /mob/living/simple_mob/slime)) + var/mob/living/simple_mob/slime/S = L + if(S.cold_resist >= 1) // Immune to cold. to_chat(S, "A chill is felt around you, however it cannot harm you.") continue if(S.client) // Don't instantly kill player slimes. to_chat(S, "You feel your body crystalize as an intense chill overwhelms you!") - S.adjustToxLoss(S.cold_damage_per_tick * 2) + S.inflict_cold_damage(100) else - S.adjustToxLoss(S.cold_damage_per_tick * 5) // Metal slimes can survive this 'slime nuke'. + S.inflict_cold_damage(200) // Metal slimes can survive this 'slime nuke'. continue if(ishuman(L)) @@ -552,17 +552,21 @@ required = /obj/item/slime_extract/red /datum/chemical_reaction/slime/red_enrage/on_reaction(var/datum/reagents/holder) - for(var/mob/living/simple_animal/slime/S in view(get_turf(holder.my_atom))) - if(S.stat || S.docile || S.rabid) + for(var/mob/living/simple_mob/slime/S in view(get_turf(holder.my_atom))) + if(S.stat) continue + if(istype(S, /mob/living/simple_mob/slime/xenobio)) + var/mob/living/simple_mob/slime/xenobio/X = S + if(X.harmless) + continue + if(!X.client) + X.enrage() + S.add_modifier(/datum/modifier/berserk, 30 SECONDS) if(S.client) // Player slimes always have free will. to_chat(S, "An intense wave of rage is felt from inside, but you remain in control of yourself.") - continue - - S.enrage() for(var/mob/living/carbon/human/H in view(get_turf(holder.my_atom))) if(H.species.name == SPECIES_PROMETHEAN) @@ -957,8 +961,8 @@ /datum/chemical_reaction/slime/rainbow_random_slime/on_reaction(var/datum/reagents/holder) - var/mob/living/simple_animal/slime/S - var/list/slime_types = typesof(/mob/living/simple_animal/slime) + var/mob/living/simple_mob/slime/xenobio/S + var/list/slime_types = typesof(/mob/living/simple_mob/slime/xenobio) while(slime_types.len) S = pick(slime_types) diff --git a/code/modules/xenobio/items/slimepotions.dm b/code/modules/xenobio/items/slimepotions.dm index df03fdab6f..944853bec7 100644 --- a/code/modules/xenobio/items/slimepotions.dm +++ b/code/modules/xenobio/items/slimepotions.dm @@ -21,7 +21,7 @@ icon_state = "potcyan" description_info = "The slime needs to be alive for this to work. It will reduce the chances of mutation by 15%." -/obj/item/slimepotion/stabilizer/attack(mob/living/simple_animal/slime/M, mob/user) +/obj/item/slimepotion/stabilizer/attack(mob/living/simple_mob/slime/xenobio/M, mob/user) if(!istype(M)) to_chat(user, "The stabilizer only works on slimes!") return ..() @@ -45,7 +45,7 @@ description_info = "The slime needs to be alive for this to work. It will increase the chances of mutation by 12%." icon_state = "potred" -/obj/item/slimepotion/mutator/attack(mob/living/simple_animal/slime/M, mob/user) +/obj/item/slimepotion/mutator/attack(mob/living/simple_mob/slime/xenobio/M, mob/user) if(!istype(M)) to_chat(user, "The mutator only works on slimes!") return ..() @@ -67,20 +67,25 @@ name = "docility agent" desc = "A potent chemical mix that nullifies a slime's hunger, causing it to become docile and tame. It might also work on other creatures?" icon_state = "potlightpink" - description_info = "The target needs to be alive, not already passive, and have animal-like intelligence." + description_info = "The target needs to be alive, not already passive, and be an animal or slime type entity." -/obj/item/slimepotion/docility/attack(mob/living/simple_animal/M, mob/user) +/obj/item/slimepotion/docility/attack(mob/living/simple_mob/M, mob/user) if(!istype(M)) to_chat(user, "The agent only works on creatures!") return ..() if(M.stat == DEAD) to_chat(user, "\The [M] is dead!") return ..() + if(!M.has_AI()) + to_chat(user, span("warning", "\The [M] is too strongly willed for this to affect them.")) // Most likely player controlled. + return + + var/datum/ai_holder/AI = M.ai_holder // Slimes. - if(istype(M, /mob/living/simple_animal/slime)) - var/mob/living/simple_animal/slime/S = M - if(S.docile) + if(istype(M, /mob/living/simple_mob/slime/xenobio)) + var/mob/living/simple_mob/slime/xenobio/S = M + if(S.harmless) to_chat(user, "The slime is already docile!") return ..() @@ -89,22 +94,22 @@ to_chat(M, "You absorb the agent and feel your intense desire to feed melt away.") to_chat(user, "You feed the slime the agent, removing its hunger and calming it.") - // Simple Animals. - else if(istype(M, /mob/living/simple_animal)) - var/mob/living/simple_animal/SA = M - if(SA.intelligence_level > SA_ANIMAL) // So you can't use this on Russians/syndies/hivebots/etc. - to_chat(user, "\The [SA] is too intellient for this to affect them.") + // Simple Mobs. + else if(istype(M, /mob/living/simple_mob)) + var/mob/living/simple_mob/SM = M + if(!(SM.mob_class & MOB_CLASS_SLIME|MOB_CLASS_ANIMAL)) // So you can't use this on Russians/syndies/hivebots/etc. + to_chat(user, "\The [SM] only works on slimes and animals.") return ..() - if(!SA.hostile) - to_chat(user, "\The [SA] is already passive!") + if(!AI.hostile) + to_chat(user, "\The [SM] is already passive!") return ..() - SA.hostile = FALSE + AI.hostile = FALSE to_chat(M, "You consume the agent and feel a serene sense of peace.") - to_chat(user, "You feed \the [SA] the agent, calming it.") + to_chat(user, "You feed \the [SM] the agent, calming it.") playsound(src, 'sound/effects/bubbles.ogg', 50, 1) - M.LoseTarget() // So hostile things stop attacking people even if not hostile anymore. + AI.lost_target() // So hostile things stop attacking people even if not hostile anymore. var/newname = copytext(sanitize(input(user, "Would you like to give \the [M] a name?", "Name your new pet", M.name) as null|text),1,MAX_NAME_LEN) if(newname) @@ -121,7 +126,7 @@ Extra extracts are not passed down to offspring when reproducing." icon_state = "potpurple" -/obj/item/slimepotion/steroid/attack(mob/living/simple_animal/slime/M, mob/user) +/obj/item/slimepotion/steroid/attack(mob/living/simple_mob/slime/xenobio/M, mob/user) if(!istype(M)) to_chat(user, "The steroid only works on slimes!") return ..() @@ -149,7 +154,7 @@ carry over to offspring when reproducing." icon_state = "potpink" -/obj/item/slimepotion/unity/attack(mob/living/simple_animal/slime/M, mob/user) +/obj/item/slimepotion/unity/attack(mob/living/simple_mob/slime/M, mob/user) if(!istype(M)) to_chat(user, "The agent only works on slimes!") return ..() @@ -175,12 +180,12 @@ the user's faction, which means the slime will attack things that are hostile to the user's faction, such as carp, spiders, and other slimes." icon_state = "potred" -/obj/item/slimepotion/loyalty/attack(mob/living/simple_animal/M, mob/user) +/obj/item/slimepotion/loyalty/attack(mob/living/simple_mob/M, mob/user) if(!istype(M)) - to_chat(user, "The agent only works on animals!") + to_chat(user, "The agent only works on creatures!") return ..() - if(M.intelligence_level > SA_ANIMAL) // So you can't use this on Russians/syndies/hivebots/etc. - to_chat(user, "\The [M] is too intellient for this to affect them.") + if(!(M.mob_class & MOB_CLASS_SLIME|MOB_CLASS_ANIMAL)) // So you can't use this on Russians/syndies/hivebots/etc. + to_chat(user, "\The [M] only works on slimes and animals.") return ..() if(M.stat == DEAD) to_chat(user, "The animal is dead!") @@ -188,12 +193,16 @@ if(M.faction == user.faction) to_chat(user, "\The [M] is already loyal to your species!") return ..() + if(!M.has_AI()) + to_chat(user, span("warning", "\The [M] is too strong-willed for this to affect them.")) + return ..() + + var/datum/ai_holder/AI = M.ai_holder to_chat(user, "You feed \the [M] the agent. It will now try to murder things that want to murder you instead.") to_chat(M, "\The [user] feeds you \the [src], and feel that the others will regard you as an outsider now.") M.faction = user.faction - M.attack_same = FALSE - M.LoseTarget() // So hostile things stop attacking people even if not hostile anymore. + AI.lost_target() // So hostile things stop attacking people even if not hostile anymore. playsound(src, 'sound/effects/bubbles.ogg', 50, 1) qdel(src) @@ -206,28 +215,29 @@ their 'friend', and will never attack them. This might also work on other things besides slimes." icon_state = "potlightpink" -/obj/item/slimepotion/friendship/attack(mob/living/simple_animal/M, mob/user) +/obj/item/slimepotion/friendship/attack(mob/living/simple_mob/M, mob/user) if(!istype(M)) - to_chat(user, "The agent only works on animals!") + to_chat(user, "The agent only works on creatures!") return ..() - if(M.intelligence_level > SA_ANIMAL) // So you can't use this on Russians/syndies/hivebots/etc. - to_chat(user, "\The [M] is too intellient for this to affect them.") + if(!(M.mob_class & MOB_CLASS_SLIME|MOB_CLASS_ANIMAL)) // So you can't use this on Russians/syndies/hivebots/etc. + to_chat(user, "\The [M] only works on slimes and animals.") return ..() if(M.stat == DEAD) - to_chat(user, "The animal is dead!") + to_chat(user, "\The [M] is dead!") return ..() if(user in M.friends) to_chat(user, "\The [M] is already loyal to you!") return ..() + if(!M.has_AI()) + to_chat(user, span("warning", "\The [M] is too strong-willed for this to affect them.")) + return ..() + + var/datum/ai_holder/AI = M.ai_holder to_chat(user, "You feed \the [M] the agent. It will now be your best friend.") to_chat(M, "\The [user] feeds you \the [src], and feel that \the [user] wants to be best friends with you.") - if(isslime(M)) - var/mob/living/simple_animal/slime/S = M - S.befriend(user) - else - M.friends.Add(user) - M.LoseTarget() // So hostile things stop attacking people even if not hostile anymore. + M.friends.Add(user) + AI.lost_target() // So hostile things stop attacking people even if not hostile anymore. playsound(src, 'sound/effects/bubbles.ogg', 50, 1) qdel(src) @@ -239,15 +249,16 @@ description_info = "The slime needs to be alive for this to work. It will instantly grow the slime enough to reproduce." icon_state = "potyellow" -/obj/item/slimepotion/feeding/attack(mob/living/simple_animal/slime/M, mob/user) +/obj/item/slimepotion/feeding/attack(mob/living/simple_mob/slime/xenobio/M, mob/user) if(!istype(M)) - to_chat(user, "The mutator only works on slimes!") + to_chat(user, "The feeding agent only works on slimes!") return ..() if(M.stat == DEAD) to_chat(user, "The slime is dead!") return ..() to_chat(user, "You feed the slime the feeding agent. It will now instantly reproduce.") + M.amount_grown = 10 M.make_adult() M.amount_grown = 10 M.reproduce() diff --git a/code/modules/xenobio/items/weapons.dm b/code/modules/xenobio/items/weapons.dm index 8fb5f9eea6..bb735c35f2 100644 --- a/code/modules/xenobio/items/weapons.dm +++ b/code/modules/xenobio/items/weapons.dm @@ -1,6 +1,6 @@ /obj/item/weapon/melee/baton/slime name = "slimebaton" - desc = "A modified stun baton designed to stun slimes and other lesser xeno lifeforms for handling." + desc = "A modified stun baton designed to stun slimes and other lesser slimy xeno lifeforms for handling." icon_state = "slimebaton" item_state = "slimebaton" slot_flags = SLOT_BELT @@ -9,31 +9,30 @@ origin_tech = list(TECH_COMBAT = 2, TECH_BIO = 2) agonyforce = 10 //It's not supposed to be great at stunning human beings. hitcost = 48 //Less zap for less cost - description_info = "This baton will stun a slime or other lesser lifeform for about five seconds, if hit with it while on." + description_info = "This baton will stun a slime or other slime-based lifeform for about five seconds, if hit with it while on." -/obj/item/weapon/melee/baton/slime/attack(mob/M, mob/user, hit_zone) - // Simple Animals. - if(istype(M, /mob/living/simple_animal/slime) && status) - var/mob/living/simple_animal/SA = M - if(SA.intelligence_level <= SA_ANIMAL) // So it doesn't stun hivebots or syndies. - SA.Weaken(5) - if(isslime(SA)) - var/mob/living/simple_animal/slime/S = SA - S.adjust_discipline(3) +/obj/item/weapon/melee/baton/slime/attack(mob/living/L, mob/user, hit_zone) + if(istype(L) && status) // Is it on? + if(L.mob_class & MOB_CLASS_SLIME) // Are they some kind of slime? (Prommies might pass this check someday). + if(isslime(L)) + var/mob/living/simple_mob/slime/S = L + S.slimebatoned(user, 5) // Feral and xenobio slimes will react differently to this. + else + L.Weaken(5) + + // Now for prommies. + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.species && H.species.name == SPECIES_PROMETHEAN) + var/agony_to_apply = 60 - agonyforce + H.apply_damage(agony_to_apply, HALLOSS) - // Prometheans. - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.species && H.species.name == SPECIES_PROMETHEAN && status) - var/agony_to_apply = 60 - agonyforce - H.apply_damage(agony_to_apply, HALLOSS) ..() -/obj/item/weapon/melee/baton/slime/loaded/New() - ..() +/obj/item/weapon/melee/baton/slime/loaded/initialize() bcell = new/obj/item/weapon/cell/device(src) update_icon() - return + return ..() // Research borg's version @@ -61,9 +60,9 @@ charge_cost = 120 // Twice as many shots. projectile_type = /obj/item/projectile/beam/stun/xeno accuracy = 30 // Make it a bit easier to hit the slimes. - description_info = "This gun will stun a slime or other lesser lifeform for about two seconds, if hit with the projectile it fires." + description_info = "This gun will stun a slime or other lesser slimy lifeform for about two seconds, if hit with the projectile it fires." description_fluff = "An easy to use weapon designed by NanoTrasen, for NanoTrasen. This weapon is designed to subdue lesser \ - xeno lifeforms at a distance. It is ineffective at stunning larger lifeforms such as humanoids." + slime-based xeno lifeforms at a distance. It is ineffective at stunning non-slimy lifeforms such as humanoids." /obj/item/weapon/gun/energy/taser/xeno/robot // Borg version self_recharge = 1 @@ -71,7 +70,7 @@ recharge_time = 3 /obj/item/weapon/gun/energy/taser/xeno/sec //NT's corner-cutting option for their on-station security. - desc = "An NT Mk30 NL retrofitted to fire beams for subduing non-humanoid xeno life forms." + desc = "An NT Mk30 NL retrofitted to fire beams for subduing non-humanoid slimy xeno life forms." icon_state = "taserblue" item_state = "taser" projectile_type = /obj/item/projectile/beam/stun/xeno/weak @@ -102,20 +101,17 @@ /obj/item/projectile/beam/stun/xeno/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) if(istype(target, /mob/living)) var/mob/living/L = target + if(L.mob_class & MOB_CLASS_SLIME) + if(isslime(L)) + var/mob/living/simple_mob/slime/S = L + S.slimebatoned(firer, round(agony/2)) + else + L.Weaken(round(agony/2)) - // Simple Animals. - if(istype(L, /mob/living/simple_animal/slime)) - var/mob/living/simple_animal/SA = L - if(SA.intelligence_level <= SA_ANIMAL) // So it doesn't stun hivebots or syndies. - SA.Weaken(round(agony/2)) // Less powerful since its ranged, and therefore safer. - if(isslime(SA)) - var/mob/living/simple_animal/slime/S = SA - S.adjust_discipline(round(agony/2)) - - // Prometheans. if(ishuman(L)) var/mob/living/carbon/human/H = L if(H.species && H.species.name == SPECIES_PROMETHEAN) - if(agony == initial(agony)) + if(agony == initial(agony)) // ?????? agony = round((14 * agony) - agony) //60-4 = 56, 56 / 4 = 14. Prior was flat 60 - agony of the beam to equate to 60. + ..() diff --git a/code/modules/xenobio/machinery/processor.dm b/code/modules/xenobio/machinery/processor.dm index 56b7cee3bf..6d888adb26 100644 --- a/code/modules/xenobio/machinery/processor.dm +++ b/code/modules/xenobio/machinery/processor.dm @@ -79,8 +79,8 @@ playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) /obj/machinery/processor/proc/extract(var/atom/movable/AM) - if(istype(AM, /mob/living/simple_animal/slime)) - var/mob/living/simple_animal/slime/S = AM + if(istype(AM, /mob/living/simple_mob/slime)) + var/mob/living/simple_mob/slime/S = AM while(S.cores) new S.coretype(get_turf(src)) playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) @@ -98,8 +98,8 @@ sleep(1 SECOND) /obj/machinery/processor/proc/can_insert(var/atom/movable/AM) - if(istype(AM, /mob/living/simple_animal/slime)) - var/mob/living/simple_animal/slime/S = AM + if(istype(AM, /mob/living/simple_mob/slime)) + var/mob/living/simple_mob/slime/S = AM if(S.stat != DEAD) return FALSE return TRUE diff --git a/code/stylesheet.dm b/code/stylesheet.dm index 7c595b923f..dd992a62bf 100644 --- a/code/stylesheet.dm +++ b/code/stylesheet.dm @@ -110,4 +110,11 @@ h1.alert, h2.alert {color: #000000;} BIG IMG.icon {width: 32px; height: 32px;} +/* Debug Logs */ +.debug_error {color:#FF0000; font-weight:bold} +.debug_warning {color:#FF0000;} +.debug_info {} +.debug_debug {color:#0000FF;} +.debug_trace {color:#888888;} + "} diff --git a/html/changelog.html b/html/changelog.html index 91fdd23b30..6c8289bb04 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -85,7 +85,7 @@

Mechoid updated:

    -
  • Hallucinations are no longer only Pun Pun.
  • +
  • Hallucinations are no longer only Pun Pun.

Neerti updated:

    @@ -187,8 +187,8 @@

Mechoid updated:

    -
  • Promethean limbs store more damage.
  • -
  • Promethean limbs, in addition to normal severing rules, have a higher chance to be splattered or ashed once they reach maximum damage.
  • +
  • Promethean limbs store more damage.
  • +
  • Promethean limbs, in addition to normal severing rules, have a higher chance to be splattered or ashed once they reach maximum damage.
  • Limbs can now spread their damage to neighbors with the spread_dam var, when reaching max damage.

PrismaticGynoid updated:

@@ -322,7 +322,7 @@