From 284e9d03258bce07f73f3767f3929d3021a4d935 Mon Sep 17 00:00:00 2001 From: Poojawa Date: Tue, 11 Sep 2018 07:51:01 -0500 Subject: [PATCH] module things, jfc --- code/modules/VR/vr_sleeper.dm | 4 +- code/modules/admin/DB_ban/functions.dm | 34 +- code/modules/admin/IsBanned.dm | 4 +- code/modules/admin/NewBan.dm | 4 +- code/modules/admin/admin.dm | 14 +- code/modules/admin/admin_ranks.dm | 71 +- code/modules/admin/admin_verbs.dm | 2 + code/modules/admin/banjob.dm | 1 + code/modules/admin/chat_commands.dm | 6 +- code/modules/admin/check_antagonists.dm | 8 +- code/modules/admin/create_poll.dm | 1 + code/modules/admin/fun_balloon.dm | 17 +- code/modules/admin/holder2.dm | 5 +- code/modules/admin/permissionedit.dm | 4 +- code/modules/admin/secrets.dm | 103 +- code/modules/admin/sql_message_system.dm | 243 ++- code/modules/admin/topic.dm | 117 +- code/modules/admin/verbs/SDQL2/SDQL_2.dm | 9 +- .../admin/verbs/SDQL2/SDQL_2_parser.dm | 6 +- code/modules/admin/verbs/adminhelp.dm | 5 +- code/modules/admin/verbs/adminjump.dm | 5 +- code/modules/admin/verbs/adminsay.dm | 11 +- code/modules/admin/verbs/borgpanel.dm | 19 +- code/modules/admin/verbs/deadsay.dm | 9 +- code/modules/admin/verbs/debug.dm | 53 + .../modules/admin/verbs/individual_logging.dm | 71 +- .../admin/verbs/map_template_loadverb.dm | 49 +- code/modules/admin/verbs/mapping.dm | 3 +- code/modules/admin/verbs/modifyvariables.dm | 2 + code/modules/admin/verbs/one_click_antag.dm | 2 +- code/modules/admin/verbs/pray.dm | 9 +- code/modules/admin/verbs/randomverbs.dm | 108 +- .../antagonists/_common/antag_datum.dm | 3 +- .../antagonists/_common/antag_spawner.dm | 2 +- .../abductor/equipment/abduction_gear.dm | 15 +- .../antagonists/blob/blob/blobs/blob_mobs.dm | 1 + .../modules/antagonists/blob/blob/overmind.dm | 5 +- code/modules/antagonists/brother/brother.dm | 1 + .../antagonists/changeling/changeling.dm | 2 +- .../changeling/changeling_power.dm | 2 +- .../antagonists/changeling/powers/absorb.dm | 2 +- .../changeling/powers/fakedeath.dm | 3 +- .../changeling/powers/mutations.dm | 21 +- .../antagonists/changeling/powers/revive.dm | 2 +- .../changeling/powers/tiny_prick.dm | 18 +- .../clock_effects/city_of_cogs_rift.dm | 2 +- .../clockcult/clock_effects/clock_sigils.dm | 4 +- .../clock_effects/general_markers.dm | 2 +- .../clock_effects/servant_blocker.dm | 4 +- .../clock_effects/spatial_gateway.dm | 2 +- .../clock_helpers/hierophant_network.dm | 2 +- .../clock_helpers/ratvarian_language.dm | 4 +- .../clockcult/clock_helpers/slab_abilities.dm | 10 +- .../clockcult/clock_items/clock_components.dm | 2 +- .../clockcult/clock_items/clockwork_armor.dm | 10 +- .../clockcult/clock_items/clockwork_slab.dm | 2 +- .../clockcult/clock_items/judicial_visor.dm | 4 +- .../clock_items/wraith_spectacles.dm | 4 +- .../clockcult/clock_mobs/_eminence.dm | 4 +- .../ark_of_the_clockwork_justicar.dm | 2 +- .../clock_structures/ocular_warden.dm | 2 +- .../ratvar_the_clockwork_justicar.dm | 21 +- .../clock_structures/taunting_trail.dm | 4 +- .../antagonists/clockcult/clockcult.dm | 4 +- code/modules/antagonists/cult/blood_magic.dm | 32 +- code/modules/antagonists/cult/cult.dm | 85 +- code/modules/antagonists/cult/cult_comms.dm | 14 +- code/modules/antagonists/cult/cult_items.dm | 45 +- .../antagonists/cult/cult_structures.dm | 19 +- code/modules/antagonists/cult/ritual.dm | 10 +- code/modules/antagonists/cult/runes.dm | 36 +- .../devil/true_devil/_true_devil.dm | 5 +- .../antagonists/disease/disease_disease.dm | 2 +- .../antagonists/disease/disease_mob.dm | 6 +- code/modules/antagonists/ert/ert.dm | 1 + .../antagonists/highlander/highlander.dm | 1 + code/modules/antagonists/ninja/ninja.dm | 7 + .../nukeop/equipment/nuclearbomb.dm | 10 +- code/modules/antagonists/nukeop/nukeop.dm | 32 +- code/modules/antagonists/official/official.dm | 1 + .../antagonists/overthrow/overthrow.dm | 157 ++ .../overthrow/overthrow_converter.dm | 56 + .../antagonists/overthrow/overthrow_team.dm | 41 + code/modules/antagonists/pirate/pirate.dm | 58 +- code/modules/antagonists/revenant/revenant.dm | 7 +- .../revenant/revenant_abilities.dm | 2 +- .../antagonists/revolution/revolution.dm | 6 +- code/modules/antagonists/swarmer/swarmer.dm | 3 +- .../antagonists/traitor/datum_traitor.dm | 1 + .../antagonists/wishgranter/wishgranter.dm | 1 + .../antagonists/wizard/equipment/artefact.dm | 21 +- .../antagonists/wizard/equipment/soulstone.dm | 2 +- .../antagonists/wizard/equipment/spellbook.dm | 8 +- code/modules/antagonists/wizard/wizard.dm | 3 +- code/modules/assembly/assembly.dm | 2 + code/modules/assembly/doorcontrol.dm | 2 +- code/modules/assembly/flash.dm | 22 +- code/modules/assembly/health.dm | 8 +- code/modules/assembly/holder.dm | 2 + code/modules/assembly/infrared.dm | 4 +- .../environmental/LINDA_system.dm | 1 + .../atmospherics/gasmixtures/gas_types.dm | 7 + .../atmospherics/gasmixtures/reactions.dm | 29 +- .../atmospherics/machinery/airalarm.dm | 78 +- .../components/binary_devices/circulator.dm | 2 + .../components/binary_devices/volume_pump.dm | 5 + .../components/unary_devices/cryo.dm | 2 +- .../components/unary_devices/thermomachine.dm | 11 +- .../components/unary_devices/vent_pump.dm | 11 + .../components/unary_devices/vent_scrubber.dm | 20 +- .../atmospherics/machinery/other/meter.dm | 13 +- .../machinery/portable/canister.dm | 8 + .../portable/portable_atmospherics.dm | 1 + code/modules/awaymissions/away_props.dm | 30 + code/modules/awaymissions/capture_the_flag.dm | 33 +- code/modules/awaymissions/corpse.dm | 28 +- code/modules/awaymissions/gateway.dm | 4 +- .../awaymissions/mission_code/challenge.dm | 2 +- .../awaymissions/mission_code/snowdin.dm | 14 +- .../awaymissions/mission_code/spacebattle.dm | 9 + .../mission_code/stationCollision.dm | 4 +- .../awaymissions/mission_code/wildwest.dm | 4 +- code/modules/cargo/bounties/assistant.dm | 73 +- code/modules/cargo/bounties/botany.dm | 194 ++ code/modules/cargo/bounties/chef.dm | 7 - code/modules/cargo/bounties/engineering.dm | 31 + code/modules/cargo/bounties/mech.dm | 7 +- code/modules/cargo/bounties/medical.dm | 59 + code/modules/cargo/bounties/mining.dm | 51 + code/modules/cargo/bounties/science.dm | 8 +- code/modules/cargo/bounties/security.dm | 49 - code/modules/cargo/bounties/special.dm | 9 +- code/modules/cargo/bounty.dm | 86 +- code/modules/cargo/console.dm | 13 +- code/modules/cargo/export_scanner.dm | 8 +- code/modules/cargo/exports.dm | 122 +- code/modules/cargo/exports/large_objects.dm | 88 +- code/modules/cargo/exports/lavaland.dm | 65 + code/modules/cargo/exports/seeds.dm | 7 +- code/modules/cargo/exports/sheets.dm | 8 +- code/modules/cargo/exports/tools.dm | 20 + code/modules/cargo/exports/weapons.dm | 94 - code/modules/cargo/expressconsole.dm | 12 +- code/modules/cargo/packs.dm | 83 +- code/modules/cargo/supplypod_beacon.dm | 2 + code/modules/client/asset_cache.dm | 87 +- code/modules/client/client_defines.dm | 4 +- code/modules/client/client_procs.dm | 27 +- code/modules/client/player_details.dm | 3 +- code/modules/client/preferences.dm | 459 ++-- code/modules/client/preferences_savefile.dm | 83 +- code/modules/client/verbs/ooc.dm | 4 +- code/modules/client/verbs/suicide.dm | 22 +- code/modules/clothing/clothing.dm | 16 + code/modules/clothing/gloves/_gloves.dm | 4 +- code/modules/clothing/gloves/miscellaneous.dm | 2 +- code/modules/clothing/head/hardhat.dm | 2 +- code/modules/clothing/head/jobs.dm | 6 +- code/modules/clothing/head/misc.dm | 4 +- code/modules/clothing/head/misc_special.dm | 4 +- code/modules/clothing/masks/hailer.dm | 2 + code/modules/clothing/masks/miscellaneous.dm | 21 + code/modules/clothing/outfits/standard.dm | 17 +- code/modules/clothing/outfits/vv_outfit.dm | 143 ++ code/modules/clothing/shoes/_shoes.dm | 4 +- code/modules/clothing/shoes/bananashoes.dm | 2 +- code/modules/clothing/shoes/miscellaneous.dm | 8 +- code/modules/clothing/spacesuits/hardsuit.dm | 26 +- .../clothing/spacesuits/miscellaneous.dm | 16 +- code/modules/clothing/suits/cloaks.dm | 4 +- code/modules/clothing/suits/miscellaneous.dm | 24 +- .../modules/clothing/suits/reactive_armour.dm | 2 +- code/modules/clothing/suits/utility.dm | 2 +- code/modules/clothing/suits/wiz_robe.dm | 4 +- code/modules/clothing/under/accessories.dm | 5 + code/modules/clothing/under/jobs/civilian.dm | 60 +- code/modules/clothing/under/jobs/medsci.dm | 1 + code/modules/clothing/under/jobs/security.dm | 1 + code/modules/clothing/under/miscellaneous.dm | 17 +- code/modules/crafting/recipes.dm | 28 +- code/modules/detectivework/evidence.dm | 1 + .../detectivework/footprints_and_rag.dm | 8 +- code/modules/detectivework/scanner.dm | 6 + code/modules/error_handler/error_handler.dm | 14 +- code/modules/error_handler/error_viewer.dm | 4 +- code/modules/events/holiday/vday.dm | 4 +- code/modules/events/immovable_rod.dm | 2 +- code/modules/events/pirates.dm | 260 ++- code/modules/events/shuttle_loan.dm | 114 +- code/modules/events/spacevine.dm | 2 +- code/modules/events/vent_clog.dm | 16 + .../modules/events/wizard/departmentrevolt.dm | 4 +- code/modules/events/wizard/greentext.dm | 4 +- code/modules/events/wizard/imposter.dm | 2 +- code/modules/events/wizard/rpgloot.dm | 1 + code/modules/fields/fields.dm | 2 +- code/modules/flufftext/Hallucination.dm | 5 +- code/modules/food_and_drinks/drinks/drinks.dm | 5 +- .../food_and_drinks/drinks/drinks/bottle.dm | 2 +- .../drinks/drinks/drinkingglass.dm | 4 +- .../modules/food_and_drinks/food/condiment.dm | 6 +- code/modules/food_and_drinks/food/snacks.dm | 77 +- .../food_and_drinks/food/snacks/meat.dm | 23 + .../food_and_drinks/food/snacks_bread.dm | 8 +- .../food_and_drinks/food/snacks_meat.dm | 2 +- .../food_and_drinks/food/snacks_pastry.dm | 9 +- .../food_and_drinks/food/snacks_pizza.dm | 55 + .../kitchen_machinery/gibber.dm | 6 +- .../kitchen_machinery/microwave.dm | 6 + .../kitchen_machinery/smartfridge.dm | 17 +- code/modules/food_and_drinks/pizzabox.dm | 7 +- .../recipes/tablecraft/recipes_misc.dm | 16 +- .../recipes/tablecraft/recipes_pastry.dm | 10 + .../recipes/tablecraft/recipes_pizza.dm | 14 +- .../recipes/tablecraft/recipes_soup.dm | 2 +- code/modules/goonchat/browserOutput.dm | 55 +- .../browserassets/css/browserOutput.css | 4 +- code/modules/holiday/holidays.dm | 55 +- code/modules/holodeck/computer.dm | 2 +- code/modules/holodeck/holo_effect.dm | 2 +- code/modules/holodeck/turfs.dm | 8 + code/modules/hydroponics/fermenting_barrel.dm | 77 + code/modules/hydroponics/gene_modder.dm | 1 + code/modules/hydroponics/grown.dm | 4 + code/modules/hydroponics/grown/ambrosia.dm | 4 + code/modules/hydroponics/grown/apple.dm | 3 + code/modules/hydroponics/grown/banana.dm | 6 +- code/modules/hydroponics/grown/beans.dm | 2 + code/modules/hydroponics/grown/berries.dm | 11 + code/modules/hydroponics/grown/cannabis.dm | 6 +- code/modules/hydroponics/grown/cereals.dm | 4 + code/modules/hydroponics/grown/chili.dm | 3 + code/modules/hydroponics/grown/citrus.dm | 3 + .../hydroponics/grown/cocoa_vanilla.dm | 2 + code/modules/hydroponics/grown/corn.dm | 1 + code/modules/hydroponics/grown/eggplant.dm | 4 + code/modules/hydroponics/grown/flowers.dm | 6 +- .../modules/hydroponics/grown/grass_carpet.dm | 4 +- code/modules/hydroponics/grown/kudzu.dm | 1 + code/modules/hydroponics/grown/melon.dm | 3 + code/modules/hydroponics/grown/misc.dm | 6 +- code/modules/hydroponics/grown/mushrooms.dm | 12 +- code/modules/hydroponics/grown/nettle.dm | 30 +- code/modules/hydroponics/grown/onion.dm | 2 + code/modules/hydroponics/grown/pineapple.dm | 1 + code/modules/hydroponics/grown/potato.dm | 3 +- code/modules/hydroponics/grown/pumpkin.dm | 2 + code/modules/hydroponics/grown/random.dm | 8 +- code/modules/hydroponics/grown/root.dm | 4 + code/modules/hydroponics/grown/tea_coffee.dm | 2 + code/modules/hydroponics/grown/tobacco.dm | 5 +- code/modules/hydroponics/grown/tomato.dm | 9 +- .../integrated_electronics/core/analyzer.dm | 3 +- .../integrated_electronics/core/assemblies.dm | 131 +- .../integrated_electronics/core/debugger.dm | 27 +- .../core/integrated_circuit.dm | 7 + .../integrated_electronics/core/printer.dm | 24 +- .../core/special_pins/ref_pin.dm | 12 +- .../core/special_pins/selfref_pin.dm | 27 + .../integrated_electronics/subtypes/access.dm | 6 +- .../subtypes/arithmetic.dm | 30 + .../subtypes/converters.dm | 66 + .../subtypes/data_transfer.dm | 51 +- .../integrated_electronics/subtypes/input.dm | 25 +- .../integrated_electronics/subtypes/logic.dm | 2 +- .../subtypes/manipulation.dm | 85 +- .../integrated_electronics/subtypes/memory.dm | 1 + .../integrated_electronics/subtypes/output.dm | 108 +- .../subtypes/reagents.dm | 69 +- .../integrated_electronics/subtypes/smart.dm | 7 +- .../integrated_electronics/subtypes/text.dm | 29 + .../integrated_electronics/subtypes/time.dm | 37 +- code/modules/jobs/access.dm | 2 +- code/modules/jobs/job_exp.dm | 5 +- code/modules/jobs/job_types/captain.dm | 7 + code/modules/jobs/job_types/civilian.dm | 1 - code/modules/jobs/job_types/engineering.dm | 1 + code/modules/jobs/job_types/job.dm | 12 +- code/modules/jobs/job_types/medical.dm | 8 + code/modules/jobs/job_types/security.dm | 11 +- code/modules/jobs/job_types/silicon.dm | 5 +- code/modules/keybindings/readme.md | 12 +- code/modules/language/narsian.dm | 4 +- code/modules/library/lib_items.dm | 9 +- code/modules/library/lib_machines.dm | 5 +- code/modules/library/soapstone.dm | 1 + code/modules/lighting/lighting_atom.dm | 9 +- code/modules/lighting/lighting_corner.dm | 2 +- code/modules/mapping/README.txt | 52 + code/modules/mapping/map_template.dm | 31 +- code/modules/mapping/mapping_helpers.dm | 25 +- code/modules/mapping/preloader.dm | 31 + code/modules/mapping/reader.dm | 489 +++-- code/modules/mapping/verify.dm | 98 + code/modules/mining/aux_base.dm | 9 +- .../modules/mining/equipment/explorer_gear.dm | 4 +- .../mining/equipment/kinetic_crusher.dm | 1 + .../mining/equipment/lazarus_injector.dm | 1 + .../mining/equipment/regenerative_core.dm | 5 +- code/modules/mining/equipment/resonator.dm | 2 +- code/modules/mining/equipment/survival_pod.dm | 10 +- code/modules/mining/fulton.dm | 1 + code/modules/mining/laborcamp/laborstacker.dm | 1 + code/modules/mining/lavaland/ash_flora.dm | 7 +- .../mining/lavaland/necropolis_chests.dm | 39 +- code/modules/mining/lavaland/ruins/gym.dm | 107 +- code/modules/mining/machine_processing.dm | 2 +- code/modules/mining/machine_redemption.dm | 131 +- code/modules/mining/machine_silo.dm | 234 ++ code/modules/mining/machine_stacking.dm | 45 +- code/modules/mining/machine_vending.dm | 6 +- code/modules/mining/minebot.dm | 1 + code/modules/mining/mint.dm | 2 +- code/modules/mining/ores_coins.dm | 1 - code/modules/mob/camera/camera.dm | 2 +- code/modules/mob/dead/dead.dm | 38 +- .../modules/mob/dead/new_player/new_player.dm | 2 +- code/modules/mob/dead/new_player/poll.dm | 60 +- .../mob/dead/new_player/sprite_accessories.dm | 1895 ++++++++--------- code/modules/mob/dead/observer/login.dm | 4 + code/modules/mob/dead/observer/logout.dm | 1 + code/modules/mob/dead/observer/observer.dm | 8 + code/modules/mob/dead/observer/say.dm | 5 +- code/modules/mob/death.dm | 3 +- code/modules/mob/emote.dm | 4 +- code/modules/mob/inventory.dm | 8 +- code/modules/mob/living/blood.dm | 4 +- code/modules/mob/living/brain/brain_item.dm | 10 +- code/modules/mob/living/brain/say.dm | 2 +- .../mob/living/carbon/alien/alien_defense.dm | 4 +- .../carbon/alien/humanoid/alien_powers.dm | 12 +- .../carbon/alien/humanoid/caste/drone.dm | 5 - .../carbon/alien/humanoid/caste/hunter.dm | 9 +- .../carbon/alien/humanoid/caste/praetorian.dm | 9 - .../carbon/alien/humanoid/caste/sentinel.dm | 5 - .../living/carbon/alien/humanoid/humanoid.dm | 11 +- .../carbon/alien/humanoid/humanoid_defense.dm | 4 +- .../mob/living/carbon/alien/humanoid/queen.dm | 6 +- .../mob/living/carbon/alien/larva/larva.dm | 1 + .../carbon/alien/larva/larva_defense.dm | 2 +- .../mob/living/carbon/alien/larva/life.dm | 4 +- .../mob/living/carbon/alien/larva/powers.dm | 2 +- .../modules/mob/living/carbon/alien/organs.dm | 4 + code/modules/mob/living/carbon/alien/say.dm | 2 +- code/modules/mob/living/carbon/carbon.dm | 47 +- .../mob/living/carbon/carbon_movement.dm | 18 +- .../modules/mob/living/carbon/damage_procs.dm | 24 +- code/modules/mob/living/carbon/death.dm | 5 + code/modules/mob/living/carbon/human/emote.dm | 51 +- .../mob/living/carbon/human/examine.dm | 35 +- code/modules/mob/living/carbon/human/human.dm | 65 +- .../mob/living/carbon/human/human_defense.dm | 14 +- .../mob/living/carbon/human/human_defines.dm | 3 +- .../mob/living/carbon/human/human_helpers.dm | 2 +- .../mob/living/carbon/human/human_movement.dm | 44 +- code/modules/mob/living/carbon/human/life.dm | 27 +- .../mob/living/carbon/human/species.dm | 141 +- .../carbon/human/species_types/dullahan.dm | 3 +- .../carbon/human/species_types/felinid.dm | 130 ++ .../carbon/human/species_types/golems.dm | 53 +- .../carbon/human/species_types/humans.dm | 29 +- .../carbon/human/species_types/jellypeople.dm | 8 +- .../human/species_types/lizardpeople.dm | 28 +- .../carbon/human/species_types/mothmen.dm | 2 +- .../human/species_types/shadowpeople.dm | 1 + .../carbon/human/species_types/skeletons.dm | 2 +- .../carbon/human/species_types/vampire.dm | 2 +- .../carbon/human/species_types/zombies.dm | 29 +- code/modules/mob/living/carbon/life.dm | 125 +- .../mob/living/carbon/monkey/combat.dm | 4 +- code/modules/mob/living/carbon/monkey/life.dm | 4 +- .../mob/living/carbon/monkey/monkey.dm | 39 +- .../living/carbon/monkey/monkey_defense.dm | 8 +- code/modules/mob/living/carbon/say.dm | 2 +- .../modules/mob/living/carbon/status_procs.dm | 2 +- code/modules/mob/living/death.dm | 9 +- code/modules/mob/living/emote.dm | 4 +- code/modules/mob/living/life.dm | 5 +- code/modules/mob/living/living.dm | 90 +- code/modules/mob/living/living_defense.dm | 32 +- code/modules/mob/living/living_defines.dm | 4 +- code/modules/mob/living/living_movement.dm | 27 + code/modules/mob/living/say.dm | 42 +- code/modules/mob/living/silicon/ai/ai.dm | 44 +- code/modules/mob/living/silicon/ai/death.dm | 13 +- .../living/silicon/ai/freelook/cameranet.dm | 67 +- .../mob/living/silicon/ai/freelook/chunk.dm | 16 +- .../mob/living/silicon/ai/freelook/eye.dm | 81 +- code/modules/mob/living/silicon/ai/login.dm | 4 +- code/modules/mob/living/silicon/ai/logout.dm | 4 +- .../modules/mob/living/silicon/ai/multicam.dm | 5 + code/modules/mob/living/silicon/ai/say.dm | 4 +- code/modules/mob/living/silicon/pai/pai.dm | 16 +- .../mob/living/silicon/pai/pai_defense.dm | 1 - .../mob/living/silicon/pai/pai_shell.dm | 4 - code/modules/mob/living/silicon/pai/say.dm | 4 +- .../modules/mob/living/silicon/robot/death.dm | 2 +- .../modules/mob/living/silicon/robot/robot.dm | 27 +- .../mob/living/silicon/robot/robot_defense.dm | 4 +- .../living/silicon/robot/robot_movement.dm | 7 - code/modules/mob/living/silicon/say.dm | 3 +- .../mob/living/silicon/silicon_defense.dm | 6 +- .../living/simple_animal/animal_defense.dm | 6 +- .../living/simple_animal/bot/SuperBeepsky.dm | 150 ++ .../mob/living/simple_animal/bot/bot.dm | 10 +- .../living/simple_animal/bot/construction.dm | 57 + .../mob/living/simple_animal/bot/ed209bot.dm | 30 +- .../mob/living/simple_animal/bot/honkbot.dm | 31 +- .../mob/living/simple_animal/bot/medbot.dm | 12 +- .../mob/living/simple_animal/bot/mulebot.dm | 6 +- .../mob/living/simple_animal/bot/secbot.dm | 58 +- .../mob/living/simple_animal/constructs.dm | 13 +- .../mob/living/simple_animal/corpse.dm | 23 +- .../mob/living/simple_animal/damage_procs.dm | 2 +- .../mob/living/simple_animal/friendly/cat.dm | 2 + .../mob/living/simple_animal/friendly/dog.dm | 18 +- .../simple_animal/friendly/drone/_drone.dm | 4 +- .../simple_animal/friendly/farm_animals.dm | 8 + .../mob/living/simple_animal/friendly/fox.dm | 2 + .../living/simple_animal/friendly/gondola.dm | 4 +- .../living/simple_animal/friendly/penguin.dm | 4 +- .../living/simple_animal/friendly/sloth.dm | 2 + .../living/simple_animal/guardian/guardian.dm | 5 +- .../simple_animal/guardian/types/explosive.dm | 2 +- .../simple_animal/guardian/types/fire.dm | 4 +- .../mob/living/simple_animal/hostile/alien.dm | 2 + .../mob/living/simple_animal/hostile/bear.dm | 3 + .../mob/living/simple_animal/hostile/bees.dm | 19 +- .../simple_animal/hostile/bosses/boss.dm | 2 +- .../hostile/bosses/paperwizard.dm | 2 + .../living/simple_animal/hostile/faithless.dm | 2 + .../simple_animal/hostile/giant_spider.dm | 6 +- .../simple_animal/hostile/gorilla/emotes.dm | 2 +- .../simple_animal/hostile/gorilla/gorilla.dm | 6 +- .../living/simple_animal/hostile/hivebot.dm | 2 + .../living/simple_animal/hostile/hostile.dm | 90 +- .../simple_animal/hostile/jungle/leaper.dm | 5 +- .../hostile/jungle/mega_arachnid.dm | 2 + .../simple_animal/hostile/jungle/mook.dm | 2 + .../simple_animal/hostile/jungle/seedling.dm | 2 +- .../hostile/megafauna/blood_drunk_miner.dm | 2 + .../hostile/megafauna/bubblegum.dm | 4 +- .../hostile/megafauna/colossus.dm | 6 +- .../simple_animal/hostile/megafauna/drake.dm | 4 +- .../hostile/megafauna/hierophant.dm | 2 +- .../simple_animal/hostile/megafauna/legion.dm | 2 +- .../hostile/megafauna/megafauna.dm | 11 +- .../hostile/mining_mobs/goliath.dm | 2 + .../hostile/mining_mobs/hivelord.dm | 2 +- .../simple_animal/hostile/nanotrasen.dm | 2 + .../simple_animal/hostile/netherworld.dm | 4 +- .../living/simple_animal/hostile/pirate.dm | 79 +- .../simple_animal/hostile/retaliate/clown.dm | 2 + .../hostile/retaliate/spaceman.dm | 2 + .../living/simple_animal/hostile/russian.dm | 2 + .../living/simple_animal/hostile/skeleton.dm | 2 + .../living/simple_animal/hostile/statue.dm | 2 +- .../living/simple_animal/hostile/syndicate.dm | 206 +- .../living/simple_animal/hostile/wizard.dm | 2 + .../simple_animal/hostile/wumborian_fugu.dm | 1 + .../living/simple_animal/hostile/zombie.dm | 58 + .../mob/living/simple_animal/parrot.dm | 9 +- .../mob/living/simple_animal/simple_animal.dm | 42 +- .../mob/living/simple_animal/slime/slime.dm | 58 +- code/modules/mob/living/status_procs.dm | 18 +- code/modules/mob/living/ventcrawling.dm | 1 + code/modules/mob/login.dm | 2 +- code/modules/mob/logout.dm | 2 +- code/modules/mob/mob.dm | 82 +- code/modules/mob/mob_defines.dm | 12 +- code/modules/mob/mob_helpers.dm | 33 +- code/modules/mob/mob_movement.dm | 19 +- code/modules/mob/mob_movespeed.dm | 106 + code/modules/mob/say.dm | 13 +- .../computers/machinery/console_presets.dm | 38 +- .../modular_computers/documentation.md | 69 +- .../file_system/programs/ntnrc_client.dm | 2 +- code/modules/ninja/energy_katana.dm | 6 +- .../suit/n_suit_verbs/ninja_adrenaline.dm | 2 +- .../ninja/suit/n_suit_verbs/ninja_net.dm | 2 +- code/modules/orbit/orbit.dm | 5 + code/modules/paperwork/contract.dm | 4 +- code/modules/paperwork/handlabeler.dm | 3 +- code/modules/paperwork/paperplane.dm | 4 + code/modules/paperwork/pen.dm | 3 +- code/modules/paperwork/photocopier.dm | 2 +- code/modules/photography/_pictures.dm | 30 +- code/modules/photography/camera/camera.dm | 97 +- .../camera/camera_image_capturing.dm | 5 +- code/modules/photography/photos/album.dm | 36 +- code/modules/photography/photos/frame.dm | 6 + code/modules/photography/photos/photo.dm | 10 +- code/modules/power/apc.dm | 68 +- code/modules/power/generator.dm | 2 + code/modules/power/lighting.dm | 7 +- code/modules/power/power.dm | 21 +- code/modules/power/rtg.dm | 6 - code/modules/power/singularity/collector.dm | 2 + .../power/singularity/containment_field.dm | 2 +- code/modules/power/singularity/emitter.dm | 48 +- code/modules/power/singularity/generator.dm | 6 - code/modules/power/singularity/narsie.dm | 20 +- .../particle_accelerator/particle.dm | 2 +- code/modules/power/singularity/singularity.dm | 18 +- code/modules/power/smes.dm | 56 +- code/modules/power/supermatter/supermatter.dm | 18 +- code/modules/power/tesla/coil.dm | 15 +- code/modules/power/tesla/energy_ball.dm | 18 +- .../mapGenerators/repair.dm | 6 +- .../projectiles/ammunition/_ammunition.dm | 5 +- .../ammunition/ballistic/shotgun.dm | 8 +- .../projectiles/ammunition/caseless/misc.dm | 16 +- .../projectiles/ammunition/energy/gravity.dm | 47 +- .../projectiles/ammunition/special/magic.dm | 6 +- .../boxes_magazines/external/rechargable.dm | 14 +- code/modules/projectiles/gun.dm | 35 +- code/modules/projectiles/guns/ballistic.dm | 2 +- .../projectiles/guns/ballistic/automatic.dm | 9 +- .../guns/ballistic/laser_gatling.dm | 2 +- .../projectiles/guns/ballistic/pistol.dm | 7 + .../projectiles/guns/ballistic/revolver.dm | 4 + code/modules/projectiles/guns/energy/laser.dm | 2 +- .../projectiles/guns/energy/special.dm | 2 +- code/modules/projectiles/guns/magic.dm | 2 +- code/modules/projectiles/guns/magic/staff.dm | 14 +- code/modules/projectiles/guns/magic/wand.dm | 4 +- .../projectiles/guns/misc/beam_rifle.dm | 18 +- .../projectiles/guns/misc/grenade_launcher.dm | 12 +- code/modules/projectiles/pins.dm | 8 +- code/modules/projectiles/projectile.dm | 25 +- code/modules/projectiles/projectile/beams.dm | 6 +- .../projectiles/projectile/bullets/lmg.dm | 2 +- .../projectile/bullets/revolver.dm | 4 +- .../projectiles/projectile/energy/misc.dm | 2 +- .../projectiles/projectile/energy/stun.dm | 2 +- code/modules/projectiles/projectile/magic.dm | 95 +- .../projectiles/projectile/special/gravity.dm | 6 +- .../projectile/special/hallucination.dm | 4 +- .../projectiles/projectile/special/meteor.dm | 2 +- .../projectile/special/wormhole.dm | 4 +- code/modules/reagents/chemistry/holder.dm | 10 +- .../chemistry/machinery/chem_dispenser.dm | 76 +- .../reagents/chemistry/machinery/pandemic.dm | 2 +- .../chemistry/machinery/smoke_machine.dm | 4 +- code/modules/reagents/chemistry/reagents.dm | 10 +- .../chemistry/reagents/alcohol_reagents.dm | 187 +- .../chemistry/reagents/drink_reagents.dm | 13 + .../chemistry/reagents/drug_reagents.dm | 6 +- .../chemistry/reagents/food_reagents.dm | 23 +- .../chemistry/reagents/medicine_reagents.dm | 10 +- .../chemistry/reagents/other_reagents.dm | 46 +- .../chemistry/reagents/toxin_reagents.dm | 26 +- .../reagents/chemistry/recipes/others.dm | 2 +- .../chemistry/recipes/pyrotechnics.dm | 25 + .../chemistry/recipes/slime_extracts.dm | 108 +- .../reagents/chemistry/recipes/toxins.dm | 6 + code/modules/reagents/reagent_containers.dm | 7 +- .../reagents/reagent_containers/borghydro.dm | 5 +- .../reagents/reagent_containers/bottle.dm | 102 + .../reagents/reagent_containers/dropper.dm | 3 +- .../reagents/reagent_containers/glass.dm | 114 +- .../reagents/reagent_containers/hypospray.dm | 4 +- .../reagents/reagent_containers/medspray.dm | 4 +- .../reagents/reagent_containers/patch.dm | 4 +- .../reagents/reagent_containers/pill.dm | 44 +- .../reagents/reagent_containers/spray.dm | 5 +- .../reagents/reagent_containers/syringes.dm | 18 +- code/modules/reagents/reagent_dispenser.dm | 11 +- code/modules/recycling/conveyor2.dm | 2 + code/modules/recycling/disposal/bin.dm | 4 +- code/modules/recycling/disposal/holder.dm | 1 + code/modules/recycling/sortingmachinery.dm | 8 +- code/modules/research/designs.dm | 2 + .../research/designs/AI_module_designs.dm | 30 +- .../research/designs/autolathe_designs.dm | 12 +- .../research/designs/biogenerator_designs.dm | 10 +- .../research/designs/comp_board_designs.dm | 2 + .../research/designs/machine_designs.dm | 80 +- .../designs/mechfabricator_designs.dm | 9 + .../research/designs/medical_designs.dm | 161 +- code/modules/research/designs/misc_designs.dm | 20 + .../research/designs/nanite_designs.dm | 4 +- .../research/designs/smelting_designs.dm | 36 +- .../research/designs/weapon_designs.dm | 2 +- code/modules/research/destructive_analyzer.dm | 13 +- code/modules/research/experimentor.dm | 2 +- .../modules/research/machinery/_production.dm | 82 +- .../research/nanites/nanite_chamber.dm | 8 +- .../nanites/nanite_cloud_controller.dm | 2 +- .../research/nanites/nanite_hijacker.dm | 5 + .../research/nanites/nanite_programs.dm | 2 +- .../nanites/nanite_programs/buffing.dm | 8 +- .../nanites/nanite_programs/healing.dm | 23 +- .../nanites/nanite_programs/suppression.dm | 2 +- .../research/nanites/public_chamber.dm | 8 +- code/modules/research/rdconsole.dm | 63 +- code/modules/research/techweb/_techweb.dm | 1 - .../modules/research/techweb/_techweb_node.dm | 2 +- code/modules/research/techweb/all_nodes.dm | 140 +- .../xenobiology/crossbreeding/_corecross.dm | 4 +- .../crossbreeding/_status_effects.dm | 5 +- .../xenobiology/crossbreeding/burning.dm | 4 +- .../xenobiology/crossbreeding/charged.dm | 8 +- .../xenobiology/crossbreeding/consuming.dm | 2 +- .../xenobiology/crossbreeding/industrial.dm | 2 +- .../xenobiology/crossbreeding/prismatic.dm | 1 + .../xenobiology/crossbreeding/recurring.dm | 4 +- .../xenobiology/crossbreeding/regenerative.dm | 5 +- .../crossbreeding/selfsustaining.dm | 6 +- .../xenobiology/crossbreeding/stabilized.dm | 4 +- .../research/xenobiology/xenobio_camera.dm | 2 +- .../research/xenobiology/xenobiology.dm | 50 +- .../ruins/lavalandruin_code/pizzaparty.dm | 9 + .../modules/ruins/lavalandruin_code/puzzle.dm | 12 +- .../ruins/lavalandruin_code/syndicate_base.dm | 22 + .../ruins/objects_and_mobs/sin_ruins.dm | 2 +- code/modules/ruins/spaceruin_code/miracle.dm | 4 + .../ruins/spaceruin_code/oldstation.dm | 4 + code/modules/shuttle/arrivals.dm | 8 +- code/modules/shuttle/assault_pod.dm | 3 +- code/modules/shuttle/computer.dm | 3 + code/modules/shuttle/docking.dm | 21 +- code/modules/shuttle/elevator.dm | 3 +- code/modules/shuttle/emergency.dm | 58 +- code/modules/shuttle/manipulator.dm | 121 +- code/modules/shuttle/navigation_computer.dm | 24 +- code/modules/shuttle/on_move.dm | 8 +- code/modules/shuttle/shuttle.dm | 84 +- code/modules/shuttle/shuttle_rotate.dm | 8 +- code/modules/shuttle/special.dm | 2 +- code/modules/shuttle/supply.dm | 33 +- code/modules/shuttle/white_ship.dm | 33 +- code/modules/spells/spell.dm | 6 +- .../spells/spell_types/area_teleport.dm | 4 +- .../spells/spell_types/construct_spells.dm | 4 +- code/modules/spells/spell_types/godhand.dm | 10 +- .../spells/spell_types/rightandwrong.dm | 4 +- .../spell_types/spacetime_distortion.dm | 3 +- code/modules/spells/spell_types/summonitem.dm | 2 +- code/modules/spells/spell_types/wizard.dm | 2 +- code/modules/station_goals/bsa.dm | 4 +- code/modules/station_goals/dna_vault.dm | 2 +- .../advanced/bioware/nerve_grounding.dm | 8 +- .../advanced/bioware/nerve_splicing.dm | 8 +- .../advanced/bioware/vein_threading.dm | 8 +- code/modules/surgery/advanced/brainwashing.dm | 3 +- code/modules/surgery/advanced/lobotomy.dm | 8 +- .../surgery/advanced/necrotic_revival.dm | 8 +- code/modules/surgery/advanced/pacification.dm | 8 +- .../surgery/advanced/reconstruction.dm | 8 +- code/modules/surgery/advanced/revival.dm | 8 +- .../modules/surgery/advanced/viral_bonding.dm | 8 +- code/modules/surgery/bodyparts/bodyparts.dm | 154 +- .../surgery/bodyparts/dismemberment.dm | 21 +- code/modules/surgery/bodyparts/head.dm | 7 + code/modules/surgery/bodyparts/helpers.dm | 57 +- .../surgery/bodyparts/robot_bodyparts.dm | 26 + code/modules/surgery/dental_implant.dm | 2 +- code/modules/surgery/helpers.dm | 2 +- code/modules/surgery/limb_augmentation.dm | 23 +- code/modules/surgery/organ_manipulation.dm | 2 +- code/modules/surgery/organic_steps.dm | 2 +- code/modules/surgery/organs/augments_arms.dm | 3 + .../surgery/organs/augments_internal.dm | 2 +- code/modules/surgery/organs/autosurgeon.dm | 2 + code/modules/surgery/organs/eyes.dm | 4 +- code/modules/surgery/organs/heart.dm | 4 +- code/modules/surgery/organs/liver.dm | 2 +- code/modules/surgery/organs/lungs.dm | 59 +- code/modules/surgery/organs/stomach.dm | 6 +- code/modules/surgery/organs/tails.dm | 5 +- code/modules/surgery/organs/tongue.dm | 2 +- code/modules/surgery/surgery.dm | 12 +- code/modules/surgery/surgery_step.dm | 2 + code/modules/surgery/tools.dm | 27 + code/modules/unit_tests/_unit_tests.dm | 10 +- code/modules/unit_tests/spawn_humans.dm | 7 + code/modules/unit_tests/timer_sanity.dm | 3 + code/modules/uplink/uplink_items.dm | 76 +- code/modules/vehicles/_vehicle.dm | 15 +- code/modules/vehicles/cars/car.dm | 78 + code/modules/vehicles/cars/clowncar.dm | 130 ++ code/modules/vehicles/entered.dm | 62 +- code/modules/vehicles/ridden.dm | 5 +- code/modules/vehicles/scooter.dm | 24 +- code/modules/vehicles/speedbike.dm | 10 +- code/modules/vehicles/vehicle_actions.dm | 54 + code/modules/vending/boozeomat.dm | 3 + code/modules/vending/cigarette.dm | 10 + code/modules/vending/medical.dm | 4 + code/modules/vending/medical_wall.dm | 6 + code/modules/zombie/items.dm | 5 +- code/modules/zombie/organs.dm | 22 +- .../carbon/human/species_types/furrypeople.dm | 102 +- .../carbon/human/species_types/jellypeople.dm | 6 +- 695 files changed, 11343 insertions(+), 5661 deletions(-) create mode 100644 code/modules/antagonists/overthrow/overthrow.dm create mode 100644 code/modules/antagonists/overthrow/overthrow_converter.dm create mode 100644 code/modules/antagonists/overthrow/overthrow_team.dm create mode 100644 code/modules/awaymissions/away_props.dm create mode 100644 code/modules/cargo/bounties/botany.dm create mode 100644 code/modules/cargo/bounties/engineering.dm create mode 100644 code/modules/cargo/bounties/medical.dm create mode 100644 code/modules/cargo/bounties/mining.dm create mode 100644 code/modules/cargo/exports/lavaland.dm create mode 100644 code/modules/clothing/outfits/vv_outfit.dm create mode 100644 code/modules/hydroponics/fermenting_barrel.dm create mode 100644 code/modules/integrated_electronics/core/special_pins/selfref_pin.dm create mode 100644 code/modules/integrated_electronics/subtypes/text.dm create mode 100644 code/modules/mapping/README.txt create mode 100644 code/modules/mapping/preloader.dm create mode 100644 code/modules/mapping/verify.dm create mode 100644 code/modules/mining/machine_silo.dm create mode 100644 code/modules/mob/living/carbon/human/species_types/felinid.dm create mode 100644 code/modules/mob/living/living_movement.dm create mode 100644 code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm create mode 100644 code/modules/mob/living/simple_animal/hostile/zombie.dm create mode 100644 code/modules/mob/mob_movespeed.dm create mode 100644 code/modules/ruins/lavalandruin_code/pizzaparty.dm create mode 100644 code/modules/ruins/lavalandruin_code/syndicate_base.dm create mode 100644 code/modules/ruins/spaceruin_code/miracle.dm create mode 100644 code/modules/unit_tests/spawn_humans.dm create mode 100644 code/modules/unit_tests/timer_sanity.dm create mode 100644 code/modules/vehicles/cars/car.dm create mode 100644 code/modules/vehicles/cars/clowncar.dm diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm index c36f0f040b..eed6f9b3ae 100644 --- a/code/modules/VR/vr_sleeper.dm +++ b/code/modules/VR/vr_sleeper.dm @@ -95,7 +95,7 @@ SStgui.close_user_uis(occupant, src) vr_human.real_mind = human_occupant.mind vr_human.ckey = human_occupant.ckey - to_chat(vr_human, "Transfer successful! you are now playing as [vr_human] in VR!") + to_chat(vr_human, "Transfer successful! You are now playing as [vr_human] in VR!") else if(allow_creating_vr_humans) to_chat(occupant, "Virtual avatar not found, attempting to create one...") @@ -104,7 +104,7 @@ if(T) SStgui.close_user_uis(occupant, src) build_virtual_human(occupant, T, V.vr_outfit) - to_chat(vr_human, "Transfer successful! you are now playing as [vr_human] in VR!") + to_chat(vr_human, "Transfer successful! You are now playing as [vr_human] in VR!") else to_chat(occupant, "Virtual world misconfigured, aborting transfer") else diff --git a/code/modules/admin/DB_ban/functions.dm b/code/modules/admin/DB_ban/functions.dm index ceabbb0734..8a426b9115 100644 --- a/code/modules/admin/DB_ban/functions.dm +++ b/code/modules/admin/DB_ban/functions.dm @@ -80,8 +80,8 @@ var/client/banned_client = banned_mob?.client var/banned_mob_guest_key = had_banned_mob && IsGuestKey(banned_mob.key) banned_mob = null - - var/datum/DBQuery/query_add_ban_get_ckey = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("player")] WHERE ckey = '[ckey]'") + var/sql_ckey = sanitizeSQL(ckey) + var/datum/DBQuery/query_add_ban_get_ckey = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'") if(!query_add_ban_get_ckey.warn_execute()) qdel(query_add_ban_get_ckey) return @@ -123,9 +123,9 @@ adminwho += ", [C]" reason = sanitizeSQL(reason) - + var/sql_a_ckey = sanitizeSQL(a_ckey) if(maxadminbancheck) - var/datum/DBQuery/query_check_adminban_amt = SSdbcore.NewQuery("SELECT count(id) AS num FROM [format_table_name("ban")] WHERE (a_ckey = '[a_ckey]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)") + var/datum/DBQuery/query_check_adminban_amt = SSdbcore.NewQuery("SELECT count(id) AS num FROM [format_table_name("ban")] WHERE (a_ckey = '[sql_a_ckey]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)") if(!query_check_adminban_amt.warn_execute()) qdel(query_check_adminban_amt) return @@ -143,7 +143,12 @@ computerid = "0" if(!ip) ip = "0.0.0.0" - var/sql = "INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', INET_ATON('[ip]'), '[a_ckey]', '[a_computerid]', INET_ATON('[a_ip]'), '[who]', '[adminwho]')" + var/sql_job = sanitizeSQL(job) + var/sql_computerid = sanitizeSQL(computerid) + var/sql_ip = sanitizeSQL(ip) + var/sql_a_computerid = sanitizeSQL(a_computerid) + var/sql_a_ip = sanitizeSQL(a_ip) + var/sql = "INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]', '[bantype_str]', '[reason]', '[sql_job]', [(duration)?"[duration]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[sql_ckey]', '[sql_computerid]', INET_ATON('[sql_ip]'), '[sql_a_ckey]', '[sql_a_computerid]', INET_ATON('[sql_a_ip]'), '[who]', '[adminwho]')" var/datum/DBQuery/query_add_ban = SSdbcore.NewQuery(sql) if(!query_add_ban.warn_execute()) qdel(query_add_ban) @@ -207,10 +212,11 @@ bantype_sql = "(bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now() ) )" else bantype_sql = "bantype = '[bantype_str]'" - - var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = '[ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)" + var/sql_ckey = sanitizeSQL(ckey) + var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)" if(job) - sql += " AND job = '[job]'" + var/sql_job = sanitizeSQL(job) + sql += " AND job = '[sql_job]'" if(!SSdbcore.Connect()) return @@ -252,7 +258,7 @@ to_chat(usr, "Cancelled") return - var/datum/DBQuery/query_edit_ban_get_details = SSdbcore.NewQuery("SELECT (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), duration, reason FROM [format_table_name("ban")] WHERE id = [banid]") + var/datum/DBQuery/query_edit_ban_get_details = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), duration, reason FROM [format_table_name("ban")] WHERE id = [banid]") if(!query_edit_ban_get_details.warn_execute()) qdel(query_edit_ban_get_details) return @@ -319,7 +325,7 @@ if(!check_rights(R_BAN)) return - var/sql = "SELECT (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey) FROM [format_table_name("ban")] WHERE id = [id]" + var/sql = "SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey) FROM [format_table_name("ban")] WHERE id = [id]" if(!SSdbcore.Connect()) return @@ -406,6 +412,12 @@ output += "IP: " output += "Computer id: " output += "Duration: " + output += "Severity:" output += "" for(var/j in get_all_jobs()) @@ -477,7 +489,7 @@ output += "OPTIONS" output += "" var/limit = " LIMIT [bansperpage * page], [bansperpage]" - var/datum/DBQuery/query_search_bans = SSdbcore.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), unbanned, (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].unbanned_ckey), unbanned_datetime, edits, round_id FROM [format_table_name("ban")] WHERE [search] ORDER BY bantime DESC[limit]") + var/datum/DBQuery/query_search_bans = SSdbcore.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey), unbanned, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].unbanned_ckey), unbanned_ckey), unbanned_datetime, edits, round_id FROM [format_table_name("ban")] WHERE [search] ORDER BY bantime DESC[limit]") if(!query_search_bans.warn_execute()) qdel(query_search_bans) return diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index 91d0c57ac8..839292871c 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -75,8 +75,8 @@ if(computer_id) cidquery = " OR computerid = '[computer_id]' " - var/datum/DBQuery/query_ban_check = SSdbcore.NewQuery("SELECT (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), reason, expiration_time, duration, bantime, bantype, id, round_id FROM [format_table_name("ban")] WHERE (ckey = '[ckey]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND isnull(unbanned)") - if(!query_ban_check.Execute()) + var/datum/DBQuery/query_ban_check = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey), reason, expiration_time, duration, bantime, bantype, id, round_id FROM [format_table_name("ban")] WHERE (ckey = '[ckey]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND isnull(unbanned)") + if(!query_ban_check.Execute(async = TRUE)) qdel(query_ban_check) return while(query_ban_check.NextRow()) diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index d81d037c50..7905a8d155 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -123,9 +123,9 @@ GLOBAL_PROTECT(Banlist) if (temp) WRITE_FILE(GLOB.Banlist["minutes"], bantimestamp) if(!temp) - create_message("note", key, bannedby, "Permanently banned - [reason]", null, null, 0, 0) + create_message("note", key, bannedby, "Permanently banned - [reason]", null, null, 0, 0, null, 0, 0) else - create_message("note", key, bannedby, "Banned for [minutes] minutes - [reason]", null, null, 0, 0) + create_message("note", key, bannedby, "Banned for [minutes] minutes - [reason]", null, null, 0, 0, null, 0, 0) return 1 /proc/RemoveBan(foldername) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index d280f5040e..76cc17b791 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -29,7 +29,7 @@ body += "Options panel for [M]" if(M.client) body += " played by [M.client] " - body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" + body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" if(CONFIG_GET(flag/use_exp_tracking)) body += "\[" + M.client.get_exp_living() + "\]" @@ -50,6 +50,10 @@ body += "\[decrease\] " body += "\[set\] " body += "\[zero\]" + var/full_version = "Unknown" + if(M.client.byond_version) + full_version = "[M.client.byond_version].[M.client.byond_build ? M.client.byond_build : "xxx"]" + body += "
\[Byond version: [full_version]\]
" body += "

\[ " @@ -241,7 +245,7 @@ if(CHANNEL.is_admin_channel) dat+="[CHANNEL.channel_name]
" else - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
" + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
" dat+="

Refresh" dat+="
Back" if(2) @@ -313,7 +317,7 @@ dat+="No feed channels found active...
" else for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
" + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
" dat+="
Cancel" if(11) dat+="Nanotrasen D-Notice Handler
" @@ -324,7 +328,7 @@ dat+="No feed channels found active...
" else for(var/datum/newscaster/feed_channel/CHANNEL in GLOB.news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ()]
" + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
" dat+="
Back" if(12) @@ -546,7 +550,7 @@ /datum/admins/proc/toggleaooc() set category = "Server" - set desc="Toggle dis bitch" + set desc="Toggle to bitch" set name="Toggle Antag OOC" toggle_aooc() log_admin("[key_name(usr)] toggled Antagonist OOC.") diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index b6824cc1a0..af6f3e2b2b 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -42,9 +42,6 @@ GLOBAL_PROTECT(protected_ranks) return QDEL_HINT_LETMELIVE . = ..() -/datum/admin_rank/can_vv_get(var_name) - return FALSE - /datum/admin_rank/vv_edit_var(var_name, var_value) return FALSE @@ -74,7 +71,7 @@ GLOBAL_PROTECT(protected_ranks) if("varedit") flag = R_VAREDIT if("everything","host","all") - flag = ALL + flag = R_EVERYTHING if("sound","sounds") flag = R_SOUNDS if("spawn","create") @@ -112,6 +109,22 @@ GLOBAL_PROTECT(protected_ranks) if(flag) return ((rank.rights & flag) == flag) //true only if right has everything in flag +/proc/sync_ranks_with_db() + set waitfor = FALSE + + if(IsAdminAdvancedProcCall()) + to_chat(usr, "Admin rank DB Sync blocked: Advanced ProcCall detected.") + return + + var/list/sql_ranks = list() + for(var/datum/admin_rank/R in GLOB.protected_ranks) + var/sql_rank = sanitizeSQL(R.name) + var/sql_flags = sanitizeSQL(R.include_rights) + var/sql_exclude_flags = sanitizeSQL(R.exclude_rights) + var/sql_can_edit_flags = sanitizeSQL(R.can_edit_rights) + sql_ranks += list(list("rank" = "'[sql_rank]'", "flags" = "[sql_flags]", "exclude_flags" = "[sql_exclude_flags]", "can_edit_flags" = "[sql_can_edit_flags]")) + SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE) + //load our rank - > rights associations /proc/load_admin_ranks(dbfail, no_update) if(IsAdminAdvancedProcCall()) @@ -139,14 +152,7 @@ GLOBAL_PROTECT(protected_ranks) if(!CONFIG_GET(flag/admin_legacy_system) || dbfail) if(CONFIG_GET(flag/load_legacy_ranks_only)) if(!no_update) - var/list/sql_ranks = list() - for(var/datum/admin_rank/R in GLOB.admin_ranks) - var/sql_rank = sanitizeSQL(R.name) - var/sql_flags = sanitizeSQL(R.include_rights) - var/sql_exclude_flags = sanitizeSQL(R.exclude_rights) - var/sql_can_edit_flags = sanitizeSQL(R.can_edit_rights) - sql_ranks += list(list("rank" = "'[sql_rank]'", "flags" = "[sql_flags]", "exclude_flags" = "[sql_exclude_flags]", "can_edit_flags" = "[sql_can_edit_flags]")) - SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE, blocking = TRUE) + sync_ranks_with_db() else var/datum/DBQuery/query_load_admin_ranks = SSdbcore.NewQuery("SELECT rank, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]") if(!query_load_admin_ranks.Execute()) @@ -172,20 +178,23 @@ GLOBAL_PROTECT(protected_ranks) qdel(query_load_admin_ranks) //load ranks from backup file if(dbfail) - var/backup_file = file("data/admins_backup.json") - if(!fexists(backup_file)) + var/backup_file = file2text("data/admins_backup.json") + if(backup_file == null) log_world("Unable to locate admins backup file.") - return - var/list/json = json_decode(file2text(backup_file)) + return FALSE + var/list/json = json_decode(backup_file) for(var/J in json["ranks"]) + var/skip for(var/datum/admin_rank/R in GLOB.admin_ranks) if(R.name == "[J]") //this rank was already loaded from txt override - continue + skip = TRUE + if(skip) + continue var/datum/admin_rank/R = new("[J]", json["ranks"]["[J]"]["include rights"], json["ranks"]["[J]"]["exclude rights"], json["ranks"]["[J]"]["can edit rights"]) if(!R) continue GLOB.admin_ranks += R - return 1 + return json #ifdef TESTING var/msg = "Permission Sets Built:\n" for(var/datum/admin_rank/R in GLOB.admin_ranks) @@ -210,7 +219,8 @@ GLOBAL_PROTECT(protected_ranks) GLOB.admins.Cut() GLOB.protected_admins.Cut() GLOB.deadmins.Cut() - dbfail = load_admin_ranks(dbfail, no_update) + var/list/backup_file_json = load_admin_ranks(dbfail, no_update) + dbfail = backup_file_json != null //Clear profile access for(var/A in world.GetConfig("admin")) world.SetConfig("APP/admin", A, null) @@ -251,16 +261,23 @@ GLOBAL_PROTECT(protected_ranks) qdel(query_load_admins) //load admins from backup file if(dbfail) - var/backup_file = file("data/admins_backup.json") - if(!fexists(backup_file)) - log_world("Unable to locate admins backup file.") - return - var/list/json = json_decode(file2text(backup_file)) - for(var/J in json["admins"]) + if(!backup_file_json) + if(backup_file_json != null) + //already tried + return + var/backup_file = file2text("data/admins_backup.json") + if(backup_file == null) + log_world("Unable to locate admins backup file.") + return + backup_file_json = json_decode(backup_file) + for(var/J in backup_file_json["admins"]) + var/skip for(var/A in GLOB.admin_datums + GLOB.deadmins) if(A == "[J]") //this admin was already loaded from txt override - continue - new /datum/admins(rank_names[ckeyEx(json["admins"]["[J]"])], ckey("[J]")) + skip = TRUE + if(skip) + continue + new /datum/admins(rank_names[ckeyEx(backup_file_json["admins"]["[J]"])], ckey("[J]")) #ifdef TESTING var/msg = "Admins Built:\n" for(var/ckey in GLOB.admin_datums) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index b34e02bb99..47368a664d 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -72,6 +72,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin()) /client/proc/stop_sounds, /client/proc/hide_verbs, /*hides all our adminverbs*/ /client/proc/hide_most_verbs /*hides all our hideable adminverbs*/ + /datum/admins/proc/open_borgopanel ) GLOBAL_PROTECT(admin_verbs_ban) GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel)) @@ -161,6 +162,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug()) /client/proc/pump_random_event, /client/proc/cmd_display_init_log, /client/proc/cmd_display_overlay_log, + /client/proc/reload_configuration, /datum/admins/proc/create_or_modify_area, ) GLOBAL_PROTECT(admin_verbs_possess) diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index fbb97fcea7..20a7228fb5 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -30,6 +30,7 @@ C.jobbancache = list() var/datum/DBQuery/query_jobban_build_cache = SSdbcore.NewQuery("SELECT job, reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(C.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)") if(!query_jobban_build_cache.warn_execute()) + qdel(query_jobban_build_cache) return while(query_jobban_build_cache.NextRow()) C.jobbancache[query_jobban_build_cache.item[1]] = query_jobban_build_cache.item[2] diff --git a/code/modules/admin/chat_commands.dm b/code/modules/admin/chat_commands.dm index c4e5679c76..b222d0d8a6 100644 --- a/code/modules/admin/chat_commands.dm +++ b/code/modules/admin/chat_commands.dm @@ -28,7 +28,7 @@ return last_irc_check = rtod var/server = CONFIG_GET(string/server) - return "[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.config.map_name]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]" //CIT CHANGE - obfuscates the current gamemode from players + return "[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.config.map_name], Mode: [GLOB.master_mode]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]" /datum/tgs_chat_command/ahelp name = "ahelp" @@ -84,7 +84,7 @@ GLOBAL_LIST(round_end_notifiees) if(!SSticker.IsRoundInProgress() && SSticker.HasRoundStarted()) return "[sender.mention], the round has already ended!" LAZYINITLIST(GLOB.round_end_notifiees) - GLOB.round_end_notifiees[sender] = TRUE + GLOB.round_end_notifiees[sender.mention] = TRUE return "I will notify [sender.mention] when the round ends." /datum/tgs_chat_command/sdql @@ -116,4 +116,4 @@ GLOBAL_LIST(round_end_notifiees) /datum/tgs_chat_command/reload_admins/proc/ReloadAsync() set waitfor = FALSE - load_admins() \ No newline at end of file + load_admins() diff --git a/code/modules/admin/check_antagonists.dm b/code/modules/admin/check_antagonists.dm index 9adc583705..30fa664f42 100644 --- a/code/modules/admin/check_antagonists.dm +++ b/code/modules/admin/check_antagonists.dm @@ -154,12 +154,12 @@ else dat += "ETA: [(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]
" dat += "Continuous Round Status
" - dat += "[CONFIG_GET(keyed_flag_list/continuous)[SSticker.mode.config_tag] ? "Continue if antagonists die" : "End on antagonist death"]" - if(CONFIG_GET(keyed_flag_list/continuous)[SSticker.mode.config_tag]) - dat += ", [CONFIG_GET(keyed_flag_list/midround_antag)[SSticker.mode.config_tag] ? "creating replacement antagonists" : "not creating new antagonists"]
" + dat += "[CONFIG_GET(keyed_list/continuous)[SSticker.mode.config_tag] ? "Continue if antagonists die" : "End on antagonist death"]" + if(CONFIG_GET(keyed_list/continuous)[SSticker.mode.config_tag]) + dat += ", [CONFIG_GET(keyed_list/midround_antag)[SSticker.mode.config_tag] ? "creating replacement antagonists" : "not creating new antagonists"]
" else dat += "
" - if(CONFIG_GET(keyed_flag_list/midround_antag)[SSticker.mode.config_tag]) + if(CONFIG_GET(keyed_list/midround_antag)[SSticker.mode.config_tag]) dat += "Time limit: [CONFIG_GET(number/midround_antag_time_check)] minutes into round
" dat += "Living crew limit: [CONFIG_GET(number/midround_antag_life_check) * 100]% of crew alive
" dat += "If limits past: [SSticker.mode.round_ends_with_antag_death ? "End The Round" : "Continue As Extended"]
" diff --git a/code/modules/admin/create_poll.dm b/code/modules/admin/create_poll.dm index f098dc1f05..9f002f92e0 100644 --- a/code/modules/admin/create_poll.dm +++ b/code/modules/admin/create_poll.dm @@ -43,6 +43,7 @@ var/checktime = text2num(query_validate_time.item[1]) if(!checktime) to_chat(src, "Datetime entered is improperly formatted or not later than current server time.") + qdel(query_validate_time) return endtime = query_validate_time.item[1] qdel(query_validate_time) diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm index c88c610841..da811a1974 100644 --- a/code/modules/admin/fun_balloon.dm +++ b/code/modules/admin/fun_balloon.dm @@ -102,19 +102,6 @@ qdel(src) -//Shuttle Build - -/obj/effect/shuttle_build - name = "shuttle_build" - desc = "Some assembly required." - icon = 'icons/obj/items_and_weapons.dmi' - icon_state = "syndballoon" - anchored = TRUE - -/obj/effect/shuttle_build/New() - SSshuttle.emergency.initiate_docking(SSshuttle.getDock("emergency_home")) - qdel(src) - //Arena /obj/effect/forcefield/arena_shuttle @@ -127,7 +114,7 @@ for(var/obj/effect/landmark/shuttle_arena_safe/exit in GLOB.landmarks_list) warp_points += exit -/obj/effect/forcefield/arena_shuttle/CollidedWith(atom/movable/AM) +/obj/effect/forcefield/arena_shuttle/Bumped(atom/movable/AM) if(!isliving(AM)) return @@ -158,7 +145,7 @@ timeleft = 0 var/list/warp_points = list() -/obj/effect/forcefield/arena_shuttle_entrance/CollidedWith(atom/movable/AM) +/obj/effect/forcefield/arena_shuttle_entrance/Bumped(atom/movable/AM) if(!isliving(AM)) return diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index fd526e4aab..11fb4e7a8b 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -137,7 +137,7 @@ GLOBAL_PROTECT(href_token) /datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other) if(!other) return 1 //they have no rights - if(rank.rights == 65535) + if(rank.rights == R_EVERYTHING) return 1 //we have all the rights if(src == other) return 1 //you always have more rights than yourself @@ -146,9 +146,6 @@ GLOBAL_PROTECT(href_token) return 1 //we have all the rights they have and more return 0 -/datum/admins/can_vv_get(var_name, var_value) - return FALSE //nice try trialmin - /datum/admins/vv_edit_var(var_name, var_value) return FALSE //nice try trialmin diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm index c962aaac41..b77b7465e8 100644 --- a/code/modules/admin/permissionedit.dm +++ b/code/modules/admin/permissionedit.dm @@ -44,7 +44,7 @@ pagecount++ output += "|" var/limit = " LIMIT [logssperpage * page], [logssperpage]" - var/datum/DBQuery/query_search_admin_logs = SSdbcore.NewQuery("SELECT datetime, round_id, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), operation, IF(ckey IS NULL, target, byond_key), log FROM [format_table_name("admin_log")] LEFT JOIN [format_table_name("player")] ON target = ckey[search] ORDER BY datetime DESC[limit]") + var/datum/DBQuery/query_search_admin_logs = SSdbcore.NewQuery("SELECT datetime, round_id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), operation, IF(ckey IS NULL, target, byond_key), log FROM [format_table_name("admin_log")] LEFT JOIN [format_table_name("player")] ON target = ckey[search] ORDER BY datetime DESC[limit]") if(!query_search_admin_logs.warn_execute()) qdel(query_search_admin_logs) return @@ -59,7 +59,7 @@ qdel(query_search_admin_logs) if(action == 2) output += "

Admin ckeys with invalid ranks

" - var/datum/DBQuery/query_check_admin_errors = SSdbcore.NewQuery("SELECT (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("admin")].ckey), [format_table_name("admin")].rank FROM [format_table_name("admin")] LEFT JOIN [format_table_name("admin_ranks")] ON [format_table_name("admin_ranks")].rank = [format_table_name("admin")].rank WHERE [format_table_name("admin_ranks")].rank IS NULL") + var/datum/DBQuery/query_check_admin_errors = SSdbcore.NewQuery("SELECT IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("admin")].ckey), ckey), [format_table_name("admin")].rank FROM [format_table_name("admin")] LEFT JOIN [format_table_name("admin_ranks")] ON [format_table_name("admin_ranks")].rank = [format_table_name("admin")].rank WHERE [format_table_name("admin_ranks")].rank IS NULL") if(!query_check_admin_errors.warn_execute()) qdel(query_check_admin_errors) return diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index 95e040ebd5..66358dc080 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -8,8 +8,8 @@ General Secrets

Admin Log
- Mentor Log
Show Admin List
+ Mentor Log

"} @@ -66,6 +66,7 @@ Break all lights
Fix all lights
The floor is lava! (DANGEROUS: extremely lame)
+ Spawn a custom portal storm

Flip client movement directions
Randomize client movement directions
@@ -110,6 +111,7 @@ if("mentor_log") CitadelMentorLogSecret() + if("show_admins") var/dat = "Current admins:
" if(GLOB.admin_datums) @@ -475,7 +477,7 @@ message_admins("[key_name_admin(usr)] activated AK-47s for Everyone!") usr.client.ak47s() sound_to_playing_players('sound/misc/ak47s.ogg') - + if("guns") if(!check_rights(R_FUN)) return @@ -643,6 +645,77 @@ message_admins("[key_name_admin(usr)] has reset all movement keys.") log_admin("[key_name(usr)] has reset all movement keys.") + if("customportal") + if(!check_rights(R_FUN)) + return + + var/list/settings = list( + "mainsettings" = list( + "typepath" = list("desc" = "Path to spawn", "type" = "datum", "path" = "/mob/living", "subtypesonly" = TRUE, "value" = /mob/living/simple_animal/hostile/poison/bees), + "humanoutfit" = list("desc" = "Outfit if human", "type" = "datum", "path" = "/datum/outfit", "subtypesonly" = TRUE, "value" = /datum/outfit), + "amount" = list("desc" = "Number per portal", "type" = "number", "value" = 1), + "portalnum" = list("desc" = "Number of total portals", "type" = "number", "value" = 10), + "offerghosts" = list("desc" = "Get ghosts to play mobs", "type" = "boolean", "value" = "No"), + "minplayers" = list("desc" = "Minimum number of ghosts", "type" = "number", "value" = 1), + "playersonly" = list("desc" = "Only spawn ghost-controlled mobs", "type" = "boolean", "value" = "No"), + "ghostpoll" = list("desc" = "Ghost poll question", "type" = "string", "value" = "Do you want to play as %TYPE% portal invader?"), + "delay" = list("desc" = "Time between portals, in deciseconds", "type" = "number", "value" = 50), + "color" = list("desc" = "Portal color", "type" = "color", "value" = "#00FF00"), + "playlightning" = list("desc" = "Play lightning sounds on announcement", "type" = "boolean", "value" = "Yes"), + "announce_players" = list("desc" = "Make an announcement", "type" = "boolean", "value" = "Yes"), + "announcement" = list("desc" = "Announcement", "type" = "string", "value" = "Massive bluespace anomaly detected en route to %STATION%. Brace for impact."), + ) + ) + + message_admins("[key_name(usr)] is creating a custom portal storm...") + var/list/prefreturn = presentpreflikepicker(usr,"Customize Portal Storm", "Customize Portal Storm", Button1="Ok", width = 600, StealFocus = 1,Timeout = 0, settings=settings) + + if (prefreturn["button"] == 1) + var/list/prefs = settings["mainsettings"] + + if (prefs["amount"]["value"] < 1 || prefs["portalnum"]["value"] < 1) + to_chat(usr, "Number of portals and mobs to spawn must be at least 1") + return + + var/mob/pathToSpawn = prefs["typepath"]["value"] + if (!ispath(pathToSpawn)) + pathToSpawn = text2path(pathToSpawn) + + if (!ispath(pathToSpawn)) + to_chat(usr, "Invalid path [pathToSpawn]") + return + + var/list/candidates = list() + + if (prefs["offerghosts"]["value"] == "Yes") + candidates = pollGhostCandidates(replacetext(prefs["ghostpoll"]["value"], "%TYPE%", initial(pathToSpawn.name)), ROLE_TRAITOR) + + if (prefs["playersonly"]["value"] == "Yes" && length(candidates) < prefs["minplayers"]["value"]) + message_admins("Not enough players signed up to create a portal storm, the minimum was [prefs["minplayers"]["value"]] and the number of signups [length(candidates)]") + return + + if (prefs["announce_players"]["value"] == "Yes") + portalAnnounce(prefs["announcement"]["value"], (prefs["playlightning"]["value"] == "Yes" ? TRUE : FALSE)) + + var/mutable_appearance/storm = mutable_appearance('icons/obj/tesla_engine/energy_ball.dmi', "energy_ball_fast", FLY_LAYER) + storm.color = prefs["color"]["value"] + + message_admins("[key_name_admin(usr)] has created a customized portal storm that will spawn [prefs["portalnum"]["value"]] portals, each of them spawning [prefs["amount"]["value"]] of [pathToSpawn]") + log_admin("[key_name(usr)] has created a customized portal storm that will spawn [prefs["portalnum"]["value"]] portals, each of them spawning [prefs["amount"]["value"]] of [pathToSpawn]") + + var/outfit = prefs["humanoutfit"]["value"] + if (!ispath(outfit)) + outfit = text2path(outfit) + + for (var/i in 1 to prefs["portalnum"]["value"]) + if (length(candidates)) // if we're spawning players, gotta be a little tricky and also not spawn players on top of NPCs + var/ghostcandidates = list() + for (var/j in 1 to min(prefs["amount"]["value"], length(candidates))) + ghostcandidates += pick_n_take(candidates) + addtimer(CALLBACK(GLOBAL_PROC, .proc/doPortalSpawn, get_random_station_turf(), pathToSpawn, length(ghostcandidates), storm, ghostcandidates, outfit), i*prefs["delay"]["value"]) + else if (prefs["playersonly"]["value"] != "Yes") + addtimer(CALLBACK(GLOBAL_PROC, .proc/doPortalSpawn, get_random_station_turf(), pathToSpawn, prefs["amount"]["value"], storm, null, outfit), i*prefs["delay"]["value"]) + if(E) E.processing = FALSE if(E.announceWhen>0) @@ -653,3 +726,29 @@ log_admin("[key_name(usr)] used secret [item]") if (ok) to_chat(world, text("A secret has been activated by []!", usr.key)) + +/proc/portalAnnounce(announcement, playlightning) + set waitfor = 0 + if (playlightning) + sound_to_playing_players('sound/magic/lightning_chargeup.ogg') + sleep(80) + priority_announce(replacetext(announcement, "%STATION%", station_name())) + if (playlightning) + sleep(20) + sound_to_playing_players('sound/magic/lightningbolt.ogg') + +/proc/doPortalSpawn(turf/loc, mobtype, numtospawn, portal_appearance, players, humanoutfit) + for (var/i in 1 to numtospawn) + var/mob/spawnedMob = new mobtype(loc) + if (length(players)) + var/mob/chosen = players[1] + if (chosen.client) + chosen.client.prefs.copy_to(spawnedMob) + spawnedMob.key = chosen.key + players -= chosen + if (ishuman(spawnedMob) && ispath(humanoutfit, /datum/outfit)) + var/mob/living/carbon/human/H = spawnedMob + H.equipOutfit(humanoutfit) + var/turf/T = get_step(loc, SOUTHWEST) + flick_overlay_static(portal_appearance, T, 15) + playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), 1) diff --git a/code/modules/admin/sql_message_system.dm b/code/modules/admin/sql_message_system.dm index fc155e4aed..45a643dcac 100644 --- a/code/modules/admin/sql_message_system.dm +++ b/code/modules/admin/sql_message_system.dm @@ -1,10 +1,10 @@ -/proc/create_message(type, target_key, admin_ckey, text, timestamp, server, secret, logged = 1, browse) +/proc/create_message(type, target_key, admin_ckey, text, timestamp, server, secret, logged = 1, browse, expiry, note_severity) if(!SSdbcore.Connect()) to_chat(usr, "Failed to establish database connection.") return if(!type) return - var/target_ckey + var/target_ckey = ckey(target_key) if(!target_key && (type == "note" || type == "message" || type == "watchlist entry")) var/new_key = input(usr,"Who would you like to create a [type] for?","Enter a key or ckey",null) as null|text if(!new_key) @@ -23,13 +23,8 @@ target_key = new_key if(QDELETED(usr)) return - if(!target_ckey) - if(target_key) - target_ckey = ckey(target_key) - else - return if(target_ckey) - target_ckey = sanitizeSQL(ckey(target_ckey)) + target_ckey = sanitizeSQL(target_ckey) if(!target_key) target_key = target_ckey if(!admin_ckey) @@ -59,7 +54,30 @@ secret = 0 else return - var/datum/DBQuery/query_create_message = SSdbcore.NewQuery("INSERT INTO [format_table_name("messages")] (type, targetckey, adminckey, text, timestamp, server, server_ip, server_port, round_id, secret) VALUES ('[type]', '[target_ckey]', '[admin_ckey]', '[text]', '[timestamp]', '[server]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]','[secret]')") + if(isnull(expiry)) + if(alert(usr, "Set an expiry time? Expired messages are hidden like deleted ones.", "Expiry time?", "Yes", "No", "Cancel") == "Yes") + var/expire_time = input("Set expiry time for [type] as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than current time for obvious reasons.", "Set expiry time", SQLtime()) as null|text + if(!expire_time) + return + expire_time = sanitizeSQL(expire_time) + var/datum/DBQuery/query_validate_expire_time = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[expire_time]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[expire_time]','%Y-%c-%d %T'), 0)") + if(!query_validate_expire_time.warn_execute()) + qdel(query_validate_expire_time) + return + if(query_validate_expire_time.NextRow()) + var/checktime = text2num(query_validate_expire_time.item[1]) + if(!checktime) + to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.") + qdel(query_validate_expire_time) + return + expiry = query_validate_expire_time.item[1] + qdel(query_validate_expire_time) + if(type == "note" && isnull(note_severity)) + note_severity = input("Set the severity of the note.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") + if(!note_severity) + return + note_severity = sanitizeSQL(note_severity) + var/datum/DBQuery/query_create_message = SSdbcore.NewQuery("INSERT INTO [format_table_name("messages")] (type, targetckey, adminckey, text, timestamp, server, server_ip, server_port, round_id, secret, expire_timestamp, severity) VALUES ('[type]', '[target_ckey]', '[admin_ckey]', '[text]', '[timestamp]', '[server]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]','[secret]', [expiry ? "'[expiry]'" : "NULL"], [note_severity ? "'[note_severity]'" : "NULL"])") var/pm = "[key_name(usr)] has created a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""]: [text]" var/header = "[key_name_admin(usr)] has created a [type][(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""]" if(!query_create_message.warn_execute()) @@ -88,7 +106,7 @@ var/text var/user_key_name = key_name(usr) var/user_name_admin = key_name_admin(usr) - var/datum/DBQuery/query_find_del_message = SSdbcore.NewQuery("SELECT type, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") + var/datum/DBQuery/query_find_del_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") if(!query_find_del_message.warn_execute()) qdel(query_find_del_message) return @@ -123,7 +141,7 @@ var/editor_key = sanitizeSQL(usr.key) var/kn = key_name(usr) var/kna = key_name_admin(usr) - var/datum/DBQuery/query_find_edit_message = SSdbcore.NewQuery("SELECT type, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") + var/datum/DBQuery/query_find_edit_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey), text FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") if(!query_find_edit_message.warn_execute()) qdel(query_find_edit_message) return @@ -151,6 +169,103 @@ browse_messages(target_ckey = ckey(target_key), agegate = TRUE) qdel(query_find_edit_message) +/proc/edit_message_expiry(message_id, browse) + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.") + return + message_id = text2num(message_id) + if(!message_id) + return + var/editor_ckey = sanitizeSQL(usr.ckey) + var/editor_key = sanitizeSQL(usr.key) + var/kn = key_name(usr) + var/kna = key_name_admin(usr) + var/datum/DBQuery/query_find_edit_expiry_message = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), expire_timestamp FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") + if(!query_find_edit_expiry_message.warn_execute()) + qdel(query_find_edit_expiry_message) + return + if(query_find_edit_expiry_message.NextRow()) + var/type = query_find_edit_expiry_message.item[1] + var/target_key = query_find_edit_expiry_message.item[2] + var/admin_key = query_find_edit_expiry_message.item[3] + var/old_expiry = query_find_edit_expiry_message.item[4] + var/new_expiry + var/expire_time = input("Set expiry time for [type] as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than current time for obvious reasons. Enter -1 to remove expiry time.", "Set expiry time", old_expiry) as null|text + if(!expire_time) + qdel(query_find_edit_expiry_message) + return + if(expire_time == "-1") + new_expiry = "non-expiring" + else + expire_time = sanitizeSQL(expire_time) + var/datum/DBQuery/query_validate_expire_time_edit = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[expire_time]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[expire_time]','%Y-%c-%d %T'), 0)") + if(!query_validate_expire_time_edit.warn_execute()) + qdel(query_validate_expire_time_edit) + qdel(query_find_edit_expiry_message) + return + if(query_validate_expire_time_edit.NextRow()) + var/checktime = text2num(query_validate_expire_time_edit.item[1]) + if(!checktime) + to_chat(usr, "Datetime entered is improperly formatted or not later than current server time.") + qdel(query_validate_expire_time_edit) + qdel(query_find_edit_expiry_message) + return + new_expiry = query_validate_expire_time_edit.item[1] + qdel(query_validate_expire_time_edit) + var/edit_text = sanitizeSQL("Expiration time edited by [editor_key] on [SQLtime()] from [old_expiry] to [new_expiry]
") + var/datum/DBQuery/query_edit_message_expiry = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET expire_timestamp = [expire_time == "-1" ? "NULL" : "'[new_expiry]'"], lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0") + if(!query_edit_message_expiry.warn_execute()) + qdel(query_edit_message_expiry) + qdel(query_find_edit_expiry_message) + return + qdel(query_edit_message_expiry) + log_admin_private("[kn] has edited the expiration time of a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from [old_expiry] to [new_expiry]") + message_admins("[kna] has edited the expiration time of a [type] [(type == "note" || type == "message" || type == "watchlist entry") ? " for [target_key]" : ""] made by [admin_key] from [old_expiry] to [new_expiry]") + if(browse) + browse_messages("[type]") + else + browse_messages(target_ckey = ckey(target_key), agegate = TRUE) + qdel(query_find_edit_expiry_message) + +/proc/edit_message_severity(message_id) + if(!SSdbcore.Connect()) + to_chat(usr, "Failed to establish database connection.") + return + message_id = text2num(message_id) + if(!message_id) + return + var/kn = key_name(usr) + var/kna = key_name_admin(usr) + var/datum/DBQuery/query_find_edit_note_severity = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), severity FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") + if(!query_find_edit_note_severity.warn_execute()) + qdel(query_find_edit_note_severity) + return + if(query_find_edit_note_severity.NextRow()) + var/type = query_find_edit_note_severity.item[1] + var/target_key = query_find_edit_note_severity.item[2] + var/admin_key = query_find_edit_note_severity.item[3] + var/old_severity = query_find_edit_note_severity.item[4] + if(!old_severity) + old_severity = "NA" + var/editor_key = sanitizeSQL(usr.key) + var/editor_ckey = sanitizeSQL(usr.ckey) + var/new_severity = input("Set the severity of the note.", "Severity", null, null) as null|anything in list("high", "medium", "minor", "none") //lowercase for edit log consistency + if(!new_severity) + qdel(query_find_edit_note_severity) + return + new_severity = sanitizeSQL(new_severity) + var/edit_text = sanitizeSQL("Note severity edited by [editor_key] on [SQLtime()] from [old_severity] to [new_severity]
") + var/datum/DBQuery/query_edit_note_severity = SSdbcore.NewQuery("UPDATE [format_table_name("messages")] SET severity = '[new_severity]', lasteditor = '[editor_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [message_id] AND deleted = 0") + if(!query_edit_note_severity.warn_execute(async = TRUE)) + qdel(query_edit_note_severity) + qdel(qdel(query_find_edit_note_severity)) + return + qdel(query_edit_note_severity) + log_admin_private("[kn] has edited the severity of a [type] for [target_key] made by [admin_key] from [old_severity] to [new_severity]") + message_admins("[kna] has edited the severity time of a [type] for [target_key] made by [admin_key] from [old_severity] to [new_severity]") + browse_messages(target_ckey = ckey(target_key), agegate = TRUE) + qdel(query_find_edit_note_severity) + /proc/toggle_message_secrecy(message_id) if(!SSdbcore.Connect()) to_chat(usr, "Failed to establish database connection.") @@ -162,7 +277,7 @@ var/editor_key = sanitizeSQL(usr.key) var/kn = key_name(usr) var/kna = key_name_admin(usr) - var/datum/DBQuery/query_find_message_secret = SSdbcore.NewQuery("SELECT type, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), secret FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") + var/datum/DBQuery/query_find_message_secret = SSdbcore.NewQuery("SELECT type, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), targetckey), secret FROM [format_table_name("messages")] WHERE id = [message_id] AND deleted = 0") if(!query_find_message_secret.warn_execute()) qdel(query_find_message_secret) return @@ -189,10 +304,10 @@ return var/list/output = list() var/ruler = "
" - var/list/navbar = list("\[All\]|\[#\]") + var/list/navbar = list("All#") for(var/letter in GLOB.alphabet) - navbar += "|\[[letter]\]" - navbar += "|\[Memos\]|\[Watchlist\]" + navbar += "[letter]" + navbar += "MemosWatchlist" navbar += "
\ \ [HrefTokenFormField()]\ @@ -203,16 +318,16 @@ if(type == "memo" || type == "watchlist entry") if(type == "memo") output += "

Admin memos

" - output += "\[Add memo\]" + output += "Add memo" else if(type == "watchlist entry") output += "

Watchlist entries

" - output += "\[Add watchlist entry\]" + output += "Add watchlist entry" if(filter) - output += "|\[Unfilter clients\]" + output += "Unfilter clients" else - output += "|\[Filter offline clients\]" + output += "Filter offline clients" output += ruler - var/datum/DBQuery/query_get_type_messages = SSdbcore.NewQuery("SELECT id, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), text, timestamp, server, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor) FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0") + var/datum/DBQuery/query_get_type_messages = SSdbcore.NewQuery("SELECT id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), targetckey, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), text, timestamp, server, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor), expire_timestamp FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)") if(!query_get_type_messages.warn_execute()) qdel(query_get_type_messages) return @@ -229,12 +344,17 @@ var/timestamp = query_get_type_messages.item[6] var/server = query_get_type_messages.item[7] var/editor_key = query_get_type_messages.item[8] + var/expire_timestamp = query_get_type_messages.item[9] output += "" if(type == "watchlist entry") output += "[t_key] | " - output += "[timestamp] | [server] | [admin_key]" - output += " \[Delete\]" - output += " \[Edit\]" + output += "[timestamp] | [server] | [admin_key]" + if(expire_timestamp) + output += " | Expires [expire_timestamp]" + output += "" + output += " Change Expiry Time" + output += " Delete" + output += " Edit" if(editor_key) output += " Last edit by [editor_key] (Click here to see edit log)" output += "
[text]
" @@ -242,7 +362,7 @@ if(target_ckey) target_ckey = sanitizeSQL(target_ckey) var/target_key - var/datum/DBQuery/query_get_messages = SSdbcore.NewQuery("SELECT type, secret, id, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), text, timestamp, server, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), DATEDIFF(NOW(), timestamp) AS `age`, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey) FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey = '[target_ckey]' AND deleted = 0 ORDER BY timestamp DESC") + var/datum/DBQuery/query_get_messages = SSdbcore.NewQuery("SELECT type, secret, id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), text, timestamp, server, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor), DATEDIFF(NOW(), timestamp), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey), targetckey), expire_timestamp, severity FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey = '[target_ckey]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) ORDER BY timestamp DESC") if(!query_get_messages.warn_execute()) qdel(query_get_messages) return @@ -267,6 +387,8 @@ var/editor_key = query_get_messages.item[8] var/age = text2num(query_get_messages.item[9]) target_key = query_get_messages.item[10] + var/expire_timestamp = query_get_messages.item[11] + var/severity = query_get_messages.item[12] var/alphatext = "" var/nsd = CONFIG_GET(number/note_stale_days) var/nfd = CONFIG_GET(number/note_fresh_days) @@ -280,21 +402,33 @@ alpha = 10 skipped = TRUE alphatext = "filter: alpha(opacity=[alpha]); opacity: [alpha/100];" - - var/list/data = list("

[timestamp] | [server] | [admin_key]") + var/list/data = list("

") + if(severity) + data += " " + data += "[timestamp] | [server] | [admin_key][secret ? " | - Secret" : ""]" + if(expire_timestamp) + data += " | Expires [expire_timestamp]" + data += "

" if(!linkless) - data += " \[Delete\]" if(type == "note") - data += " [secret ? "\[Secret\]" : "\[Not secret\]"]" + if(severity) + data += "[severity=="none" ? "No" : "[capitalize(severity)]"] Severity" + else + data += "N/A Severity" + data += " Change Expiry Time" + data += " Delete" + if(type == "note") + data += " [secret ? "Secret" : "Not secret"]" if(type == "message sent") data += " Message has been sent" if(editor_key) data += "|" else - data += " \[Edit\]" + data += " Edit" if(editor_key) data += " Last edit by [editor_key] (Click here to see edit log)" - data += "
[text]


" + data += "
" + data += "

[text]


" switch(type) if("message") messagedata += data @@ -305,36 +439,43 @@ if("note") notedata += data qdel(query_get_messages) + if(!target_key) + var/datum/DBQuery/query_get_message_key = SSdbcore.NewQuery("SELECT byond_key FROM [format_table_name("player")] WHERE ckey = '[target_ckey]'") + if(!query_get_message_key.warn_execute()) + qdel(query_get_message_key) + return + if(query_get_message_key.NextRow()) + target_key = query_get_message_key.item[1] + qdel(query_get_message_key) output += "

[target_key]

" if(!linkless) - output += "\[Add note\]" - output += " \[Add message\]" - output += " \[Add to watchlist\]" - output += " \[Refresh page\]
" + output += "Add note" + output += " Add message" + output += " Add to watchlist" + output += " Refresh page" else - output += " \[Refresh page\]" + output += " Refresh page" output += ruler if(messagedata) - output += "

Messages

" + output += "

Messages

" output += messagedata if(watchdata) - output += "

Watchlist

" + output += "

Watchlist

" output += watchdata if(notedata) - output += "

Notes

" + output += "

Notes

" output += notedata if(!linkless) if (agegate) if (skipped) //the first skipped message is still shown so that we can put this link over it. - output += "
\[Show [skipped] hidden messages\]
" + output += "
Show [skipped] hidden messages
" else - output += "
\[Show All\]
" - + output += "
Show All
" else - output += "
\[Hide Old\]
" + output += "
Hide Old
" if(index) var/search - output += "
\[Add message\]\[Add watchlist entry\]\[Add note\]
" + output += "
Add messageAdd watchlist entryAdd note
" output += ruler if(!isnum(index)) index = sanitizeSQL(index) @@ -345,7 +486,7 @@ search = "^\[^\[:alpha:\]\]" else search = "^[index]" - var/datum/DBQuery/query_list_messages = SSdbcore.NewQuery("SELECT DISTINCT targetckey, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey) FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey REGEXP '[search]' AND deleted = 0 ORDER BY targetckey") + var/datum/DBQuery/query_list_messages = SSdbcore.NewQuery("SELECT DISTINCT targetckey, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = targetckey) FROM [format_table_name("messages")] WHERE type <> 'memo' AND targetckey REGEXP '[search]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL) ORDER BY targetckey") if(!query_list_messages.warn_execute()) qdel(query_list_messages) return @@ -354,12 +495,18 @@ return var/index_ckey = query_list_messages.item[1] var/index_key = query_list_messages.item[2] + if(!index_key) + index_key = index_ckey output += "[index_key]
" qdel(query_list_messages) else if(!type && !target_ckey && !index) - output += "
\[Add message\]\[Add watchlist entry\]\[Add note\]
" + output += "
Add messageAdd watchlist entryAdd note
" output += ruler - usr << browse({"[jointext(output, "")]"}, "window=browse_messages;size=900x500") + var/datum/browser/browser = new(usr, "Note panel", "Manage player notes", 1000, 500) + var/datum/asset/notes_assets = get_asset_datum(/datum/asset/simple/notes) + notes_assets.send(src) + browser.set_content(jointext(output, "")) + browser.open() /proc/get_message_output(type, target_ckey) if(!SSdbcore.Connect()) @@ -370,7 +517,7 @@ var/output if(target_ckey) target_ckey = sanitizeSQL(target_ckey) - var/query = "SELECT id, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), text, timestamp, (SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor) FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0" + var/query = "SELECT id, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), text, timestamp, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = lasteditor), lasteditor) FROM [format_table_name("messages")] WHERE type = '[type]' AND deleted = 0 AND (expire_timestamp > NOW() OR expire_timestamp IS NULL)" if(type == "message" || type == "watchlist entry") query += " AND targetckey = '[target_ckey]'" var/datum/DBQuery/query_get_message_output = SSdbcore.NewQuery(query) @@ -432,7 +579,7 @@ timestamp = query_convert_time.item[1] qdel(query_convert_time) if(ckey && notetext && timestamp && admin_ckey && server) - create_message("note", ckey, admin_ckey, notetext, timestamp, server, 1, 0) + create_message("note", ckey, admin_ckey, notetext, timestamp, server, 1, 0, null, 0, 0) notesfile.cd = "/" notesfile.dir.Remove(ckey) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 745328ab13..226f2a5456 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -215,37 +215,38 @@ var/banduration = text2num(href_list["dbbaddduration"]) var/banjob = href_list["dbbanaddjob"] var/banreason = href_list["dbbanreason"] + var/banseverity = href_list["dbbanaddseverity"] switch(bantype) if(BANTYPE_PERMA) - if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason).") + if(!banckey || !banreason || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, severity, and reason).") return banduration = null banjob = null if(BANTYPE_TEMP) - if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration).") + if(!banckey || !banreason || !banduration || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, reason, severity and duration).") return banjob = null if(BANTYPE_JOB_PERMA) - if(!banckey || !banreason || !banjob) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job).") + if(!banckey || !banreason || !banjob || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and job).") return banduration = null if(BANTYPE_JOB_TEMP) - if(!banckey || !banreason || !banjob || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job).") + if(!banckey || !banreason || !banjob || !banduration || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and job).") return if(BANTYPE_ADMIN_PERMA) - if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason).") + if(!banckey || !banreason || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, severity and reason).") return banduration = null banjob = null if(BANTYPE_ADMIN_TEMP) - if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration).") + if(!banckey || !banreason || !banduration || !banseverity) + to_chat(usr, "Not enough parameters (Requires ckey, severity, reason and duration).") return banjob = null @@ -270,7 +271,7 @@ if(!DB_ban_record(bantype, playermob, banduration, banreason, banjob, bankey, banip, bancid )) to_chat(usr, "Failed to apply ban.") return - create_message("note", bankey, null, banreason, null, null, 0, 0) + create_message("note", bankey, null, banreason, null, null, 0, 0, null, 0, banseverity) else if(href_list["editrightsbrowser"]) edit_admin_permissions(0) @@ -340,7 +341,7 @@ else if(href_list["toggle_continuous"]) if(!check_rights(R_ADMIN)) return - var/list/continuous = CONFIG_GET(keyed_flag_list/continuous) + var/list/continuous = CONFIG_GET(keyed_list/continuous) if(!continuous[SSticker.mode.config_tag]) continuous[SSticker.mode.config_tag] = TRUE else @@ -353,7 +354,7 @@ if(!check_rights(R_ADMIN)) return - var/list/midround_antag = CONFIG_GET(keyed_flag_list/midround_antag) + var/list/midround_antag = CONFIG_GET(keyed_list/midround_antag) if(!midround_antag[SSticker.mode.config_tag]) midround_antag[SSticker.mode.config_tag] = TRUE else @@ -599,6 +600,9 @@ var/reason = input(usr,"Please State Reason.","Reason") as message|null if(!reason) return + var/severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") + if(!severity) + return if(!DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, "appearance")) to_chat(usr, "Failed to apply ban.") return @@ -606,7 +610,7 @@ jobban_buildcache(M.client) ban_unban_log_save("[key_name(usr)] appearance banned [key_name(M)]. reason: [reason]") log_admin_private("[key_name(usr)] appearance banned [key_name(M)]. \nReason: [reason]") - create_message("note", M.key, null, "Appearance banned - [reason]", null, null, 0, 0) + create_message("note", M.key, null, "Appearance banned - [reason]", null, null, 0, 0, null, 0, severity) message_admins("[key_name_admin(usr)] appearance banned [key_name_admin(M)].") to_chat(M, "You have been appearance banned by [usr.client.key].") to_chat(M, "The reason is: [reason]") @@ -804,30 +808,35 @@ //Drones - if(jobban_isbanned(M, "drone")) - dat += "Drone" + if(jobban_isbanned(M, ROLE_DRONE)) + dat += "Drone" else - dat += "Drone" + dat += "Drone" //Positronic Brains - if(jobban_isbanned(M, "posibrain")) - dat += "Posibrain" + if(jobban_isbanned(M, ROLE_POSIBRAIN)) + dat += "Posibrain" else - dat += "Posibrain" + dat += "Posibrain" + //Sentience Potion Spawn + if(jobban_isbanned(M, ROLE_SENTIENCE)) + dat += "Sentience Potion Spawn" + else + dat += "Sentience Potion Spawn" //Deathsquad - if(jobban_isbanned(M, "deathsquad")) - dat += "Deathsquad" + if(jobban_isbanned(M, ROLE_DEATHSQUAD)) + dat += "Deathsquad" else - dat += "Deathsquad" + dat += "Deathsquad" //Lavaland roles - if(jobban_isbanned(M, "lavaland")) - dat += "Lavaland" + if(jobban_isbanned(M, ROLE_LAVALAND)) + dat += "Lavaland" else - dat += "Lavaland" + dat += "Lavaland" dat += "" @@ -894,6 +903,16 @@ else dat += "Alien" + //Other Roles (black) + dat += "" + dat += "" + + //Mind Transfer Potion + if(jobban_isbanned(M, ROLE_MIND_TRANSFER)) + dat += "" + else + dat += "" + dat += "
Other Roles
Mind Transfer PotionMind Transfer Potion
" usr << browse(dat, "window=jobban2;size=800x450") return @@ -953,11 +972,13 @@ continue joblist += jobPos if("ghostroles") - joblist += list(ROLE_PAI, "posibrain", "drone", "deathsquad", "lavaland") + joblist += list(ROLE_PAI, ROLE_POSIBRAIN, ROLE_DRONE , ROLE_DEATHSQUAD, ROLE_LAVALAND, ROLE_SENTIENCE) if("teamantags") joblist += list(ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ABDUCTOR, ROLE_ALIEN) if("convertantags") joblist += list(ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ALIEN) + if("otherroles") + joblist += list(ROLE_MIND_TRANSFER) else joblist += href_list["jobban3"] @@ -969,6 +990,7 @@ //Banning comes first if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban. + var/severity = null switch(alert("Temporary Ban for [M.key]?",,"Yes","No", "Cancel")) if("Yes") var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null @@ -978,7 +1000,9 @@ var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null if(!reason) return - + severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") + if(!severity) + return var/msg for(var/job in notbannedlist) if(!DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job)) @@ -992,7 +1016,7 @@ msg = job else msg += ", [job]" - create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0) + create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0, null, 0, severity) message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes.") to_chat(M, "You have been [(msg == ("ooc" || "appearance")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg].") to_chat(M, "The reason is: [reason]") @@ -1001,6 +1025,9 @@ return 1 if("No") var/reason = input(usr,"Please State Reason For Banning [M.key].","Reason") as message|null + severity = input("Set the severity of the note/ban.", "Severity", null, null) as null|anything in list("High", "Medium", "Minor", "None") + if(!severity) + return if(reason) var/msg for(var/job in notbannedlist) @@ -1015,7 +1042,7 @@ msg = job else msg += ", [job]" - create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0) + create_message("note", M.key, null, "Banned from [msg] - [reason]", null, null, 0, 0, null, 0, severity) message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg].") to_chat(M, "You have been [(msg == ("ooc" || "appearance")) ? "banned" : "jobbanned"] by [usr.client.key] from: [msg].") to_chat(M, "The reason is: [reason]") @@ -1140,6 +1167,24 @@ var/message_id = href_list["editmessageempty"] edit_message(message_id, browse = 1) + else if(href_list["editmessageexpiry"]) + if(!check_rights(R_ADMIN)) + return + var/message_id = href_list["editmessageexpiry"] + edit_message_expiry(message_id) + + else if(href_list["editmessageexpiryempty"]) + if(!check_rights(R_ADMIN)) + return + var/message_id = href_list["editmessageexpiryempty"] + edit_message_expiry(message_id, browse = 1) + + else if(href_list["editmessageseverity"]) + if(!check_rights(R_ADMIN)) + return + var/message_id = href_list["editmessageseverity"] + edit_message_severity(message_id) + else if(href_list["secretmessage"]) if(!check_rights(R_ADMIN)) return @@ -1204,7 +1249,9 @@ if(query_get_message_edits.NextRow()) var/edit_log = query_get_message_edits.item[1] if(!QDELETED(usr)) - usr << browse(edit_log,"window=noteedits") + var/datum/browser/browser = new(usr, "Note edits", "Note edits") + browser.set_content(jointext(edit_log, "")) + browser.open() qdel(query_get_message_edits) else if(href_list["newban"]) @@ -1365,10 +1412,10 @@ if(!ismob(M)) to_chat(usr, "this can only be used on instances of type /mob.") - var/speech = input("What will [key_name(M)] say?.", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. + var/speech = input("What will [key_name(M)] say?", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. if(!speech) return - M.say(speech) + M.say(speech, forced = "admin speech") speech = sanitize(speech) // Nah, we don't trust them log_admin("[key_name(usr)] forced [key_name(M)] to say: [speech]") message_admins("[key_name_admin(usr)] forced [key_name_admin(M)] to say: [speech]") diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm index 68aafde8b0..603e4daf2d 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm @@ -196,9 +196,10 @@ /proc/SDQL_testout(list/query_tree, indent = 0) + var/static/whitespace = "    " var/spaces = "" for(var/s = 0, s < indent, s++) - spaces += " " + spaces += whitespace for(var/item in query_tree) if(istype(item, /list)) @@ -212,12 +213,12 @@ if(!isnum(item) && query_tree[item]) if(istype(query_tree[item], /list)) - to_chat(usr, "[spaces] (") + to_chat(usr, "[spaces][whitespace](") SDQL_testout(query_tree[item], indent + 2) - to_chat(usr, "[spaces] )") + to_chat(usr, "[spaces][whitespace])") else - to_chat(usr, "[spaces] [query_tree[item]]") + to_chat(usr, "[spaces][whitespace][query_tree[item]]") diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm index 61945f1dde..1bd95bc239 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm @@ -294,8 +294,8 @@ break parse_error("Expected ',' or ']' after array assoc value, but found '[token(i)]'") return i - i++ - continue + i++ + continue temp_expression_list = list() i = expression(i, temp_expression_list) while(token(i) && token(i) != "]") @@ -331,7 +331,7 @@ //string: ''' ''' | '"' '"' /datum/SDQL_parser/proc/string(i, list/node) if(copytext(token(i), 1, 2) in list("'", "\"")) - node += copytext(token(i),2,-1) + node += token(i) else parse_error("Expected string but found '[token(i)]'") return i + 1 diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 8f4fba7aa5..c81c92bd0a 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -372,7 +372,6 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) var/msg = "- AdminHelp marked as IC issue! -
" msg += "Losing is part of the game!
" msg += "It is also possible that your ahelp is unable to be answered properly, due to events occurring in the round." - if(initiator) to_chat(initiator, msg) @@ -607,7 +606,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) message["key"] = comms_key message += type - var/list/servers = CONFIG_GET(keyed_string_list/cross_server) + var/list/servers = CONFIG_GET(keyed_list/cross_server) for(var/I in servers) world.Export("[servers[I]]?[list2params(message)]") @@ -686,7 +685,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) var/is_antag = 0 if(found.mind && found.mind.special_role) is_antag = 1 - founds += "Name: [found.name]([found.real_name]) Ckey: [found.key] [is_antag ? "(Antag)" : null] " + founds += "Name: [found.name]([found.real_name]) Key: [found.key] Ckey: [found.ckey] [is_antag ? "(Antag)" : null] " msg += "[original_word](?|F) " continue msg += "[original_word] " diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index 6dcd4e98ca..d3631f24d0 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -66,9 +66,8 @@ if(src.mob) var/mob/A = src.mob - A.x = tx - A.y = ty - A.z = tz + var/turf/T = locate(tx,ty,tz) + A.forceMove(T) SSblackbox.record_feedback("tally", "admin_verb", 1, "Jump To Coordiate") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]") diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 03f891798e..7adb128239 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -9,16 +9,11 @@ if(!msg) return - msg = emoji_parse(msg) + mob.log_talk(msg, LOG_ASAY) - log_talk(mob,"[key_name(src)] : [msg]",LOGASAY) msg = keywords_lookup(msg) - if(check_rights(R_ADMIN,0)) - msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" - to_chat(GLOB.admins, msg) - else - msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" - to_chat(GLOB.admins, msg) + msg = "ADMIN: [key_name(usr, 1)] [ADMIN_FLW(mob)]: [msg]" + to_chat(GLOB.admins, msg) SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/borgpanel.dm b/code/modules/admin/verbs/borgpanel.dm index ec921e78bc..6295d4be43 100644 --- a/code/modules/admin/verbs/borgpanel.dm +++ b/code/modules/admin/verbs/borgpanel.dm @@ -21,18 +21,17 @@ var/mob/living/silicon/robot/borg var/user -/datum/borgpanel/New(user, mob/living/silicon/robot/borg) - if(!istype(borg)) +/datum/borgpanel/New(to_user, mob/living/silicon/robot/to_borg) + if(!istype(to_borg)) CRASH("Borg panel is only available for borgs") qdel(src) - if (istype(user, /mob)) - var/mob/M = user - if (!M.client) - CRASH("Borg panel attempted to open to a mob without a client") - src.user = M.client - else - src.user = user - src.borg = borg + + user = CLIENT_FROM_VAR(to_user) + + if (!user) + CRASH("Borg panel attempted to open to a mob without a client") + + borg = to_borg /datum/borgpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index 1d200ff9c1..30afa659f5 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -1,6 +1,6 @@ /client/proc/dsay(msg as text) set category = "Special Verbs" - set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite + set name = "Dsay" set hidden = 1 if(!src.holder) to_chat(src, "Only administrators may use this command.") @@ -15,16 +15,13 @@ return msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN) - log_talk(mob,"[key_name(src)] : [msg]",LOGDSAY) + mob.log_talk(msg, LOG_DSAY) if (!msg) return - - msg = emoji_parse(msg) - var/static/nicknames = world.file2list("[global.config.directory]/admin_nicknames.txt") - var/rendered = "DEAD: [uppertext(holder.rank)]([src.holder.fakekey ? pick(nicknames) : src.key]) says, \"[msg]\"" + var/rendered = "DEAD: [uppertext(holder.rank)]([src.holder.fakekey ? pick(nicknames) : src.key]) says, \"[emoji_parse(msg)]\"" for (var/mob/M in GLOB.player_list) if(isnewplayer(M)) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 4ba3c94711..67b33afcc6 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -959,6 +959,50 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) to_chat(usr, "[template.name]") to_chat(usr, "[template.description]") +/client/proc/place_ruin() + set category = "Debug" + set name = "Spawn Ruin" + set desc = "Attempt to randomly place a specific ruin." + if (!holder) + return + + var/list/exists = list() + for(var/landmark in GLOB.ruin_landmarks) + var/obj/effect/landmark/ruin/L = landmark + exists[L.ruin_template] = landmark + + var/list/names = list() + names += "---- Space Ruins ----" + for(var/name in SSmapping.space_ruins_templates) + names[name] = list(SSmapping.space_ruins_templates[name], ZTRAIT_SPACE_RUINS, /area/space) + names += "---- Lava Ruins ----" + for(var/name in SSmapping.lava_ruins_templates) + names[name] = list(SSmapping.lava_ruins_templates[name], ZTRAIT_LAVA_RUINS, /area/lavaland/surface/outdoors/unexplored) + + var/ruinname = input("Select ruin", "Spawn Ruin") as null|anything in names + var/data = names[ruinname] + if (!data) + return + var/datum/map_template/ruin/template = data[1] + if (exists[template]) + var/response = alert("There is already a [template] in existence.", "Spawn Ruin", "Jump", "Place Another", "Cancel") + if (response == "Jump") + usr.forceMove(get_turf(exists[template])) + return + else if (response == "Cancel") + return + + var/len = GLOB.ruin_landmarks.len + seedRuins(SSmapping.levels_by_trait(data[2]), max(1, template.cost), data[3], list(ruinname = template)) + if (GLOB.ruin_landmarks.len > len) + var/obj/effect/landmark/ruin/landmark = GLOB.ruin_landmarks[GLOB.ruin_landmarks.len] + log_admin("[key_name(src)] randomly spawned ruin [ruinname] at [COORD(landmark)].") + usr.forceMove(get_turf(landmark)) + to_chat(src, "[template.name]") + to_chat(src, "[template.description]") + else + to_chat(src, "Failed to place [template.name].") + /client/proc/clear_dynamic_transit() set category = "Debug" set name = "Clear Dynamic Turf Reservations" @@ -1047,3 +1091,12 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) return sort = sortlist[sort] profile_show(src, sort) + +/client/proc/reload_configuration() + set category = "Debug" + set name = "Reload Configuration" + set desc = "Force config reload to world default" + if(!check_rights(R_DEBUG)) + return + if(alert(usr, "Are you absolutely sure you want to reload the configuration from the default path on the disk, wiping any in-round modificatoins?", "Really reset?", "No", "Yes") == "Yes") + config.admin_reload() diff --git a/code/modules/admin/verbs/individual_logging.dm b/code/modules/admin/verbs/individual_logging.dm index df151cfef9..7fe4c070d2 100644 --- a/code/modules/admin/verbs/individual_logging.dm +++ b/code/modules/admin/verbs/individual_logging.dm @@ -1,29 +1,45 @@ /proc/show_individual_logging_panel(mob/M, source = LOGSRC_CLIENT, type = INDIVIDUAL_ATTACK_LOG) if(!M || !ismob(M)) return - + + var/ntype = text2num(type) + //Add client links var/dat = "" - if(M.client) - dat += "

Client

" - dat += "
Attack log | " - dat += "Say log | " - dat += "Emote log | " - dat += "OOC log | " - dat += "Show all | " - dat += "Refresh
" + if(M.client) + dat += "

Client

" + dat += "
" + dat += individual_logging_panel_link(M, INDIVIDUAL_ATTACK_LOG, LOGSRC_CLIENT, "Attack Log", source, ntype) + dat += " | " + dat += individual_logging_panel_link(M, INDIVIDUAL_SAY_LOG, LOGSRC_CLIENT, "Say Log", source, ntype) + dat += " | " + dat += individual_logging_panel_link(M, INDIVIDUAL_EMOTE_LOG, LOGSRC_CLIENT, "Emote Log", source, ntype) + dat += " | " + dat += individual_logging_panel_link(M, INDIVIDUAL_COMMS_LOG, LOGSRC_CLIENT, "Comms Log", source, ntype) + dat += " | " + dat += individual_logging_panel_link(M, INDIVIDUAL_OOC_LOG, LOGSRC_CLIENT, "OOC Log", source, ntype) + dat += " | " + dat += individual_logging_panel_link(M, INDIVIDUAL_SHOW_ALL_LOG, LOGSRC_CLIENT, "Show All", source, ntype) + dat += "
" else dat += "

No client attached to mob

" dat += "
" - dat += "

Mob

" + dat += "

Mob

" //Add the links for the mob specific log - dat += "
Attack log | " - dat += "Say log | " - dat += "Emote log | " - dat += "OOC log | " - dat += "Show all | " - dat += "Refresh
" + dat += "
" + dat += individual_logging_panel_link(M, INDIVIDUAL_ATTACK_LOG, LOGSRC_MOB, "Attack Log", source, ntype) + dat += " | " + dat += individual_logging_panel_link(M, INDIVIDUAL_SAY_LOG, LOGSRC_MOB, "Say Log", source, ntype) + dat += " | " + dat += individual_logging_panel_link(M, INDIVIDUAL_EMOTE_LOG, LOGSRC_MOB, "Emote Log", source, ntype) + dat += " | " + dat += individual_logging_panel_link(M, INDIVIDUAL_COMMS_LOG, LOGSRC_MOB, "Comms Log", source, ntype) + dat += " | " + dat += individual_logging_panel_link(M, INDIVIDUAL_OOC_LOG, LOGSRC_MOB, "OOC Log", source, ntype) + dat += " | " + dat += individual_logging_panel_link(M, INDIVIDUAL_SHOW_ALL_LOG, LOGSRC_MOB, "Show All", source, ntype) + dat += "
" dat += "
" @@ -31,22 +47,21 @@ if(source == LOGSRC_CLIENT && M.client) //if client doesn't exist just fall back to the mob log log_source = M.client.player_details.logging //should exist, if it doesn't that's a bug, don't check for it not existing - if(type == INDIVIDUAL_SHOW_ALL_LOG) - dat += "
Displaying all [source] logs of [key_name(M)]


" - for(var/log_type in log_source) - dat += "
[log_type]

" + for(var/log_type in log_source) + var/nlog_type = text2num(log_type) + if(nlog_type & ntype) var/list/reversed = log_source[log_type] if(islist(reversed)) reversed = reverseRange(reversed.Copy()) for(var/entry in reversed) - dat += "[entry]: [reversed[entry]]
" + dat += "[entry]
[reversed[entry]]

" dat += "
" - else - dat += "
[source] [type] of [key_name(M)]

" - var/list/reversed = log_source[type] - if(reversed) - reversed = reverseRange(reversed.Copy()) - for(var/entry in reversed) - dat += "[entry]: [reversed[entry]]
" usr << browse(dat, "window=invidual_logging_[key_name(M)];size=600x480") + +/proc/individual_logging_panel_link(mob/M, log_type, log_src, label, selected_src, selected_type) + var/slabel = label + if(selected_type == log_type && selected_src == log_src) + slabel = "\[[label]\]" + + return "[slabel]" \ No newline at end of file diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index 22402a8c0d..c5c9a0daf4 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -4,7 +4,7 @@ var/datum/map_template/template - var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in SSmapping.map_templates + var/map = input(src, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in SSmapping.map_templates if(!map) return template = SSmapping.map_templates[map] @@ -18,35 +18,50 @@ var/image/item = image('icons/turf/overlays.dmi',S,"greenOverlay") item.plane = ABOVE_LIGHTING_PLANE preview += item - usr.client.images += preview - if(alert(usr,"Confirm location.","Template Confirm","Yes","No") == "Yes") + images += preview + if(alert(src,"Confirm location.","Template Confirm","Yes","No") == "Yes") if(template.load(T, centered = TRUE)) - message_admins("[key_name_admin(usr)] has placed a map template ([template.name]) at [ADMIN_COORDJMP(T)]") + message_admins("[key_name_admin(src)] has placed a map template ([template.name]) at [ADMIN_COORDJMP(T)]") else - to_chat(usr, "Failed to place map") - usr.client.images -= preview + to_chat(src, "Failed to place map") + images -= preview /client/proc/map_template_upload() set category = "Debug" set name = "Map Template - Upload" - var/map = input(usr, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file + var/map = input(src, "Choose a Map Template to upload to template storage","Upload Map Template") as null|file if(!map) return if(copytext("[map]",-4) != ".dmm") - to_chat(usr, "Bad map file: [map]") + to_chat(src, "Filename must end in '.dmm': [map]") return var/datum/map_template/M - switch(alert(usr, "What kind of map is this?", "Map type", "Normal", "Shuttle", "Cancel")) + switch(alert(src, "What kind of map is this?", "Map type", "Normal", "Shuttle", "Cancel")) if("Normal") - M = new /datum/map_template(map, "[map]") + M = new /datum/map_template(map, "[map]", TRUE) if("Shuttle") - M = new /datum/map_template/shuttle(map, "[map]") + M = new /datum/map_template/shuttle(map, "[map]", TRUE) else return - if(M.preload_size(map)) - to_chat(usr, "Map template '[map]' ready to place ([M.width]x[M.height])") - SSmapping.map_templates[M.name] = M - message_admins("[key_name_admin(usr)] has uploaded a map template ([map])") - else - to_chat(usr, "Map template '[map]' failed to load properly") + if(!M.cached_map) + to_chat(src, "Map template '[map]' failed to parse properly.") + return + + var/datum/map_report/report = M.cached_map.check_for_errors() + var/report_link + if(report) + report.show_to(src) + report_link = " - validation report" + to_chat(src, "Map template '[map]' failed validation.") + if(report.loadable) + var/response = alert(src, "The map failed validation, would you like to load it anyways?", "Map Errors", "Cancel", "Upload Anyways") + if(response != "Upload Anyways") + return + else + alert(src, "The map failed validation and cannot be loaded.", "Map Errors", "Oh Darn") + return + + SSmapping.map_templates[M.name] = M + message_admins("[key_name_admin(src)] has uploaded a map template '[map]' ([M.width]x[M.height])[report_link].") + to_chat(src, "Map template '[map]' ready to place ([M.width]x[M.height])") diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index f2df21c988..8559a3235e 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -47,7 +47,8 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list( /client/proc/stop_line_profiling, /client/proc/show_line_profiling, /client/proc/create_mapping_job_icons, - /client/proc/debug_z_levels + /client/proc/debug_z_levels, + /client/proc/place_ruin )) /obj/effect/debugging/mapfix_marker diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index 405595d1de..ace5e09a14 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -629,9 +629,11 @@ GLOBAL_PROTECT(VVpixelmovement) if (O.vv_edit_var(variable, var_new) == FALSE) to_chat(src, "Your edit was rejected by the object.") return + vv_update_display(O, "varedited", VV_MSG_EDITED) SEND_GLOBAL_SIGNAL(COMSIG_GLOB_VAR_EDIT, args) log_world("### VarEdit by [key_name(src)]: [O.type] [variable]=[var_value] => [var_new]") log_admin("[key_name(src)] modified [original_name]'s [variable] from [html_encode("[var_value]")] to [html_encode("[var_new]")]") var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] from [var_value] to [var_new]" message_admins(msg) admin_ticket_log(O, msg) + return TRUE diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index 190ddd7e63..d9732818bd 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -351,7 +351,7 @@ "template" = list("desc" = "Template", "callback" = CALLBACK(src, .proc/makeERTTemplateModified), "type" = "datum", "path" = "/datum/ert", "subtypesonly" = TRUE, "value" = ertemplate.type), "teamsize" = list("desc" = "Team Size", "type" = "number", "value" = ertemplate.teamsize), "mission" = list("desc" = "Mission", "type" = "string", "value" = ertemplate.mission), - "polldesc" = list("desc" = "Ghost poll description", "string" = "text", "value" = ertemplate.polldesc), + "polldesc" = list("desc" = "Ghost poll description", "type" = "string", "value" = ertemplate.polldesc), "enforce_human" = list("desc" = "Enforce human authority", "type" = "boolean", "value" = "[(CONFIG_GET(flag/enforce_human_authority) ? "Yes" : "No")]"), "open_armory" = list("desc" = "Open armory doors", "type" = "boolean", "value" = "[(ertemplate.opendoors ? "Yes" : "No")]"), ) diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index e690d0a0ad..da1b404ea5 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -31,23 +31,24 @@ cross.icon_state = "tome" font_color = "red" prayer_type = "CULTIST PRAYER" - deity = "Nar-Sie" + deity = "Nar'Sie" else if(isliving(usr)) var/mob/living/L = usr if(L.has_trait(TRAIT_SPIRITUAL)) cross.icon_state = "holylight" font_color = "blue" prayer_type = "SPIRITUAL PRAYER" - + + var/msg_tmp = msg msg = "[icon2html(cross, GLOB.admins)][prayer_type][deity ? " (to [deity])" : ""]: [ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]: [msg]" - + for(var/client/C in GLOB.admins) if(C.prefs.chat_toggles & CHAT_PRAYER) to_chat(C, msg) if(C.prefs.toggles & SOUND_PRAYERS) if(usr.job == "Chaplain") SEND_SOUND(C, sound('sound/effects/pray.ogg')) - to_chat(usr, "Your prayers have been received by the gods.") + to_chat(usr, "You pray to the gods: \"[msg_tmp]\"") SSblackbox.record_feedback("tally", "admin_verb", 1, "Prayer") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! //log_admin("HELP: [key_name(src)]: [msg]") diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index de30b69f97..c1cece152d 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -48,12 +48,12 @@ /client/proc/cmd_admin_headset_message(mob/M in GLOB.mob_list) set category = "Special Verbs" set name = "Headset Message" - + admin_headset_message(M) /client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null) var/mob/living/carbon/human/H = M - + if(!check_rights(R_ADMIN)) return @@ -75,7 +75,7 @@ message_admins("[key_name_admin(src)] decided not to answer [key_name_admin(H)]'s [sender] request.") return - log_admin("[key_name(src)] replied to [key_name(H)]'s [sender] message with the message [input].") + log_directed_talk(src, H, input, LOG_ADMIN, "reply") message_admins("[key_name_admin(src)] replied to [key_name_admin(H)]'s [sender] message with: \"[input]\"") to_chat(H, "You hear something crackle in your ears for a moment before a voice speaks. \"Please stand by for a message from [sender == "Syndicate" ? "your benefactor" : "Central Command"]. Message as follows[sender == "Syndicate" ? ", agent." : ":"] [input]. Message ends.\"") @@ -596,16 +596,28 @@ Traitors and the like can also be revived with the previous role mostly intact. /client/proc/admin_delete(datum/D) var/atom/A = D - var/coords = istype(A) ? " at ([A.x], [A.y], [A.z])" : "" - if (alert(src, "Are you sure you want to delete:\n[D]\nat[coords]?", "Confirmation", "Yes", "No") == "Yes") - log_admin("[key_name(usr)] deleted [D][coords]") - message_admins("[key_name_admin(usr)] deleted [D][coords]") + var/coords = "" + var/jmp_coords = "" + if(istype(A)) + var/turf/T = get_turf(A) + if(T) + coords = "at [COORD(T)]" + jmp_coords = "at [ADMIN_COORDJMP(T)]" + else + jmp_coords = coords = "in nullspace" + + if (alert(src, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", "Yes", "No") == "Yes") + log_admin("[key_name(usr)] deleted [D] [coords]") + message_admins("[key_name_admin(usr)] deleted [D] [jmp_coords]") SSblackbox.record_feedback("tally", "admin_verb", 1, "Delete") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! if(isturf(D)) var/turf/T = D T.ScrapeAway() else + vv_update_display(D, "deleted", VV_MSG_DELETED) qdel(D) + if(!QDELETED(D)) + vv_update_display(D, "deleted", "") /client/proc/cmd_admin_list_open_jobs() set category = "Admin" @@ -1117,7 +1129,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits return for(var/mob/living/carbon/human/H in GLOB.carbon_list) - new /obj/item/organ/zombie_infection(H) + new /obj/item/organ/zombie_infection/nodamage(H) message_admins("[key_name_admin(usr)] added a latent zombie infection to all humans.") log_admin("[key_name(usr)] added a latent zombie infection to all humans.") @@ -1134,7 +1146,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits if(confirm != "Yes") return - for(var/obj/item/organ/zombie_infection/I in GLOB.zombie_infection_list) + for(var/obj/item/organ/zombie_infection/nodamage/I in GLOB.zombie_infection_list) qdel(I) message_admins("[key_name_admin(usr)] cured all zombies.") @@ -1201,82 +1213,6 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits log_admin("[key_name(usr)] sent \"[input]\" as the Tip of the Round.") SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Tip") -/proc/mass_purrbation() - for(var/M in GLOB.mob_list) - if(ishumanbasic(M)) - purrbation_apply(M) - CHECK_TICK - -/proc/mass_remove_purrbation() - for(var/M in GLOB.mob_list) - if(ishumanbasic(M)) - purrbation_remove(M) - CHECK_TICK - -/proc/purrbation_toggle(mob/living/carbon/human/H, silent = FALSE) - if(!ishumanbasic(H)) - return - if(!iscatperson(H)) - purrbation_apply(H, silent) - . = TRUE - else - purrbation_remove(H, silent) - . = FALSE - -/proc/purrbation_apply(mob/living/carbon/human/H, silent = FALSE) - if(!ishuman(H)) - return - if(iscatperson(H)) - return - - var/obj/item/organ/ears/cat/ears = new - var/obj/item/organ/tail/cat/tail = new - ears.Insert(H, drop_if_replaced=FALSE) - tail.Insert(H, drop_if_replaced=FALSE) - - if(!silent) - to_chat(H, "Something is nya~t right.") - playsound(get_turf(H), 'sound/effects/meow1.ogg', 50, 1, -1) - -/proc/purrbation_remove(mob/living/carbon/human/H, silent = FALSE) - if(!ishuman(H)) - return - if(!iscatperson(H)) - return - - var/obj/item/organ/ears/cat/ears = H.getorgan(/obj/item/organ/ears/cat) - var/obj/item/organ/tail/cat/tail = H.getorgan(/obj/item/organ/tail/cat) - - if(ears) - var/obj/item/organ/ears/NE - if(H.dna.species && H.dna.species.mutantears) - // Roundstart cat ears override H.dna.species.mutantears, reset it here. - H.dna.species.mutantears = initial(H.dna.species.mutantears) - if(H.dna.species.mutantears) - NE = new H.dna.species.mutantears() - - if(!NE) - // Go with default ears - NE = new /obj/item/organ/ears() - - NE.Insert(H, drop_if_replaced = FALSE) - - if(tail) - var/obj/item/organ/tail/NT - if(H.dna.species && H.dna.species.mutanttail) - // Roundstart cat tail overrides H.dna.species.mutanttail, reset it here. - H.dna.species.mutanttail = initial(H.dna.species.mutanttail) - if(H.dna.species.mutanttail) - NT = new H.dna.species.mutanttail() - - if(NT) - NT.Insert(H, drop_if_replaced = FALSE) - else - tail.Remove(H) - - if(!silent) - to_chat(H, "You are no longer a cat.") - /client/proc/modify_goals() set category = "Debug" set name = "Modify goals" @@ -1310,7 +1246,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits /client/proc/smite(mob/living/carbon/human/target as mob) set name = "Smite" set category = "Fun" - if(!check_rights(R_ADMIN)) + if(!check_rights(R_ADMIN) || !check_rights(R_FUN)) return var/list/punishment_list = list(ADMIN_PUNISHMENT_LIGHTNING, ADMIN_PUNISHMENT_BRAINDAMAGE, ADMIN_PUNISHMENT_GIB, ADMIN_PUNISHMENT_BSA, ADMIN_PUNISHMENT_FIREBALL, ADMIN_PUNISHMENT_ROD, ADMIN_PUNISHMENT_SUPPLYPOD, ADMIN_PUNISHMENT_MAZING) diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 50281d54b4..aa91af1dd8 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -14,7 +14,8 @@ GLOBAL_LIST_EMPTY(antagonists) var/list/objectives = list() var/antag_memory = ""//These will be removed with antag datum var/antag_moodlet //typepath of moodlet that the mob will gain with their status - + var/can_hijack = HIJACK_NEUTRAL //If these antags are alone on shuttle hijack happens. + //Antag panel properties var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index ca87b94d5b..edf703b34d 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -170,7 +170,7 @@ //////SYNDICATE BORG /obj/item/antag_spawner/nuke_ops/borg_tele name = "syndicate cyborg teleporter" - desc = "A single-use teleporter designed to quickly reinforce operatives in the field.." + desc = "A single-use teleporter designed to quickly reinforce operatives in the field." icon = 'icons/obj/device.dmi' icon_state = "locator" diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/abduction_gear.dm index c985ee2236..3fa1020000 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_gear.dm @@ -190,6 +190,7 @@ /obj/item/abductor/gizmo/afterattack(atom/target, mob/living/user, flag, params) + . = ..() if(flag) return if(!ScientistCheck(user)) @@ -251,6 +252,7 @@ radio_off(M, user) /obj/item/abductor/silencer/afterattack(atom/target, mob/living/user, flag, params) + . = ..() if(flag) return if(!AbductorCheck(user)) @@ -303,6 +305,7 @@ to_chat(user, "You switch the device to [mode==MIND_DEVICE_MESSAGE? "TRANSMISSION": "COMMAND"] MODE") /obj/item/abductor/mind_device/afterattack(atom/target, mob/living/user, flag, params) + . = ..() if(!ScientistCheck(user)) return @@ -359,7 +362,7 @@ to_chat(L, "You hear a voice in your head saying: [message]") to_chat(user, "You send the message to your target.") - log_talk(user,"[key_name(user)] sent an abductor mind message to [key_name(L)]: '[message]'", LOGSAY) + log_directed_talk(user, L, message, LOG_SAY, "abductor whisper") /obj/item/firing_pin/abductor @@ -512,7 +515,7 @@ Congratulations! You are now trained for invasive xenobiology research!"} var/mob/living/carbon/human/H = L H.forcesay(GLOB.hit_appends) - add_logs(user, L, "stunned") + log_combat(user, L, "stunned") /obj/item/abductor_baton/proc/SleepAttack(mob/living/L,mob/living/user) if(L.incapacitated(TRUE, TRUE)) @@ -526,7 +529,7 @@ Congratulations! You are now trained for invasive xenobiology research!"} "You suddenly feel very drowsy!") playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) L.Sleeping(1200) - add_logs(user, L, "put to sleep") + log_combat(user, L, "put to sleep") else if(istype(L.get_item_by_slot(SLOT_HEAD), /obj/item/clothing/head/foilhat)) to_chat(user, "The specimen's protective headgear is completely blocking our sleep inducement methods!") @@ -543,16 +546,16 @@ Congratulations! You are now trained for invasive xenobiology research!"} return var/mob/living/carbon/C = L if(!C.handcuffed) - if(C.get_num_arms() >= 2 || C.get_arm_ignore()) + if(C.get_num_arms(FALSE) >= 2 || C.get_arm_ignore()) playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) C.visible_message("[user] begins restraining [C] with [src]!", \ "[user] begins shaping an energy field around your hands!") - if(do_mob(user, C, 30) && (C.get_num_arms() >= 2 || C.get_arm_ignore())) + if(do_mob(user, C, 30) && (C.get_num_arms(FALSE) >= 2 || C.get_arm_ignore())) if(!C.handcuffed) C.handcuffed = new /obj/item/restraints/handcuffs/energy/used(C) C.update_handcuffed() to_chat(user, "You restrain [C].") - add_logs(user, C, "handcuffed") + log_combat(user, C, "handcuffed") else to_chat(user, "You fail to restrain [C].") else diff --git a/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm b/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm index 69af4bf584..e60209cb7b 100644 --- a/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm +++ b/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm @@ -213,6 +213,7 @@ mob_size = MOB_SIZE_LARGE see_in_dark = 8 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + hud_type = /datum/hud/blobbernaut var/independent = FALSE /mob/living/simple_animal/hostile/blob/blobbernaut/Initialize() diff --git a/code/modules/antagonists/blob/blob/overmind.dm b/code/modules/antagonists/blob/blob/overmind.dm index 0f156e5216..ddf745c0f9 100644 --- a/code/modules/antagonists/blob/blob/overmind.dm +++ b/code/modules/antagonists/blob/blob/overmind.dm @@ -21,6 +21,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) faction = list(ROLE_BLOB) lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE call_life = TRUE + hud_type = /datum/hud/blob_overmind var/obj/structure/blob/core/blob_core = null // The blob overmind's core var/blob_points = 0 var/max_blob_points = 100 @@ -184,7 +185,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) blob_points = CLAMP(blob_points + points, 0, max_blob_points) hud_used.blobpwrdisplay.maptext = "
[round(blob_points)]
" -/mob/camera/blob/say(message) +/mob/camera/blob/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) if (!message) return @@ -207,7 +208,7 @@ GLOBAL_LIST_EMPTY(blob_nodes) if (!message) return - log_talk(src,"[key_name(src)] : [message]",LOGSAY) + src.log_talk(message, LOG_SAY) var/message_a = say_quote(message, get_spans()) var/rendered = "\[Blob Telepathy\] [name]([blob_reagent_datum.name]) [message_a]" diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm index 8527ab533c..f63e81acbf 100644 --- a/code/modules/antagonists/brother/brother.dm +++ b/code/modules/antagonists/brother/brother.dm @@ -5,6 +5,7 @@ var/special_role = ROLE_BROTHER var/datum/team/brother_team/team antag_moodlet = /datum/mood_event/focused + can_hijack = HIJACK_HIJACKER /datum/antagonist/brother/create_team(datum/team/brother_team/new_team) if(!new_team) diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm index ab6dd3607d..cc3b3a3057 100644 --- a/code/modules/antagonists/changeling/changeling.dm +++ b/code/modules/antagonists/changeling/changeling.dm @@ -167,7 +167,7 @@ to_chat(owner.current, "We have reached our capacity for abilities.") return - if(owner.current.has_trait(TRAIT_FAKEDEATH))//To avoid potential exploits by buying new powers while in stasis, which clears your verblist. + if(owner.current.has_trait(TRAIT_DEATHCOMA))//To avoid potential exploits by buying new powers while in stasis, which clears your verblist. to_chat(owner.current, "We lack the energy to evolve new abilities right now.") return diff --git a/code/modules/antagonists/changeling/changeling_power.dm b/code/modules/antagonists/changeling/changeling_power.dm index 6ae73336d4..ae255a04f8 100644 --- a/code/modules/antagonists/changeling/changeling_power.dm +++ b/code/modules/antagonists/changeling/changeling_power.dm @@ -65,7 +65,7 @@ if(req_stat < user.stat) to_chat(user, "We are incapacitated.") return 0 - if((user.has_trait(TRAIT_FAKEDEATH)) && (!ignores_fakedeath)) + if((user.has_trait(TRAIT_DEATHCOMA)) && (!ignores_fakedeath)) to_chat(user, "We are incapacitated.") return 0 return 1 diff --git a/code/modules/antagonists/changeling/powers/absorb.dm b/code/modules/antagonists/changeling/powers/absorb.dm index a8bec72771..2f8fc6943f 100644 --- a/code/modules/antagonists/changeling/powers/absorb.dm +++ b/code/modules/antagonists/changeling/powers/absorb.dm @@ -68,7 +68,7 @@ //Recent as opposed to all because rounds tend to have a LOT of text. var/list/recent_speech = list() - var/list/say_log = target.logging[INDIVIDUAL_SAY_LOG] + var/list/say_log = target.logging[LOG_SAY] if(LAZYLEN(say_log) > LING_ABSORB_RECENT_SPEECH) recent_speech = say_log.Copy(say_log.len-LING_ABSORB_RECENT_SPEECH+1,0) //0 so len-LING_ARS+1 to end of list diff --git a/code/modules/antagonists/changeling/powers/fakedeath.dm b/code/modules/antagonists/changeling/powers/fakedeath.dm index 754c894278..753d858cef 100644 --- a/code/modules/antagonists/changeling/powers/fakedeath.dm +++ b/code/modules/antagonists/changeling/powers/fakedeath.dm @@ -5,6 +5,7 @@ dna_cost = 0 req_dna = 1 req_stat = DEAD + ignores_fakedeath = TRUE //Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay. /obj/effect/proc_holder/changeling/fakedeath/sting_action(mob/living/user) @@ -27,7 +28,7 @@ C.purchasedpowers += new /obj/effect/proc_holder/changeling/revive(null) /obj/effect/proc_holder/changeling/fakedeath/can_sting(mob/living/user) - if(user.has_trait(TRAIT_FAKEDEATH, "changeling")) + if(user.has_trait(TRAIT_DEATHCOMA, "changeling")) to_chat(user, "We are already reviving.") return if(!user.stat) //Confirmation for living changelings if they want to fake their death diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 08fe6bd0ce..6a952d09f8 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -159,6 +159,7 @@ attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") sharpness = IS_SHARP var/can_drop = FALSE + var/fake = FALSE /obj/item/melee/arm_blade/Initialize(mapload,silent,synthetic) . = ..() @@ -169,6 +170,7 @@ AddComponent(/datum/component/butchering, 60, 80) /obj/item/melee/arm_blade/afterattack(atom/target, mob/user, proximity) + . = ..() if(!proximity) return if(istype(target, /obj/structure/table)) @@ -236,6 +238,7 @@ fire_sound = 'sound/effects/splat.ogg' force = 0 max_charges = 1 + fire_delay = 1 throwforce = 0 //Just to be on the safe side throw_range = 0 throw_speed = 0 @@ -252,6 +255,11 @@ /obj/item/gun/magic/tentacle/shoot_with_empty_chamber(mob/living/user as mob|obj) to_chat(user, "The [name] is not ready yet.") +/obj/item/gun/magic/tentacle/process_chamber() + . = ..() + if(charges == 0) + qdel(src) + /obj/item/gun/magic/tentacle/suicide_act(mob/user) user.visible_message("[user] coils [src] tightly around [user.p_their()] neck! It looks like [user.p_theyre()] trying to commit suicide!") return (OXYLOSS) @@ -300,8 +308,12 @@ /obj/item/projectile/tentacle/proc/tentacle_grab(mob/living/carbon/human/H, mob/living/carbon/C) if(H.Adjacent(C)) + if(H.get_active_held_item() && !H.get_inactive_held_item()) + H.swap_hand() + if(H.get_active_held_item()) + return C.grabbedby(H) - C.grippedby(H) //instant aggro grab + C.grippedby(H, instant = TRUE) //instant aggro grab /obj/item/projectile/tentacle/proc/tentacle_stab(mob/living/carbon/human/H, mob/living/carbon/C) if(H.Adjacent(C)) @@ -316,7 +328,6 @@ /obj/item/projectile/tentacle/on_hit(atom/target, blocked = FALSE) var/mob/living/carbon/human/H = firer - H.dropItemToGround(source.gun, TRUE) //Unequip thus delete the tentacle on hit if(blocked >= 100) return 0 if(isitem(target)) @@ -332,7 +343,11 @@ if(!L.anchored && !L.throwing)//avoid double hits if(iscarbon(L)) var/mob/living/carbon/C = L - switch(firer.a_intent) + var/firer_intent = INTENT_HARM + var/mob/M = firer + if(istype(M)) + firer_intent = M.a_intent + switch(firer_intent) if(INTENT_HELP) C.visible_message("[L] is pulled by [H]'s tentacle!","A tentacle grabs you and pulls you towards [H]!") C.throw_at(get_step_towards(H,C), 8, 2) diff --git a/code/modules/antagonists/changeling/powers/revive.dm b/code/modules/antagonists/changeling/powers/revive.dm index d9c1ca7221..937748a7ef 100644 --- a/code/modules/antagonists/changeling/powers/revive.dm +++ b/code/modules/antagonists/changeling/powers/revive.dm @@ -33,7 +33,7 @@ if(!.) return - if(user.has_trait(CHANGELING_DRAIN) || ((user.stat != DEAD) && !(user.has_trait(TRAIT_FAKEDEATH)))) + if(user.has_trait(CHANGELING_DRAIN) || ((user.stat != DEAD) && !(user.has_trait(TRAIT_DEATHCOMA)))) var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) changeling.purchasedpowers -= src return FALSE diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm index a801c87ac2..9bf4a76454 100644 --- a/code/modules/antagonists/changeling/powers/tiny_prick.dm +++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm @@ -93,7 +93,7 @@ return 1 /obj/effect/proc_holder/changeling/sting/transformation/sting_action(mob/user, mob/target) - add_logs(user, target, "stung", "transformation sting", " new identity is [selected_dna.dna.real_name]") + log_combat(user, target, "stung", "transformation sting", " new identity is '[selected_dna.dna.real_name]'") var/datum/dna/NewDNA = selected_dna.dna if(ismonkey(target)) to_chat(user, "Our genes cry out as we sting [target.name]!") @@ -119,9 +119,7 @@ /obj/item/melee/arm_blade/false desc = "A grotesque mass of flesh that used to be your arm. Although it looks dangerous at first, you can tell it's actually quite dull and useless." force = 5 //Basically as strong as a punch - -/obj/item/melee/arm_blade/false/afterattack(atom/target, mob/user, proximity) - return + fake = TRUE /obj/effect/proc_holder/changeling/sting/false_armblade/can_sting(mob/user, mob/target) if(!..()) @@ -134,7 +132,7 @@ return 1 /obj/effect/proc_holder/changeling/sting/false_armblade/sting_action(mob/user, mob/target) - add_logs(user, target, "stung", object="false armblade sting") + log_combat(user, target, "stung", object="false armblade sting") var/obj/item/held = target.get_active_held_item() if(held && !target.dropItemToGround(held)) @@ -176,7 +174,7 @@ return changeling.can_absorb_dna(target) /obj/effect/proc_holder/changeling/sting/extract_dna/sting_action(mob/user, mob/living/carbon/human/target) - add_logs(user, target, "stung", "extraction sting") + log_combat(user, target, "stung", "extraction sting") var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) if(!(changeling.has_dna(target.dna))) changeling.add_new_profile(target) @@ -191,7 +189,7 @@ dna_cost = 2 /obj/effect/proc_holder/changeling/sting/mute/sting_action(mob/user, mob/living/carbon/target) - add_logs(user, target, "stung", "mute sting") + log_combat(user, target, "stung", "mute sting") target.silent += 30 return TRUE @@ -204,7 +202,7 @@ dna_cost = 1 /obj/effect/proc_holder/changeling/sting/blind/sting_action(mob/user, mob/living/carbon/target) - add_logs(user, target, "stung", "blind sting") + log_combat(user, target, "stung", "blind sting") to_chat(target, "Your eyes burn horrifically!") target.become_nearsighted(EYE_DAMAGE) target.blind_eyes(20) @@ -220,7 +218,7 @@ dna_cost = 1 /obj/effect/proc_holder/changeling/sting/LSD/sting_action(mob/user, mob/living/carbon/target) - add_logs(user, target, "stung", "LSD sting") + log_combat(user, target, "stung", "LSD sting") addtimer(CALLBACK(src, .proc/hallucination_time, target), rand(100,200)) return TRUE @@ -237,7 +235,7 @@ dna_cost = 2 /obj/effect/proc_holder/changeling/sting/cryo/sting_action(mob/user, mob/target) - add_logs(user, target, "stung", "cryo sting") + log_combat(user, target, "stung", "cryo sting") if(target.reagents) target.reagents.add_reagent("frostoil", 30) return TRUE diff --git a/code/modules/antagonists/clockcult/clock_effects/city_of_cogs_rift.dm b/code/modules/antagonists/clockcult/clock_effects/city_of_cogs_rift.dm index e318126caf..21d0035ef1 100644 --- a/code/modules/antagonists/clockcult/clock_effects/city_of_cogs_rift.dm +++ b/code/modules/antagonists/clockcult/clock_effects/city_of_cogs_rift.dm @@ -40,7 +40,7 @@ /obj/effect/clockwork/city_of_cogs_rift/attack_hand(atom/movable/AM) beckon(AM) -/obj/effect/clockwork/city_of_cogs_rift/CollidedWith(atom/movable/AM) +/obj/effect/clockwork/city_of_cogs_rift/Bumped(atom/movable/AM) if(!QDELETED(AM)) if(isliving(AM)) var/mob/living/L = AM diff --git a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm index b06a6ef3d8..97d7f0c128 100644 --- a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm +++ b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm @@ -57,7 +57,7 @@ /obj/effect/clockwork/sigil/proc/sigil_effects(mob/living/L) -//Sigil of Transgression: Stuns the first non-servant to walk on it and flashes all nearby non_servants. Nar-Sian cultists are damaged and knocked down for a longer time +//Sigil of Transgression: Stuns the first non-servant to walk on it and flashes all nearby non_servants. Nar'Sian cultists are damaged and knocked down for a longer time /obj/effect/clockwork/sigil/transgression name = "dull sigil" desc = "A dull, barely-visible golden sigil. It's as though light was carved into the ground." @@ -143,7 +143,7 @@ GLOB.application_scripture_unlocked = TRUE hierophant_message("With the conversion of a new servant the Ark's power grows. Application scriptures are now available.") if(add_servant_of_ratvar(L)) - L.log_message("Conversion was done with a [sigil_name].", INDIVIDUAL_ATTACK_LOG) + L.log_message("conversion was done with a [sigil_name]", LOG_ATTACK, color="BE8700") if(iscarbon(L)) var/mob/living/carbon/M = L M.uncuff() diff --git a/code/modules/antagonists/clockcult/clock_effects/general_markers.dm b/code/modules/antagonists/clockcult/clock_effects/general_markers.dm index 6e6704a68c..2e2a01d386 100644 --- a/code/modules/antagonists/clockcult/clock_effects/general_markers.dm +++ b/code/modules/antagonists/clockcult/clock_effects/general_markers.dm @@ -21,7 +21,7 @@ name = "Inath-neq, the Resonant Cogwheel" desc = "A humanoid form blazing with blue fire. It radiates an aura of kindness and caring." clockwork_desc = "One of Ratvar's four generals. Before her current form, Inath-neq was a powerful warrior priestess commanding the Resonant Cogs, a sect of Ratvarian warriors renowned for \ - their prowess. After a lost battle with Nar-Sian cultists, Inath-neq was struck down and stated in her dying breath, \ + their prowess. After a lost battle with Nar'Sian cultists, Inath-neq was struck down and stated in her dying breath, \ \"The Resonant Cogs shall not fall silent this day, but will come together to form a wheel that shall never stop turning.\" Ratvar, touched by this, granted Inath-neq an eternal body and \ merged her soul with those of the Cogs slain with her on the battlefield." icon = 'icons/effects/119x268.dmi' diff --git a/code/modules/antagonists/clockcult/clock_effects/servant_blocker.dm b/code/modules/antagonists/clockcult/clock_effects/servant_blocker.dm index 0e6e1c3821..12b2af3f64 100644 --- a/code/modules/antagonists/clockcult/clock_effects/servant_blocker.dm +++ b/code/modules/antagonists/clockcult/clock_effects/servant_blocker.dm @@ -15,9 +15,7 @@ /obj/effect/clockwork/servant_blocker/Destroy(force) if(!force) return QDEL_HINT_LETMELIVE - var/turf/T = get_turf(src) - . = ..() - T.air_update_turf(TRUE) + return ..() /obj/effect/clockwork/servant_blocker/CanPass(atom/movable/M, turf/target) var/list/target_contents = M.GetAllContents() + M diff --git a/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm b/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm index 31c5883790..ea2ec4d6ef 100644 --- a/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm +++ b/code/modules/antagonists/clockcult/clock_effects/spatial_gateway.dm @@ -119,7 +119,7 @@ return -/obj/effect/clockwork/spatial_gateway/CollidedWith(atom/movable/AM) +/obj/effect/clockwork/spatial_gateway/Bumped(atom/movable/AM) ..() if(!QDELETED(AM)) pass_through_gateway(AM, FALSE) diff --git a/code/modules/antagonists/clockcult/clock_helpers/hierophant_network.dm b/code/modules/antagonists/clockcult/clock_helpers/hierophant_network.dm index 4c1789c50d..37f6f0b2d7 100644 --- a/code/modules/antagonists/clockcult/clock_helpers/hierophant_network.dm +++ b/code/modules/antagonists/clockcult/clock_helpers/hierophant_network.dm @@ -46,5 +46,5 @@ return if(ishuman(owner)) clockwork_say(owner, "[text2ratvar("Servants, hear my words: [input]")]", TRUE) - log_talk(owner,"CLOCK:[key_name(owner)] : [input]",LOGSAY) + owner.log_talk(input, LOG_SAY, tag="clockwork") titled_hierophant_message(owner, input, span_for_name, span_for_message, title) diff --git a/code/modules/antagonists/clockcult/clock_helpers/ratvarian_language.dm b/code/modules/antagonists/clockcult/clock_helpers/ratvarian_language.dm index 2d49787348..c784739d2e 100644 --- a/code/modules/antagonists/clockcult/clock_helpers/ratvarian_language.dm +++ b/code/modules/antagonists/clockcult/clock_helpers/ratvarian_language.dm @@ -1,7 +1,7 @@ /* The Ratvarian Language - In the lore of the Servants of Ratvar, the Ratvarian tongue is a timeless language and full of power. It sounds like gibberish, much like Nar-Sie's language, but is in fact derived from -aforementioned language, and may induce miracles when spoken in the correct way with an amplifying tool (similar to runes used by the Nar-Sian cult). + In the lore of the Servants of Ratvar, the Ratvarian tongue is a timeless language and full of power. It sounds like gibberish, much like Nar'Sie's language, but is in fact derived from +aforementioned language, and may induce miracles when spoken in the correct way with an amplifying tool (similar to runes used by the Nar'Sian cult). While the canon states that the language of Ratvar and his servants is incomprehensible to the unenlightened as it is a derivative of the most ancient known language, in reality it is actually very simple. To translate a plain English sentence to Ratvar's tongue, simply move all of the letters thirteen places ahead, starting from "a" if the end of the alphabet is reached. diff --git a/code/modules/antagonists/clockcult/clock_helpers/slab_abilities.dm b/code/modules/antagonists/clockcult/clock_helpers/slab_abilities.dm index aaeb871e5d..ad83d5f4d2 100644 --- a/code/modules/antagonists/clockcult/clock_helpers/slab_abilities.dm +++ b/code/modules/antagonists/clockcult/clock_helpers/slab_abilities.dm @@ -53,7 +53,7 @@ L.handcuffed = new/obj/item/restraints/handcuffs/clockwork(L) L.update_handcuffed() to_chat(ranged_ability_user, "You shackle [L].") - add_logs(ranged_ability_user, L, "handcuffed") + log_combat(ranged_ability_user, L, "handcuffed") else to_chat(ranged_ability_user, "You fail to shackle [L].") @@ -117,12 +117,12 @@ L.adjustOxyLoss(-oxydamage) L.adjustToxLoss(totaldamage * 0.5, TRUE, TRUE) clockwork_say(ranged_ability_user, text2ratvar("[has_holy_water ? "Heal tainted" : "Mend wounded"] flesh!")) - add_logs(ranged_ability_user, L, "healed with Sentinel's Compromise") + log_combat(ranged_ability_user, L, "healed with Sentinel's Compromise") L.visible_message("A blue light washes over [L], [has_holy_water ? "causing [L.p_them()] to briefly glow as it mends" : " mending"] [L.p_their()] bruises and burns!", \ "You feel Inath-neq's power healing your wounds[has_holy_water ? " and purging the darkness within you" : ""], but a deep nausea overcomes you!") else clockwork_say(ranged_ability_user, text2ratvar("Purge foul darkness!")) - add_logs(ranged_ability_user, L, "purged of holy water with Sentinel's Compromise") + log_combat(ranged_ability_user, L, "purged of holy water with Sentinel's Compromise") L.visible_message("A blue light washes over [L], causing [L.p_them()] to briefly glow!", \ "You feel Inath-neq's power purging the darkness within you!") playsound(targetturf, 'sound/magic/staff_healing.ogg', 50, 1) @@ -153,7 +153,7 @@ var/turf/U = get_turf(target) to_chat(ranged_ability_user, "You release the light of Ratvar!") clockwork_say(ranged_ability_user, text2ratvar("Purge all untruths and honor Engine!")) - add_logs(ranged_ability_user, U, "fired at with Kindle") + log_combat(ranged_ability_user, U, "fired at with Kindle") playsound(ranged_ability_user, 'sound/magic/blink.ogg', 50, TRUE, frequency = 0.5) var/obj/item/projectile/kindle/A = new(T) A.preparePixelProjectile(target, caller, params) @@ -265,7 +265,7 @@ "You direct the judicial force to [target].") var/turf/targetturf = get_turf(target) new/obj/effect/clockwork/judicial_marker(targetturf, ranged_ability_user) - add_logs(ranged_ability_user, targetturf, "created a judicial marker") + log_combat(ranged_ability_user, targetturf, "created a judicial marker") remove_ranged_ability() return TRUE diff --git a/code/modules/antagonists/clockcult/clock_items/clock_components.dm b/code/modules/antagonists/clockcult/clock_items/clock_components.dm index e8f4fd5503..561d49e9ac 100644 --- a/code/modules/antagonists/clockcult/clock_items/clock_components.dm +++ b/code/modules/antagonists/clockcult/clock_items/clock_components.dm @@ -5,7 +5,7 @@ clockwork_desc = null resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF var/component_id //What the component is identified as - var/cultist_message = "You are not worthy of this meme." //Showed to Nar-Sian cultists if they pick up the component in addition to chaplains + var/cultist_message = "You are not worthy of this meme." //Showed to Nar'Sian cultists if they pick up the component in addition to chaplains var/list/servant_of_ratvar_messages = list("ayy" = FALSE, "lmao" = TRUE) //Fluff, shown to servants of Ratvar on a low chance, if associated value is TRUE, will automatically apply ratvarian var/message_span = "heavy_brass" diff --git a/code/modules/antagonists/clockcult/clock_items/clockwork_armor.dm b/code/modules/antagonists/clockcult/clock_items/clockwork_armor.dm index 8ed8edae8a..8411fe9bc9 100644 --- a/code/modules/antagonists/clockcult/clock_items/clockwork_armor.dm +++ b/code/modules/antagonists/clockcult/clock_items/clockwork_armor.dm @@ -22,12 +22,12 @@ if(GLOB.ratvar_awakens) armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) clothing_flags |= STOPSPRESSUREDAMAGE - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT else if(GLOB.ratvar_approaches) armor = list("melee" = 70, "bullet" = 80, "laser" = -15, "energy" = 25, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) clothing_flags |= STOPSPRESSUREDAMAGE - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT else armor = list("melee" = 60, "bullet" = 70, "laser" = -25, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) @@ -83,12 +83,12 @@ if(GLOB.ratvar_awakens) armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) clothing_flags |= STOPSPRESSUREDAMAGE - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT else if(GLOB.ratvar_approaches) armor = list("melee" = 70, "bullet" = 80, "laser" = -15, "energy" = 25, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) clothing_flags |= STOPSPRESSUREDAMAGE - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT else armor = list("melee" = 60, "bullet" = 70, "laser" = -25, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) @@ -149,7 +149,7 @@ if(GLOB.ratvar_awakens) armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) clothing_flags |= STOPSPRESSUREDAMAGE - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT else armor = list("melee" = 80, "bullet" = 70, "laser" = -25, "energy" = 0, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) diff --git a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm index 1cdc05a07a..d7ae0c7fc6 100644 --- a/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm +++ b/code/modules/antagonists/clockcult/clock_items/clockwork_slab.dm @@ -313,7 +313,7 @@ dat += "-=-=-=-=-=-" if("Scripture") dat += "The Ancient Scripture

" - dat += "If you have experience with the Nar-Sian cult (or the \"blood cult\") then you will know of runes. They are the manifestations of the Geometer's power, and where most \ + dat += "If you have experience with the Nar'Sian cult (or the \"blood cult\") then you will know of runes. They are the manifestations of the Geometer's power, and where most \ of the cult's supernatural ability comes from. The Servant equivalent of runes is called scripture, and unlike runes, scripture is loaded into your clockwork slab.

" dat += "Each piece of scripture has widely-varying effects. Your most important scripture, Geis, is obvious and suspicious, but charges your slab with energy and allows \ you to attack a non-Servant in melee range to restrain them and begin converting them into a Servant. This is just one example; each piece of scripture can be simple or \ diff --git a/code/modules/antagonists/clockcult/clock_items/judicial_visor.dm b/code/modules/antagonists/clockcult/clock_items/judicial_visor.dm index f54d88fc49..f44f67e9cb 100644 --- a/code/modules/antagonists/clockcult/clock_items/judicial_visor.dm +++ b/code/modules/antagonists/clockcult/clock_items/judicial_visor.dm @@ -136,7 +136,7 @@ ranged_ability_user.visible_message("[ranged_ability_user]'s judicial visor fires a stream of energy at [target], creating a strange mark!", "You direct [visor]'s power to [target]. You must wait for some time before doing this again.") var/turf/targetturf = get_turf(target) new/obj/effect/clockwork/judicial_marker(targetturf, ranged_ability_user) - add_logs(ranged_ability_user, targetturf, "created a judicial marker") + log_combat(ranged_ability_user, targetturf, "created a judicial marker") ranged_ability_user.update_action_buttons_icon() ranged_ability_user.update_inv_glasses() addtimer(CALLBACK(visor, /obj/item/clothing/glasses/judicial_visor.proc/recharge_visor, ranged_ability_user), GLOB.ratvar_awakens ? visor.recharge_cooldown*0.1 : visor.recharge_cooldown)//Cooldown is reduced by 10x if Ratvar is up @@ -209,7 +209,7 @@ targetsjudged++ if(!QDELETED(L)) L.adjustBruteLoss(20) //does a decent amount of damage - add_logs(user, L, "struck with a judicial blast") + log_combat(user, L, "struck with a judicial blast") to_chat(user, "[targetsjudged ? "Successfully judged [targetsjudged]":"Judged no"] heretic[targetsjudged == 1 ? "":"s"].") sleep(3) //so the animation completes properly qdel(src) diff --git a/code/modules/antagonists/clockcult/clock_items/wraith_spectacles.dm b/code/modules/antagonists/clockcult/clock_items/wraith_spectacles.dm index 6fda25a1b5..51521ada24 100644 --- a/code/modules/antagonists/clockcult/clock_items/wraith_spectacles.dm +++ b/code/modules/antagonists/clockcult/clock_items/wraith_spectacles.dm @@ -1,4 +1,4 @@ -//Wraith spectacles: Grants X-ray and night vision at the eventual cost of the wearer's sight if worn too long. Nar-Sian cultists are instantly blinded. +//Wraith spectacles: Grants X-ray and night vision at the eventual cost of the wearer's sight if worn too long. Nar'Sian cultists are instantly blinded. /obj/item/clothing/glasses/wraith_spectacles name = "antique spectacles" desc = "Unnerving glasses with opaque yellow lenses." @@ -48,7 +48,7 @@ /obj/item/clothing/glasses/wraith_spectacles/proc/blind_cultist(mob/living/victim) if(iscultist(victim)) - to_chat(victim, "\"It looks like Nar-Sie's dogs really don't value their eyes.\"") + to_chat(victim, "\"It looks like Nar'Sie's dogs really don't value their eyes.\"") to_chat(victim, "Your eyes explode with horrific pain!") victim.emote("scream") victim.become_blind(EYE_DAMAGE) diff --git a/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm b/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm index 0127b8bf69..47faea6dc8 100644 --- a/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm +++ b/code/modules/antagonists/clockcult/clock_mobs/_eminence.dm @@ -76,7 +76,7 @@ E = new V E.Grant(src) -/mob/camera/eminence/say(message) +/mob/camera/eminence/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) if(client) if(client.prefs.muted & MUTE_IC) to_chat(src, "You cannot send IC messages (muted).") @@ -86,7 +86,7 @@ message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) if(!message) return - log_talk(src, "[key_name(src)] : [message]", LOGSAY) + src.log_talk(message, LOG_SAY, tag="clockwork eminence") if(GLOB.ratvar_awakens) visible_message("You feel light slam into your mind and form words: \"[capitalize(message)]\"") playsound(src, 'sound/machines/clockcult/ark_scream.ogg', 50, FALSE) diff --git a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm index 35a6c34aaf..2b1d9d5f02 100644 --- a/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm +++ b/code/modules/antagonists/clockcult/clock_structures/ark_of_the_clockwork_justicar.dm @@ -335,7 +335,7 @@ sleep(3) GLOB.clockwork_gateway_activated = TRUE var/turf/T = SSmapping.get_station_center() - new /obj/structure/destructible/clockwork/massive/ratvar(T, TRUE) //Citadel edit - hugboxes ratvar spawning by admins + new /obj/structure/destructible/clockwork/massive/ratvar(T) SSticker.force_ending = TRUE var/x0 = T.x var/y0 = T.y diff --git a/code/modules/antagonists/clockcult/clock_structures/ocular_warden.dm b/code/modules/antagonists/clockcult/clock_structures/ocular_warden.dm index f6df9efbbe..8dfadb60bc 100644 --- a/code/modules/antagonists/clockcult/clock_structures/ocular_warden.dm +++ b/code/modules/antagonists/clockcult/clock_structures/ocular_warden.dm @@ -71,7 +71,7 @@ L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot1.ogg',75 * get_efficiency_mod(),1) else L.playsound_local(null,'sound/machines/clockcult/ocularwarden-dot2.ogg',75 * get_efficiency_mod(),1) - L.adjustFireLoss((!iscultist(L) ? damage_per_tick : damage_per_tick * 2) * get_efficiency_mod()) //Nar-Sian cultists take additional damage + L.adjustFireLoss((!iscultist(L) ? damage_per_tick : damage_per_tick * 2) * get_efficiency_mod()) //Nar'Sian cultists take additional damage if(GLOB.ratvar_awakens && L) L.adjust_fire_stacks(damage_per_tick) L.IgniteMob() diff --git a/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm b/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm index b8b5c90dd8..9341a7ee6a 100644 --- a/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm +++ b/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm @@ -13,11 +13,11 @@ light_range = 15 light_color = "#BE8700" var/atom/prey //Whatever Ratvar is chasing - var/clashing = FALSE //If Ratvar is fighting with Nar-Sie + var/clashing = FALSE //If Ratvar is fighting with Nar'Sie var/convert_range = 10 obj_flags = CAN_BE_HIT | DANGEROUS_POSSESSION -/obj/structure/destructible/clockwork/massive/ratvar/Initialize(mapload, is_from_gateway = FALSE) +/obj/structure/destructible/clockwork/massive/ratvar/Initialize() . = ..() GLOB.ratvar_awakens++ for(var/obj/O in GLOB.all_clockwork_objects) @@ -29,8 +29,7 @@ sound_to_playing_players('sound/effects/ratvar_reveal.ogg') var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/clockwork_effects.dmi', "ratvar_alert") notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [get_area_name(src)] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay) - if(is_from_gateway) //citadel edit - hugbox - INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 10, null, FALSE, 0) + INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 10, null, FALSE, 0) /obj/structure/destructible/clockwork/massive/ratvar/Destroy() GLOB.ratvar_awakens-- @@ -48,7 +47,7 @@ R.visible_message("[R] forms, and its eyes blink open, glowing bright red!") R.key = O.key -/obj/structure/destructible/clockwork/massive/ratvar/Collide(atom/A) +/obj/structure/destructible/clockwork/massive/ratvar/Bump(atom/A) var/turf/T = get_turf(A) if(T == loc) T = get_step(T, dir) //please don't run into a window like a bird, ratvar @@ -72,7 +71,7 @@ if(L.z == z && !is_servant_of_ratvar(L) && L.mind) meals += L if(GLOB.cult_narsie && GLOB.cult_narsie.z == z) - meals = list(GLOB.cult_narsie) //if you're in the way, handy for him, but ratvar only cares about nar-sie! + meals = list(GLOB.cult_narsie) //if you're in the way, handy for him, but ratvar only cares about Nar'Sie! prey = GLOB.cult_narsie if(get_dist(src, prey) <= 10) clash() @@ -94,7 +93,7 @@ You feel tremendous relief as the crushing focus relents...") prey = null else - dir_to_step_in = get_dir(src, prey) //Unlike Nar-Sie, Ratvar ruthlessly chases down his target + dir_to_step_in = get_dir(src, prey) //Unlike Nar'Sie, Ratvar ruthlessly chases down his target step(src, dir_to_step_in) /obj/structure/destructible/clockwork/massive/ratvar/proc/clash() @@ -107,7 +106,7 @@ clash_of_the_titans(GLOB.cult_narsie) // >:( return TRUE -//Put me in Reebe, will you? Ratvar has found and is going to do a hecking murder on Nar-Sie +//Put me in Reebe, will you? Ratvar has found and is going to do a hecking murder on Nar'Sie /obj/structure/destructible/clockwork/massive/ratvar/proc/clash_of_the_titans(obj/singularity/narsie/narsie) var/winner = "Undeclared" var/base_victory_chance = 1 @@ -133,18 +132,18 @@ flash_color(M, flash_color="#C80000", flash_time=1) shake_camera(M, 4, 3) if(narsie_chance > ratvar_chance) - winner = "Nar-Sie" + winner = "Nar'Sie" break base_victory_chance *= 2 //The clash has a higher chance of resolving each time both gods attack one another switch(winner) if("Ratvar") send_to_playing_players("\"[pick("DIE.", "ROT.")]\"\n\ - \"[pick("Nooooo...", "Not die. To y-", "Die. Ratv-", "Sas tyen re-")]\"") //nar-sie get out + \"[pick("Nooooo...", "Not die. To y-", "Die. Ratv-", "Sas tyen re-")]\"") //Nar'Sie get out sound_to_playing_players('sound/magic/clockwork/anima_fragment_attack.ogg') sound_to_playing_players('sound/magic/demon_dies.ogg', 50) clashing = FALSE qdel(narsie) - if("Nar-Sie") + if("Nar'Sie") send_to_playing_players("\"[pick("Ha.", "Ra'sha fonn dest.", "You fool. To come here.")]\"") //Broken English sound_to_playing_players('sound/magic/demon_attack1.ogg') sound_to_playing_players('sound/magic/clockwork/anima_fragment_death.ogg', 62) diff --git a/code/modules/antagonists/clockcult/clock_structures/taunting_trail.dm b/code/modules/antagonists/clockcult/clock_structures/taunting_trail.dm index 3ffe93ff08..10c68b606f 100644 --- a/code/modules/antagonists/clockcult/clock_structures/taunting_trail.dm +++ b/code/modules/antagonists/clockcult/clock_structures/taunting_trail.dm @@ -43,11 +43,11 @@ affect_mob(AM) return ..() -/obj/structure/destructible/clockwork/taunting_trail/CollidedWith(atom/movable/AM) +/obj/structure/destructible/clockwork/taunting_trail/Bumped(atom/movable/AM) affect_mob(AM) return ..() -/obj/structure/destructible/clockwork/taunting_trail/Collide(atom/movable/AM) +/obj/structure/destructible/clockwork/taunting_trail/Bump(atom/movable/AM) affect_mob(AM) return ..() diff --git a/code/modules/antagonists/clockcult/clockcult.dm b/code/modules/antagonists/clockcult/clockcult.dm index f2c0518cc2..d68e9b594d 100644 --- a/code/modules/antagonists/clockcult/clockcult.dm +++ b/code/modules/antagonists/clockcult/clockcult.dm @@ -53,7 +53,7 @@ SSticker.mode.servants_of_ratvar += owner SSticker.mode.update_servant_icons_added(owner) owner.special_role = ROLE_SERVANT_OF_RATVAR - owner.current.log_message("Has been converted to the cult of Ratvar!", INDIVIDUAL_ATTACK_LOG) + owner.current.log_message("has been converted to the cult of Ratvar!", LOG_ATTACK, color="#BE8700") if(issilicon(current)) if(iscyborg(current) && !silent) var/mob/living/silicon/robot/R = current @@ -161,7 +161,7 @@ if(!silent) owner.current.visible_message("[owner.current] seems to have remembered [owner.current.p_their()] true allegiance!", null, null, null, owner.current) to_chat(owner, "A cold, cold darkness flows through your mind, extinguishing the Justiciar's light and all of your memories as his servant.") - owner.current.log_message("Has renounced the cult of Ratvar!", INDIVIDUAL_ATTACK_LOG) + owner.current.log_message("has renounced the cult of Ratvar!", LOG_ATTACK, color="#BE8700") owner.special_role = null if(iscyborg(owner.current)) to_chat(owner.current, "Despite your freedom from Ratvar's influence, you are still irreparably damaged and no longer possess certain functions such as AI linking.") diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm index 847b49c354..ac31118083 100644 --- a/code/modules/antagonists/cult/blood_magic.dm +++ b/code/modules/antagonists/cult/blood_magic.dm @@ -7,8 +7,8 @@ /datum/action/innate/cult/blood_magic/Grant() ..() - button.screen_loc = "6:-29,4:-2" - button.moved = "6:-29,4:-2" + button.screen_loc = DEFAULT_BLOODSPELLS + button.moved = DEFAULT_BLOODSPELLS button.ordered = FALSE /datum/action/innate/cult/blood_magic/Remove() @@ -84,7 +84,7 @@ to_chat(owner, "Your wounds glows with power, you have prepared a [new_spell.name] invocation!") channeling = FALSE -/datum/action/innate/cult/blood_spell //The next generation of talismans +/datum/action/innate/cult/blood_spell //The next generation of talismans, handles storage/creation of blood magic name = "Blood Magic" button_icon_state = "telerune" desc = "Fear the Old Blood." @@ -161,7 +161,7 @@ /datum/action/innate/cult/blood_spell/emp/Activate() owner.visible_message("[owner]'s hand flashes a bright blue!", \ "You speak the cursed words, emitting an EMP blast from your hand.") - empulse(owner, 3, 6) + empulse(owner, 2, 5) owner.whisper(invocation, language = /datum/language/common) charges-- if(charges<=0) @@ -179,11 +179,11 @@ desc = "A sinister spell used to convert:
Plasteel into runed metal
25 metal into a construct shell
Cyborgs directly into constructs
Cyborg shells into construct shells
Airlocks into runed airlocks (harm intent)" button_icon_state = "transmute" magic_path = "/obj/item/melee/blood_magic/construction" - health_cost = 10 + health_cost = 12 /datum/action/innate/cult/blood_spell/equipment name = "Summon Equipment" - desc = "A crucial spell that enables you to summon either a ritual dagger or combat gear including armored robes, the nar'sien bola, and an eldritch longsword." + desc = "A crucial spell that enables you to summon either a ritual dagger or combat gear including armored robes, the Nar'Sien bola, and an eldritch longsword." button_icon_state = "equip" magic_path = "/obj/item/melee/blood_magic/armor" @@ -378,11 +378,12 @@ uses = 0 qdel(src) return - add_logs(user, M, "used a cult spell on", source.name, "") + log_combat(user, M, "used a cult spell on", source.name, "") M.lastattacker = user.real_name M.lastattackerckey = user.ckey /obj/item/melee/blood_magic/afterattack(atom/target, mob/living/carbon/user, proximity) + . = ..() if(invocation) user.whisper(invocation, language = /datum/language/common) if(health_cost) @@ -400,7 +401,7 @@ //Stun /obj/item/melee/blood_magic/stun name = "Stunning Aura " - color = "#ff0000" // red + color = RUNE_COLOR_RED invocation = "Fuu ma'jin!" /obj/item/melee/blood_magic/stun/afterattack(atom/target, mob/living/carbon/user, proximity) @@ -488,7 +489,7 @@ /obj/item/melee/blood_magic/shackles/afterattack(atom/target, mob/living/carbon/user, proximity) if(iscultist(user) && iscarbon(target) && proximity) var/mob/living/carbon/C = target - if(C.get_num_arms() >= 2 || C.get_arm_ignore()) + if(C.get_num_arms(FALSE) >= 2 || C.get_arm_ignore()) CuffAttack(C, user) else user.visible_message("This victim doesn't have enough arms to complete the restraint!") @@ -506,7 +507,7 @@ C.update_handcuffed() C.silent += 5 to_chat(user, "You shackle [C].") - add_logs(user, C, "shackled") + log_combat(user, C, "shackled") uses-- else to_chat(user, "[C] is already bound.") @@ -585,10 +586,13 @@ new /obj/structure/constructshell(T) SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25)) else if(istype(target,/obj/machinery/door/airlock)) - target.narsie_act() - uses-- - user.visible_message("Black ribbons suddenly eminate from [user]'s hand and cling to the airlock - twisting and corrupting it!") - SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25)) + playsound(T, 'sound/machines/airlockforced.ogg', 50, 1) + do_sparks(5, TRUE, target) + if(do_after(user, 50, target = user)) + target.narsie_act() + uses-- + user.visible_message("Black ribbons suddenly emanate from [user]'s hand and cling to the airlock - twisting and corrupting it!") + SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25)) else to_chat(user, "The spell will not work on [target]!") return diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm index 8ea391d2ca..74b322b258 100644 --- a/code/modules/antagonists/cult/cult.dm +++ b/code/modules/antagonists/cult/cult.dm @@ -11,9 +11,9 @@ job_rank = ROLE_CULTIST var/ignore_implant = FALSE var/give_equipment = FALSE - var/datum/team/cult/cult_team + /datum/antagonist/cult/get_team() return cult_team @@ -60,7 +60,7 @@ equip_cultist(TRUE) SSticker.mode.cult += owner // Only add after they've been given objectives SSticker.mode.update_cult_icons_added(owner) - current.log_message("Has been converted to the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG) + current.log_message("has been converted to the cult of Nar'Sie!", LOG_ATTACK, color="#960000") if(cult_team.blood_target && cult_team.blood_target_image && current.client) current.client.images += cult_team.blood_target_image @@ -111,7 +111,11 @@ if(ishuman(current)) magic.Grant(current) current.throw_alert("bloodsense", /obj/screen/alert/bloodsense) - + if(cult_team.cult_risen) + cult_team.rise(current) + if(cult_team.cult_ascendent) + cult_team.ascend(current) + /datum/antagonist/cult/remove_innate_effects(mob/living/mob_override) . = ..() var/mob/living/current = owner.current @@ -123,14 +127,19 @@ communion.Remove(current) magic.Remove(current) current.clear_alert("bloodsense") - + if(ishuman(current)) + var/mob/living/carbon/human/H = current + H.eye_color = initial(H.eye_color) + H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK) + H.remove_trait(CULT_EYES) + H.update_body() /datum/antagonist/cult/on_removal() SSticker.mode.cult -= owner SSticker.mode.update_cult_icons_removed(owner) if(!silent) owner.current.visible_message("[owner.current] looks like [owner.current.p_theyve()] just reverted to [owner.current.p_their()] old faith!", null, null, null, owner.current) to_chat(owner.current, "An unfamiliar white light flashes through your mind, cleansing the taint of the Geometer and all your memories as her servant.") - owner.current.log_message("Has renounced the cult of Nar'Sie!", INDIVIDUAL_ATTACK_LOG) + owner.current.log_message("has renounced the cult of Nar'Sie!", LOG_ATTACK, color="#960000") if(cult_team.blood_target && cult_team.blood_target_image && owner.current.client) owner.current.client.images -= cult_team.blood_target_image . = ..() @@ -193,7 +202,10 @@ throwing.Grant(current) current.update_action_buttons_icon() current.apply_status_effect(/datum/status_effect/cult_master) - + if(cult_team.cult_risen) + cult_team.rise(current) + if(cult_team.cult_ascendent) + cult_team.ascend(current) /datum/antagonist/cult/master/remove_innate_effects(mob/living/mob_override) . = ..() var/mob/living/current = owner.current @@ -204,6 +216,14 @@ throwing.Remove(current) current.update_action_buttons_icon() current.remove_status_effect(/datum/status_effect/cult_master) + + if(ishuman(current)) + var/mob/living/carbon/human/H = current + H.eye_color = initial(H.eye_color) + H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK) + H.remove_trait(CULT_EYES) + H.cut_overlays() + H.regenerate_icons() /datum/team/cult name = "Cult" @@ -215,7 +235,53 @@ var/cult_vote_called = FALSE var/mob/living/cult_master var/reckoning_complete = FALSE - + var/cult_risen = FALSE + var/cult_ascendent = FALSE + +/datum/team/cult/proc/check_size() + if(cult_ascendent) + return + var/alive = 0 + var/cultplayers = 0 + for(var/I in GLOB.player_list) + var/mob/M = I + if(M.stat != DEAD) + if(iscultist(M)) + ++cultplayers + else + ++alive + var/ratio = cultplayers/alive + if(ratio > CULT_RISEN && !cult_risen) + for(var/datum/mind/B in members) + if(B.current) + SEND_SOUND(B.current, 'sound/hallucinations/i_see_you2.ogg') + to_chat(B.current, "The veil weakens as your cult grows, your eyes begin to glow...") + addtimer(CALLBACK(src, .proc/rise, B.current), 200) + cult_risen = TRUE + + if(ratio > CULT_ASCENDENT && !cult_ascendent) + for(var/datum/mind/B in members) + if(B.current) + SEND_SOUND(B.current, 'sound/hallucinations/im_here1.ogg') + to_chat(B.current, "Your cult is ascendent and the red harvest approaches - you cannot hide your true nature for much longer!!") + addtimer(CALLBACK(src, .proc/ascend, B.current), 200) + cult_ascendent = TRUE + + +/datum/team/cult/proc/rise(cultist) + if(ishuman(cultist)) + var/mob/living/carbon/human/H = cultist + H.eye_color = "f00" + H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK) + H.add_trait(CULT_EYES) + H.update_body() + +/datum/team/cult/proc/ascend(cultist) + if(ishuman(cultist)) + var/mob/living/carbon/human/H = cultist + new /obj/effect/temp_visual/cult/sparks(get_turf(H), H.dir) + var/istate = pick("halo1","halo2","halo3","halo4","halo5","halo6") + H.add_overlay(mutable_appearance('icons/effects/32x64.dmi', istate, -BODY_FRONT_LAYER)) /datum/team/cult/proc/setup_objectives() //SAC OBJECTIVE , todo: move this to objective internals @@ -257,6 +323,7 @@ summon_objective.team = src objectives += summon_objective + /datum/objective/sacrifice var/sacced = FALSE var/sac_image @@ -285,7 +352,7 @@ update_explanation_text() /datum/objective/eldergod/update_explanation_text() - explanation_text = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie'. The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin." + explanation_text = "Summon Nar'Sie by invoking the rune 'Summon Nar'Sie'. The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin." /datum/objective/eldergod/check_completion() return summoned || completed @@ -300,7 +367,7 @@ var/list/parts = list() if(check_cult_victory()) - parts += "The cult has succeeded! Nar-sie has snuffed out another torch in the void!" + parts += "The cult has succeeded! Nar'Sie has snuffed out another torch in the void!" else parts += "The staff managed to stop the cult! Dark words and heresy are no match for Nanotrasen's finest!" diff --git a/code/modules/antagonists/cult/cult_comms.dm b/code/modules/antagonists/cult/cult_comms.dm index fd1d0d1c67..375d250f59 100644 --- a/code/modules/antagonists/cult/cult_comms.dm +++ b/code/modules/antagonists/cult/cult_comms.dm @@ -45,7 +45,7 @@ var/link = FOLLOW_LINK(M, user) to_chat(M, "[link] [my_message]") - log_talk(user,"CULT:[key_name(user)] : [message]",LOGSAY) + user.log_talk(message, LOG_SAY, tag="cult") /datum/action/innate/cult/comm/spirit name = "Spiritual Communion" @@ -79,7 +79,7 @@ return ..() /datum/action/innate/cult/mastervote/Activate() - var/choice = alert(owner, "The mantle of leadership is a heavy. Success in this role requires an expert level of communication and experience. Are you sure?",, "Yes", "No") + var/choice = alert(owner, "The mantle of leadership is heavy. Success in this role requires an expert level of communication and experience. Are you sure?",, "Yes", "No") if(choice == "Yes" && IsAvailable()) var/datum/antagonist/cult/C = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE) pollCultists(owner,C.cult_team) @@ -196,15 +196,15 @@ /datum/action/innate/cult/master/finalreck/proc/chant(chant_number) switch(chant_number) if(1) - owner.say("C'arta forbici!", language = /datum/language/common) + owner.say("C'arta forbici!", language = /datum/language/common, forced = "cult invocation") if(2) - owner.say("Pleggh e'ntrath!", language = /datum/language/common) + owner.say("Pleggh e'ntrath!", language = /datum/language/common, forced = "cult invocation") playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 50, 1) if(3) - owner.say("Barhah hra zar'garis!", language = /datum/language/common) + owner.say("Barhah hra zar'garis!", language = /datum/language/common, forced = "cult invocation") playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 75, 1) if(4) - owner.say("N'ath reth sh'yro eth d'rekkathnor!!!", language = /datum/language/common) + owner.say("N'ath reth sh'yro eth d'rekkathnor!!!", language = /datum/language/common, forced = "cult invocation") playsound(get_turf(owner),'sound/magic/clockwork/narsie_attack.ogg', 100, 1) /datum/action/innate/cult/master/cultmark @@ -334,7 +334,7 @@ if(cooldown>world.time) reset_blood_target(C.cult_team) to_chat(owner, "You have cleared the cult's blood target!") - qdel(C.cult_team.blood_target_reset_timer) + deltimer(C.cult_team.blood_target_reset_timer) return else to_chat(owner, "The cult has already designated a target!") diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index e1710dae96..5e3c0aa872 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -23,7 +23,7 @@ actions_types = list(/datum/action/item_action/cult_dagger) /obj/item/melee/cultblade/dagger/Initialize() - ..() + . = ..() var/image/I = image(icon = 'icons/effects/blood.dmi' , icon_state = null, loc = src) I.override = TRUE add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/silicons, "cult_dagger", I) @@ -81,7 +81,7 @@ /obj/item/twohanded/required/cult_bastard name = "bloody bastard sword" - desc = "An enormous sword used by Nar-Sien cultists to rapidly harvest the souls of non-believers." + desc = "An enormous sword used by Nar'Sien cultists to rapidly harvest the souls of non-believers." w_class = WEIGHT_CLASS_HUGE block_chance = 50 throwforce = 20 @@ -251,8 +251,8 @@ holder.update_action_buttons_icon() /obj/item/restraints/legcuffs/bola/cult - name = "nar'sien bola" - desc = "A strong bola, bound with dark magic that allows it to pass harmlessly through Nar'sien cultists. Throw it to trip and slow your victim." + name = "\improper Nar'Sien bola" + desc = "A strong bola, bound with dark magic that allows it to pass harmlessly through Nar'Sien cultists. Throw it to trip and slow your victim." icon_state = "bola_cult" breakouttime = 60 knockdown = 20 @@ -297,7 +297,7 @@ /obj/item/clothing/head/culthood/alt name = "cultist hood" - desc = "An armored hood worn by the followers of Nar-Sie." + desc = "An armored hood worn by the followers of Nar'Sie." icon_state = "cult_hoodalt" item_state = "cult_hoodalt" @@ -306,7 +306,7 @@ /obj/item/clothing/suit/cultrobes/alt name = "cultist robes" - desc = "An armored set of robes worn by the followers of Nar-Sie." + desc = "An armored set of robes worn by the followers of Nar'Sie." icon_state = "cultrobesalt" item_state = "cultrobesalt" @@ -318,14 +318,14 @@ name = "magus helm" icon_state = "magus" item_state = "magus" - desc = "A helm worn by the followers of Nar-Sie." + desc = "A helm worn by the followers of Nar'Sie." flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDEEARS|HIDEEYES armor = list("melee" = 30, "bullet" = 30, "laser" = 30,"energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 10, "acid" = 10) flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH /obj/item/clothing/suit/magusred name = "magus robes" - desc = "A set of armored robes worn by the followers of Nar-Sie." + desc = "A set of armored robes worn by the followers of Nar'Sie." icon_state = "magusred" item_state = "magusred" body_parts_covered = CHEST|GROIN|LEGS|ARMS @@ -334,8 +334,8 @@ flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT /obj/item/clothing/head/helmet/space/hardsuit/cult - name = "\improper Nar-Sien hardened helmet" - desc = "A heavily-armored helmet worn by warriors of the Nar-Sien cult. It can withstand hard vacuum." + name = "\improper Nar'Sien hardened helmet" + desc = "A heavily-armored helmet worn by warriors of the Nar'Sien cult. It can withstand hard vacuum." icon_state = "cult_helmet" item_state = "cult_helmet" armor = list("melee" = 60, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) @@ -343,10 +343,10 @@ actions_types = list() /obj/item/clothing/suit/space/hardsuit/cult - name = "\improper Nar-Sien hardened armor" + name = "\improper Nar'Sien hardened armor" icon_state = "cult_armor" item_state = "cult_armor" - desc = "A heavily-armored exosuit worn by warriors of the Nar-Sien cult. It can withstand hard vacuum." + desc = "A heavily-armored exosuit worn by warriors of the Nar'Sien cult. It can withstand hard vacuum." w_class = WEIGHT_CLASS_BULKY allowed = list(/obj/item/tome, /obj/item/melee/cultblade, /obj/item/tank/internals/) armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) @@ -456,13 +456,13 @@ user.dropItemToGround(src, TRUE) /obj/item/clothing/glasses/hud/health/night/cultblind - desc = "may Nar-Sie guide you through the darkness and shield you from the light." + desc = "may Nar'Sie guide you through the darkness and shield you from the light." name = "zealot's blindfold" icon_state = "blindfold" item_state = "blindfold" flash_protect = 1 -/obj/item/clothing/glasses/night/cultblind/equipped(mob/living/user, slot) +/obj/item/clothing/glasses/hud/health/night/cultblind/equipped(mob/living/user, slot) ..() if(!iscultist(user)) to_chat(user, "\"You want to be blind, do you?\"") @@ -496,7 +496,7 @@ to_chat(user, "We have exhausted our ability to curse the shuttle.") return if(locate(/obj/singularity/narsie) in GLOB.poi_list) - to_chat(user, "Nar-Sie is already on this plane, there is no delaying the end of all things.") + to_chat(user, "Nar'Sie is already on this plane, there is no delaying the end of all things.") return if(SSshuttle.emergency.mode == SHUTTLE_CALL) @@ -589,7 +589,7 @@ /obj/item/flashlight/flare/culttorch name = "void torch" - desc = "Used by veteran cultists to instantly transport items to their needful bretheren." + desc = "Used by veteran cultists to instantly transport items to their needful brethren." w_class = WEIGHT_CLASS_SMALL brightness_on = 1 icon_state = "torch" @@ -770,7 +770,7 @@ damage_type = BRUTE impact_effect_type = /obj/effect/temp_visual/dir_setting/bloodsplatter -/obj/item/projectile/magic/arcane_barrage/blood/Collide(atom/target) +/obj/item/projectile/magic/arcane_barrage/blood/Bump(atom/target) var/turf/T = get_turf(target) playsound(T, 'sound/effects/splat.ogg', 50, TRUE) if(iscultist(target)) @@ -804,6 +804,7 @@ /obj/item/blood_beam/afterattack(atom/A, mob/living/user, flag, params) + . = ..() if(firing || charging) return var/C = user.client @@ -897,7 +898,7 @@ /obj/item/shield/mirror name = "mirror shield" - desc = "An infamous shield used by Nar'sien sects to confuse and disorient their enemies. Its edges are weighted for use as a throwing weapon - capable of disabling multiple foes with preternatural accuracy." + desc = "An infamous shield used by Nar'Sien sects to confuse and disorient their enemies. Its edges are weighted for use as a throwing weapon - capable of disabling multiple foes with preternatural accuracy." icon = 'icons/obj/items_and_weapons.dmi' icon_state = "mirror_shield" // eshield1 for expanded lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' @@ -905,22 +906,22 @@ force = 5 throwforce = 15 throw_speed = 1 - throw_range = 6 + throw_range = 4 w_class = WEIGHT_CLASS_BULKY attack_verb = list("bumped", "prodded") hitsound = 'sound/weapons/smash.ogg' - var/illusions = 3 + var/illusions = 2 /obj/item/shield/mirror/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) if(iscultist(owner)) if(istype(hitby, /obj/item/projectile)) var/obj/item/projectile/P = hitby - if(P.damage >= 35) + if(P.damage >= 30) var/turf/T = get_turf(owner) T.visible_message("The sheer force from [P] shatters the mirror shield!") new /obj/effect/temp_visual/cult/sparks(T) playsound(T, 'sound/effects/glassbr3.ogg', 100) - owner.Knockdown(20) + owner.Knockdown(25) qdel(src) return FALSE if(P.is_reflectable) diff --git a/code/modules/antagonists/cult/cult_structures.dm b/code/modules/antagonists/cult/cult_structures.dm index a9d59cb5cd..64d57c2f94 100644 --- a/code/modules/antagonists/cult/cult_structures.dm +++ b/code/modules/antagonists/cult/cult_structures.dm @@ -76,7 +76,7 @@ /obj/structure/destructible/cult/talisman name = "altar" - desc = "A bloodstained altar dedicated to Nar-Sie." + desc = "A bloodstained altar dedicated to Nar'Sie." icon_state = "talismanaltar" break_message = "The altar shatters, leaving only the wailing of the damned!" @@ -110,7 +110,7 @@ /obj/structure/destructible/cult/forge name = "daemon forge" - desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie." + desc = "A forge used in crafting the unholy weapons used by the armies of Nar'Sie." icon_state = "forge" light_range = 2 light_color = LIGHT_COLOR_LAVA @@ -131,7 +131,7 @@ return var/choice if(user.mind.has_antag_datum(/datum/antagonist/cult/master)) - choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Bastard Sword") + choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Mirror Shield") else choice = alert(user,"You study the schematics etched into the forge...",,"Shielded Robe","Flagellant's Robe","Mirror Shield") var/list/pickedtype = list() @@ -140,14 +140,6 @@ pickedtype += /obj/item/clothing/suit/hooded/cultrobes/cult_shield if("Flagellant's Robe") pickedtype += /obj/item/clothing/suit/hooded/cultrobes/berserker - if("Bastard Sword") - if((world.time - SSticker.round_start_time) >= 12000) - pickedtype += /obj/item/twohanded/required/cult_bastard - else - cooldowntime = 12000 - (world.time - SSticker.round_start_time) - to_chat(user, "The forge fires are not yet hot enough for this weapon, give it another [DisplayTimeText(cooldowntime)].") - cooldowntime = 0 - return if("Mirror Shield") pickedtype += /obj/item/shield/mirror if(src && !QDELETED(src) && anchored && pickedtype && Adjacent(user) && !user.incapacitated() && iscultist(user) && cooldowntime <= world.time) @@ -221,7 +213,10 @@ var/turf/T = safepick(validturfs) if(T) - T.ChangeTurf(/turf/open/floor/engine/cult) + if(istype(T, /turf/open/floor/plating)) + T.PlaceOnTop(/turf/open/floor/engine/cult) + else + T.ChangeTurf(/turf/open/floor/engine/cult) else var/turf/open/floor/engine/cult/F = safepick(cultturfs) if(F) diff --git a/code/modules/antagonists/cult/ritual.dm b/code/modules/antagonists/cult/ritual.dm index 3f1bc1b04a..69941f582d 100644 --- a/code/modules/antagonists/cult/ritual.dm +++ b/code/modules/antagonists/cult/ritual.dm @@ -17,7 +17,7 @@ This file contains the cult dagger and rune list code /obj/item/melee/cultblade/dagger/examine(mob/user) ..() if(iscultist(user) || isobserver(user)) - to_chat(user, "The scriptures of the Geometer. Allows the scribing of runes and access to the knowledge archives of the cult of Nar-Sie.") + to_chat(user, "The scriptures of the Geometer. Allows the scribing of runes and access to the knowledge archives of the cult of Nar'Sie.") to_chat(user, "Striking a cult structure will unanchor or reanchor it.") to_chat(user, "Striking another cultist with it will purge holy water from them.") to_chat(user, "Striking a noncultist, however, will tear their flesh.") @@ -29,7 +29,7 @@ This file contains the cult dagger and rune list code var/holy2unholy = M.reagents.get_reagent_amount("holywater") M.reagents.del_reagent("holywater") M.reagents.add_reagent("unholywater",holy2unholy) - add_logs(user, M, "smacked", src, " removing the holy water from them") + log_combat(user, M, "smacked", src, " removing the holy water from them") return FALSE . = ..() @@ -78,7 +78,7 @@ This file contains the cult dagger and rune list code return var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives if(!(A in summon_objective.summon_spots)) - to_chat(user, "The Apocalypse rune will remove a ritual site (where Nar-sie can be summoned), it can only be scribed in [english_list(summon_objective.summon_spots)]!") + to_chat(user, "The Apocalypse rune will remove a ritual site (where Nar'Sie can be summoned), it can only be scribed in [english_list(summon_objective.summon_spots)]!") return if(summon_objective.summon_spots.len < 2) to_chat(user, "Only one ritual site remains - it must be reserved for the final summoning!") @@ -87,7 +87,7 @@ This file contains the cult dagger and rune list code var/datum/objective/eldergod/summon_objective = locate() in user_antag.cult_team.objectives var/datum/objective/sacrifice/sac_objective = locate() in user_antag.cult_team.objectives if(!summon_objective) - to_chat(user, "Nar-Sie does not wish to be summoned!") + to_chat(user, "Nar'Sie does not wish to be summoned!") return if(sac_objective && !sac_objective.check_completion()) to_chat(user, "The sacrifice is not complete. The portal would lack the power to open if you tried!") @@ -98,7 +98,7 @@ This file contains the cult dagger and rune list code if(!(A in summon_objective.summon_spots)) to_chat(user, "The Geometer can only be summoned where the veil is weak - in [english_list(summon_objective.summon_spots)]!") return - var/confirm_final = alert(user, "This is the FINAL step to summon Nar-Sie; it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar-Sie!", "No") + var/confirm_final = alert(user, "This is the FINAL step to summon Nar'Sie; it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for Nar'Sie!", "No") if(confirm_final == "No") to_chat(user, "You decide to prepare further before scribing the rune.") return diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index df4715bc69..0c592c65e7 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -62,7 +62,7 @@ Runes can either be invoked by one's self or with many different cultists. Each to_chat(user, "You carefully erase the [lowertext(cultist_name)] rune.") qdel(src) else if(istype(I, /obj/item/nullrod)) - user.say("BEGONE FOUL MAGIKS!!") + user.say("BEGONE FOUL MAGIKS!!", forced = "nullrod") to_chat(user, "You disrupt the magic of [src] with [I].") qdel(src) @@ -136,7 +136,7 @@ structure_check() searches for nearby cultist structures required for the invoca if(isliving(M)) var/mob/living/L = M if(invocation) - L.say(invocation, language = /datum/language/common, ignore_spam = TRUE) + L.say(invocation, language = /datum/language/common, ignore_spam = TRUE, forced = "cult invocation") if(invoke_damage) L.apply_damage(invoke_damage, BRUTE) to_chat(L, "[src] saps your strength!") @@ -179,7 +179,7 @@ structure_check() searches for nearby cultist structures required for the invoca //Rite of Offering: Converts or sacrifices a target. /obj/effect/rune/convert cultist_name = "Offer" - cultist_desc = "offers a noncultist above it to Nar-Sie, either converting them or sacrificing them." + cultist_desc = "offers a noncultist above it to Nar'Sie, either converting them or sacrificing them." req_cultists_text = "2 for conversion, 3 for living sacrifices and sacrifice targets." invocation = "Mah'weyh pleggh at e'ntrath!" icon_state = "3" @@ -211,7 +211,7 @@ structure_check() searches for nearby cultist structures required for the invoca var/mob/living/F = invokers[1] var/datum/antagonist/cult/C = F.mind.has_antag_datum(/datum/antagonist/cult,TRUE) - + var/datum/team/cult/Cult_team = C.cult_team var/is_convertable = is_convertable_to_cult(L,C.cult_team) if(L.stat != DEAD && (is_clock || is_convertable)) invocation = "Mah'weyh pleggh at e'ntrath!" @@ -229,8 +229,8 @@ structure_check() searches for nearby cultist structures required for the invoca do_sacrifice(L, invokers) animate(src, color = oldcolor, time = 5) addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 5) + Cult_team.check_size() // Triggers the eye glow or aura effects if the cult has grown large enough relative to the crew rune_in_use = FALSE - /obj/effect/rune/convert/proc/do_convert(mob/living/convertee, list/invokers) if(invokers.len < 2) for(var/M in invokers) @@ -315,6 +315,7 @@ structure_check() searches for nearby cultist structures required for the invoca return TRUE + /obj/effect/rune/empower cultist_name = "Empower" cultist_desc = "allows cultists to prepare greater amounts of blood magic at far less of a cost." @@ -439,9 +440,9 @@ structure_check() searches for nearby cultist structures required for the invoca light_range = 0 update_light() -//Ritual of Dimensional Rending: Calls forth the avatar of Nar-Sie upon the station. +//Ritual of Dimensional Rending: Calls forth the avatar of Nar'Sie upon the station. /obj/effect/rune/narsie - cultist_name = "Nar-Sie" + cultist_name = "Nar'Sie" cultist_desc = "tears apart dimensional barriers, calling forth the Geometer. Requires 9 invokers." invocation = "TOK-LYR RQA-NAP G'OLT-ULOFT!!" req_cultists = 9 @@ -479,8 +480,8 @@ structure_check() searches for nearby cultist structures required for the invoca return if(locate(/obj/singularity/narsie) in GLOB.poi_list) for(var/M in invokers) - to_chat(M, "Nar-Sie is already on this plane!") - log_game("Nar-Sie rune failed - already summoned") + to_chat(M, "Nar'Sie is already on this plane!") + log_game("Nar'Sie rune failed - already summoned") return //BEGIN THE SUMMONING used = TRUE @@ -490,7 +491,7 @@ structure_check() searches for nearby cultist structures required for the invoca sleep(40) if(src) color = RUNE_COLOR_RED - new /obj/singularity/narsie/large/cult(T) //Causes Nar-Sie to spawn even if the rune has been removed + new /obj/singularity/narsie/large/cult(T) //Causes Nar'Sie to spawn even if the rune has been removed /obj/effect/rune/narsie/attackby(obj/I, mob/user, params) //Since the narsie rune takes a long time to make, add logging to removal. if((istype(I, /obj/item/melee/cultblade/dagger) && iscultist(user))) @@ -512,7 +513,7 @@ structure_check() searches for nearby cultist structures required for the invoca invocation = "Pasnar val'keriam usinar. Savrae ines amutan. Yam'toth remium il'tarat!" //Depends on the name of the user - see below icon_state = "1" color = RUNE_COLOR_MEDIUMRED - var/static/revives_used = 0 + var/static/revives_used = -SOULS_TO_REVIVE // Cultists get one "free" revive /obj/effect/rune/raise_dead/examine(mob/user) ..() @@ -549,11 +550,12 @@ structure_check() searches for nearby cultist structures required for the invoca invocation = initial(invocation) ..() if(mob_to_revive.stat == DEAD) - if(LAZYLEN(GLOB.sacrificed) <= revives_used) - to_chat(user, "Your cult must carry out another sacrifice before it can revive a cultist!") + var/diff = LAZYLEN(GLOB.sacrificed) - revives_used - SOULS_TO_REVIVE + if(diff < 0) + to_chat(user, "Your cult must carry out [abs(diff)] more sacrifice\s before it can revive another cultist!") fail_invoke() return - revives_used++ + revives_used += SOULS_TO_REVIVE mob_to_revive.revive(1, 1) //This does remove traits and such, but the rune might actually see some use because of it! mob_to_revive.grab_ghost() if(!mob_to_revive.client || mob_to_revive.client.is_afk()) @@ -618,9 +620,7 @@ structure_check() searches for nearby cultist structures required for the invoca to_chat(user, "The air above this rune has hardened into a barrier that will last [DisplayTimeText(TMR.timeToRun - world.time)].") /obj/effect/rune/wall/Destroy() - density = FALSE GLOB.wall_runes -= src - air_update_turf(1) return ..() /obj/effect/rune/wall/BlockSuperconductivity() @@ -863,7 +863,7 @@ structure_check() searches for nearby cultist structures required for the invoca var/obj/structure/emergency_shield/invoker/N = new(T) new_human.key = ghost_to_spawn.key SSticker.mode.add_cultist(new_human.mind, 0) - to_chat(new_human, "You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar-Sie, and you are to serve them at all costs.") + to_chat(new_human, "You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar'Sie, and you are to serve them at all costs.") while(!QDELETED(src) && !QDELETED(user) && !QDELETED(new_human) && (user in T)) if(user.stat || new_human.InCritical()) @@ -946,7 +946,7 @@ structure_check() searches for nearby cultist structures required for the invoca to_chat(user, "Only one ritual site remains - it must be reserved for the final summoning!") return if(!(place in summon_objective.summon_spots)) - to_chat(user, "The Apocalypse rune will remove a ritual site, where Nar-sie can be summoned, it can only be scribed in [english_list(summon_objective.summon_spots)]!") + to_chat(user, "The Apocalypse rune will remove a ritual site, where Nar'Sie can be summoned, it can only be scribed in [english_list(summon_objective.summon_spots)]!") return summon_objective.summon_spots -= place rune_in_use = TRUE diff --git a/code/modules/antagonists/devil/true_devil/_true_devil.dm b/code/modules/antagonists/devil/true_devil/_true_devil.dm index 4e1175c13e..923a224b81 100644 --- a/code/modules/antagonists/devil/true_devil/_true_devil.dm +++ b/code/modules/antagonists/devil/true_devil/_true_devil.dm @@ -21,6 +21,7 @@ held_items = list(null, null) bodyparts = list(/obj/item/bodypart/chest/devil, /obj/item/bodypart/head/devil, /obj/item/bodypart/l_arm/devil, /obj/item/bodypart/r_arm/devil, /obj/item/bodypart/r_leg/devil, /obj/item/bodypart/l_leg/devil) + hud_type = /datum/hud/devil var/ascended = FALSE var/mob/living/oldform var/list/devil_overlays[DEVIL_TOTAL_LAYERS] @@ -172,14 +173,14 @@ visible_message("[M] has punched [src]!", \ "[M] has punched [src]!") adjustBruteLoss(damage) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") updatehealth() if ("disarm") if (!lying && !ascended) //No stealing the arch devil's pitchfork. if (prob(5)) Unconscious(40) playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - add_logs(M, src, "pushed") + log_combat(M, src, "pushed") visible_message("[M] has pushed down [src]!", \ "[M] has pushed down [src]!") else diff --git a/code/modules/antagonists/disease/disease_disease.dm b/code/modules/antagonists/disease/disease_disease.dm index 8ee36e8829..21d0381982 100644 --- a/code/modules/antagonists/disease/disease_disease.dm +++ b/code/modules/antagonists/disease/disease_disease.dm @@ -27,7 +27,7 @@ /datum/disease/advance/sentient_disease/IsSame(datum/disease/D) - if(istype(src, D.type)) + if(istype(D, /datum/disease/advance/sentient_disease)) var/datum/disease/advance/sentient_disease/V = D if(V.overmind == overmind) return TRUE diff --git a/code/modules/antagonists/disease/disease_mob.dm b/code/modules/antagonists/disease/disease_mob.dm index 6908c5eebf..4d378af724 100644 --- a/code/modules/antagonists/disease/disease_mob.dm +++ b/code/modules/antagonists/disease/disease_mob.dm @@ -107,7 +107,7 @@ the new instance inside the host to be updated to the template's stats. if(istype(B)) to_chat(user, "[B.name]") -/mob/camera/disease/say(message) +/mob/camera/disease/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) return /mob/camera/disease/Move(NewLoc, Dir = 0) @@ -245,7 +245,7 @@ the new instance inside the host to be updated to the template's stats. /mob/camera/disease/proc/set_following(mob/living/L) following_host = L if(!move_listener) - move_listener = L.AddComponent(/datum/component/redirect, COMSIG_MOVABLE_MOVED, CALLBACK(src, .proc/follow_mob)) + move_listener = L.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/follow_mob))) else L.TakeComponent(move_listener) if(QDELING(move_listener)) @@ -261,7 +261,7 @@ the new instance inside the host to be updated to the template's stats. index = index == hosts.len ? 1 : index + 1 set_following(hosts[index]) -/mob/camera/disease/proc/follow_mob(newloc, dir) +/mob/camera/disease/proc/follow_mob(datum/source, newloc, dir) var/turf/T = get_turf(following_host) if(T) forceMove(T) diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm index f1e1d92c9d..53f8aa2da1 100644 --- a/code/modules/antagonists/ert/ert.dm +++ b/code/modules/antagonists/ert/ert.dm @@ -12,6 +12,7 @@ var/list/name_source show_in_antagpanel = FALSE antag_moodlet = /datum/mood_event/focused + can_hijack = HIJACK_PREVENT /datum/antagonist/ert/on_gain() update_name() diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm index 62da2df87f..1fa37f3a51 100644 --- a/code/modules/antagonists/highlander/highlander.dm +++ b/code/modules/antagonists/highlander/highlander.dm @@ -3,6 +3,7 @@ var/obj/item/claymore/highlander/sword show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE + can_hijack = HIJACK_HIJACKER /datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override) var/mob/living/L = owner.current || mob_override diff --git a/code/modules/antagonists/ninja/ninja.dm b/code/modules/antagonists/ninja/ninja.dm index f157bc6080..a8bce90f1c 100644 --- a/code/modules/antagonists/ninja/ninja.dm +++ b/code/modules/antagonists/ninja/ninja.dm @@ -8,6 +8,11 @@ var/give_objectives = TRUE var/give_equipment = TRUE +/datum/antagonist/ninja/New() + if(helping_station) + can_hijack = HIJACK_PREVENT + . = ..() + /datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override) var/mob/living/M = mob_override || owner.current update_ninja_icons_added(M) @@ -137,6 +142,8 @@ adj = "objectiveless" else return + if(helping_station) + can_hijack = HIJACK_PREVENT new_owner.assigned_role = ROLE_NINJA new_owner.special_role = ROLE_NINJA new_owner.add_antag_datum(src) diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm index df9b35ee8b..e365f09b55 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm @@ -28,8 +28,8 @@ var/deconstruction_state = NUKESTATE_INTACT var/lights = "" var/interior = "" + var/proper_bomb = TRUE //Please var/obj/effect/countdown/nuclearbomb/countdown - var/static/bomb_set /obj/machinery/nuclearbomb/Initialize() . = ..() @@ -227,7 +227,6 @@ /obj/machinery/nuclearbomb/process() if(timing && !exploding) - bomb_set = TRUE if(detonation_timer < world.time) explode() else @@ -358,26 +357,23 @@ S.switch_mode_to(initial(S.mode)) S.alert = FALSE timing = FALSE - bomb_set = TRUE detonation_timer = null countdown.stop() update_icon() /obj/machinery/nuclearbomb/proc/set_active() - if(safety && !bomb_set) + if(safety) to_chat(usr, "The safety is still on.") return timing = !timing if(timing) previous_level = get_security_level() - bomb_set = TRUE detonation_timer = world.time + (timer_set * 10) for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list) S.switch_mode_to(TRACK_INFILTRATOR) countdown.start() set_security_level("delta") else - bomb_set = FALSE detonation_timer = null set_security_level(previous_level) for(var/obj/item/pinpointer/nuke/syndicate/S in GLOB.pinpointer_list) @@ -460,6 +456,7 @@ /obj/machinery/nuclearbomb/beer name = "Nanotrasen-brand nuclear fission explosive" desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap to produce and devastatingly effective. Signs explain that though this particular device has been decommissioned, every Nanotrasen station is equipped with an equivalent one, just in case. All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back." + proper_bomb = FALSE var/obj/structure/reagent_dispensers/beerkeg/keg /obj/machinery/nuclearbomb/beer/Initialize() @@ -498,7 +495,6 @@ addtimer(CALLBACK(src, .proc/fizzbuzz), 110) /obj/machinery/nuclearbomb/beer/proc/disarm() - bomb_set = FALSE detonation_timer = null exploding = FALSE exploded = TRUE diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm index 05f74fcd56..33dd3833b8 100644 --- a/code/modules/antagonists/nukeop/nukeop.dm +++ b/code/modules/antagonists/nukeop/nukeop.dm @@ -8,6 +8,7 @@ var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team. var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint. var/nukeop_outfit = /datum/outfit/syndicate + can_hijack = HIJACK_HIJACKER //Alternative way to wipe out the station. /datum/antagonist/nukeop/proc/update_synd_icons_added(mob/living/M) var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] @@ -32,14 +33,9 @@ return var/mob/living/carbon/human/H = owner.current + H.set_species(/datum/species/human) //Plasamen burn up otherwise, and lizards are vulnerable to asimov AIs + H.equipOutfit(nukeop_outfit) - - if(!isplasmaman(owner.current)) - return TRUE - var/mob/living/carbon/human/plasma = owner.current - - plasma.set_species(/datum/species/human) //Plasmamen burn up otherwise. - return TRUE /datum/antagonist/nukeop/greet() @@ -250,8 +246,18 @@ /datum/team/nuclear/proc/disk_rescued() for(var/obj/item/disk/nuclear/D in GLOB.poi_list) - if(!D.onCentCom()) - return FALSE + //If emergency shuttle is in transit disk is only safe on it + if(SSshuttle.emergency.mode == SHUTTLE_ESCAPE) + if(!SSshuttle.emergency.is_in_shuttle_bounds(D)) + return FALSE + //If shuttle escaped check if it's on centcom side + else if(SSshuttle.emergency.mode == SHUTTLE_ENDGAME) + if(!D.onCentCom()) + return FALSE + else //Otherwise disk is safe when on station + var/turf/T = get_turf(D) + if(!T || !is_station_level(T.z)) + return FALSE return TRUE /datum/team/nuclear/proc/operatives_dead() @@ -267,7 +273,7 @@ return S && (is_centcom_level(S.z) || T) /datum/team/nuclear/proc/get_result() - var/evacuation = SSshuttle.emergency.mode == SHUTTLE_ENDGAME + var/evacuation = EMERGENCY_ESCAPED_OR_ENDGAMED var/disk_rescued = disk_rescued() var/syndies_didnt_escape = !syndies_escaped() var/station_was_nuked = SSticker.mode.station_was_nuked @@ -275,9 +281,9 @@ if(nuke_off_station == NUKE_SYNDICATE_BASE) return NUKE_RESULT_FLUKE - else if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) + else if(station_was_nuked && !syndies_didnt_escape) return NUKE_RESULT_NUKE_WIN - else if (!disk_rescued && station_was_nuked && syndies_didnt_escape) + else if (station_was_nuked && syndies_didnt_escape) return NUKE_RESULT_NOSURVIVORS else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) return NUKE_RESULT_WRONG_STATION @@ -289,7 +295,7 @@ return NUKE_RESULT_CREW_WIN else if (!disk_rescued && operatives_dead()) return NUKE_RESULT_DISK_LOST - else if (!disk_rescued && evacuation) + else if (!disk_rescued && evacuation) return NUKE_RESULT_DISK_STOLEN else return //Undefined result diff --git a/code/modules/antagonists/official/official.dm b/code/modules/antagonists/official/official.dm index 23bf472422..26de196cb4 100644 --- a/code/modules/antagonists/official/official.dm +++ b/code/modules/antagonists/official/official.dm @@ -4,6 +4,7 @@ show_in_antagpanel = FALSE var/datum/objective/mission var/datum/team/ert/ert_team + can_hijack = HIJACK_PREVENT /datum/antagonist/official/greet() to_chat(owner, "You are a CentCom Official.") diff --git a/code/modules/antagonists/overthrow/overthrow.dm b/code/modules/antagonists/overthrow/overthrow.dm new file mode 100644 index 0000000000..2f08824c0b --- /dev/null +++ b/code/modules/antagonists/overthrow/overthrow.dm @@ -0,0 +1,157 @@ +#define INITIAL_CRYSTALS 5 // initial telecrystals in the boss' uplink + +// Syndicate mutineer agents. They're agents selected by the Syndicate to take control of stations when assault teams like nuclear operatives cannot be sent. +// They sent teams made of 3 agents, of which only one is woke up at round start. The others are, lore-wise, sleeping agents and must be implanted with the converter to wake up. +// Mechanics wise, it's just 1 dude per team and he can convert maximum 2 more people of his choice, based on the implanter use var, Upon converting, the newly made guys are given access +// to a storage implant they came with when the Syndicate sent them aboard, with one random low-cost traitor item. The initial agent also has this. The only difference between +// initial agents and converted ones is that the initial agent has the items required to convert people and the AI. +/datum/antagonist/overthrow + name = "Syndicate mutineer" + roundend_category = "syndicate mutineers" + antagpanel_category = "Syndicate Mutineers" + job_rank = ROLE_TRAITOR // simply use the traitor preference & jobban settings + var/datum/team/overthrow/team + var/static/list/possible_useful_items + +// Overthrow agent. The idea is based on sleeping agents being sent as crewmembers, with one for each team that starts woken up who can also wake up others with their converter implant. +// Obviously they can just convert anyone, the idea of sleeping agents is just lore. This also explains why this antag type has no deconversion way: they're traitors. Traitors cannot be +// deconverted. +// Generates the list of possible items for the storage implant given on_gain +/datum/antagonist/overthrow/New() + ..() + if(!possible_useful_items) + possible_useful_items = list(/obj/item/gun/ballistic/automatic/pistol, /obj/item/storage/box/syndie_kit/throwing_weapons, /obj/item/pen/edagger, /obj/item/pen/sleepy, \ + /obj/item/soap/syndie, /obj/item/card/id/syndicate, /obj/item/storage/box/syndie_kit/chameleon) + +// Sets objectives, equips all antags with the storage implant. +/datum/antagonist/overthrow/on_gain() + objectives += team.objectives + owner.objectives += objectives + ..() + owner.announce_objectives() + equip_overthrow() + owner.special_role = ROLE_OVERTHROW + +/datum/antagonist/overthrow/on_removal() + owner.special_role = null + owner.objectives -= objectives + ..() + +// Creates the overthrow team, or sets it. The objectives are static for all the team members. +/datum/antagonist/overthrow/create_team(datum/team/overthrowers) + if(!overthrowers) + team = new() + team.add_member(owner) + name_team() + team.create_objectives() + else + team = overthrowers + team.add_member(owner) + +// Used to name the team at round start. If no name is passed, a syndicate themed one is given randomly. +/datum/antagonist/overthrow/proc/name_team() + var/team_name = stripped_input(owner.current, "Name your team:", "Team name", , MAX_NAME_LEN) + var/already_taken = FALSE + for(var/datum/antagonist/overthrow/O in GLOB.antagonists) + if(team_name == O.name) + already_taken = TRUE + break + if(!team_name || already_taken) // basic protection against two teams with the same name. This could still happen with extreme unluck due to syndicate_name() but it shouldn't break anything. + team.name = syndicate_name() + to_chat(owner, "Since you gave [already_taken ? "an already used" : "no"] name, your team's name has been randomly generated: [team.name]!") + return + team.name = team_name + +// CLOWNMUT removal and HUD creation/being given +/datum/antagonist/overthrow/apply_innate_effects() + ..() + if(owner.assigned_role == "Clown") + var/mob/living/carbon/human/traitor_mob = owner.current + if(traitor_mob && istype(traitor_mob)) + if(!silent) + to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") + traitor_mob.dna.remove_mutation(CLOWNMUT) + update_overthrow_icons_added() + +// The opposite +/datum/antagonist/overthrow/remove_innate_effects() + update_overthrow_icons_removed() + if(owner.assigned_role == "Clown") + var/mob/living/carbon/human/traitor_mob = owner.current + if(traitor_mob && istype(traitor_mob)) + traitor_mob.dna.add_mutation(CLOWNMUT) + ..() + +/datum/antagonist/overthrow/get_admin_commands() + . = ..() + .["Give storage with random item"] = CALLBACK(src,.proc/equip_overthrow) + .["Give overthrow boss equip"] = CALLBACK(src,.proc/equip_initial_overthrow_agent) + +// Dynamically creates the HUD for the team if it doesn't exist already, inserting it into the global huds list, and assigns it to the user. The index is saved into a var owned by the team datum. +/datum/antagonist/overthrow/proc/update_overthrow_icons_added(datum/mind/traitor_mind) + var/datum/atom_hud/antag/overthrowhud = GLOB.huds[team.hud_entry_num] + if(!overthrowhud) + overthrowhud = new() + team.hud_entry_num = GLOB.huds.len + 1 // the index of the hud inside huds list + GLOB.huds += overthrowhud + overthrowhud.join_hud(owner.current) + set_antag_hud(owner.current, "traitor") +// Removes hud. Destroying the hud datum itself in case the team is deleted is done on team Destroy(). +/datum/antagonist/overthrow/proc/update_overthrow_icons_removed(datum/mind/traitor_mind) + var/datum/atom_hud/antag/overthrowhud = GLOB.huds[team.hud_entry_num] + if(overthrowhud) + overthrowhud.leave_hud(owner.current) + set_antag_hud(owner.current, null) + +// Gives the storage implant with a random item. They're sleeping agents, after all. +/datum/antagonist/overthrow/proc/equip_overthrow() + if(!owner || !owner.current || !ishuman(owner.current)) // only equip existing human overthrow members. This excludes the AI, in particular. + return + var/obj/item/implant/storage/S = locate(/obj/item/implant/storage) in owner.current + if(!S) + S = new(owner.current) + S.implant(owner.current) + var/I = pick(possible_useful_items) + if(ispath(I)) // in case some admin decides to fuck the list up for fun + I = new I() + SEND_SIGNAL(S, COMSIG_TRY_STORAGE_INSERT, I, null, TRUE, TRUE) + +// Equip the initial overthrow agent. Manually called in overthrow gamemode, when the initial agents are chosen. Gives uplink, AI module board and the converter. +/datum/antagonist/overthrow/proc/equip_initial_overthrow_agent() + if(!owner || !owner.current || !ishuman(owner.current)) + return + var/mob/living/carbon/human/H = owner.current + // Give uplink + var/obj/item/uplink_holder = owner.equip_traitor(uplink_owner = src) + var/datum/component/uplink/uplink = uplink_holder.GetComponent(/datum/component/uplink) + uplink.telecrystals = INITIAL_CRYSTALS + // Give AI hacking board + var/obj/item/aiModule/core/full/overthrow/O = new(H) + var/list/slots = list ( + "backpack" = SLOT_IN_BACKPACK, + "left pocket" = SLOT_L_STORE, + "right pocket" = SLOT_R_STORE + ) + var/where = H.equip_in_one_of_slots(O, slots) + if (!where) + to_chat(H, "The Syndicate were unfortunately unable to get you the AI module.") + else + to_chat(H, "Use the AI board in your [where] to take control of the AI, as requested by the Syndicate.") + // Give the implant converter + var/obj/item/overthrow_converter/I = new(H) + where = H.equip_in_one_of_slots(I, slots) + if (!where) + to_chat(H, "The Syndicate were unfortunately unable to get you a converter implant.") + else + to_chat(H, "Use the implanter in your [where] to wake up sleeping syndicate agents, so that they can aid you.") + +/datum/antagonist/overthrow/get_team() + return team + +/datum/antagonist/overthrow/greet() + to_chat(owner.current, "You are a syndicate sleeping agent! Your job is to stage a swift, fairly bloodless coup. Your team has a two-use converter that can be used to convert \ + anyone you want, although mind shield implants need to be removed firstly for it to work. Your team also has a special version of the Syndicate module to be used to convert the AI, too. You \ + will be able to use the special storage implant you came aboard with, which contains a random, cheap item from our special selection which will aid in your mission. \ + Your team objective is to deal with the heads, the AI and a special target who angered us for several reasons which you're not entitled to know. Converting to your team will let us \ + take control of the station faster, so it should be prioritized, especially over killing, which should be avoided where possible. The other Syndicate teams are NOT friends and should not \ + be trusted.") diff --git a/code/modules/antagonists/overthrow/overthrow_converter.dm b/code/modules/antagonists/overthrow/overthrow_converter.dm new file mode 100644 index 0000000000..c94a838165 --- /dev/null +++ b/code/modules/antagonists/overthrow/overthrow_converter.dm @@ -0,0 +1,56 @@ +/obj/item/overthrow_converter // nearly equal to an implanter, as an object + name = "agent activation implant" + desc = "Wakes up syndicate sleeping agents." + icon = 'icons/obj/items_and_weapons.dmi' + icon_state = "implanter1" + item_state = "syringe_0" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=600, MAT_GLASS=200) + var/uses = 2 + +/obj/item/overthrow_converter/proc/convert(mob/living/carbon/human/target, mob/living/carbon/human/user) // Should probably also delete any mindshield implant. Not sure. + if(istype(target) && target.mind && user && user.mind) + var/datum/mind/target_mind = target.mind + var/datum/mind/user_mind = user.mind + var/datum/antagonist/overthrow/TO = target_mind.has_antag_datum(/datum/antagonist/overthrow) + var/datum/antagonist/overthrow/UO = user_mind.has_antag_datum(/datum/antagonist/overthrow) + if(!UO) + to_chat(user, "You don't know how to use this thing!") // It needs a valid team to work, if you aren't an antag don't use this thing + return FALSE + if(TO) + to_chat(user, "[target.name] woke up already, the implant would be ineffective against him!") + return FALSE + target_mind.add_antag_datum(/datum/antagonist/overthrow, UO.team) + log_combat(user, target, "implanted", "\a [name]") + return TRUE + +/obj/item/overthrow_converter/attack(mob/living/carbon/human/M, mob/living/carbon/human/user) + if(!istype(M) || !istype(user)) + return + if(!uses) + to_chat(user,"The converter is empty!") + return + if(M == user) + to_chat(user,"You cannot convert yourself!") + return + if(M.has_trait(TRAIT_MINDSHIELD)) + to_chat(user, "This mind is too strong to convert, try to remove whatever is protecting it first!") + return + M.visible_message("[user] is attempting to implant [M].") + if(do_mob(user, M, 50)) + if(convert(M,user)) + M.visible_message("[user] has implanted [M].", "[user] implants you.") + uses-- + update_icon() + else + to_chat(user, "[user] fails to implant [M].") + +/obj/item/overthrow_converter/update_icon() + if(uses) + icon_state = "implanter1" + else + icon_state = "implanter0" diff --git a/code/modules/antagonists/overthrow/overthrow_team.dm b/code/modules/antagonists/overthrow/overthrow_team.dm new file mode 100644 index 0000000000..7f5d010013 --- /dev/null +++ b/code/modules/antagonists/overthrow/overthrow_team.dm @@ -0,0 +1,41 @@ +/datum/team/overthrow + name = "overthrow" // The team name is set on creation by the leader. + member_name = "syndicate agent" + var/hud_entry_num // A number holding the hud's index inside 'huds' global list. Gets set on hud update, if a hud doesn't exist already. Must be a number, otherwise BYOND shits up with assoc lists and everything goes to hell. + +/datum/team/overthrow/Destroy() + var/datum/atom_hud/antag/overthrowhud = GLOB.huds[hud_entry_num] + GLOB.huds -= GLOB.huds[hud_entry_num] + qdel(overthrowhud) + . = ..() + +/datum/team/overthrow/proc/create_objectives() + // Heads objective + var/datum/objective/overthrow/heads/heads = new() + heads.team = src + heads.find_target() + objectives += heads + // AI objective + var/datum/objective/overthrow/AI/AI = new() + AI.team = src + AI.update_explanation_text() + objectives += AI + // Target objective + var/datum/objective/overthrow/target/target = new() + target.team = src + target.find_target() + objectives += target + addtimer(CALLBACK(src,.proc/update_objectives),OBJECTIVE_UPDATING_TIME,TIMER_UNIQUE) + +/datum/team/overthrow/proc/update_objectives() + var/datum/objective/overthrow/heads/heads_obj = locate() in objectives + if(!heads_obj) + heads_obj = new() + heads_obj.team = src + objectives += heads_obj + for(var/i in members) + var/datum/mind/M = i + M.objectives += heads_obj + heads_obj.find_targets() + + addtimer(CALLBACK(src,.proc/update_objectives),OBJECTIVE_UPDATING_TIME,TIMER_UNIQUE) diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm index cdd871ff35..96830562e9 100644 --- a/code/modules/antagonists/pirate/pirate.dm +++ b/code/modules/antagonists/pirate/pirate.dm @@ -45,65 +45,43 @@ /datum/team/pirate/proc/forge_objectives() var/datum/objective/loot/getbooty = new() getbooty.team = src - getbooty.storage_area = locate(/area/shuttle/pirate/vault) in GLOB.sortedAreas - getbooty.update_initial_value() + for(var/obj/machinery/computer/piratepad_control/P in GLOB.machines) + var/area/A = get_area(P) + if(istype(A,/area/shuttle/pirate)) + getbooty.cargo_hold = P + break getbooty.update_explanation_text() objectives += getbooty for(var/datum/mind/M in members) M.objectives |= objectives -GLOBAL_LIST_INIT(pirate_loot_cache, typecacheof(list( - /obj/structure/reagent_dispensers/beerkeg, - /mob/living/simple_animal/parrot, - /obj/item/stack/sheet/mineral/gold, - /obj/item/stack/sheet/mineral/diamond, - /obj/item/stack/spacecash, - /obj/item/melee/sabre,))) - /datum/objective/loot - var/area/storage_area //Place where we we will look for the loot. + var/obj/machinery/computer/piratepad_control/cargo_hold explanation_text = "Acquire valuable loot and store it in designated area." var/target_value = 50000 - var/initial_value = 0 //Things in the vault at spawn time do not count + /datum/objective/loot/update_explanation_text() - if(storage_area) - explanation_text = "Acquire loot and store [target_value] of credits worth in [storage_area.name]." + if(cargo_hold) + var/area/storage_area = get_area(cargo_hold) + explanation_text = "Acquire loot and store [target_value] of credits worth in [storage_area.name] cargo hold." /datum/objective/loot/proc/loot_listing() //Lists notable loot. - if(!storage_area) + if(!cargo_hold || !cargo_hold.total_report) return "Nothing" - var/list/loot_table = list() - for(var/atom/movable/AM in storage_area.GetAllContents()) - if(is_type_in_typecache(AM,GLOB.pirate_loot_cache)) - var/lootname = AM.name - var/count = 1 - if(istype(AM,/obj/item/stack)) //Ugh. - var/obj/item/stack/S = AM - lootname = S.singular_name - count = S.amount - if(!loot_table[lootname]) - loot_table[lootname] = count - else - loot_table[lootname] += count + cargo_hold.total_report.total_value = sortTim(cargo_hold.total_report.total_value, cmp = /proc/cmp_numeric_dsc, associative = TRUE) + var/count = 0 var/list/loot_texts = list() - for(var/key in loot_table) - var/amount = loot_table[key] - loot_texts += "[amount] [key][amount > 1 ? "s":""]" + for(var/datum/export/E in cargo_hold.total_report.total_value) + if(++count > 5) + break + loot_texts += E.total_printout(cargo_hold.total_report,notes = FALSE) return loot_texts.Join(", ") /datum/objective/loot/proc/get_loot_value() - if(!storage_area) - return 0 - var/value = 0 - for(var/turf/T in storage_area.contents) - value += export_item_and_contents(T,TRUE, TRUE, dry_run = TRUE) - return value - initial_value - -/datum/objective/loot/proc/update_initial_value() - initial_value = get_loot_value() + return cargo_hold.points /datum/objective/loot/check_completion() return ..() || get_loot_value() >= target_value diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index 03150c0254..e421e7fad8 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -48,6 +48,7 @@ speed = 1 unique_name = TRUE hud_possible = list(ANTAG_HUD) + hud_type = /datum/hud/revenant var/essence = 75 //The resource, and health, of revenants. var/essence_regen_cap = 75 //The regeneration cap of essence (go figure); regenerates every Life() tick up to this amount. @@ -143,10 +144,10 @@ /mob/living/simple_animal/revenant/med_hud_set_status() return //we use no hud -/mob/living/simple_animal/revenant/say(message) +/mob/living/simple_animal/revenant/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) if(!message) return - log_talk(src,"[key_name(src)] : [message]",LOGSAY) + src.log_talk(message, LOG_SAY) var/rendered = "[src] says, \"[message]\"" for(var/mob/M in GLOB.mob_list) if(isrevenant(M)) @@ -199,7 +200,7 @@ if(!essence) death() -/mob/living/simple_animal/revenant/dust() +/mob/living/simple_animal/revenant/dust(just_ash, drop_items, force) death() /mob/living/simple_animal/revenant/gib() diff --git a/code/modules/antagonists/revenant/revenant_abilities.dm b/code/modules/antagonists/revenant/revenant_abilities.dm index 0736af1dc0..4056ac1cea 100644 --- a/code/modules/antagonists/revenant/revenant_abilities.dm +++ b/code/modules/antagonists/revenant/revenant_abilities.dm @@ -114,7 +114,7 @@ if(!msg) charge_counter = charge_max return - log_talk(user,"RevenantTransmit: [key_name(user)]->[key_name(M)] : [msg]",LOGSAY) + log_directed_talk(user, M, msg, LOG_SAY, "revenant whisper") to_chat(user, "You transmit to [M]: [msg]") if(!M.anti_magic_check(FALSE, TRUE)) //hear no evil to_chat(M, "You hear something behind you talking... [msg]") diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm index ad72682773..a397fbb0d5 100644 --- a/code/modules/antagonists/revolution/revolution.dm +++ b/code/modules/antagonists/revolution/revolution.dm @@ -35,7 +35,7 @@ . = ..() create_objectives() equip_rev() - owner.current.log_message("Has been converted to the revolution!", INDIVIDUAL_ATTACK_LOG) + owner.current.log_message("has been converted to the revolution!", LOG_ATTACK, color="red") /datum/antagonist/rev/on_removal() remove_objectives() @@ -209,7 +209,7 @@ //blunt trauma deconversions call this through species.dm spec_attacked_by() /datum/antagonist/rev/proc/remove_revolutionary(borged, deconverter) - log_attack("[owner.current] (Key: [key_name(owner.current)]) has been deconverted from the revolution by [deconverter] (Key: [key_name(deconverter)])!") + log_attack("[key_name(owner.current)] has been deconverted from the revolution by [key_name(deconverter)]!") if(borged) message_admins("[ADMIN_LOOKUPFLW(owner.current)] has been borged while being a [name]") owner.special_role = null @@ -281,7 +281,7 @@ var/list/datum/mind/heads = SSjob.get_all_heads() var/list/sec = SSjob.get_all_sec() - if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((3 - sec.len) / 3))) + if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3))) var/list/datum/mind/non_heads = members - head_revolutionaries var/list/datum/mind/promotable = list() for(var/datum/mind/khrushchev in non_heads) diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm index d4d86cc087..a51b80b567 100644 --- a/code/modules/antagonists/swarmer/swarmer.dm +++ b/code/modules/antagonists/swarmer/swarmer.dm @@ -98,6 +98,7 @@ del_on_death = 1 deathmessage = "explodes with a sharp pop!" light_color = LIGHT_COLOR_CYAN + hud_type = /datum/hud/swarmer var/resources = 0 //Resource points, generated by consuming metal/glass var/max_resources = 100 @@ -482,7 +483,7 @@ if(ishuman(target) && (!H.handcuffed)) H.handcuffed = new /obj/item/restraints/handcuffs/energy/used(H) H.update_handcuffed() - add_logs(src, H, "handcuffed") + log_combat(src, H, "handcuffed") var/datum/effect_system/spark_spread/S = new S.set_up(4,0,get_turf(target)) diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 9066759648..47823a2dc4 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -13,6 +13,7 @@ var/should_give_codewords = TRUE var/should_equip = TRUE var/traitor_kind = TRAITOR_HUMAN //Set on initial assignment + can_hijack = HIJACK_HIJACKER /datum/antagonist/traitor/on_gain() if(owner.current && isAI(owner.current)) diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm index 3f7fac9d18..318de51eb4 100644 --- a/code/modules/antagonists/wishgranter/wishgranter.dm +++ b/code/modules/antagonists/wishgranter/wishgranter.dm @@ -2,6 +2,7 @@ name = "Wishgranter Avatar" show_in_antagpanel = FALSE show_name_in_check_antagonists = TRUE + can_hijack = HIJACK_HIJACKER /datum/antagonist/wishgranter/proc/forge_objectives() var/datum/objective/hijack/hijack = new diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm index 415d1318cb..7cfefd5413 100644 --- a/code/modules/antagonists/wizard/equipment/artefact.dm +++ b/code/modules/antagonists/wizard/equipment/artefact.dm @@ -110,6 +110,25 @@ eat() return +/obj/singularity/wizard/attack_tk(mob/user) + if(iscarbon(user)) + var/mob/living/carbon/C = user + GET_COMPONENT_FROM(insaneinthemembrane, /datum/component/mood, C) + if(insaneinthemembrane.sanity < 15) + return //they've already seen it and are about to die, or are just too insane to care + to_chat(C, "OH GOD! NONE OF IT IS REAL! NONE OF IT IS REEEEEEEEEEEEEEEEEEEEEEEEAL!") + insaneinthemembrane.sanity = 0 + for(var/lore in typesof(/datum/brain_trauma/severe)) + C.gain_trauma(lore) + addtimer(CALLBACK(src, /obj/singularity/wizard.proc/deranged, C), 100) + +/obj/singularity/wizard/proc/deranged(mob/living/carbon/C) + if(!C || C.stat == DEAD) + return + C.vomit(0, TRUE, TRUE, 3, TRUE) + C.spew_organ(3, 2) + C.death() + /obj/singularity/wizard/mapped/admin_investigate_setup() return @@ -282,7 +301,7 @@ switch(user.zone_selected) if(BODY_ZONE_PRECISE_MOUTH) var/wgw = sanitize(input(user, "What would you like the victim to say", "Voodoo", null) as text) - target.say(wgw) + target.say(wgw, forced = "voodoo doll") log_game("[key_name(user)] made [key_name(target)] say [wgw] with a voodoo doll.") if(BODY_ZONE_PRECISE_EYES) user.set_machine(src) diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 50bc4f54a4..40551ae2fc 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -66,7 +66,7 @@ if(iscultist(user)) to_chat(user, "\"Come now, do not capture your bretheren's soul.\"") return - add_logs(user, M, "captured [M.name]'s soul", src) + log_combat(user, M, "captured [M.name]'s soul", src) transfer_soul("VICTIM", M, user) ///////////////////Options for using captured souls/////////////////////////////////////// diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm index afefaa68c1..bb7ffdc85d 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook.dm @@ -76,7 +76,7 @@ return 0 /datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user,obj/item/spellbook/book) //return point value or -1 for failure - var/area/wizard_station/A = locate() in GLOB.sortedAreas + var/area/wizard_station/A = GLOB.areas_by_type[/area/wizard_station] if(!(user in A.contents)) to_chat(user, "You can only refund spells at the wizard lair") return -1 @@ -316,6 +316,12 @@ cost = 1 category = "Defensive" +/datum/spellbook_entry/item/lockerstaff + name = "Staff of the Locker" + desc = "A staff that shoots lockers. It eats anyone it hits on its way, leaving a welded locker with your victims behind." + item_path = /obj/item/gun/magic/staff/locker + category = "Defensive" + /datum/spellbook_entry/item/scryingorb name = "Scrying Orb" desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you X-ray vision." diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm index c5bc0cc5f7..6fac9730f3 100644 --- a/code/modules/antagonists/wizard/wizard.dm +++ b/code/modules/antagonists/wizard/wizard.dm @@ -12,12 +12,13 @@ var/move_to_lair = TRUE var/outfit_type = /datum/outfit/wizard var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */ + can_hijack = HIJACK_HIJACKER /datum/antagonist/wizard/on_gain() register() + equip_wizard() if(give_objectives) create_objectives() - equip_wizard() if(move_to_lair) send_to_lair() . = ..() diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index 3e291d0eee..67527b2e91 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -101,6 +101,8 @@ ..() /obj/item/assembly/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE if(toggle_secure()) to_chat(user, "\The [src] is ready!") else diff --git a/code/modules/assembly/doorcontrol.dm b/code/modules/assembly/doorcontrol.dm index 51b0be95b6..aa1ee8adbc 100644 --- a/code/modules/assembly/doorcontrol.dm +++ b/code/modules/assembly/doorcontrol.dm @@ -56,7 +56,7 @@ if(D.secondsElectrified) D.secondsElectrified = -1 LAZYADD(D.shockedby, "\[[time_stamp()]\] [key_name(usr)]") - add_logs(usr, D, "electrified") + log_combat(usr, D, "electrified") else D.secondsElectrified = 0 if(specialfunctions & SAFE) diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm index 5f2771190f..ca60ef5c08 100644 --- a/code/modules/assembly/flash.dm +++ b/code/modules/assembly/flash.dm @@ -1,3 +1,4 @@ +#define CONFUSION_STACK_MAX_MULTIPLIER 2 /obj/item/assembly/flash name = "flash" desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production." @@ -86,7 +87,7 @@ if(isturf(target_loc) || (ismob(target_loc) && isturf(target_loc.loc))) return viewers(range, get_turf(target_loc)) else - return typecache_filter_list(target_loc.GetAllContents(), typecacheof(list(/mob/living))) + return typecache_filter_list(target_loc.GetAllContents(), GLOB.typecache_living) /obj/item/assembly/flash/proc/try_use_flash(mob/user = null) if(crit_fail || (world.time < last_trigger + cooldown)) @@ -103,12 +104,17 @@ /obj/item/assembly/flash/proc/flash_carbon(mob/living/carbon/M, mob/user, power = 15, targeted = TRUE, generic_message = FALSE) if(!istype(M)) return - add_logs(user, M, "[targeted? "flashed(targeted)" : "flashed(AOE)"]", src) + if(user) + log_combat(user, M, "[targeted? "flashed(targeted)" : "flashed(AOE)"]", src) + else //caused by emp/remote signal + M.log_message("was [targeted? "flashed(targeted)" : "flashed(AOE)"]",LOG_ATTACK) if(generic_message && M != user) to_chat(M, "[src] emits a blinding light!") if(targeted) if(M.flash_act(1, 1)) - M.confused = CLAMP(M.confused + power, 0, power * 2) + if(M.confused < power) + var/diff = power * CONFUSION_STACK_MAX_MULTIPLIER - M.confused + M.confused += min(power, diff) if(user) terrible_conversion_proc(M, user) visible_message("[user] blinds [M] with the flash!") @@ -125,7 +131,8 @@ to_chat(M, "[src] fails to blind you!") else if(M.flash_act()) - M.confused = CLAMP(M.confused + power, 0, power * 2) + var/diff = power * CONFUSION_STACK_MAX_MULTIPLIER - M.confused + M.confused += min(power, diff) /obj/item/assembly/flash/attack(mob/living/M, mob/user) if(!try_use_flash(user)) @@ -135,10 +142,11 @@ return TRUE else if(issilicon(M)) var/mob/living/silicon/robot/R = M - add_logs(user, R, "flashed", src) + log_combat(user, R, "flashed", src) update_icon(1) - M.Knockdown(rand(80,120)) - R.confused = CLAMP(R.confused + 5, 0, 10) + R.Knockdown(rand(80,120)) + var/diff = 5 * CONFUSION_STACK_MAX_MULTIPLIER - M.confused + R.confused += min(5, diff) R.flash_act(affect_silicon = 1) user.visible_message("[user] overloads [R]'s sensors with the flash!", "You overload [R]'s sensors with the flash!") return TRUE diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm index 78d3f686ab..782d729ce4 100644 --- a/code/modules/assembly/health.dm +++ b/code/modules/assembly/health.dm @@ -8,7 +8,7 @@ var/scanning = FALSE var/health_scan - var/alarm_health = 0 + var/alarm_health = HEALTH_THRESHOLD_CRIT /obj/item/assembly/health/examine(mob/user) ..() @@ -31,11 +31,11 @@ return secured /obj/item/assembly/health/multitool_act(mob/living/user, obj/item/I) - if(alarm_health == 0) - alarm_health = -90 + if(alarm_health == HEALTH_THRESHOLD_CRIT) + alarm_health = HEALTH_THRESHOLD_DEAD to_chat(user, "You toggle [src] to \"detect death\" mode.") else - alarm_health = 0 + alarm_health = HEALTH_THRESHOLD_CRIT to_chat(user, "You toggle [src] to \"detect critical state\" mode.") return TRUE diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 031ce866ba..74e9cfeff9 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -97,6 +97,8 @@ a_right.attack_hand() /obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/tool) + if(..()) + return TRUE to_chat(user, "You disassemble [src]!") if(a_left) a_left.on_detach() diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index 94359f0a10..07f31a6678 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -164,9 +164,9 @@ /obj/item/assembly/infra/proc/switchListener(turf/newloc) QDEL_NULL(listener) - listener = newloc.AddComponent(/datum/component/redirect, COMSIG_ATOM_EXITED, CALLBACK(src, .proc/check_exit)) + listener = newloc.AddComponent(/datum/component/redirect, list(COMSIG_ATOM_EXITED = CALLBACK(src, .proc/check_exit))) -/obj/item/assembly/infra/proc/check_exit(atom/movable/offender) +/obj/item/assembly/infra/proc/check_exit(datum/source, atom/movable/offender) if(QDELETED(src)) return if(offender == src || istype(offender,/obj/effect/beam/i_beam)) diff --git a/code/modules/atmospherics/environmental/LINDA_system.dm b/code/modules/atmospherics/environmental/LINDA_system.dm index 268528355e..084c35684c 100644 --- a/code/modules/atmospherics/environmental/LINDA_system.dm +++ b/code/modules/atmospherics/environmental/LINDA_system.dm @@ -120,4 +120,5 @@ G.parse_gas_string(text) air.merge(G) + archive() SSair.add_to_active(src, 0) diff --git a/code/modules/atmospherics/gasmixtures/gas_types.dm b/code/modules/atmospherics/gasmixtures/gas_types.dm index 0787871ca4..85933d3a68 100644 --- a/code/modules/atmospherics/gasmixtures/gas_types.dm +++ b/code/modules/atmospherics/gasmixtures/gas_types.dm @@ -129,6 +129,13 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(/datum/gas/oxygen, /datum/g name = "Pluoxium" fusion_power = 10 +/datum/gas/miasma + id = "miasma" + specific_heat = 20 + name = "Miasma" + gas_overlay = "miasma" + moles_visible = MOLES_GAS_VISIBLE * 60 + /obj/effect/overlay/gas icon = 'icons/effects/tile_effects.dmi' mouse_opacity = MOUSE_OPACITY_TRANSPARENT diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm index 0524783b05..1c39e9a584 100644 --- a/code/modules/atmospherics/gasmixtures/reactions.dm +++ b/code/modules/atmospherics/gasmixtures/reactions.dm @@ -257,7 +257,7 @@ reaction_energy += gases_fused * FUSION_RELEASE_ENERGY_SUPER * (power_ratio / FUSION_ENERGY_DIVISOR_SUPER) cached_gases[/datum/gas/tritium][MOLES] += gases_fused * FUSION_GAS_CREATION_FACTOR_TRITIUM //60% of the gas is converted to energy, 40% to trit fusion_prepare_to_die_edition_rng = 100 //Wait a minute.. - do_explosion = TRUE + do_explosion = TRUE zap_range = FUSION_ZAP_RANGE_SUPER else if (power_ratio > FUSION_HIGH_TIER_THRESHOLD) //power ratio 20-50; High tier. The reaction is so energized that it fuses into a small amount of stimulum, and some pluoxium. Very dangerous, but super cool and super useful. @@ -433,3 +433,30 @@ var/new_heat_capacity = air.heat_capacity() if(new_heat_capacity > MINIMUM_HEAT_CAPACITY) air.temperature = max(((air.temperature*old_heat_capacity - energy_taken)/new_heat_capacity),TCMB) + + +/datum/gas_reaction/miaster //dry heat sterilization: clears out pathogens in the air + priority = -10 //after all the heating from fires etc. is done + name = "Dry Heat Sterilization" + id = "sterilization" + +/datum/gas_reaction/miaster/init_reqs() + min_requirements = list( + "TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST+70, + /datum/gas/miasma = MINIMUM_MOLE_COUNT + ) + +/datum/gas_reaction/miaster/react(datum/gas_mixture/air, datum/holder) + var/list/cached_gases = air.gases + // As the name says it, it needs to be dry + if(/datum/gas/water_vapor in cached_gases) + if(cached_gases[/datum/gas/water_vapor]/air.total_moles() > 0.1) + return + + //Replace miasma with oxygen + var/cleaned_air = min(cached_gases[/datum/gas/miasma][MOLES], 20 + (air.temperature - FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 70) / 20) + cached_gases[/datum/gas/miasma][MOLES] -= cleaned_air + cached_gases[/datum/gas/oxygen][MOLES] += cleaned_air + + //Possibly burning a bit of organic matter through maillard reaction, so a *tiny* bit more heat would be understandable + air.temperature += cleaned_air * 0.002 diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm index 6e6355ed72..8cc5dfafab 100644 --- a/code/modules/atmospherics/machinery/airalarm.dm +++ b/code/modules/atmospherics/machinery/airalarm.dm @@ -89,6 +89,7 @@ /datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa /datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000), /datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10), + /datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5), /datum/gas/plasma = new/datum/tlv/dangerous, /datum/gas/nitrous_oxide = new/datum/tlv/dangerous, /datum/gas/bz = new/datum/tlv/dangerous, @@ -107,6 +108,7 @@ /datum/gas/oxygen = new/datum/tlv/no_checks, /datum/gas/nitrogen = new/datum/tlv/no_checks, /datum/gas/carbon_dioxide = new/datum/tlv/no_checks, + /datum/gas/miasma = new/datum/tlv/no_checks, /datum/gas/plasma = new/datum/tlv/no_checks, /datum/gas/nitrous_oxide = new/datum/tlv/no_checks, /datum/gas/bz = new/datum/tlv/no_checks, @@ -125,6 +127,7 @@ /datum/gas/oxygen = new/datum/tlv(16, 19, 135, 140), // Partial pressure, kpa /datum/gas/nitrogen = new/datum/tlv(-1, -1, 1000, 1000), /datum/gas/carbon_dioxide = new/datum/tlv(-1, -1, 5, 10), + /datum/gas/miasma = new/datum/tlv/(-1, -1, 2, 5), /datum/gas/plasma = new/datum/tlv/dangerous, /datum/gas/nitrous_oxide = new/datum/tlv/dangerous, /datum/gas/bz = new/datum/tlv/dangerous, @@ -145,6 +148,12 @@ req_access = null req_one_access = list(ACCESS_ATMOSPHERICS, ACCESS_ENGINE) +/obj/machinery/airalarm/mixingchamber + name = "chamber air alarm" + locked = FALSE + req_access = null + req_one_access = list(ACCESS_ATMOSPHERICS, ACCESS_TOX, ACCESS_TOX_STORAGE) + /obj/machinery/airalarm/all_access name = "all-access air alarm" desc = "This particular atmos control unit appears to have no access restrictions." @@ -155,6 +164,22 @@ /obj/machinery/airalarm/syndicate //general syndicate access req_access = list(ACCESS_SYNDICATE) +/obj/machinery/airalarm/directional/north //Pixel offsets get overwritten on New() + dir = SOUTH + pixel_y = 24 + +/obj/machinery/airalarm/directional/south + dir = NORTH + pixel_y = -24 + +/obj/machinery/airalarm/directional/east + dir = WEST + pixel_x = 24 + +/obj/machinery/airalarm/directional/west + dir = EAST + pixel_x = -24 + //all air alarms in area are connected via magic /area var/list/air_vent_names = list() @@ -189,6 +214,16 @@ . = ..() set_frequency(frequency) +/obj/machinery/airalarm/examine(mob/user) + . = ..() + switch(buildstage) + if(0) + to_chat(user, "It is missing air alarm electronics.") + if(1) + to_chat(user, "It is missing wiring.") + if(2) + to_chat(user, "Alt-click to [locked ? "unlock" : "lock"] the interface.") + /obj/machinery/airalarm/ui_status(mob/user) if(user.has_unlimited_silicon_privilege && aidisabled) to_chat(user, "AI control has been disabled.") @@ -338,25 +373,25 @@ locked = !locked . = TRUE if("power", "toggle_filter", "widenet", "scrubbing") - send_signal(device_id, list("[action]" = params["val"])) + send_signal(device_id, list("[action]" = params["val"]), usr) . = TRUE if("excheck") - send_signal(device_id, list("checks" = text2num(params["val"])^1)) + send_signal(device_id, list("checks" = text2num(params["val"])^1), usr) . = TRUE if("incheck") - send_signal(device_id, list("checks" = text2num(params["val"])^2)) + send_signal(device_id, list("checks" = text2num(params["val"])^2), usr) . = TRUE if("set_external_pressure", "set_internal_pressure") var/area/A = get_area(src) var/target = input("New target pressure:", name, A.air_vent_info[device_id][(action == "set_external_pressure" ? "external" : "internal")]) as num|null if(!isnull(target) && !..()) - send_signal(device_id, list("[action]" = target)) + send_signal(device_id, list("[action]" = target), usr) . = TRUE if("reset_external_pressure") - send_signal(device_id, list("reset_external_pressure")) + send_signal(device_id, list("reset_external_pressure"), usr) . = TRUE if("reset_internal_pressure") - send_signal(device_id, list("reset_internal_pressure")) + send_signal(device_id, list("reset_internal_pressure"), usr) . = TRUE if("threshold") var/env = params["env"] @@ -373,9 +408,11 @@ tlv.vars[name] = -1 else tlv.vars[name] = round(value, 0.01) + investigate_log(" treshold value for [env]:[name] was set to [value] by [key_name(usr)]",INVESTIGATE_ATMOS) . = TRUE if("mode") mode = text2num(params["mode"]) + investigate_log("was turned to [get_mode_name(mode)] mode by [key_name(usr)]",INVESTIGATE_ATMOS) apply_mode() . = TRUE if("alarm") @@ -433,17 +470,39 @@ frequency = new_frequency radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) -/obj/machinery/airalarm/proc/send_signal(target, list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise +/obj/machinery/airalarm/proc/send_signal(target, list/command, mob/user)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise if(!radio_connection) return 0 var/datum/signal/signal = new(command) signal.data["tag"] = target signal.data["sigtype"] = "command" + signal.data["user"] = user radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) return 1 +/obj/machinery/airalarm/proc/get_mode_name(mode_value) + switch(mode_value) + if(AALARM_MODE_SCRUBBING) + return "Filtering" + if(AALARM_MODE_CONTAMINATED) + return "Contaminated" + if(AALARM_MODE_VENTING) + return "Draught" + if(AALARM_MODE_REFILL) + return "Refill" + if(AALARM_MODE_PANIC) + return "Panic Siphon" + if(AALARM_MODE_REPLACEMENT) + return "Cycle" + if(AALARM_MODE_SIPHON) + return "Siphon" + if(AALARM_MODE_OFF) + return "Off" + if(AALARM_MODE_FLOOD) + return "Flood" + /obj/machinery/airalarm/proc/apply_mode() var/area/A = get_area(src) switch(mode) @@ -451,7 +510,7 @@ for(var/device_id in A.air_scrub_names) send_signal(device_id, list( "power" = 1, - "set_filters" = list(/datum/gas/carbon_dioxide), + "set_filters" = list(/datum/gas/carbon_dioxide, /datum/gas/miasma), "scrubbing" = 1, "widenet" = 0, )) @@ -467,6 +526,7 @@ "power" = 1, "set_filters" = list( /datum/gas/carbon_dioxide, + /datum/gas/miasma, /datum/gas/plasma, /datum/gas/water_vapor, /datum/gas/hypernoblium, @@ -503,7 +563,7 @@ for(var/device_id in A.air_scrub_names) send_signal(device_id, list( "power" = 1, - "set_filters" = list(/datum/gas/carbon_dioxide), + "set_filters" = list(/datum/gas/carbon_dioxide, /datum/gas/miasma), "scrubbing" = 1, "widenet" = 0, )) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm index dd593c53bc..6bafba9abc 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm @@ -145,6 +145,8 @@ return TRUE /obj/machinery/atmospherics/components/binary/circulator/screwdriver_act(mob/user, obj/item/I) + if(..()) + return TRUE panel_open = !panel_open I.play_tool_sound(src) to_chat(user, "You [panel_open?"open":"close"] the panel on [src].") diff --git a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm index a3ecda947a..6d24d51054 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm @@ -131,6 +131,7 @@ Thus, the two variables affect pump operation are set in New(): switch(action) if("power") on = !on + message_admins("Pump, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]") investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", INVESTIGATE_ATMOS) . = TRUE if("rate") @@ -185,3 +186,7 @@ Thus, the two variables affect pump operation are set in New(): if(. && on && is_operational()) to_chat(user, "You cannot unwrench [src], turn it off first!") return FALSE + else + investigate_log("Pump, [src.name], was unwrenched by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS) + message_admins("Pump, [src.name], was unwrenched by [ADMIN_LOOKUPFLW(user)] at [A]") + return TRUE \ No newline at end of file diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index cca6c4d15a..9b69b2fa6f 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -294,7 +294,7 @@ user.visible_message("[user] places [I] in [src].", \ "You place [I] in [src].") var/reagentlist = pretty_string_from_reagent_list(I.reagents.reagent_list) - log_game("[key_name(user)] added an [I] to cyro containing [reagentlist]") + log_game("[key_name(user)] added an [I] to cryo containing [reagentlist]") return if(!on && !occupant && !state_open && (default_deconstruction_screwdriver(user, "pod-off", "pod-off", I)) \ || default_change_direction_wrench(user, I) \ diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm index 5359e2324e..8a1bae5e68 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm @@ -3,8 +3,9 @@ desc = "Heats or cools gas in connected pipes." icon = 'icons/obj/atmospherics/components/thermomachine.dmi' icon_state = "freezer" - var/icon_state_on = "cold_on" - var/icon_state_open = "cold_off" + var/icon_state_off = "freezer" + var/icon_state_on = "freezer_1" + var/icon_state_open = "freezer-o" density = TRUE max_integrity = 300 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) @@ -37,7 +38,7 @@ else if(on && is_operational()) icon_state = icon_state_on else - icon_state = initial(icon_state) + icon_state = icon_state_off /obj/machinery/atmospherics/components/unary/thermomachine/update_icon_nopipes() cut_overlays() @@ -72,7 +73,7 @@ /obj/machinery/atmospherics/components/unary/thermomachine/attackby(obj/item/I, mob/user, params) if(!on) - if(default_deconstruction_screwdriver(user, icon_state_open, initial(icon_state), I)) + if(default_deconstruction_screwdriver(user, icon_state_open, icon_state_off, I)) return if(default_change_direction_wrench(user, I)) return @@ -157,6 +158,7 @@ /obj/machinery/atmospherics/components/unary/thermomachine/freezer name = "freezer" icon_state = "freezer" + icon_state_off = "freezer" icon_state_on = "freezer_1" icon_state_open = "freezer-o" max_temperature = T20C @@ -182,6 +184,7 @@ /obj/machinery/atmospherics/components/unary/thermomachine/heater name = "heater" icon_state = "heater" + icon_state_off = "heater" icon_state_on = "heater_1" icon_state_open = "heater-o" max_temperature = 140 //actual maximum temperature is defined by RefreshParts() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm index ced4855b1a..5b37242c78 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm @@ -337,6 +337,8 @@ if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) return + var/mob/signal_sender = signal.data["user"] + if("purge" in signal.data) pressure_checks &= ~EXT_BOUND pump_direction = SIPHONING @@ -352,7 +354,10 @@ on = !on if("checks" in signal.data) + var/old_checks = pressure_checks pressure_checks = text2num(signal.data["checks"]) + if(pressure_checks != old_checks) + investigate_log(" pressure checks were set to [pressure_checks] by [key_name(signal_sender)]",INVESTIGATE_ATMOS) if("checks_toggle" in signal.data) pressure_checks = (pressure_checks?0:NO_BOUND) @@ -361,10 +366,16 @@ pump_direction = text2num(signal.data["direction"]) if("set_internal_pressure" in signal.data) + var/old_pressure = internal_pressure_bound internal_pressure_bound = CLAMP(text2num(signal.data["set_internal_pressure"]),0,ONE_ATMOSPHERE*50) + if(old_pressure != internal_pressure_bound) + investigate_log(" internal pressure was set to [internal_pressure_bound] by [key_name(signal_sender)]",INVESTIGATE_ATMOS) if("set_external_pressure" in signal.data) + var/old_pressure = external_pressure_bound external_pressure_bound = CLAMP(text2num(signal.data["set_external_pressure"]),0,ONE_ATMOSPHERE*50) + if(old_pressure != external_pressure_bound) + investigate_log(" external pressure was set to [external_pressure_bound] by [key_name(signal_sender)]",INVESTIGATE_ATMOS) if("reset_external_pressure" in signal.data) external_pressure_bound = ONE_ATMOSPHERE diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm index 7a207cbed5..ee819d4f74 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm @@ -17,7 +17,6 @@ var/scrubbing = SCRUBBING //0 = siphoning, 1 = scrubbing var/filter_types = list(/datum/gas/carbon_dioxide) - var/volume_rate = 200 var/widenet = 0 //is this scrubber acting on the 3x3 area around it. var/list/turf/adjacent_turfs = list() @@ -43,6 +42,7 @@ ..() if(!id_tag) id_tag = assign_uid_vents() + for(var/f in filter_types) if(istext(f)) filter_types -= f @@ -118,7 +118,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/proc/broadcast_status() if(!radio_connection) return FALSE - + var/list/f_types = list() for(var/path in GLOB.meta_gas_info) var/list/gas = GLOB.meta_gas_info[path] @@ -140,8 +140,8 @@ if(!A.air_scrub_names[id_tag]) name = "\improper [A.name] air scrubber #[A.air_scrub_names.len + 1]" A.air_scrub_names[id_tag] = name - A.air_scrub_info[id_tag] = signal.data + A.air_scrub_info[id_tag] = signal.data radio_connection.post_signal(src, signal, radio_filter_out) return TRUE @@ -171,7 +171,6 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/proc/scrub(var/turf/tile) if(!istype(tile)) return FALSE - var/datum/gas_mixture/environment = tile.return_air() var/datum/gas_mixture/air_contents = airs[1] var/list/env_gases = environment.gases @@ -185,9 +184,11 @@ //Take a gas sample var/datum/gas_mixture/removed = tile.remove_air(transfer_moles) + //Nothing left to remove from the tile if(isnull(removed)) return FALSE + var/list/removed_gases = removed.gases //Filter it @@ -204,10 +205,9 @@ //Remix the resulting gases air_contents.merge(filtered_out) - tile.assume_air(removed) tile.air_update_turf() - + else //Just siphoning all air var/transfer_moles = environment.total_moles()*(volume_rate/environment.volume) @@ -221,7 +221,6 @@ return TRUE - //There is no easy way for an object to be notified of changes to atmos can pass flags // So we check every machinery process (2 seconds) /obj/machinery/atmospherics/components/unary/vent_scrubber/process() @@ -230,17 +229,19 @@ //we populate a list of turfs with nonatmos-blocked cardinal turfs AND // diagonal turfs that can share atmos with *both* of the cardinal turfs + /obj/machinery/atmospherics/components/unary/vent_scrubber/proc/check_turfs() adjacent_turfs.Cut() var/turf/T = get_turf(src) if(istype(T)) adjacent_turfs = T.GetAtmosAdjacentTurfs(alldir = 1) - /obj/machinery/atmospherics/components/unary/vent_scrubber/receive_signal(datum/signal/signal) if(!is_operational() || !signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command")) return 0 + var/mob/signal_sender = signal.data["user"] + if("power" in signal.data) on = text2num(signal.data["power"]) if("power_toggle" in signal.data) @@ -251,10 +252,13 @@ if("toggle_widenet" in signal.data) widenet = !widenet + var/old_scrubbing = scrubbing if("scrubbing" in signal.data) scrubbing = text2num(signal.data["scrubbing"]) if("toggle_scrubbing" in signal.data) scrubbing = !scrubbing + if(scrubbing != old_scrubbing) + investigate_log(" was toggled to [scrubbing ? "scrubbing" : "siphon"] mode by [key_name(signal_sender)]",INVESTIGATE_ATMOS) if("toggle_filter" in signal.data) filter_types ^= gas_id2path(signal.data["toggle_filter"]) diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index b0afefd58a..b10ff85ba0 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -40,11 +40,15 @@ return ..() /obj/machinery/meter/proc/reattach_to_layer() + var/obj/machinery/atmospherics/candidate for(var/obj/machinery/atmospherics/pipe/pipe in loc) if(pipe.piping_layer == target_layer) - target = pipe - setAttachLayer(pipe.piping_layer) - break + candidate = pipe + if(pipe.level == 2) + break + if(candidate) + target = candidate + setAttachLayer(candidate.piping_layer) /obj/machinery/meter/proc/setAttachLayer(var/new_layer) target_layer = new_layer @@ -140,6 +144,5 @@ // why are you yelling? /obj/machinery/meter/turf -/obj/machinery/meter/turf/Initialize() - . = ..() +/obj/machinery/meter/turf/reattach_to_layer() target = loc diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index 1771609125..87a7d43b37 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -50,6 +50,7 @@ "stimulum" = /obj/machinery/portable_atmospherics/canister/stimulum, "pluoxium" = /obj/machinery/portable_atmospherics/canister/pluoxium, "caution" = /obj/machinery/portable_atmospherics/canister, + "miasma" = /obj/machinery/portable_atmospherics/canister/miasma ) /obj/machinery/portable_atmospherics/canister/interact(mob/user) @@ -137,6 +138,13 @@ gas_type = /datum/gas/water_vapor filled = 1 +/obj/machinery/portable_atmospherics/canister/miasma + name = "miasma canister" + desc = "Miasma. Makes you wish your nose were blocked." + icon_state = "miasma" + gas_type = /datum/gas/miasma + filled = 1 + /obj/machinery/portable_atmospherics/canister/proc/get_time_left() if(timing) . = round(max(0, valve_timer - world.time) / 10, 1) diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm index 1a276d03c5..b575639246 100644 --- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm +++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm @@ -91,6 +91,7 @@ ..() if(holding) to_chat(user, "\The [src] contains [holding]. Alt-click [src] to remove it.") + to_chat(user, "Click [src] with another gas tank to hot swap [holding].") /obj/machinery/portable_atmospherics/proc/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank) if(holding) diff --git a/code/modules/awaymissions/away_props.dm b/code/modules/awaymissions/away_props.dm new file mode 100644 index 0000000000..2081077b90 --- /dev/null +++ b/code/modules/awaymissions/away_props.dm @@ -0,0 +1,30 @@ +/obj/effect/oneway + name = "one way effect" + desc = "Only lets things in from it's dir." + icon = 'icons/effects/mapping_helpers.dmi' + icon_state = "field_dir" + invisibility = INVISIBILITY_MAXIMUM + anchored = TRUE + +/obj/effect/oneway/CanPass(atom/movable/mover, turf/target) + var/turf/T = get_turf(src) + var/turf/MT = get_turf(mover) + return ..() && (T == MT || get_dir(MT,T) == dir) + + +/obj/effect/wind + name = "wind effect" + desc = "Creates pressure effect in it's direction. Use sparingly." + icon = 'icons/effects/mapping_helpers.dmi' + icon_state = "field_dir" + invisibility = INVISIBILITY_MAXIMUM + var/strength = 30 + +/obj/effect/wind/Initialize() + . = ..() + START_PROCESSING(SSobj,src) + +/obj/effect/wind/process() + var/turf/open/T = get_turf(src) + if(istype(T)) + T.consider_pressure_difference(get_step(T,dir),strength) \ No newline at end of file diff --git a/code/modules/awaymissions/capture_the_flag.dm b/code/modules/awaymissions/capture_the_flag.dm index 56f5d63b0c..3e6bf0315b 100644 --- a/code/modules/awaymissions/capture_the_flag.dm +++ b/code/modules/awaymissions/capture_the_flag.dm @@ -125,8 +125,12 @@ /proc/toggle_all_ctf(mob/user) var/ctf_enabled = FALSE + var/area/A for(var/obj/machinery/capture_the_flag/CTF in GLOB.machines) ctf_enabled = CTF.toggle_ctf() + A = get_area(CTF) + for(var/obj/machinery/power/emitter/E in A) + E.active = ctf_enabled message_admins("[key_name_admin(user)] has [ctf_enabled? "enabled" : "disabled"] CTF!") notify_ghosts("CTF has been [ctf_enabled? "enabled" : "disabled"]!",'sound/effects/ghost2.ogg') @@ -154,22 +158,11 @@ var/list/dead_barricades = list() - var/static/ctf_object_typecache var/static/arena_reset = FALSE var/static/list/people_who_want_to_play = list() /obj/machinery/capture_the_flag/Initialize() . = ..() - if(!ctf_object_typecache) - ctf_object_typecache = typecacheof(list( - /turf, - /mob, - /area, - /obj/machinery, - /obj/structure, - /obj/effect/ctf, - /obj/item/twohanded/ctf - )) GLOB.poi_list |= src /obj/machinery/capture_the_flag/Destroy() @@ -338,12 +331,20 @@ /obj/machinery/capture_the_flag/proc/reset_the_arena() var/area/A = get_area(src) + var/list/ctf_object_typecache = typecacheof(list( + /obj/machinery, + /obj/effect/ctf, + /obj/item/twohanded/ctf + )) for(var/atm in A) - if(!is_type_in_typecache(atm, ctf_object_typecache)) - qdel(atm) + if (isturf(A) || ismob(A) || isarea(A)) + continue if(isstructure(atm)) var/obj/structure/S = atm S.obj_integrity = S.max_integrity + else if(!is_type_in_typecache(atm, ctf_object_typecache)) + qdel(atm) + /obj/machinery/capture_the_flag/proc/stop_ctf() ctf_enabled = FALSE @@ -555,7 +556,7 @@ anchored = TRUE alpha = 255 -/obj/structure/trap/examine(mob/user) +/obj/structure/trap/ctf/examine(mob/user) return /obj/structure/trap/ctf/trap_effect(mob/living/L) @@ -606,10 +607,10 @@ /obj/effect/ctf/ammo/Crossed(atom/movable/AM) reload(AM) -/obj/effect/ctf/ammo/Collide(atom/movable/AM) +/obj/effect/ctf/ammo/Bump(atom/movable/AM) reload(AM) -/obj/effect/ctf/ammo/CollidedWith(atom/movable/AM) +/obj/effect/ctf/ammo/Bumped(atom/movable/AM) reload(AM) /obj/effect/ctf/ammo/proc/reload(mob/living/M) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 6e326530c4..a105fe49ca 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -26,10 +26,11 @@ var/assignedrole var/show_flavour = TRUE var/banType = "lavaland" + var/ghost_usable = TRUE //ATTACK GHOST IGNORING PARENT RETURN VALUE /obj/effect/mob_spawn/attack_ghost(mob/user) - if(!SSticker.HasRoundStarted() || !loc) + if(!SSticker.HasRoundStarted() || !loc || !ghost_usable) return if(!uses) to_chat(user, "This spawner is out of charges!") @@ -49,7 +50,7 @@ . = ..() if(instant || (roundstart && (mapload || (SSticker && SSticker.current_state > GAME_STATE_SETTING_UP)))) create() - else + else if(ghost_usable) GLOB.poi_list |= src LAZYADD(GLOB.mob_spawners[name], src) @@ -224,6 +225,9 @@ death = FALSE roundstart = FALSE //you could use these for alive fake humans on roundstart but this is more common scenario +/obj/effect/mob_spawn/human/corpse/delayed + ghost_usable = FALSE //These are just not-yet-set corpses. + instant = FALSE //Non-human spawners @@ -336,10 +340,10 @@ /obj/effect/mob_spawn/human/miner name = "Shaft Miner" - outfit = /datum/outfit/job/miner/asteroid + outfit = /datum/outfit/job/miner /obj/effect/mob_spawn/human/miner/rig - outfit = /datum/outfit/job/miner/equipped/asteroid + outfit = /datum/outfit/job/miner/equipped/hardsuit /obj/effect/mob_spawn/human/miner/explorer outfit = /datum/outfit/job/miner/equipped @@ -396,7 +400,7 @@ name = "lifeguard sleeper" id_job = "Lifeguard" uniform = /obj/item/clothing/under/shorts/red - + /datum/outfit/beachbum name = "Beach Bum" glasses = /obj/item/clothing/glasses/sunglasses @@ -554,17 +558,3 @@ shoes = /obj/item/clothing/shoes/sneakers/black suit = /obj/item/clothing/suit/armor/vest glasses = /obj/item/clothing/glasses/sunglasses/reagent - - -//Aliens for the alien nest space ruin. -/obj/effect/mob_spawn/alien/corpse/humanoid/drone - mob_type = /mob/living/carbon/alien/humanoid/drone - death = TRUE - name = "alien drone" - mob_name = "alien drone" - -/obj/effect/mob_spawn/alien/corpse/humanoid/queen - mob_type = /mob/living/carbon/alien/humanoid/royal/queen - death = TRUE - name = "alien queen" - mob_name = "alien queen" \ No newline at end of file diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index 5c3e87a367..2cdbb41d05 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -134,7 +134,7 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation) update_icon() //okay, here's the good teleporting stuff -/obj/machinery/gateway/centerstation/CollidedWith(atom/movable/AM) +/obj/machinery/gateway/centerstation/Bumped(atom/movable/AM) if(!active) return if(!detect()) @@ -210,7 +210,7 @@ GLOBAL_DATUM(the_gateway, /obj/machinery/gateway/centerstation) return TRUE return FALSE -/obj/machinery/gateway/centeraway/CollidedWith(atom/movable/AM) +/obj/machinery/gateway/centeraway/Bumped(atom/movable/AM) if(!detect()) return if(!active) diff --git a/code/modules/awaymissions/mission_code/challenge.dm b/code/modules/awaymissions/mission_code/challenge.dm index 0f28f6b3e0..673f13c067 100644 --- a/code/modules/awaymissions/mission_code/challenge.dm +++ b/code/modules/awaymissions/mission_code/challenge.dm @@ -28,7 +28,7 @@ idle_power_usage = 0 active_power_usage = 0 - active = 1 + active = TRUE locked = TRUE state = 2 diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm index b3018d9d1c..c4a4b167c0 100644 --- a/code/modules/awaymissions/mission_code/snowdin.dm +++ b/code/modules/awaymissions/mission_code/snowdin.dm @@ -149,11 +149,6 @@ //liquid plasma!!!!!!// -/turf/open/floor/plasteel/vault/snowdin - initial_gas_mix = "o2=22;n2=82;TEMP=180" - planetary_atmos = 1 - temperature = 180 - /turf/open/floor/plasteel/dark/snowdin initial_gas_mix = "o2=22;n2=82;TEMP=180" planetary_atmos = 1 @@ -265,7 +260,7 @@ /obj/item/paper/crumpled/ruins/snowdin/foreshadowing name = "scribbled note" info = {"Something's gone VERY wrong here. Jouslen has been mumbling about some weird shit in his cabin during the night and he seems always tired when we're working. I tried to confront him about it and he blew up on me, - telling me to mind my own business. I reported him to the officer, said he'd look into it. We only got another 2 months here before we're pulled for another assignment, so this shit can't go any quicker.."} + telling me to mind my own business. I reported him to the officer, said he'd look into it. We only got another 2 months here before we're pulled for another assignment, so this shit can't go any quicker..."} /obj/item/paper/crumpled/ruins/snowdin/misc1 name = "Mission Prologue" @@ -276,7 +271,7 @@ name = "scribbled note" info = {"If you're reading this: GET OUT! The mining go on here has unearthed something that was once-trapped by the layers of ice on this hell-hole. The overseer and Jouslen have gone missing. The officer is keeping the rest of us on lockdown and I swear to god I keep hearing strange noises outside the walls at night. The gateway link has gone dead and without a supply of resources from Central, we're left - for dead here. We haven't heard anything back from the mining squad either, so I can only assume whatever the fuck they unearthed got them first before coming for us. I don't want to die here.."} + for dead here. We haven't heard anything back from the mining squad either, so I can only assume whatever the fuck they unearthed got them first before coming for us. I don't want to die here..."} /obj/item/paper/fluff/awaymissions/snowdin/saw_usage name = "SAW Usage" @@ -563,6 +558,7 @@ armor = list("melee" = 20, "bullet" = 10, "laser" = 0,"energy" = 5, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 25, "acid" = 25) cold_protection = CHEST|GROIN|ARMS|LEGS min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + /obj/item/clothing/shoes/combat/coldres name = "insulated combat boots" desc = "High speed, low drag combat boots, now with an added layer of insulation." @@ -595,8 +591,8 @@ death = FALSE faction = ROLE_SYNDICATE outfit = /datum/outfit/snowsyndie - flavour_text = {"You are a syndicate operative recently awoken from cyrostatis in an underground outpost. Monitor Nanotrasen communications and record information. All intruders should be - disposed of swirfly to assure no gathered information is stolen or lost. Try not to wander too far from the outpost as the caves can be a deadly place even for a trained operative such as yourself."} + flavour_text = "You are a syndicate operative recently awoken from cryostasis in an underground outpost. Monitor Nanotrasen communications and record information. All intruders should be \ + disposed of swiftly to assure no gathered information is stolen or lost. Try not to wander too far from the outpost as the caves can be a deadly place even for a trained operative such as yourself." /datum/outfit/snowsyndie name = "Syndicate Snow Operative" diff --git a/code/modules/awaymissions/mission_code/spacebattle.dm b/code/modules/awaymissions/mission_code/spacebattle.dm index 5084663f5f..a477a223b2 100644 --- a/code/modules/awaymissions/mission_code/spacebattle.dm +++ b/code/modules/awaymissions/mission_code/spacebattle.dm @@ -40,3 +40,12 @@ /area/awaymission/spacebattle/secret name = "Hidden Chamber" icon_state = "awaycontent10" + +/mob/living/simple_animal/hostile/syndicate/ranged/spacebattle + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, + /obj/item/gun/ballistic/automatic/c20r, + /obj/item/shield/energy) + +/mob/living/simple_animal/hostile/syndicate/melee/spacebattle + deathmessage = "falls limp as they release their grip from the energy weapons, activating their self-destruct function!" + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) diff --git a/code/modules/awaymissions/mission_code/stationCollision.dm b/code/modules/awaymissions/mission_code/stationCollision.dm index e9c2b993fa..232ffe5754 100644 --- a/code/modules/awaymissions/mission_code/stationCollision.dm +++ b/code/modules/awaymissions/mission_code/stationCollision.dm @@ -11,7 +11,7 @@ * Guns * Safe code hints * Captain's safe - * Modified Nar-Sie + * Modified Nar'Sie */ @@ -134,7 +134,7 @@ GLOBAL_VAR_INIT(sc_safecode5, "[rand(0,9)]") new /obj/item/stack/ore/diamond(src) /* - * Modified Nar-Sie + * Modified Nar'Sie */ /obj/singularity/narsie/mini desc = "Your body becomes weak and your feel your mind slipping away as you try to comprehend what you know can't be possible." diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm index acb329b5c5..4971d82053 100644 --- a/code/modules/awaymissions/mission_code/wildwest.dm +++ b/code/modules/awaymissions/mission_code/wildwest.dm @@ -132,9 +132,9 @@ var/triggered = 0 /obj/effect/meatgrinder/Crossed(atom/movable/AM) - CollidedWith(AM) + Bumped(AM) -/obj/effect/meatgrinder/CollidedWith(atom/movable/AM) +/obj/effect/meatgrinder/Bumped(atom/movable/AM) if(triggered) return diff --git a/code/modules/cargo/bounties/assistant.dm b/code/modules/cargo/bounties/assistant.dm index 08bd74acad..cf4efaa8c6 100644 --- a/code/modules/cargo/bounties/assistant.dm +++ b/code/modules/cargo/bounties/assistant.dm @@ -13,7 +13,7 @@ /datum/bounty/item/assistant/skateboard name = "Skateboard" - description = "Nanotrasen has determined walking to be a wasteful. Ship a skateboard to CentCom to speed operations up." + description = "Nanotrasen has determined walking to be wasteful. Ship a skateboard to CentCom to speed operations up." reward = 900 // the tony hawk wanted_types = list(/obj/vehicle/ridden/scooter/skateboard) @@ -95,7 +95,7 @@ description = "Central Command will be holding a business convention this year. Ship a few briefcases in support." reward = 2500 required_count = 5 - wanted_types = list(/obj/item/storage/briefcase) + wanted_types = list(/obj/item/storage/briefcase, /obj/item/storage/secure/briefcase) /datum/bounty/item/assistant/sunglasses name = "Sunglasses" @@ -104,43 +104,12 @@ required_count = 2 wanted_types = list(/obj/item/clothing/glasses/sunglasses) -/datum/bounty/item/assistant/gondola_hide - name = "Gondola Hide" - description = "Central Command has recently learned of strange creatures called Gondolas. If you catch one, ship its hide back to CentCom." - reward = 5000 - wanted_types = list(/obj/item/stack/sheet/animalhide/gondola) - /datum/bounty/item/assistant/monkey_hide name = "Monkey Hide" description = "One of the scientists at CentCom is interested in testing products on monkey skin. Your mission is to acquire monkey's hide and ship it." reward = 1500 wanted_types = list(/obj/item/stack/sheet/animalhide/monkey) -/datum/bounty/item/assistant/heart - name = "Heart" - description = "Commander Johnson is in critical condition after suffering a heart attack. Doctors say he needs a new heart fast. Ship one, pronto!" - reward = 3000 - wanted_types = list(/obj/item/organ/heart) - -/datum/bounty/item/assistant/lung - name = "Lungs" - description = "A recent explosion at Central Command has left multiple staff with punctured lungs. Ship spare lungs to be rewarded." - reward = 3000 - required_count = 1 - wanted_types = list(/obj/item/organ/lungs) - -/datum/bounty/item/assistant/appendix - name = "Appendix" - description = "Chef Gibb of Central Command wants to prepare a meal using a very special delicacy: an appendix. If you ship one, he'll pay." - reward = 3000 - wanted_types = list(/obj/item/organ/appendix) - -/datum/bounty/item/assistant/lizard_tail - name = "Lizard Tail" - description = "The Wizard Federation has made off with Nanotrasen's supply of lizard tails. While CentCom is dealing with the wizards, can the station spare a tail of their own?" - reward = 3000 - wanted_types = list(/obj/item/organ/tail/lizard) - /datum/bounty/item/assistant/shard name = "Shards" description = "A killer clown has been stalking CentCom, and staff have been unable to catch her because she's not wearing shoes. Please ship some shards so that a booby trap can be constructed." @@ -150,23 +119,11 @@ /datum/bounty/item/assistant/comfy_chair name = "Comfy Chairs" - description = "Commander Pat is unhappy with his chair. He claims it hurts his back. Ship some alternatives out to humor him. " + description = "Commander Pat is unhappy with his chair. He claims it hurts his back. Ship some alternatives out to humor him." reward = 1500 required_count = 5 wanted_types = list(/obj/structure/chair/comfy) -/datum/bounty/item/assistant/revolver - name = "Revolver" - description = "Captain Johann of station 12 has challenged Captain Vic of station 11 to a duel. He's asked for help securing an appropriate revolver to use." - reward = 2000 - wanted_types = list(/obj/item/gun/ballistic/revolver) - -/datum/bounty/item/assistant/hand_tele - name = "Hand Tele" - description = "Central Command has come up with a genius idea: Why not teleport cargo rather than ship it? Send over a hand tele, receive payment, then wait 6-8 years while they deliberate." - reward = 2000 - wanted_types = list(/obj/item/hand_tele) - /datum/bounty/item/assistant/geranium name = "Geraniums" description = "Commander Zot has the hots for Commander Zena. Send a shipment of geraniums - her favorite flower - and he'll happily reward you." @@ -230,7 +187,7 @@ /datum/bounty/item/assistant/bonfire name = "Lit Bonfire" - description = "Space heaters are malfunctioning and the cargo crew of Central Command is starting to feel cold. Ship a lit bonfire to warm them up." + description = "Space heaters are malfunctioning and the cargo crew of Central Command is starting to feel cold. Ship a lit bonfire to warm them up." reward = 5000 wanted_types = list(/obj/structure/bonfire) @@ -240,31 +197,21 @@ var/obj/structure/bonfire/B = O return !!B.burning -/datum/bounty/item/assistant/plasma_tank - name = "Full Tank of Plasma" - description = "Station 12 has requested supplies to set up a singularity engine. In particular, they request 28 moles of plasma." - reward = 2500 - wanted_types = list(/obj/item/tank) - var/moles_required = 20 // A full tank is 28 moles, but CentCom ignores that fact. - -/datum/bounty/item/assistant/plasma_tank/applies_to(obj/O) - if(!..()) - return FALSE - var/obj/item/tank/T = O - if(!T.air_contents.gases[/datum/gas/plasma]) - return FALSE - return T.air_contents.gases[/datum/gas/plasma][MOLES] >= moles_required - /datum/bounty/item/assistant/corgimeat name = "Raw Corgi Meat" description = "The Syndicate recently stole all of CentCom's corgi meat. Ship out a replacement immediately." reward = 3000 wanted_types = list(/obj/item/reagent_containers/food/snacks/meat/slab/corgi) -/datum/bounty/item/chef/action_figures +/datum/bounty/item/assistant/action_figures name = "Action Figures" description = "The vice president's son saw an ad for action figures on the telescreen and now he won't shut up about them. Ship some to ease his complaints." reward = 4000 required_count = 5 wanted_types = list(/obj/item/toy/figure) +/datum/bounty/item/assistant/tail_whip + name = "Nine Tails whip" + description = "Commander Jackson is looking for a fine addition to her exotic weapons collection. She will reward you handsomely for either a Cat or Liz o' Nine Tails." + reward = 4000 + wanted_types = list(/obj/item/melee/chainofcommand/tailwhip) diff --git a/code/modules/cargo/bounties/botany.dm b/code/modules/cargo/bounties/botany.dm new file mode 100644 index 0000000000..34906b321a --- /dev/null +++ b/code/modules/cargo/bounties/botany.dm @@ -0,0 +1,194 @@ +/datum/bounty/item/botany + reward = 5000 + var/datum/bounty/item/botany/multiplier = 0 //adds bonus reward money; increased for higher tier or rare mutations + var/datum/bounty/item/botany/bonus_desc //for adding extra flavor text to bounty descriptions + var/datum/bounty/item/botany/foodtype = "meal" //same here + +/datum/bounty/item/botany/New() + ..() + description = "Central Command's head chef is looking to prepare a fine [foodtype] with [name]. [bonus_desc]" + reward += multiplier * 1000 + required_count = rand(5, 10) + +/datum/bounty/item/botany/ambrosia_vulgaris + name = "Ambrosia Vulgaris Leaves" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/ambrosia/vulgaris) + foodtype = "stew" + +/datum/bounty/item/botany/ambrosia_gaia + name = "Ambrosia Gaia Leaves" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/ambrosia/gaia) + multiplier = 4 + foodtype = "stew" + +/datum/bounty/item/botany/apple_golden + name = "Golden Apples" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/apple/gold) + multiplier = 4 + foodtype = "dessert" + +/datum/bounty/item/botany/banana + name = "Bananas" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/banana) + exclude_types = list(/obj/item/reagent_containers/food/snacks/grown/banana/bluespace) + foodtype = "banana split" + +/datum/bounty/item/botany/banana_bluespace + name = "Bluespace Bananas" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/banana/bluespace) + multiplier = 2 + foodtype = "banana split" + +/datum/bounty/item/botany/beans_koi + name = "Koi Beans" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/koibeans) + multiplier = 2 + +/datum/bounty/item/botany/berries_death + name = "Death Berries" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/berries/death) + multiplier = 2 + bonus_desc = "He insists that \"he knows what he's doing\"." + foodtype = "sorbet" + +/datum/bounty/item/botany/berries_glow + name = "Glow-Berries" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/berries/glow) + multiplier = 2 + foodtype = "sorbet" + +/datum/bounty/item/botany/cannabis + name = "Cannabis Leaves" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/cannabis) + exclude_types = list(/obj/item/reagent_containers/food/snacks/grown/cannabis/white, /obj/item/reagent_containers/food/snacks/grown/cannabis/death, /obj/item/reagent_containers/food/snacks/grown/cannabis/ultimate) + multiplier = 4 //hush money + bonus_desc = "Do not mention this shipment to security." + foodtype = "\"meal\"" + +/datum/bounty/item/botany/cannabis_white + name = "Lifeweed Leaves" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/cannabis/white) + multiplier = 6 + bonus_desc = "Do not mention this shipment to security." + foodtype = "\"meal\"" + +/datum/bounty/item/botany/cannabis_death + name = "Deathweed Leaves" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/cannabis/death) + multiplier = 6 + bonus_desc = "Do not mention this shipment to security." + foodtype = "\"meal\"" + +/datum/bounty/item/botany/cannabis_ultimate + name = "Omega Weed Leaves" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/cannabis/ultimate) + multiplier = 6 + bonus_desc = "Under no circumstances mention this shipment to security." + foodtype = "\"meal\"" + +/datum/bounty/item/botany/wheat + name = "Wheat Grains" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/wheat) + +/datum/bounty/item/botany/rice + name = "Rice Grains" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/rice) + +/datum/bounty/item/botany/chili + name = "Chili Peppers" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/chili) + +/datum/bounty/item/botany/chili + name = "Ice Chili Peppers" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/icepepper) + multiplier = 2 + +/datum/bounty/item/botany/chili + name = "Ghost Chili Peppers" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/ghost_chili) + multiplier = 2 + +/datum/bounty/item/botany/citrus_lime + name = "Limes" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/citrus/lime) + foodtype = "sorbet" + +/datum/bounty/item/botany/citrus_lemon + name = "Lemons" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/citrus/lemon) + foodtype = "sorbet" + +/datum/bounty/item/botany/citrus_oranges + name = "Oranges" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/citrus/orange) + bonus_desc = "Do not ship lemons or limes." //I vanted orahnge! + foodtype = "sorbet" + +/datum/bounty/item/botany/eggplant + name = "Eggplants" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/eggplant) + bonus_desc = "Not to be confused with egg-plants." + +/datum/bounty/item/botany/eggplant_eggy + name = "Egg-plants" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/shell/eggy) + bonus_desc = "Not to be confused with eggplants." + multiplier = 2 + +/datum/bounty/item/botany/kudzu + name = "Kudzu Pods" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/kudzupod) + bonus_desc = "Store in a dry, dark place." + multiplier = 4 + +/datum/bounty/item/botany/watermelon + name = "Watermelons" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/watermelon) + foodtype = "dessert" + +/datum/bounty/item/botany/watermelon_holy + name = "Holy Melons" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/holymelon) + multiplier = 2 + foodtype = "dessert" + +/datum/bounty/item/botany/glowshroom + name = "Glowshrooms" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom) + exclude_types = list(/obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom/glowcap, /obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom/shadowshroom) + foodtype = "omelet" + +/datum/bounty/item/botany/glowshroom_cap + name = "Glowcaps" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom/glowcap) + multiplier = 2 + foodtype = "omelet" + +/datum/bounty/item/botany/glowshroom_shadow + name = "Shadowshrooms" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom/shadowshroom) + multiplier = 2 + foodtype = "omelet" + +/datum/bounty/item/botany/nettles_death + name = "Death Nettles" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/nettle/death) + multiplier = 2 + bonus_desc = "Wear protection when handling them." + foodtype = "cheese" + +/datum/bounty/item/botany/pineapples + name = "Pineapples" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/pineapple) + bonus_desc = "Not for human consumption." + foodtype = "ashtray" + +/datum/bounty/item/botany/tomato + name = "Tomatoes" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/tomato) + exclude_types = list(/obj/item/reagent_containers/food/snacks/grown/tomato/blue) + +/datum/bounty/item/botany/tomato_bluespace + name = "Bluespace Tomatoes" + wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/tomato/blue/bluespace) + multiplier = 4 diff --git a/code/modules/cargo/bounties/chef.dm b/code/modules/cargo/bounties/chef.dm index da296911e7..b9d9ebc184 100644 --- a/code/modules/cargo/bounties/chef.dm +++ b/code/modules/cargo/bounties/chef.dm @@ -82,13 +82,6 @@ reward = 8000 wanted_types = list(/obj/item/reagent_containers/food/snacks/hotdog) -/datum/bounty/item/chef/lemon - name = "Lemons" - description = "A commander claims he can turn lemons into money. Ship him a few and he'll deposit the money into the station's account." - reward = 4444 - required_count = 10 - wanted_types = list(/obj/item/reagent_containers/food/snacks/grown/citrus/lemon) - /datum/bounty/item/chef/eggplantparm name = "Eggplant Parmigianas" description = "A famous singer will be arriving at CentCom, and their contract demands that they only be served Eggplant Parmigiana. Ship some, please!" diff --git a/code/modules/cargo/bounties/engineering.dm b/code/modules/cargo/bounties/engineering.dm new file mode 100644 index 0000000000..8b73ebac4b --- /dev/null +++ b/code/modules/cargo/bounties/engineering.dm @@ -0,0 +1,31 @@ +/datum/bounty/item/engineering/gas + name = "Full Tank of Pluoxium" + description = "CentCom RnD is researching extra compact internals. Ship us a tank full of Pluoxium and you'll be compensated." + reward = 7500 + wanted_types = list(/obj/item/tank) + var/moles_required = 20 // A full tank is 28 moles, but CentCom ignores that fact. + var/gas_type = /datum/gas/pluoxium + +/datum/bounty/item/engineering/gas/applies_to(obj/O) + if(!..()) + return FALSE + var/obj/item/tank/T = O + if(!T.air_contents.gases[gas_type]) + return FALSE + return T.air_contents.gases[gas_type][MOLES] >= moles_required + +/datum/bounty/item/engineering/gas/nitryl_tank + name = "Full Tank of Nitryl" + description = "The non-human staff of Station 88 has been volunteered to test performance enhancing drugs. Ship them a tank full of Nitryl so they can get started." + gas_type = /datum/gas/nitryl + +/datum/bounty/item/engineering/gas/tritium_tank + name = "Full Tank of Tritium" + description = "Station 49 is looking to kickstart their research program. Ship them a tank full of Tritium." + gas_type = /datum/gas/tritium + +/datum/bounty/item/engineering/energy_ball + name = "Contained Tesla Ball" + description = "Station 24 is being overrun by hordes of angry Mothpeople. They are requesting the ultimate bug zapper." + reward = 75000 //requires 14k credits of purchases, not to mention cooperation with engineering/heads of staff to set up inside the cramped shuttle + wanted_types = list(/obj/singularity/energy_ball) diff --git a/code/modules/cargo/bounties/mech.dm b/code/modules/cargo/bounties/mech.dm index c331e97266..b7eac3499f 100644 --- a/code/modules/cargo/bounties/mech.dm +++ b/code/modules/cargo/bounties/mech.dm @@ -17,6 +17,12 @@ name = "APLU \"Ripley\"" reward = 13000 wanted_types = list(/obj/mecha/working/ripley) + exclude_types = list(/obj/mecha/working/ripley/firefighter) + +/datum/bounty/item/mech/firefighter + name = "APLU \"Firefighter\"" + reward = 18000 + wanted_types = list(/obj/mecha/working/ripley/firefighter) /datum/bounty/item/mech/odysseus name = "Odysseus" @@ -37,4 +43,3 @@ name = "Phazon" reward = 50000 wanted_types = list(/obj/mecha/combat/phazon) - diff --git a/code/modules/cargo/bounties/medical.dm b/code/modules/cargo/bounties/medical.dm new file mode 100644 index 0000000000..d139775969 --- /dev/null +++ b/code/modules/cargo/bounties/medical.dm @@ -0,0 +1,59 @@ +/datum/bounty/item/medical/heart + name = "Heart" + description = "Commander Johnson is in critical condition after suffering yet another heart attack. Doctors say he needs a new heart fast. Ship one, pronto!" + reward = 3000 + wanted_types = list(/obj/item/organ/heart) + +/datum/bounty/item/medical/lung + name = "Lungs" + description = "A recent explosion at Central Command has left multiple staff with punctured lungs. Ship spare lungs to be rewarded." + reward = 10000 + required_count = 3 + wanted_types = list(/obj/item/organ/lungs) + +/datum/bounty/item/medical/appendix + name = "Appendix" + description = "Chef Gibb of Central Command wants to prepare a meal using a very special delicacy: an appendix. If you ship one, he'll pay." + reward = 5000 //there are no synthetic appendixes + wanted_types = list(/obj/item/organ/appendix) + +/datum/bounty/item/medical/ears + name = "Ears" + description = "Multiple staff at Station 12 have been left deaf due to unauthorized clowning. Ship them new ears." + reward = 10000 + required_count = 3 + wanted_types = list(/obj/item/organ/ears) + +/datum/bounty/item/medical/liver + name = "Livers" + description = "Multiple high-ranking CentCom diplomats have been hospitalized with liver failure after a recent meeting with Third Soviet Union ambassadors. Help us out, will you?" + reward = 10000 + required_count = 3 + wanted_types = list(/obj/item/organ/liver) + +/datum/bounty/item/medical/eye + name = "Organic Eyes" + description = "Station 5's Research Director Willem is requesting a few pairs of non-robotic eyes. Don't ask questions, just ship them." + reward = 10000 + required_count = 3 + wanted_types = list(/obj/item/organ/eyes) + exclude_types = list(/obj/item/organ/eyes/robotic) + +/datum/bounty/item/medical/tongue + name = "Tongues" + description = "A recent attack by Mime extremists has left staff at Station 23 speechless. Ship some spare tongues." + reward = 10000 + required_count = 3 + wanted_types = list(/obj/item/organ/tongue) + +/datum/bounty/item/medical/lizard_tail + name = "Lizard Tail" + description = "The Wizard Federation has made off with Nanotrasen's supply of lizard tails. While CentCom is dealing with the wizards, can the station spare a tail of their own?" + reward = 3000 + wanted_types = list(/obj/item/organ/tail/lizard) + +/datum/bounty/item/medical/cat_tail + name = "Cat Tail" + description = "Central Command has run out of heavy duty pipe cleaners. Can you ship over a cat tail to help us out?" + reward = 3000 + wanted_types = list(/obj/item/organ/tail/cat) diff --git a/code/modules/cargo/bounties/mining.dm b/code/modules/cargo/bounties/mining.dm new file mode 100644 index 0000000000..1b8b46734f --- /dev/null +++ b/code/modules/cargo/bounties/mining.dm @@ -0,0 +1,51 @@ +/datum/bounty/item/mining/goliath_steaks + name = "Lava-Cooked Goliath Steaks" + description = "Admiral Pavlov has gone on hunger strike ever since the canteen started serving only monkey and monkey byproducts. She is demanding lava-cooked Goliath steaks." + reward = 5000 + required_count = 3 + wanted_types = list(/obj/item/reagent_containers/food/snacks/meat/steak/goliath) + +/datum/bounty/item/mining/goliath_boat + name = "Goliath Hide Boat" + description = "Commander Menkov wants to participate in the annual Lavaland Regatta. He is asking your shipwrights to build the swiftest boat known to man." + reward = 10000 + wanted_types = list(/obj/vehicle/ridden/lavaboat) + +/datum/bounty/item/mining/bone_oar + name = "Bone Oars" + description = "Commander Menkov requires oars to participate in the annual Lavaland Regatta. Ship a pair over." + reward = 4000 + required_count = 2 + wanted_types = list(/obj/item/oar) + +/datum/bounty/item/mining/bone_axe + name = "Bone Axe" + description = "Station 12 has had their fire axes stolen by marauding clowns. Ship them a bone axe as a replacement." + reward = 7500 + wanted_types = list(/obj/item/twohanded/fireaxe/boneaxe) + +/datum/bounty/item/mining/bone_armor + name = "Bone Armor" + description = "Station 14 has volunteered their lizard crew for ballistic armor testing. Ship over some bone armor." + reward = 5000 + wanted_types = list(/obj/item/clothing/suit/armor/bone) + +/datum/bounty/item/mining/skull_helmet + name = "Skull Helmet" + description = "Station 42's Head of Security has her birthday tomorrow! We want to suprise her with a fashionable skull helmet." + reward = 4000 + wanted_types = list(/obj/item/clothing/head/helmet/skull) + +/datum/bounty/item/mining/bone_talisman + name = "Bone Talismans" + description = "Station 14's Research Director claims that pagan bone talismans protect their wearer. Ship them a few so they can start testing." + reward = 7500 + required_count = 3 + wanted_types = list(/obj/item/clothing/accessory/talisman) + +/datum/bounty/item/mining/bone_dagger + name = "Bone Daggers" + description = "Central Command's canteen is undergoing budget cuts. Ship over some bone daggers so our Chef can keep working." + reward = 5000 + required_count = 3 + wanted_types = list(/obj/item/kitchen/knife/combat/bone) diff --git a/code/modules/cargo/bounties/science.dm b/code/modules/cargo/bounties/science.dm index a2b8676ce0..33f334ac47 100644 --- a/code/modules/cargo/bounties/science.dm +++ b/code/modules/cargo/bounties/science.dm @@ -45,7 +45,7 @@ name = "Diamond Mining Drill" description = "Central Command is willing to pay three months salary in exchange for one diamond mining drill." reward = 15000 - wanted_types = list(/obj/item/mecha_parts/mecha_equipment/drill/diamonddrill) + wanted_types = list(/obj/item/pickaxe/drill/diamonddrill, /obj/item/mecha_parts/mecha_equipment/drill/diamonddrill) /datum/bounty/item/science/floor_buffer name = "Floor Buffer Upgrade" @@ -53,12 +53,6 @@ reward = 10000 wanted_types = list(/obj/item/janiupgrade) -/datum/bounty/item/science/flightsuit - name = "Flight Suit" - description = "According to all known laws of physics, flight suits are cool. CentCom will pay at a premium for them, so get shipping!" - reward = 30000 - wanted_types = list(/obj/item/clothing/suit/space/hardsuit/flightsuit) - /datum/bounty/item/science/advanced_mop name = "Advanced Mop" description = "Excuse me. I'd like to request $17 for a push broom rebristling. Either that, or an advanced mop." diff --git a/code/modules/cargo/bounties/security.dm b/code/modules/cargo/bounties/security.dm index d4ef29e194..bcf7b89f3a 100644 --- a/code/modules/cargo/bounties/security.dm +++ b/code/modules/cargo/bounties/security.dm @@ -1,21 +1,3 @@ -/datum/bounty/item/security/headset - name = "Security Headset" - description = "Nanotrasen wants to ensure that their encryption is working correctly. Ship them a security headset so that they can check." - reward = 800 - wanted_types = list(/obj/item/radio/headset/headset_sec, /obj/item/radio/headset/heads/hos) - -/datum/bounty/item/security/securitybelt - name = "Security Belt" - description = "CentCom is having difficulties with their security belts. Ship one from the station to receive compensation." - reward = 800 - wanted_types = list(/obj/item/storage/belt/security) - -/datum/bounty/item/security/sechuds - name = "Security HUDSunglasses" - description = "CentCom screwed up and ordered the wrong type of security sunglasses. They request the station ship some of theirs." - reward = 800 - wanted_types = list(/obj/item/clothing/glasses/hud/security/sunglasses) - /datum/bounty/item/security/riotshotgun name = "Riot Shotguns" description = "Hooligans have boarded CentCom! Ship riot shotguns quick, or things are going to get dirty." @@ -23,40 +5,9 @@ required_count = 2 wanted_types = list(/obj/item/gun/ballistic/shotgun/riot) -/datum/bounty/item/security/pinpointer - name = "Nuclear Pinpointer" - description = "There's a teeny-tiny itty-bitty chance CentCom may have lost a nuke disk. Can the station spare a pinpointer to help out?" - reward = 1500 - wanted_types = list(/obj/item/pinpointer/nuke) - -/datum/bounty/item/security/captains_spare - name = "Captain's Spare" - description = "Captain Bart of Station 12 has forgotten his ID! Ship him your station's spare, would you?" - reward = 1500 - wanted_types = list(/obj/item/card/id/captains_spare) - -/datum/bounty/item/security/hardsuit - name = "Security Hardsuit" - description = "Space pirates are heading towards CentCom! Quick! Ship a security hardsuit to aid the fight!" - reward = 2000 - wanted_types = list(/obj/item/clothing/suit/space/hardsuit/security) - -/datum/bounty/item/security/krav_maga - name = "Krav Maga Gloves" - description = "Chef Howerwitz of CentCom is trying to take a kung-fu Pizza out of the oven, but his mitts aren't up to the task. Ship them a pair of Krav Maga gloves to do the job right." - reward = 2000 - wanted_types = list(/obj/item/clothing/gloves/krav_maga) - /datum/bounty/item/security/recharger name = "Rechargers" description = "Nanotrasen military academy is conducting marksmanship exercises. They request that rechargers be shipped." reward = 2000 required_count = 3 wanted_types = list(/obj/machinery/recharger) - -/datum/bounty/item/security/sabre - name = "Officer's Sabre" - description = "A 3-hour LARP session will be held at CentCom in the upcoming months. A shipped officer's sabre would make a good prop." - reward = 2500 - wanted_types = list(/obj/item/melee/sabre) - diff --git a/code/modules/cargo/bounties/special.dm b/code/modules/cargo/bounties/special.dm index 42c9ab8202..adffaf999a 100644 --- a/code/modules/cargo/bounties/special.dm +++ b/code/modules/cargo/bounties/special.dm @@ -8,7 +8,7 @@ /datum/bounty/item/syndicate_documents name = "Syndicate Documents" description = "Intel regarding the syndicate is highly prized at CentCom. If you find syndicate documents, ship them. You could save lives." - reward = 10000 + reward = 15000 wanted_types = list(/obj/item/documents/syndicate, /obj/item/documents/photocopy) /datum/bounty/item/syndicate_documents/applies_to(obj/O) @@ -19,6 +19,13 @@ return (Copy.copy_type && ispath(Copy.copy_type, /obj/item/documents/syndicate)) return TRUE +/datum/bounty/item/adamantine + name = "Adamantine" + description = "Nanotrasen's anomalous materials division is in desparate need for Adamantine. Send them a large shipment and we'll make it worth your while." + reward = 35000 + required_count = 10 + wanted_types = list(/obj/item/stack/sheet/mineral/adamantine) + /datum/bounty/more_bounties name = "More Bounties" description = "Complete enough bounties and CentCom will issue new ones!" diff --git a/code/modules/cargo/bounty.dm b/code/modules/cargo/bounty.dm index 6153fd7678..6cc05f56df 100644 --- a/code/modules/cargo/bounty.dm +++ b/code/modules/cargo/bounty.dm @@ -75,7 +75,7 @@ GLOBAL_LIST_EMPTY(bounties_list) // Returns a new bounty of random type, but does not add it to GLOB.bounties_list. /proc/random_bounty() - switch(rand(1, 9)) + switch(rand(1, 13)) if(1) var/subtype = pick(subtypesof(/datum/bounty/item/assistant)) return new subtype @@ -103,40 +103,70 @@ GLOBAL_LIST_EMPTY(bounties_list) if(9) var/subtype = pick(subtypesof(/datum/bounty/item/slime)) return new subtype + if(10) + var/subtype = pick(subtypesof(/datum/bounty/item/engineering)) + return new subtype + if(11) + var/subtype = pick(subtypesof(/datum/bounty/item/mining)) + return new subtype + if(12) + var/subtype = pick(subtypesof(/datum/bounty/item/medical)) + return new subtype + if(13) + var/subtype = pick(subtypesof(/datum/bounty/item/botany)) + return new subtype // Called lazily at startup to populate GLOB.bounties_list with random bounties. /proc/setup_bounties() - for(var/i = 0; i < 3; ++i) - var/subtype = pick(subtypesof(/datum/bounty/item/assistant)) - try_add_bounty(new subtype) - - for(var/i = 0; i < 1; ++i) - var/list/subtype = pick(subtypesof(/datum/bounty/item/mech)) - try_add_bounty(new subtype) - - for(var/i = 0; i < 2; ++i) - var/list/subtype = pick(subtypesof(/datum/bounty/item/chef)) - try_add_bounty(new subtype) - - for(var/i = 0; i < 1; ++i) - var/list/subtype = pick(subtypesof(/datum/bounty/item/security)) - try_add_bounty(new subtype) - - try_add_bounty(new /datum/bounty/reagent/simple_drink) - try_add_bounty(new /datum/bounty/reagent/complex_drink) - try_add_bounty(new /datum/bounty/reagent/chemical) - - for(var/i = 0; i < 1; ++i) - var/list/subtype = pick(subtypesof(/datum/bounty/virus)) - try_add_bounty(new subtype) + var/pick // instead of creating it a bunch let's go ahead and toss it here, we know we're going to use it for dynamics and subtypes! + + /********************************Subtype Gens********************************/ + var/list/easy_add_list_subtypes = list(/datum/bounty/item/assistant = 2, + /datum/bounty/item/mech = 1, + /datum/bounty/item/chef = 2, + /datum/bounty/item/security = 1, + /datum/bounty/virus = 1, + /datum/bounty/item/engineering = 1, + /datum/bounty/item/mining = 2, + /datum/bounty/item/medical = 2, + /datum/bounty/item/botany = 2) + + for(var/the_type in easy_add_list_subtypes) + for(var/i in 1 to easy_add_list_subtypes[the_type]) + pick = pick(subtypesof(the_type)) + try_add_bounty(new pick) + + /********************************Strict Type Gens********************************/ + var/list/easy_add_list_strict_types = list(/datum/bounty/reagent/simple_drink = 1, + /datum/bounty/reagent/complex_drink = 1, + /datum/bounty/reagent/chemical = 1) + + for(var/the_strict_type in easy_add_list_strict_types) + for(var/i in 1 to easy_add_list_strict_types[the_strict_type]) + try_add_bounty(new the_strict_type) + + /********************************Dynamic Gens********************************/ + + for(var/i in 0 to 1) + if(prob(50)) + pick = pick(subtypesof(/datum/bounty/item/slime)) + else + pick = pick(subtypesof(/datum/bounty/item/science)) + try_add_bounty(new pick) + + /********************************Cutoff for Non-Low Priority Bounties********************************/ var/datum/bounty/B = pick(GLOB.bounties_list) B.mark_high_priority() - // Generate these last so they can't be high priority. - try_add_bounty(new /datum/bounty/item/alien_organs) - try_add_bounty(new /datum/bounty/item/syndicate_documents) - try_add_bounty(new /datum/bounty/more_bounties) + /********************************Low Priority Gens********************************/ + var/list/low_priority_strict_type_list = list( /datum/bounty/item/alien_organs, + /datum/bounty/item/syndicate_documents, + /datum/bounty/item/adamantine, + /datum/bounty/more_bounties) + + for(var/low_priority_bounty in low_priority_strict_type_list) + try_add_bounty(new low_priority_bounty) /proc/completed_bounty_count() var/count = 0 diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm index f0446b9771..b5b9a616d0 100644 --- a/code/modules/cargo/console.dm +++ b/code/modules/cargo/console.dm @@ -28,6 +28,13 @@ else obj_flags &= ~EMAGGED +/obj/machinery/computer/cargo/proc/get_export_categories() + var/cat = EXPORT_CARGO + if(contraband) + cat |= EXPORT_CONTRABAND + if(obj_flags & EMAGGED) + cat |= EXPORT_EMAG + /obj/machinery/computer/cargo/emag_act(mob/user) if(obj_flags & EMAGGED) return @@ -115,11 +122,7 @@ say(blockade_warning) return if(SSshuttle.supply.getDockedId() == "supply_home") - if (obj_flags & EMAGGED) - SSshuttle.supply.obj_flags |= EMAGGED - else - SSshuttle.supply.obj_flags = (SSshuttle.supply.obj_flags & ~EMAGGED) - SSshuttle.supply.contraband = contraband + SSshuttle.supply.export_categories = get_export_categories() SSshuttle.moveShuttle("supply", "supply_away", TRUE) say("The supply shuttle is departing.") investigate_log("[key_name(usr)] sent the supply shuttle away.", INVESTIGATE_CARGO) diff --git a/code/modules/cargo/export_scanner.dm b/code/modules/cargo/export_scanner.dm index 2113696e90..6a294c9eeb 100644 --- a/code/modules/cargo/export_scanner.dm +++ b/code/modules/cargo/export_scanner.dm @@ -17,6 +17,7 @@ to_chat(user, "[src] is not currently linked to a cargo console.") /obj/item/export_scanner/afterattack(obj/O, mob/user, proximity) + . = ..() if(!istype(O) || !proximity) return @@ -30,7 +31,12 @@ else // Before you fix it: // yes, checking manifests is a part of intended functionality. - var/price = export_item_and_contents(O, cargo_console.contraband, (cargo_console.obj_flags & EMAGGED), dry_run=TRUE) + + var/datum/export_report/ex = export_item_and_contents(O, cargo_console.get_export_categories(), dry_run=TRUE) + var/price = 0 + for(var/x in ex.total_amount) + price += ex.total_value[x] + if(price) to_chat(user, "Scanned [O], value: [price] credits[O.contents.len ? " (contents included)" : ""].") else diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm index 702abf2a12..b7550465d9 100644 --- a/code/modules/cargo/exports.dm +++ b/code/modules/cargo/exports.dm @@ -1,15 +1,7 @@ /* How it works: The shuttle arrives at CentCom dock and calls sell(), which recursively loops through all the shuttle contents that are unanchored. - The loop only checks contents of storage types, see supply.dm shuttle code. - + Each object in the loop is checked for applies_to() of various export datums, except the invalid ones. - Objects on shutlle floor are checked only against shuttle_floor = TRUE exports. - - If applies_to() returns TRUE, sell_object() is called on object and checks against exports are stopped for this object. - sell_object() must add object amount and cost to export's total_cost and total_amount. - - When all the shuttle objects are looped, export cycle is over. The shuttle calls total_printout() for each valid export. - If total_printout() returns something, the export datum's total_cost is added to cargo credits, then export_end() is called to reset total_cost and total_amount. */ /* The rule in figuring out item export cost: @@ -26,52 +18,57 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they then the player gets the profit from selling his own wasted time. */ -/proc/export_item_and_contents(atom/movable/AM, contraband, emagged, dry_run=FALSE) + +// Simple holder datum to pass export results around +/datum/export_report + var/list/exported_atoms = list() //names of atoms sold/deleted by export + var/list/total_amount = list() //export instance => total count of sold objects of its type, only exists if any were sold + var/list/total_value = list() //export instance => total value of sold objects + +// external_report works as "transaction" object, pass same one in if you're doing more than one export in single go +/proc/export_item_and_contents(atom/movable/AM, allowed_categories = EXPORT_CARGO, apply_elastic = TRUE, delete_unsold = TRUE, dry_run=FALSE, datum/export_report/external_report) if(!GLOB.exports_list.len) setupExports() - var/sold_str = "" - var/cost = 0 - var/list/contents = AM.GetAllContents() + + var/datum/export_report/report = external_report + if(!report) //If we don't have any longer transaction going on + report = new // We go backwards, so it'll be innermost objects sold first for(var/i in reverseRange(contents)) var/atom/movable/thing = i + var/sold = FALSE for(var/datum/export/E in GLOB.exports_list) if(!E) continue - if(E.applies_to(thing, contraband, emagged)) - if(dry_run) - cost += E.get_cost(thing, contraband, emagged) - else - E.sell_object(thing, contraband, emagged) - sold_str += " [thing.name]" + if(E.applies_to(thing, allowed_categories, apply_elastic)) + sold = E.sell_object(thing, report, dry_run, allowed_categories , apply_elastic) + report.exported_atoms += " [thing.name]" break - if(!dry_run) + if(!dry_run && (sold || delete_unsold)) + if(ismob(thing)) + thing.investigate_log("deleted through cargo export",INVESTIGATE_CARGO) qdel(thing) - if(dry_run) - return cost - else - return sold_str + return report /datum/export var/unit_name = "" // Unit name. Only used in "Received [total_amount] [name]s [message]." message var/message = "" var/cost = 100 // Cost of item, in cargo credits. Must not alow for infinite price dupes, see above. var/k_elasticity = 1/30 //coefficient used in marginal price calculation that roughly corresponds to the inverse of price elasticity, or "quantity elasticity" - var/contraband = FALSE // Export must be unlocked with multitool. - var/emagged = FALSE // Export must be unlocked with emag. var/list/export_types = list() // Type of the exported object. If none, the export datum is considered base type. var/include_subtypes = TRUE // Set to FALSE to make the datum apply only to a strict type. var/list/exclude_types = list() // Types excluded from export - // Used by print-out - var/total_cost = 0 - var/total_amount = 0 + //cost includes elasticity, this does not. var/init_cost + //All these need to be present in export call parameter for this to apply. + var/export_category = EXPORT_CARGO + /datum/export/New() ..() SSprocessing.processing += src @@ -79,7 +76,6 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they export_types = typecacheof(export_types) exclude_types = typecacheof(exclude_types) - /datum/export/Destroy() SSprocessing.processing -= src return ..() @@ -91,29 +87,30 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they cost = init_cost // Checks the cost. 0 cost items are skipped in export. -/datum/export/proc/get_cost(obj/O, contr = 0, emag = 0) - var/amount = get_amount(O, contr, emag) - if(k_elasticity!=0) - return round((cost/k_elasticity) * (1 - NUM_E**(-1 * k_elasticity * amount))) //anti-derivative of the marginal cost function +/datum/export/proc/get_cost(obj/O, allowed_categories = NONE, apply_elastic = TRUE) + var/amount = get_amount(O) + if(apply_elastic) + if(k_elasticity!=0) + return round((cost/k_elasticity) * (1 - NUM_E**(-1 * k_elasticity * amount))) //anti-derivative of the marginal cost function + else + return round(cost * amount) //alternative form derived from L'Hopital to avoid division by 0 else - return round(cost * amount) //alternative form derived from L'Hopital to avoid division by 0 + return round(init_cost * amount) // Checks the amount of exportable in object. Credits in the bill, sheets in the stack, etc. // Usually acts as a multiplier for a cost, so item that has 0 amount will be skipped in export. -/datum/export/proc/get_amount(obj/O, contr = 0, emag = 0) +/datum/export/proc/get_amount(obj/O) return 1 // Checks if the item is fit for export datum. -/datum/export/proc/applies_to(obj/O, contr = 0, emag = 0) - if(contraband && !contr) - return FALSE - if(emagged && !emag) +/datum/export/proc/applies_to(obj/O, allowed_categories = NONE, apply_elastic = TRUE) + if((allowed_categories & export_category) != export_category) return FALSE if(!include_subtypes && !(O.type in export_types)) return FALSE if(include_subtypes && (!is_type_in_typecache(O, export_types) || is_type_in_typecache(O, exclude_types))) return FALSE - if(!get_cost(O, contr, emag)) + if(!get_cost(O, allowed_categories , apply_elastic)) return FALSE if(O.flags_1 & HOLOGRAM_1) return FALSE @@ -122,26 +119,38 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they // Called only once, when the object is actually sold by the datum. // Adds item's cost and amount to the current export cycle. // get_cost, get_amount and applies_to do not neccesary mean a successful sale. -/datum/export/proc/sell_object(obj/O, contr = 0, emag = 0) - var/the_cost = get_cost(O) +/datum/export/proc/sell_object(obj/O, datum/export_report/report, dry_run = TRUE, allowed_categories = EXPORT_CARGO , apply_elastic = TRUE) + var/the_cost = get_cost(O, allowed_categories , apply_elastic) var/amount = get_amount(O) - total_cost += the_cost - if(istype(O, /datum/export/material)) - total_amount += amount*MINERAL_MATERIAL_AMOUNT - else - total_amount += amount - cost *= NUM_E**(-1*k_elasticity*amount) //marginal cost modifier - SSblackbox.record_feedback("nested tally", "export_sold_cost", 1, list("[O.type]", "[the_cost]")) + if(amount <=0 || the_cost <=0) + return FALSE + + report.total_value[src] += the_cost + + if(istype(O, /datum/export/material)) + report.total_amount[src] += amount*MINERAL_MATERIAL_AMOUNT + else + report.total_amount[src] += amount + + if(!dry_run) + if(apply_elastic) + cost *= NUM_E**(-1*k_elasticity*amount) //marginal cost modifier + SSblackbox.record_feedback("nested tally", "export_sold_cost", 1, list("[O.type]", "[the_cost]")) + return TRUE // Total printout for the cargo console. // Called before the end of current export cycle. // It must always return something if the datum adds or removes any credts. -/datum/export/proc/total_printout(contr = 0, emag = 0) - if(!total_cost && !total_amount) +/datum/export/proc/total_printout(datum/export_report/ex, notes = TRUE) + if(!ex.total_amount[src] || !ex.total_value[src]) return "" - var/msg = "[total_cost] credits: Received [total_amount] " - if(total_cost > 0) + + var/total_value = ex.total_value[src] + var/total_amount = ex.total_amount[src] + + var/msg = "[total_value] credits: Received [total_amount] " + if(total_value > 0) msg = "+" + msg if(unit_name) @@ -157,11 +166,6 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they msg += "." return msg -// The current export cycle is over now. Reset all the export temporary vars. -/datum/export/proc/export_end() - total_cost = 0 - total_amount = 0 - GLOBAL_LIST_EMPTY(exports_list) /proc/setupExports() diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm index aed640cd47..9368a41731 100644 --- a/code/modules/cargo/exports/large_objects.dm +++ b/code/modules/cargo/exports/large_objects.dm @@ -5,9 +5,9 @@ export_types = list(/obj/structure/closet/crate) exclude_types = list(/obj/structure/closet/crate/large, /obj/structure/closet/crate/wooden) -/datum/export/large/crate/total_printout() // That's why a goddamn metal crate costs that much. +/datum/export/large/crate/total_printout(datum/export_report/ex, notes = TRUE) // That's why a goddamn metal crate costs that much. . = ..() - if(.) + if(. && notes) . += " Thanks for participating in Nanotrasen Crates Recycling Program." /datum/export/large/crate/wooden @@ -56,42 +56,72 @@ export_types = list(/obj/structure/reagent_dispensers/beerkeg) - -/datum/export/large/emitter - cost = 200 - unit_name = "emitter" - export_types = list(/obj/machinery/power/emitter) - -/datum/export/large/field_generator - cost = 200 - unit_name = "field generator" - export_types = list(/obj/machinery/field/generator) - -/datum/export/large/collector - cost = 200 - unit_name = "collector" - export_types = list(/obj/machinery/power/rad_collector) - -/datum/export/large/collector/pa - cost = 300 - unit_name = "particle accelerator part" - export_types = list(/obj/structure/particle_accelerator) - -/datum/export/large/collector/pa/controls - cost = 500 - unit_name = "particle accelerator control console" - export_types = list(/obj/machinery/particle_accelerator/control_box) - /datum/export/large/pipedispenser cost = 500 unit_name = "pipe dispenser" export_types = list(/obj/machinery/pipedispenser) +/datum/export/large/emitter + cost = 550 + unit_name = "emitter" + export_types = list(/obj/machinery/power/emitter) + +/datum/export/large/field_generator + cost = 550 + unit_name = "field generator" + export_types = list(/obj/machinery/field/generator) + +/datum/export/large/collector + cost = 400 + unit_name = "radiation collector" + export_types = list(/obj/machinery/power/rad_collector) + +/datum/export/large/tesla_coil + cost = 450 + unit_name = "tesla coil" + export_types = list(/obj/machinery/power/tesla_coil) + +/datum/export/large/pa + cost = 350 + unit_name = "particle accelerator part" + export_types = list(/obj/structure/particle_accelerator) + +/datum/export/large/pa/controls + cost = 500 + unit_name = "particle accelerator control console" + export_types = list(/obj/machinery/particle_accelerator/control_box) + /datum/export/large/supermatter - cost = 9000 + cost = 8000 unit_name = "supermatter shard" export_types = list(/obj/machinery/power/supermatter_crystal/shard) +/datum/export/large/grounding_rod + cost = 350 + unit_name = "grounding rod" + export_types = list(/obj/machinery/power/grounding_rod) + +/datum/export/large/tesla_gen + cost = 4000 + unit_name = "energy ball generator" + export_types = list(/obj/machinery/the_singularitygen/tesla) + +/datum/export/large/singulo_gen + cost = 4000 + unit_name = "gravitational singularity generator" + export_types = list(/obj/machinery/the_singularitygen) + include_subtypes = FALSE + +/datum/export/large/am_control_unit + cost = 4000 + unit_name = "antimatter control unit" + export_types = list(/obj/machinery/power/am_control_unit) + +/datum/export/large/am_shielding_container + cost = 150 + unit_name = "packaged antimatter reactor section" + export_types = list(/obj/item/am_shielding_container) + /datum/export/large/iv cost = 50 diff --git a/code/modules/cargo/exports/lavaland.dm b/code/modules/cargo/exports/lavaland.dm new file mode 100644 index 0000000000..a1c6307c5e --- /dev/null +++ b/code/modules/cargo/exports/lavaland.dm @@ -0,0 +1,65 @@ +//Tendril chest artifacts and ruin loot. Includes ash drake loot since they drop two sets of armor + random item +//Consumable or one-use items like the magic D20 and gluttony's blessing are omitted + +/datum/export/lavaland/minor + cost = 10000 + unit_name = "minor lava planet artifact" + export_types = list(/obj/item/immortality_talisman, + /obj/item/book_of_babel, + /obj/item/gun/magic/hook, + /obj/item/wisp_lantern, + /obj/item/reagent_containers/glass/bottle/potion/flight, + /obj/item/katana/cursed, + /obj/item/clothing/glasses/godeye, + /obj/item/melee/ghost_sword, + /obj/item/clothing/suit/space/hardsuit/cult, + /obj/item/voodoo, + /obj/item/grenade/clusterbuster/inferno, + /obj/item/clothing/neck/necklace/memento_mori, + /obj/item/organ/heart/cursed/wizard, + /obj/item/clothing/suit/hooded/cloak/drake, + /obj/item/dragons_blood, + /obj/item/lava_staff, + /obj/item/ship_in_a_bottle, + /obj/item/clothing/shoes/clown_shoes/banana_shoes, + /obj/item/gun/magic/staff/honk, + /obj/item/kitchen/knife/envy, + /obj/item/gun/ballistic/revolver/russian/soul, + /obj/item/veilrender/vealrender) + +/datum/export/lavaland/major //valuable chest/ruin loot and staff of storms + cost = 20000 + unit_name = "lava planet artifact" + export_types = list(/obj/item/guardiancreator, + /obj/item/rod_of_asclepius, + /obj/item/clothing/suit/space/hardsuit/ert/paranormal, + /obj/item/prisoncube, + /obj/item/staff/storm) + +//Megafauna loot, except for ash drakes and legion + +/datum/export/lavaland/megafauna + cost = 40000 + unit_name = "major lava planet artifact" + export_types = list(/obj/item/hierophant_club, + /obj/item/melee/transforming/cleaving_saw, + /obj/item/organ/vocal_cords/colossus, + /obj/machinery/anomalous_crystal, + /obj/item/mayhem, + /obj/item/blood_contract, + /obj/item/gun/magic/staff/spellblade) + +/datum/export/lavaland/megafauna/total_printout(datum/export_report/ex, notes = TRUE) //in the unlikely case a miner feels like selling megafauna loot + . = ..() + if(. && notes) + . += " On behalf of the Nanotrasen RnD division: Thank you for your hard work." + +/datum/export/lavaland/megafauna/hev/suit + cost = 30000 + unit_name = "H.E.C.K. suit" + export_types = list(/obj/item/clothing/suit/space/hostile_environment) + +/datum/export/lavaland/megafauna/hev/helmet + cost = 10000 + unit_name = "H.E.C.K. helmet" + export_types = list(/obj/item/clothing/head/helmet/space/hostile_environment) diff --git a/code/modules/cargo/exports/seeds.dm b/code/modules/cargo/exports/seeds.dm index 42104ec165..a879b0c4fe 100644 --- a/code/modules/cargo/exports/seeds.dm +++ b/code/modules/cargo/exports/seeds.dm @@ -15,9 +15,10 @@ return ..() * S.rarity // That's right, no bonus for potency. Send a crappy sample first to "show improvement" later. /datum/export/seed/sell_object(obj/O) - ..() - var/obj/item/seeds/S = O - discoveredPlants[S.type] = S.potency + . = ..() + if(.) + var/obj/item/seeds/S = O + discoveredPlants[S.type] = S.potency /datum/export/seed/potency diff --git a/code/modules/cargo/exports/sheets.dm b/code/modules/cargo/exports/sheets.dm index 4c167026b4..708eb34133 100644 --- a/code/modules/cargo/exports/sheets.dm +++ b/code/modules/cargo/exports/sheets.dm @@ -16,7 +16,7 @@ /datum/export/stack/skin/human cost = 100 - contraband = TRUE + export_category = EXPORT_CONTRABAND unit_name = "piece" message = "of human skin" export_types = list(/obj/item/stack/sheet/animalhide/human) @@ -28,13 +28,13 @@ /datum/export/stack/skin/cat cost = 150 - contraband = TRUE + export_category = EXPORT_CONTRABAND unit_name = "cat hide" export_types = list(/obj/item/stack/sheet/animalhide/cat) /datum/export/stack/skin/corgi cost = 200 - contraband = TRUE + export_category = EXPORT_CONTRABAND unit_name = "corgi hide" export_types = list(/obj/item/stack/sheet/animalhide/corgi) @@ -44,7 +44,7 @@ export_types = list(/obj/item/stack/sheet/animalhide/lizard) /datum/export/stack/skin/gondola - cost = 500 + cost = 5000 unit_name = "gondola hide" export_types = list(/obj/item/stack/sheet/animalhide/gondola) diff --git a/code/modules/cargo/exports/tools.dm b/code/modules/cargo/exports/tools.dm index 024c93821a..9e58e4ba95 100644 --- a/code/modules/cargo/exports/tools.dm +++ b/code/modules/cargo/exports/tools.dm @@ -110,3 +110,23 @@ cost = 100 unit_name = "rapid piping device" export_types = list(/obj/item/pipe_dispenser) + +/datum/export/singulo //failsafe in case someone decides to ship a live singularity to CentCom without the corresponding bounty + cost = 1 + unit_name = "singularity" + export_types = list(/obj/singularity) + include_subtypes = FALSE + +/datum/export/singulo/total_printout(datum/export_report/ex, notes = TRUE) + . = ..() + if(. && notes) + . += " ERROR: Invalid object detected." + +/datum/export/singulo/tesla //see above + unit_name = "energy ball" + export_types = list(/obj/singularity/energy_ball) + +/datum/export/singulo/tesla/total_printout(datum/export_report/ex, notes = TRUE) + . = ..() + if(. && notes) + . += " ERROR: Unscheduled energy ball delivery detected." diff --git a/code/modules/cargo/exports/weapons.dm b/code/modules/cargo/exports/weapons.dm index 0d363f7132..bad221c3ac 100644 --- a/code/modules/cargo/exports/weapons.dm +++ b/code/modules/cargo/exports/weapons.dm @@ -69,97 +69,3 @@ unit_name = "pair" message = "of handcuffs" export_types = list(/obj/item/restraints/handcuffs) - -// relics of lavaland - -/datum/export/weapon/hierophant - cost = 40000 - unit_name = "Hierophant Club" - export_types = list(/obj/item/hierophant_club) - -/datum/export/weapon/lava - cost = 40000 - unit_name = "Lava Staff" - export_types = list(/obj/item/lava_staff) - -/datum/export/weapon/cleaving_saw - cost = 40000 - unit_name = "Cleaving Saw" - export_types = list(/obj/item/melee/transforming/cleaving_saw) - -/datum/export/weapon/mayhem - cost = 40000 - unit_name = "Mayhem in a bottle" - export_types = list(/obj/item/mayhem) - -/datum/export/weapon/blood_contract - cost = 40000 - unit_name = "Blood Contract" - export_types = list(/obj/item/blood_contract) - -//Artifacts of lavaland - -/datum/export/weapon/immortality_talisman - cost = 10000 - unit_name = "Immortality Talisman" - export_types = list(/obj/item/immortality_talisman) - -/datum/export/weapon/babel - cost = 10000 - unit_name = "Book of Babel" - export_types = list(/obj/item/book_of_babel) - -/datum/export/weapon/hook - cost = 10000 - unit_name = "Meat hook" - export_types = list(/obj/item/gun/magic/hook) - -/datum/export/weapon/shipbottle //the price for not breaking the bottle. - cost = 20000 - unit_name = "Ship in a bottle" - export_types = list(/obj/item/ship_in_a_bottle) - -/datum/export/weapon/tarot //price for sacraficing a very profitiable ally - cost = 20000 - unit_name = "Tarot cards" - export_types = list(/obj/item/guardiancreator) - -/datum/export/weapon/red //second half of telecube - cost = 5000 - unit_name = "Red Cube" - export_types = list(/obj/item/warp_cube/red) - -/datum/export/weapon/blue //first half of telecube - cost = 5000 - unit_name = "Blue Cube" - export_types = list(/obj/item/warp_cube) - -/datum/export/weapon/wisplantern //thermals on lavaland - cost = 10000 - unit_name = "Wisp Lantern" - export_types = list(/obj/item/wisp_lantern) - -/datum/export/weapon/flight //if xenobiology ever reaches the point to get these without shuttle being called they deserve it - cost = 10000 - unit_name = "Strange Elixir" - export_types = list(/obj/item/reagent_containers/glass/bottle/potion/flight) - -/datum/export/weapon/cheart //is a very powerfull healing artifact in the robust hands - cost = 10000 - unit_name = "Cursed Heart" - export_types = list(/obj/item/organ/heart/cursed/wizard) - -/datum/export/weapon/ckatana - cost = 10000 - unit_name = "Katana" - export_types = list(/obj/item/katana/cursed) - -/datum/export/weapon/geye //xray - cost = 10000 - unit_name = "God eye" - export_types = list(/obj/item/clothing/glasses/godeye) - -/datum/export/weapon/spectral - cost = 10000 - unit_name = "Spectral Sword" - export_types = list(/obj/item/melee/ghost_sword) diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm index c357aac5c5..55cb8406b7 100644 --- a/code/modules/cargo/expressconsole.dm +++ b/code/modules/cargo/expressconsole.dm @@ -9,7 +9,7 @@ /obj/machinery/computer/cargo/express name = "express supply console" desc = "This console allows the user to purchase a package \ - with 1/40th of the delivery time: made possible by NanoTrasen's new \"1500mm Orbital Railgun\".\ + with 1/40th of the delivery time: made possible by Nanotrasen's new \"1500mm Orbital Railgun\".\ All sales are near instantaneous - please choose carefully" icon_screen = "supply_express" circuit = /obj/item/circuitboard/computer/cargo/express @@ -109,7 +109,7 @@ if(SSshuttle.supplyBlocked) message = blockade_warning if(usingBeacon && !beacon) - message = "BEACON ERROR: BEACON MISSING"//beacon was destroyed + message = "BEACON ERROR: BEACON MISSING"//beacon was destroyed else if (usingBeacon && !canBeacon) message = "BEACON ERROR: MUST BE EXPOSED"//beacon's loc/user's loc must be a turf if(obj_flags & EMAGGED) @@ -165,9 +165,9 @@ var/LZ if (istype(beacon) && usingBeacon)//prioritize beacons over landing in cargobay LZ = get_turf(beacon) - beacon.update_status(SP_LAUNCH) + beacon.update_status(SP_LAUNCH) else if (!usingBeacon)//find a suitable supplypod landing zone in cargobay - landingzone = locate(/area/quartermaster/storage) in GLOB.sortedAreas + landingzone = GLOB.areas_by_type[/area/quartermaster/storage] if (!landingzone) WARNING("[src] couldnt find a Quartermaster/Storage (aka cargobay) area on the station, and as such it has set the supplypod landingzone to the area it resides in.") landingzone = get_area(src) @@ -177,7 +177,7 @@ LAZYADD(empty_turfs, T) CHECK_TICK if(empty_turfs && empty_turfs.len) - LZ = pick(empty_turfs) + LZ = pick(empty_turfs) if (SO.pack.cost <= SSshuttle.points && LZ)//we need to call the cost check again because of the CHECK_TICK call SSshuttle.points -= SO.pack.cost new /obj/effect/DPtarget(LZ, SO, podID) @@ -185,7 +185,7 @@ update_icon() else if(SO.pack.cost * (0.72*MAX_EMAG_ROCKETS) <= SSshuttle.points) // bulk discount :^) - landingzone = locate(pick(GLOB.the_station_areas)) in GLOB.sortedAreas //override default landing zone + landingzone = GLOB.areas_by_type[pick(GLOB.the_station_areas)] //override default landing zone for(var/turf/open/floor/T in landingzone.contents) if(is_blocked_turf(T)) continue diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm index 6e88990680..aae783ac33 100644 --- a/code/modules/cargo/packs.dm +++ b/code/modules/cargo/packs.dm @@ -460,7 +460,7 @@ /datum/supply_pack/security/armory/laserarmor name = "Reflector Vest Crate" - desc = "Contains two vests of highly reflective material. Each armor peice diffuses a laser's energy by over half, as well as offering a good chance to reflect the laser entirely. Requires Armory access to open." + desc = "Contains two vests of highly reflective material. Each armor piece diffuses a laser's energy by over half, as well as offering a good chance to reflect the laser entirely. Requires Armory access to open." cost = 2000 contains = list(/obj/item/clothing/suit/armor/laserproof, /obj/item/clothing/suit/armor/laserproof) @@ -1052,6 +1052,15 @@ /obj/item/storage/firstaid/o2) crate_name = "oxygen deprivation kit crate" +/datum/supply_pack/medical/surgery + name = "Surgical Supplies Crate" + desc = "Do you want to perform surgery, but don't have one of those fancy shmancy degrees? Just get started with this crate containing a medical duffelbag, Sterilizine spray and collapsible roller bed." + cost = 3000 + contains = list(/obj/item/storage/backpack/duffelbag/med/surgery, + /obj/item/reagent_containers/medspray/sterilizine, + /obj/item/roller) + crate_name = "surgical supplies crate" + /datum/supply_pack/medical/firstaidtoxins name = "Toxin Treatment Kit Crate" desc = "Contains three first aid kits focused on healing damage dealt by heavy toxins." @@ -1072,13 +1081,14 @@ /obj/item/reagent_containers/blood/BPlus, /obj/item/reagent_containers/blood/BMinus, /obj/item/reagent_containers/blood/OPlus, - /obj/item/reagent_containers/blood/OMinus) + /obj/item/reagent_containers/blood/OMinus, + /obj/item/reagent_containers/blood/lizard) crate_name = "blood freezer" crate_type = /obj/structure/closet/crate/freezer /datum/supply_pack/medical/defibs name = "Defibrillator Crate" - desc = "Contains two defibrillators for bringing the recently-deceased back to life." + desc = "Contains two defibrillators for bringing the recently deceased back to life." cost = 2500 contains = list(/obj/item/defibrillator/loaded, /obj/item/defibrillator/loaded) @@ -1162,7 +1172,7 @@ /datum/supply_pack/science/robotics/mecha_odysseus name = "Circuit Crate (Odysseus)" - desc = "Ever wanted to build your own giant medical robot? Well now you can! Contains the Odysseus main control board and Odysseus peripherals board. Requires Robotics access to open." + desc = "Ever wanted to build your own giant medical robot? Well, now you can! Contains the Odysseus main control board and Odysseus peripherals board. Requires Robotics access to open." cost = 2500 access = ACCESS_ROBOTICS contains = list(/obj/item/circuitboard/mecha/odysseus/peripherals, @@ -1181,6 +1191,16 @@ crate_name = "\improper APLU Ripley circuit crate" crate_type = /obj/structure/closet/crate/secure/science +/datum/supply_pack/science/circuitry + name = "Circuitry Starter Pack Crate" + desc = "Journey into the mysterious world of Circuitry with this starter pack. Contains a circuit printer, analyzer, debugger and wirer. Power cells not included." + cost = 1000 + contains = list(/obj/item/integrated_electronics/analyzer, + /obj/item/integrated_circuit_printer, + /obj/item/integrated_electronics/debugger, + /obj/item/integrated_electronics/wirer) + crate_name = "circuitry starter pack crate" + /datum/supply_pack/science/plasma name = "Plasma Assembly Crate" desc = "Everything you need to burn something to the ground, this contains three plasma assembly sets. Each set contains a plasma tank, igniter, proximity sensor, and timer! Warranty void if exposed to high temperatures. Requires Toxins access to open." @@ -1257,6 +1277,18 @@ /datum/supply_pack/service group = "Service" +/datum/supply_pack/service/cargo_supples + name = "Cargo Supplies Crate" + desc = "Sold everything that wasn't bolted down? You can get right back to work with this crate containing stamps, an export scanner, destination tagger, hand labeler and some package wrapping." + cost = 1000 + contains = list(/obj/item/stamp, + /obj/item/stamp/denied, + /obj/item/export_scanner, + /obj/item/destTagger, + /obj/item/hand_labeler, + /obj/item/stack/packageWrap) + crate_name = "cargo supplies crate" + /datum/supply_pack/service/noslipfloor name = "High-traction Floor Tiles" desc = "Make slipping a thing of the past with thirty industrial-grade anti-slip floortiles!" @@ -1332,6 +1364,16 @@ /obj/item/flashlight/glowstick/pink) crate_name = "party equipment crate" +/datum/supply_pack/service/carpet + name = "Premium Carpet Crate" + desc = "Plasteel floor tiles getting on your nerves? These stacks of extra soft carpet will tie any room together." + cost = 1000 + contains = list(/obj/item/stack/tile/carpet/fifty, + /obj/item/stack/tile/carpet/fifty, + /obj/item/stack/tile/carpet/black/fifty, + /obj/item/stack/tile/carpet/black/fifty) + crate_name = "premium carpet crate" + /datum/supply_pack/service/lightbulbs name = "Replacement Lights" desc = "May the light of Aether shine upon this station! Or at least, the light of forty two light tubes and twenty one light bulbs." @@ -1641,6 +1683,14 @@ qdel(D) new /mob/living/simple_animal/pet/dog/corgi/Lisa(.) +/datum/supply_pack/critter/corgis/exotic + name = "Exotic Corgi Crate" + desc = "Corgis fit for a king, these corgis come in a unique color to signify their superiority. Comes with a cute collar!" + cost = 5500 + contains = list(/mob/living/simple_animal/pet/dog/corgi/exoticcorgi, + /obj/item/clothing/neck/petcollar) + crate_name = "exotic corgi crate" + /datum/supply_pack/critter/cow name = "Cow Crate" desc = "The cow goes moo!" @@ -1739,13 +1789,14 @@ /obj/item/storage/pill_bottle/lsd, /obj/item/storage/pill_bottle/aranesp, /obj/item/storage/pill_bottle/stimulant, - /obj/item/toy/cards/deck/syndicate, + /obj/item/toy/cards/deck/syndicate, /obj/item/reagent_containers/food/drinks/bottle/absinthe, /obj/item/clothing/under/syndicate/tacticool, /obj/item/storage/fancy/cigarettes/cigpack_syndicate, /obj/item/storage/fancy/cigarettes/cigpack_shadyjims, /obj/item/clothing/mask/gas/syndicate, - /obj/item/clothing/neck/necklace/dope) + /obj/item/clothing/neck/necklace/dope, + /obj/item/vending_refill/donksoft) crate_name = "crate" /datum/supply_pack/costumes_toys/foamforce @@ -1942,14 +1993,14 @@ crate_name = "autodrobe supply crate" /datum/supply_pack/costumes_toys/wardrobes/cargo - name = "Cargo Department Supply Crate" + name = "Cargo Wardrobe Supply Crate" desc = "This crate contains a refill for the CargoDrobe." cost = 750 contains = list(/obj/item/vending_refill/wardrobe/cargo_wardrobe) crate_name = "cargo department supply crate" /datum/supply_pack/costumes_toys/wardrobes/engineering - name = "Engineering Department Wardrobe Supply Crate" + name = "Engineering Wardrobe Supply Crate" desc = "This crate contains refills for the EngiDrobe and AtmosDrobe." cost = 1500 contains = list(/obj/item/vending_refill/wardrobe/engi_wardrobe, @@ -1975,7 +2026,7 @@ crate_name = "hydrobe supply crate" /datum/supply_pack/costumes_toys/wardrobes/medical - name = "Medical Department Wardrobe Supply Crate" + name = "Medical Wardrobe Supply Crate" desc = "This crate contains refills for the MediDrobe, ChemDrobe, GeneDrobe, and ViroDrobe." cost = 3000 contains = list(/obj/item/vending_refill/wardrobe/medi_wardrobe, @@ -1985,7 +2036,7 @@ crate_name = "medical department wardrobe supply crate" /datum/supply_pack/costumes_toys/wardrobes/science - name = "Science Department Wardrobe Supply Crate" + name = "Science Wardrobe Supply Crate" desc = "This crate contains refills for the SciDrobe and RoboDrobe." cost = 1500 contains = list(/obj/item/vending_refill/wardrobe/robo_wardrobe, @@ -1993,7 +2044,7 @@ crate_name = "science department wardrobe supply crate" /datum/supply_pack/costumes_toys/wardrobes/security - name = "Security Department Supply Crate" + name = "Security Wardrobe Supply Crate" desc = "This crate contains refills for the SecDrobe and LawDrobe." cost = 1500 contains = list(/obj/item/vending_refill/wardrobe/sec_wardrobe, @@ -2090,6 +2141,16 @@ cost = 700 contains = list(/obj/item/storage/box/fountainpens) crate_type = /obj/structure/closet/crate/wooden + crate_name = "calligraphy crate" + +/datum/supply_pack/misc/wrapping_paper + name = "Festive Wrapping Paper Crate" + desc = "Want to mail your loved ones gift-wrapped chocolates, stuffed animals, the Clown's severed head? You can do all that, with this crate full of wrapping paper." + cost = 1000 + contains = list(/obj/item/stack/wrapping_paper) + crate_type = /obj/structure/closet/crate/wooden + crate_name = "festive wrapping paper crate" + /datum/supply_pack/misc/funeral name = "Funeral Supply crate" diff --git a/code/modules/cargo/supplypod_beacon.dm b/code/modules/cargo/supplypod_beacon.dm index f72fe595de..3c82722f69 100644 --- a/code/modules/cargo/supplypod_beacon.dm +++ b/code/modules/cargo/supplypod_beacon.dm @@ -48,6 +48,8 @@ ..() if(!express_console) to_chat(user, "[src] is not currently linked to a Express Supply console.") + else + to_chat(user, "Alt-click to unlink it from the Express Supply console.") /obj/item/supplypod_beacon/Destroy() if(express_console) diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 53a05acaf2..7c08a3332c 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -227,7 +227,8 @@ GLOBAL_LIST_EMPTY(asset_datums) var/fname = "data/spritesheets/[res_name]" fdel(fname) text2file(generate_css(), fname) - register_asset(res_name, file(fname)) + register_asset(res_name, fcopy_rsc(fname)) + fdel(fname) for(var/size_id in sizes) var/size = sizes[size_id] @@ -254,6 +255,7 @@ GLOBAL_LIST_EMPTY(asset_datums) if(length(error)) stack_trace("Failed to strip [name]_[size_id].png: [error]") size[SPRSZ_STRIPPED] = icon(fname) + fdel(fname) /datum/asset/spritesheet/proc/generate_css() var/list/out = list() @@ -549,6 +551,14 @@ GLOBAL_LIST_EMPTY(asset_datums) "padlock.png" = 'html/padlock.png' ) +/datum/asset/simple/notes + assets = list( + "high_button.png" = 'html/high_button.png', + "medium_button.png" = 'html/medium_button.png', + "minor_button.png" = 'html/minor_button.png', + "none_button.png" = 'html/none_button.png', + ) + //this exists purely to avoid meta by pre-loading all language icons. /datum/asset/language/register() for(var/path in typesof(/datum/language)) @@ -572,38 +582,53 @@ GLOBAL_LIST_EMPTY(asset_datums) for (var/path in subtypesof(/datum/design)) var/datum/design/D = path - // construct the icon and slap it into the resource cache - var/atom/item = initial(D.build_path) - if (!ispath(item, /atom)) - // biogenerator outputs to beakers by default - if (initial(D.build_type) & BIOGENERATOR) - item = /obj/item/reagent_containers/glass/beaker/large - else - continue // shouldn't happen, but just in case + var/icon_file + var/icon_state + var/icon/I - // circuit boards become their resulting machines or computers - if (ispath(item, /obj/item/circuitboard)) - var/obj/item/circuitboard/C = item - var/machine = initial(C.build_path) - if (machine) - item = machine - var/icon_file = initial(item.icon) - var/icon_state = initial(item.icon_state) - if (!(icon_state in icon_states(icon_file))) - warning("design [D] with icon '[icon_file]' missing state '[icon_state]'") - continue - var/icon/I = icon(icon_file, icon_state, SOUTH) + if(initial(D.research_icon) && initial(D.research_icon_state)) //If the design has an icon replacement skip the rest + icon_file = initial(D.research_icon) + icon_state = initial(D.research_icon_state) + if(!(icon_state in icon_states(icon_file))) + warning("design [D] with icon '[icon_file]' missing state '[icon_state]'") + continue + I = icon(icon_file, icon_state, SOUTH) - // computers (and snowflakes) get their screen and keyboard sprites - if (ispath(item, /obj/machinery/computer) || ispath(item, /obj/machinery/power/solar_control)) - var/obj/machinery/computer/C = item - var/screen = initial(C.icon_screen) - var/keyboard = initial(C.icon_keyboard) - var/all_states = icon_states(icon_file) - if (screen && (screen in all_states)) - I.Blend(icon(icon_file, screen, SOUTH), ICON_OVERLAY) - if (keyboard && (keyboard in all_states)) - I.Blend(icon(icon_file, keyboard, SOUTH), ICON_OVERLAY) + else + // construct the icon and slap it into the resource cache + var/atom/item = initial(D.build_path) + if (!ispath(item, /atom)) + // biogenerator outputs to beakers by default + if (initial(D.build_type) & BIOGENERATOR) + item = /obj/item/reagent_containers/glass/beaker/large + else + continue // shouldn't happen, but just in case + + // circuit boards become their resulting machines or computers + if (ispath(item, /obj/item/circuitboard)) + var/obj/item/circuitboard/C = item + var/machine = initial(C.build_path) + if (machine) + item = machine + + icon_file = initial(item.icon) + icon_state = initial(item.icon_state) + + if(!(icon_state in icon_states(icon_file))) + warning("design [D] with icon '[icon_file]' missing state '[icon_state]'") + continue + I = icon(icon_file, icon_state, SOUTH) + + // computers (and snowflakes) get their screen and keyboard sprites + if (ispath(item, /obj/machinery/computer) || ispath(item, /obj/machinery/power/solar_control)) + var/obj/machinery/computer/C = item + var/screen = initial(C.icon_screen) + var/keyboard = initial(C.icon_keyboard) + var/all_states = icon_states(icon_file) + if (screen && (screen in all_states)) + I.Blend(icon(icon_file, screen, SOUTH), ICON_OVERLAY) + if (keyboard && (keyboard in all_states)) + I.Blend(icon(icon_file, keyboard, SOUTH), ICON_OVERLAY) Insert(initial(D.id), I) return ..() diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 30e61dba1c..2dec6f8654 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -20,8 +20,8 @@ //OTHER// ///////// var/datum/preferences/prefs = null - var/move_delay = 1 - + var/last_turn = 0 + var/move_delay = 0 var/area = null /////////////// diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 60a1f6c589..f2bb718be9 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -159,12 +159,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( GLOBAL_LIST_EMPTY(external_rsc_urls) #endif -/client/can_vv_get(var_name) - return var_name != NAMEOF(src, holder) && ..() - -/client/vv_edit_var(var_name, var_value) - return var_name != NAMEOF(src, holder) && ..() - /client/New(TopicData) world.SetConfig("APP/admin", ckey, "role=admin") //CITADEL EDIT - Allows admins to reboot in OOM situations var/tdata = TopicData //save this for later use @@ -213,7 +207,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) if(CONFIG_GET(flag/enable_localhost_rank) && !connecting_admin) var/localhost_addresses = list("127.0.0.1", "::1") if(isnull(address) || (address in localhost_addresses)) - var/datum/admin_rank/localhost_rank = new("!localhost!", 65535, 16384, 65535) //+EVERYTHING -DBRANKS *EVERYTHING + var/datum/admin_rank/localhost_rank = new("!localhost!", R_EVERYTHING, R_DBRANKS, R_EVERYTHING) //+EVERYTHING -DBRANKS *EVERYTHING new /datum/admins(localhost_rank, ckey, 1, 1) //preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum) prefs = GLOB.preferences_datums[ckey] @@ -229,7 +223,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) if(fexists(roundend_report_file())) verbs += /client/proc/show_previous_roundend_report - log_access("Login: [key_name(src)] from [address ? address : "localhost"]-[computer_id] || BYOND v[byond_version]") + var/full_version = "[byond_version].[byond_build ? byond_build : "xxx"]" + log_access("Login: [key_name(src)] from [address ? address : "localhost"]-[computer_id] || BYOND v[full_version]") + var/alert_mob_dupe_login = FALSE if(CONFIG_GET(flag/log_access)) for(var/I in GLOB.clients) @@ -255,8 +251,10 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) if(GLOB.player_details[ckey]) player_details = GLOB.player_details[ckey] + player_details.byond_version = full_version else player_details = new + player_details.byond_version = full_version GLOB.player_details[ckey] = player_details @@ -471,6 +469,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) qdel(query_get_related_ip) var/datum/DBQuery/query_get_related_cid = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]' AND ckey != '[sql_ckey]'") if(!query_get_related_cid.Execute()) + qdel(query_get_related_cid) return related_accounts_cid = "" while (query_get_related_cid.NextRow()) @@ -596,7 +595,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) else CRASH("Key check regex failed for [ckey]") - /client/proc/check_randomizer(topic) . = FALSE if (connection != "seeker") @@ -687,7 +685,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) var/sql_system_ckey = sanitizeSQL(system_ckey) var/sql_ckey = sanitizeSQL(ckey) //check to see if we noted them in the last day. - var/datum/DBQuery/query_get_notes = SSdbcore.NewQuery("SELECT id FROM [format_table_name("messages")] WHERE type = 'note' AND targetckey = '[sql_ckey]' AND adminckey = '[sql_system_ckey]' AND timestamp + INTERVAL 1 DAY < NOW() AND deleted = 0") + var/datum/DBQuery/query_get_notes = SSdbcore.NewQuery("SELECT id FROM [format_table_name("messages")] WHERE type = 'note' AND targetckey = '[sql_ckey]' AND adminckey = '[sql_system_ckey]' AND timestamp + INTERVAL 1 DAY < NOW() AND deleted = 0 AND expire_timestamp > NOW()") if(!query_get_notes.Execute()) qdel(query_get_notes) return @@ -696,7 +694,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) return qdel(query_get_notes) //regardless of above, make sure their last note is not from us, as no point in repeating the same note over and over. - query_get_notes = SSdbcore.NewQuery("SELECT adminckey FROM [format_table_name("messages")] WHERE targetckey = '[sql_ckey]' AND deleted = 0 ORDER BY timestamp DESC LIMIT 1") + query_get_notes = SSdbcore.NewQuery("SELECT adminckey FROM [format_table_name("messages")] WHERE targetckey = '[sql_ckey]' AND deleted = 0 AND expire_timestamp > NOW() ORDER BY timestamp DESC LIMIT 1") if(!query_get_notes.Execute()) qdel(query_get_notes) return @@ -705,7 +703,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) qdel(query_get_notes) return qdel(query_get_notes) - create_message("note", key, system_ckey, message, null, null, 0, 0) + create_message("note", key, system_ckey, message, null, null, 0, 0, null, 0, 0) /client/proc/check_ip_intel() @@ -759,9 +757,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) to_chat(src, "Your previous click was ignored because you've done too many in a second") return - if(ab) - return - if (prefs.hotkeys) // If hotkey mode is enabled, then clicking the map will automatically // unfocus the text bar. This removes the red color from the text bar @@ -842,11 +837,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) /client/proc/change_view(new_size) if (isnull(new_size)) CRASH("change_view called without argument.") + //CIT CHANGES START HERE - makes change_view change DEFAULT_VIEW to 15x15 depending on preferences if(prefs && CONFIG_GET(string/default_view)) if(!prefs.widescreenpref && new_size == CONFIG_GET(string/default_view)) new_size = "15x15" //END OF CIT CHANGES + view = new_size apply_clickcatcher() mob.reload_fullscreen() diff --git a/code/modules/client/player_details.dm b/code/modules/client/player_details.dm index ecc113b89c..ac5a14a555 100644 --- a/code/modules/client/player_details.dm +++ b/code/modules/client/player_details.dm @@ -1,3 +1,4 @@ /datum/player_details var/list/player_actions = list() - var/list/logging = list(INDIVIDUAL_ATTACK_LOG, INDIVIDUAL_SAY_LOG, INDIVIDUAL_EMOTE_LOG, INDIVIDUAL_OOC_LOG) + var/list/logging = list() + var/byond_version = "Unknown" \ No newline at end of file diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 239a574d94..7ec5611f39 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -16,7 +16,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) //doohickeys for savefiles var/path var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used - var/max_save_slots = 3 + var/max_save_slots = 8 //non-preference stuff var/muted = 0 @@ -77,7 +77,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/datum/species/pref_species = new /datum/species/human() //Mutant race var/list/features = list("mcolor" = "FFF", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "ears" = "None", "wings" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None", "legs" = "Normal Legs", "moth_wings" = "Plain") - var/list/custom_names = list("human", "clown", "mime", "ai", "cyborg", "religion", "deity") + var/list/custom_names = list() var/prefered_security_department = SEC_DEPT_RANDOM //Mob preview @@ -109,9 +109,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) // 0 = character settings, 1 = game preferences var/current_tab = 0 - // OOC Metadata: - var/metadata = "" - var/unlock_content = 0 var/list/ignoring = list() @@ -132,11 +129,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) /datum/preferences/New(client/C) parent = C - custom_names["human"] = random_unique_name() - custom_names["ai"] = pick(GLOB.ai_names) - custom_names["cyborg"] = DEFAULT_CYBORG_NAME - custom_names["clown"] = pick(GLOB.clown_names) - custom_names["mime"] = pick(GLOB.mime_names) + + for(var/custom_name_id in GLOB.preferences_custom_names) + custom_names[custom_name_id] = get_default_name(custom_name_id) + UI_style = GLOB.available_ui_styles[1] if(istype(C)) if(!IsGuestKey(C.key)) @@ -157,6 +153,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) menuoptions = list() return +#define APPEARANCE_CATEGORY_COLUMN "" +#define MAX_MUTANT_ROWS 4 + /datum/preferences/proc/ShowChoices(mob/user) if(!user || !user.client) return @@ -165,13 +164,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) else update_preview_icon(nude=FALSE) //EDIT END user << browse_rsc(preview_icon, "previewicon.png") - var/dat = "
" + var/list/dat = list("
") dat += "Character Settings" + dat += "Game Preferences" dat += "Character Appearance" dat += "Loadout" - dat += "Game Preferences" - if(!path) dat += "
Please create an account to save your preferences
" @@ -186,7 +184,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(S) dat += "
" var/name + var/unspaced_slots = 0 for(var/i=1, i<=max_save_slots, i++) + unspaced_slots++ + if(unspaced_slots > 4) + dat += "
" + unspaced_slots = 0 S.cd = "/character[i]" S["real_name"] >> name if(!name) @@ -214,17 +217,16 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Age: [age]
" dat += "Special Names:
" - dat += "Backup Human Name: [custom_names["human"]] " + var/old_group + for(var/custom_name_id in GLOB.preferences_custom_names) + var/namedata = GLOB.preferences_custom_names[custom_name_id] + if(!old_group) + old_group = namedata["group"] + else if(old_group != namedata["group"]) + old_group = namedata["group"] + dat += "
" + dat += "[namedata["pref_name"]]: [custom_names[custom_name_id]] " dat += "
" - dat += "Clown: [custom_names["clown"]] " - dat += "Mime: [custom_names["mime"]]" - dat += "
" - dat += "AI: [custom_names["ai"]] " - dat += "Cyborg: [custom_names["cyborg"]]" - dat += "
" - dat += "Chaplain religion: [custom_names["religion"]] " - dat += "Chaplain deity: [custom_names["deity"]]
" - dat += "Custom job preferences:
" dat += "Prefered security department: [prefered_security_department]
" @@ -249,19 +251,43 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Backpack:
[backbag]
" dat += "Uplink Spawn Location:
[uplink_spawn_loc]
" - if(pref_species.use_skintones) + var/use_skintones = pref_species.use_skintones + if(use_skintones) - dat += "" + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Skin Tone

" dat += "[skin_tone]
" + var/mutant_colors + if((MUTCOLORS in pref_species.species_traits) || (MUTCOLORS_PARTSONLY in pref_species.species_traits)) + + if(!use_skintones) + dat += APPEARANCE_CATEGORY_COLUMN + + dat += "

Mutant Color

" + + dat += "    Change
" + + mutant_colors = TRUE + + if((EYECOLOR in pref_species.species_traits) && !(NOEYES in pref_species.species_traits)) + + if(!use_skintones && !mutant_colors) + dat += APPEARANCE_CATEGORY_COLUMN + + dat += "

Eye Color

" + + dat += "    Change
" + + dat += "" + else if(use_skintones || mutant_colors) dat += "" if(HAIR in pref_species.species_traits) - dat += "" + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Hair Style

" @@ -269,9 +295,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "< >
" dat += "    Change
" - - dat += "" - dat += "

Facial Hair Style

" dat += "[facial_hair_style]
" @@ -280,129 +303,157 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "" - if((EYECOLOR in pref_species.species_traits) && !(NOEYES in pref_species.species_traits)) + //Mutant stuff + var/mutant_category = 0 - dat += "" - - dat += "

Eye Color

" - - dat += "    Change
" - - dat += "" - - - if((MUTCOLORS in pref_species.species_traits) || (MUTCOLORS_PARTSONLY in pref_species.species_traits)) - - dat += "" - - dat += "

Mutant Color

" - - dat += "    Change
" - - dat += "" - - if("tail_lizard" in pref_species.mutant_bodyparts) - dat += "" + if("tail_lizard" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Tail

" dat += "[features["tail_lizard"]]
" - dat += "" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 - if("snout" in pref_species.mutant_bodyparts) - dat += "" + if("snout" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Snout

" dat += "[features["snout"]]
" - dat += "" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 - if("horns" in pref_species.mutant_bodyparts) - dat += "" + if("horns" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Horns

" dat += "[features["horns"]]
" - dat += "" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 - if("frills" in pref_species.mutant_bodyparts) - dat += "" + if("frills" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Frills

" dat += "[features["frills"]]
" - dat += "" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 - if("spines" in pref_species.mutant_bodyparts) - dat += "" + if("spines" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Spines

" dat += "[features["spines"]]
" - dat += "" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 - if("body_markings" in pref_species.mutant_bodyparts) - dat += "" + if("body_markings" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Body Markings

" dat += "[features["body_markings"]]
" - dat += "" - if("legs" in pref_species.mutant_bodyparts) - dat += "" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 + + if("legs" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Legs

" dat += "[features["legs"]]
" - dat += "" - if("moth_wings" in pref_species.mutant_bodyparts) + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 - dat += "" + if("moth_wings" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Moth wings

" dat += "[features["moth_wings"]]
" - dat += "" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 + if("tail_human" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN + dat += "

Tail

" + + dat += "[features["tail_human"]]
" + + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 + + if("ears" in pref_species.default_features) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN + + dat += "

Ears

" + + dat += "[features["ears"]]
" + + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 if(CONFIG_GET(flag/join_with_mutant_humans)) - if("tail_human" in pref_species.mutant_bodyparts) - dat += "" - - dat += "

Tail

" - - dat += "[features["tail_human"]]
" - - dat += "" - - if("ears" in pref_species.mutant_bodyparts) - dat += "" - - dat += "

Ears

" - - dat += "[features["ears"]]
" - - dat += "" - - if("wings" in pref_species.mutant_bodyparts && GLOB.r_wings_list.len >1) - dat += "" + if("wings" in pref_species.default_features && GLOB.r_wings_list.len >1) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN dat += "

Wings

" dat += "[features["wings"]]
" - dat += "" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 + if(mutant_category) + dat += "" + mutant_category = 0 dat += ""*/ @@ -410,37 +461,24 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "
" dat += "

General Settings

" dat += "UI Style: [UI_style]
" - dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Default"]
" - dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"]
" - dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
" - dat += "PDA Style: [pda_style]
" - dat += "PDA Color:     Change
" dat += "tgui Monitors: [(tgui_lock) ? "Primary" : "All"]
" - dat += "Window Flashing: [(windowflashing) ? "Yes" : "No"]
" - dat += "Play admin midis: [(toggles & SOUND_MIDI) ? "Yes" : "No"]
" - dat += "Play lobby music: [(toggles & SOUND_LOBBY) ? "Yes" : "No"]
" - dat += "Ghost ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
" - dat += "Ghost sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
" - dat += "Ghost whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"]
" - dat += "Ghost radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "Yes" : "No"]
" - dat += "Ghost pda: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"]
" - dat += "Pull requests: [(chat_toggles & CHAT_PULLR) ? "Yes" : "No"]
" - dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Yes" : "No"]
" - if(CONFIG_GET(flag/allow_metadata)) - dat += "OOC Notes: Edit
" + dat += "tgui Style: [(tgui_fancy) ? "Fancy" : "No Frills"]
" + dat += "
" + dat += "Action Buttons: [(buttons_locked) ? "Locked In Place" : "Unlocked"]
" + dat += "Keybindings: [(hotkeys) ? "Hotkeys" : "Default"]
" + dat += "
" + dat += "PDA Color:     Change
" + dat += "PDA Style: [pda_style]
" + dat += "
" + dat += "Ghost Ears: [(chat_toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
" + dat += "Ghost Radio: [(chat_toggles & CHAT_GHOSTRADIO) ? "All Messages":"No Messages"]
" + dat += "Ghost Sight: [(chat_toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
" + dat += "Ghost Whispers: [(chat_toggles & CHAT_GHOSTWHISPER) ? "All Speech" : "Nearest Creatures"]
" + dat += "Ghost PDA: [(chat_toggles & CHAT_GHOSTPDA) ? "All Messages" : "Nearest Creatures"]
" - if(user.client) - if(user.client.holder) - dat += "Adminhelp Sound: [(toggles & SOUND_ADMINHELP)?"On":"Off"]
" - dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"On":"Off"]
" - - if(unlock_content || check_rights_for(user.client, R_ADMIN)) - dat += "OOC:     Change
" - - if(unlock_content) - dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" - dat += "Ghost Form: [ghost_form]
" - dat += "Ghost Orbit: [ghost_orbit]
" + if(unlock_content) + dat += "Ghost Form: [ghost_form]
" + dat += "Ghost Orbit: [ghost_orbit]
" var/button_name = "If you see this something went wrong." switch(ghost_accs) @@ -523,8 +561,8 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(days_remaining) dat += "Be [capitalize(i)]: \[IN [days_remaining] DAYS]
" else - dat += "Be [capitalize(i)]: [(i in be_special) ? "Yes" : "No"]
" - dat += "
" + dat += "Be [capitalize(i)]: [(i in be_special) ? "Enabled" : "Disabled"]
" + dat += "
" dat += "

Citadel Preferences

" //Because fuck me if preferences can't be fucking modularized and expected to update in a reasonable timeframe. dat += "Arousal:[arousable == TRUE ? "Enabled" : "Disabled"]
" dat += "Exhibitionist:[features["exhibitionist"] == TRUE ? "Yes" : "No"]
" @@ -536,10 +574,42 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "Screen Shake: [(screenshake==100) ? "Full" : ((screenshake==0) ? "None" : "[screenshake]")]
" if (user && user.client && !user.client.prefs.screenshake==0) dat += "Damage Screen Shake: [(damagescreenshake==1) ? "On" : ((damagescreenshake==0) ? "Off" : "Only when down")]
" + dat += "
" + dat += "Midround Antagonist: [(toggles & MIDROUND_ANTAG) ? "Enabled" : "Disabled"]
" dat += "
" - if(3) + dat += "" + + if(user.client.holder) + dat +="" + dat += "
" + dat += "

OOC Settings

" + dat += "Window Flashing: [(windowflashing) ? "Enabled":"Disabled"]
" + dat += "
" + dat += "Play Admin MIDIs: [(toggles & SOUND_MIDI) ? "Enabled":"Disabled"]
" + dat += "Play Lobby Music: [(toggles & SOUND_LOBBY) ? "Enabled":"Disabled"]
" + dat += "See Pull Requests: [(chat_toggles & CHAT_PULLR) ? "Enabled":"Disabled"]
" + dat += "
" + + if(user.client) + if(unlock_content) + dat += "BYOND Membership Publicity: [(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]
" + + if(unlock_content || check_rights_for(user.client, R_ADMIN)) + dat += "OOC Color:     Change
" + + dat += "
" + + dat += "

Admin Settings

" + + dat += "Adminhelp Sounds: [(toggles & SOUND_ADMINHELP)?"Enabled":"Disabled"]
" + dat += "Announce Login: [(toggles & ANNOUNCE_LOGIN)?"Enabled":"Disabled"]
" + dat += "
" + dat += "Combo HUD Lighting: [(toggles & COMBOHUD_LIGHTING)?"Full-bright":"No Change"]
" + dat += "
" + + if(3) if(!gear_tab) gear_tab = GLOB.loadout_items[1] dat += "" @@ -603,9 +673,12 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "" var/datum/browser/popup = new(user, "preferences", "
Character Setup
", 640, 770) - popup.set_content(dat) + popup.set_content(dat.Join()) popup.open(0) +#undef APPEARANCE_CATEGORY_COLUMN +#undef MAX_MUTANT_ROWS + /datum/preferences/proc/SetChoices(mob/user, limit = 17, list/splitJobs = list("Chief Engineer"), widthPerColumn = 295, height = 620) if(!SSjob) return @@ -939,8 +1012,9 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(href_list["jobbancheck"]) var/job = sanitizeSQL(href_list["jobbancheck"]) var/sql_ckey = sanitizeSQL(user.ckey) - var/datum/DBQuery/query_get_jobban = SSdbcore.NewQuery("SELECT reason, bantime, duration, expiration_time, (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey) FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[job]'") + var/datum/DBQuery/query_get_jobban = SSdbcore.NewQuery("SELECT reason, bantime, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey) FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[job]'") if(!query_get_jobban.warn_execute()) + qdel(query_get_jobban) return if(query_get_jobban.NextRow()) var/reason = query_get_jobban.item[1] @@ -954,6 +1028,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)" text += "." to_chat(user, text) + qdel(query_get_jobban) return if(href_list["preference"] == "job") @@ -1064,6 +1139,11 @@ GLOBAL_LIST_EMPTY(preferences_datums) random_character() if("input") + + if(href_list["preference"] in GLOB.preferences_custom_names) + ask_for_custom_name(user,href_list["preference"]) + + switch(href_list["preference"]) if("ghostform") if(unlock_content) @@ -1097,11 +1177,13 @@ GLOBAL_LIST_EMPTY(preferences_datums) ghost_others = GHOST_OTHERS_SIMPLE if("name") - var/new_name = reject_bad_name( input(user, "Choose your character's name:", "Character Preference") as text|null ) + var/new_name = input(user, "Choose your character's name:", "Character Preference") as text|null if(new_name) - real_name = new_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + new_name = reject_bad_name(new_name) + if(new_name) + real_name = new_name + else + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") if("age") var/new_age = input(user, "Choose your character's age:\n([AGE_MIN]-[AGE_MAX])", "Character Preference") as num|null @@ -1113,7 +1195,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(new_hair) hair_color = sanitize_hexcolor(new_hair) - if("hair_style") var/new_hair_style if(gender == MALE) @@ -1299,60 +1380,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(new_loc) uplink_spawn_loc = new_loc - if("human_name") - var/new_human_name = reject_bad_name( input(user, "Choose your character's backup human name, used in the event you are assigned a command role as another species:", "Character Preference") as text|null ) - if(new_human_name) - custom_names["human"] = new_human_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("clown_name") - var/new_clown_name = reject_bad_name( input(user, "Choose your character's clown name:", "Character Preference") as text|null ) - if(new_clown_name) - custom_names["clown"] = new_clown_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("mime_name") - var/new_mime_name = reject_bad_name( input(user, "Choose your character's mime name:", "Character Preference") as text|null ) - if(new_mime_name) - custom_names["mime"] = new_mime_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("ai_name") - var/new_ai_name = reject_bad_name( input(user, "Choose your character's AI name:", "Character Preference") as text|null, 1 ) - if(new_ai_name) - custom_names["ai"] = new_ai_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and .") - - if("cyborg_name") - var/raw_name = input(user, "Choose your character's cyborg name (Leave empty to use default naming scheme):", "Character Preference") as text|null - var/new_cyborg_name - if(!raw_name) - new_cyborg_name = DEFAULT_CYBORG_NAME - else - new_cyborg_name = reject_bad_name(raw_name,1 ) - if(new_cyborg_name) - custom_names["cyborg"] = new_cyborg_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, 0-9, -, ' and .") - - if("religion_name") - var/new_religion_name = reject_bad_name( input(user, "Choose your character's religion:", "Character Preference") as text|null ) - if(new_religion_name) - custom_names["religion"] = new_religion_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - - if("deity_name") - var/new_deity_name = reject_bad_name( input(user, "Choose your character's deity:", "Character Preference") as text|null ) - if(new_deity_name) - custom_names["deity"] = new_deity_name - else - to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - if("sec_dept") var/department = input(user, "Choose your prefered security department:", "Security Departments") as null|anything in GLOB.security_depts_prefs if(department) @@ -1396,7 +1423,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) else switch(href_list["preference"]) - //CITADEL PREFERENCES EDIT - I can't figure out how to modularize these, so they have to go here. :c -Pooj if("genital_colour") features["genitals_use_skintone"] = !features["genitals_use_skintone"] @@ -1530,6 +1556,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) parallax = WRAP(parallax - 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1) if (parent && parent.mob && parent.mob.hud_used) parent.mob.hud_used.update_parallax_pref(parent.mob) + // Citadel edit - Prefs don't work outside of this. :c if("hound_sleeper") cit_toggles ^= MEDIHOUND_SLEEPER @@ -1573,7 +1600,6 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("tab") if (href_list["tab"]) current_tab = text2num(href_list["tab"]) - if(href_list["preference"] == "gear") if(href_list["clear_loadout"]) LAZYCLEARLIST(chosen_gear) @@ -1603,23 +1629,25 @@ GLOBAL_LIST_EMPTY(preferences_datums) gear_points -= initial(G.cost) process_citadel_link(user, href_list) + ShowChoices(user) return 1 -/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1) +/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1, roundstart_checks = TRUE) if(be_random_name) real_name = pref_species.random_name(gender) if(be_random_body) random_character(gender) - if(CONFIG_GET(flag/humans_need_surnames) && (pref_species.id == "human")) - var/firstspace = findtext(real_name, " ") - var/name_length = length(real_name) - if(!firstspace) //we need a surname - real_name += " [pick(GLOB.last_names)]" - else if(firstspace == name_length) - real_name += "[pick(GLOB.last_names)]" + if(roundstart_checks) + if(CONFIG_GET(flag/humans_need_surnames) && (pref_species.id == "human")) + var/firstspace = findtext(real_name, " ") + var/name_length = length(real_name) + if(!firstspace) //we need a surname + real_name += " [pick(GLOB.last_names)]" + else if(firstspace == name_length) + real_name += "[pick(GLOB.last_names)]" character.real_name = real_name character.name = character.real_name @@ -1648,15 +1676,48 @@ GLOBAL_LIST_EMPTY(preferences_datums) character.dna.features = features.Copy() character.dna.real_name = character.real_name var/datum/species/chosen_species - if(pref_species.id in GLOB.roundstart_races) + if(!roundstart_checks || (pref_species.id in GLOB.roundstart_races)) chosen_species = pref_species.type else chosen_species = /datum/species/human pref_species = new /datum/species/human save_character() - character.set_species(chosen_species, icon_update=0) + character.set_species(chosen_species, icon_update = FALSE, pref_load = TRUE) if(icon_updates) character.update_body() character.update_hair() character.update_body_parts() + +/datum/preferences/proc/get_default_name(name_id) + switch(name_id) + if("human") + return random_unique_name() + if("ai") + return pick(GLOB.ai_names) + if("cyborg") + return DEFAULT_CYBORG_NAME + if("clown") + return pick(GLOB.clown_names) + if("mime") + return pick(GLOB.mime_names) + return random_unique_name() + +/datum/preferences/proc/ask_for_custom_name(mob/user,name_id) + var/namedata = GLOB.preferences_custom_names[name_id] + if(!namedata) + return + + var/raw_name = input(user, "Choose your character's [namedata["qdesc"]]:","Character Preference") as text|null + if(!raw_name) + if(namedata["allow_null"]) + custom_names[name_id] = get_default_name(name_id) + else + return + else + var/sanitized_name = reject_bad_name(raw_name,namedata["allow_numbers"]) + if(!sanitized_name) + to_chat(user, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z,[namedata["allow_numbers"] ? ",0-9," : ""] -, ' and .") + return + else + custom_names[name_id] = sanitized_name diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 588829c9ef..8152f42efb 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -45,45 +45,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car return /datum/preferences/proc/update_character(current_version, savefile/S) - if(current_version < 20)//Raise this to the max savefile version every time we change something so we don't sanitize this whole list every time you save. - features["mam_body_markings"] = sanitize_inlist(features["mam_body_markings"], GLOB.mam_body_markings_list) - features["mam_ears"] = sanitize_inlist(features["mam_ears"], GLOB.mam_ears_list) - features["mam_tail"] = sanitize_inlist(features["mam_tail"], GLOB.mam_tails_list) - features["taur"] = sanitize_inlist(features["taur"], GLOB.taur_list) - //Xeno features - features["xenotail"] = sanitize_inlist(features["xenotail"], GLOB.xeno_tail_list) - features["xenohead"] = sanitize_inlist(features["xenohead"], GLOB.xeno_head_list) - features["xenodorsal"] = sanitize_inlist(features["xenodorsal"], GLOB.xeno_dorsal_list) - //cock features - features["has_cock"] = sanitize_integer(features["has_cock"], 0, 1, 0) - features["cock_shape"] = sanitize_inlist(features["cock_shape"], GLOB.cock_shapes_list, "Human") - features["cock_color"] = sanitize_hexcolor(features["cock_color"], 3, 0) - features["cock_length"] = sanitize_integer(features["cock_length"], COCK_SIZE_MIN, COCK_SIZE_MAX, 6) - //balls features - features["has_balls"] = sanitize_integer(features["has_balls"], 0, 1, 0) - features["balls_color"] = sanitize_hexcolor(features["balls_color"], 3, 0) - features["balls_size"] = sanitize_integer(features["balls_size"], BALLS_SIZE_MIN, BALLS_SIZE_MAX, BALLS_SIZE_DEF) - features["balls_sack_size"] = sanitize_integer(features["balls_sack_size"], BALLS_SACK_SIZE_MIN, BALLS_SACK_SIZE_MAX, BALLS_SACK_SIZE_DEF) - features["balls_fluid"] = sanitize_inlist(features["balls_fluid"], GLOB.cum_id_list, "semen") - //breasts features - features["has_breasts"] = sanitize_integer(features["has_breasts"], 0, 1, 0) - features["breasts_size"] = sanitize_inlist(features["breasts_size"], GLOB.breasts_size_list, "C") - features["breasts_shape"] = sanitize_inlist(features["breasts_shape"], GLOB.breasts_shapes_list, "Pair") - features["breasts_color"] = sanitize_hexcolor(features["breasts_color"], 3, 0) - features["breasts_fluid"] = sanitize_inlist(features["breasts_fluid"], GLOB.milk_id_list, "milk") - //vagina features - features["has_vag"] = sanitize_integer(features["has_vag"], 0, 1, 0) - features["vag_shape"] = sanitize_inlist(features["vag_shape"], GLOB.vagina_shapes_list, "Human") - features["vag_color"] = sanitize_hexcolor(features["vag_color"], 3, 0) - //womb features - features["has_womb"] = sanitize_integer(features["has_womb"], 0, 1, 0) - if(current_version < 19) pda_style = "mono" if(current_version < 20) pda_color = "#808000" - /datum/preferences/proc/load_path(ckey,filename="preferences.sav") if(!ckey) return @@ -180,6 +146,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car autostand = sanitize_integer(autostand, 0, 1, initial(autostand)) cit_toggles = sanitize_integer(cit_toggles, 0, 65535, initial(cit_toggles)) + return 1 /datum/preferences/proc/save_preferences() @@ -260,7 +227,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["species"] >> species_id if(species_id) var/newtype = GLOB.species_list[species_id] - pref_species = new newtype() + if(newtype) + pref_species = new newtype if(!S["features["mcolor"]"] || S["features["mcolor"]"] == "#000") WRITE_FILE(S["features["mcolor"]"] , "#FFF") @@ -297,13 +265,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car else S["feature_human_tail"] >> features["tail_human"] S["feature_human_ears"] >> features["ears"] - S["human_name"] >> custom_names["human"] - S["clown_name"] >> custom_names["clown"] - S["mime_name"] >> custom_names["mime"] - S["ai_name"] >> custom_names["ai"] - S["cyborg_name"] >> custom_names["cyborg"] - S["religion_name"] >> custom_names["religion"] - S["deity_name"] >> custom_names["deity"] + + //Custom names + for(var/custom_name_id in GLOB.preferences_custom_names) + var/savefile_slot_name = custom_name_id + "_name" //TODO remove this + S[savefile_slot_name] >> custom_names[custom_name_id] + S["prefered_security_department"] >> prefered_security_department //Jobs @@ -374,19 +341,30 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car else //We have no old flavortext, default to new S["feature_flavor_text"] >> features["flavor_text"] + //try to fix any outdated data if necessary if(needs_update >= 0) update_character(needs_update, S) //needs_update == savefile_version if we need an update (positive integer) //Sanitize - real_name = reject_bad_name(real_name) - if(!features["mcolor"] || features["mcolor"] == "#000") - features["mcolor"] = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F") + + real_name = reject_bad_name(real_name) + gender = sanitize_gender(gender) if(!real_name) real_name = random_unique_name(gender) + + for(var/custom_name_id in GLOB.preferences_custom_names) + var/namedata = GLOB.preferences_custom_names[custom_name_id] + custom_names[custom_name_id] = reject_bad_name(custom_names[custom_name_id],namedata["allow_numbers"]) + if(!custom_names[custom_name_id]) + custom_names[custom_name_id] = get_default_name(custom_name_id) + + if(!features["mcolor"] || features["mcolor"] == "#000") + features["mcolor"] = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F") + be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name)) be_random_body = sanitize_integer(be_random_body, 0, 1, initial(be_random_body)) - gender = sanitize_gender(gender) + if(gender == MALE) hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_male_list) facial_hair_style = sanitize_inlist(facial_hair_style, GLOB.facial_hair_styles_male_list) @@ -476,13 +454,12 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car WRITE_FILE(S["feature_lizard_body_markings"] , features["body_markings"]) WRITE_FILE(S["feature_lizard_legs"] , features["legs"]) WRITE_FILE(S["feature_moth_wings"] , features["moth_wings"]) - WRITE_FILE(S["human_name"] , custom_names["human"]) - WRITE_FILE(S["clown_name"] , custom_names["clown"]) - WRITE_FILE(S["mime_name"] , custom_names["mime"]) - WRITE_FILE(S["ai_name"] , custom_names["ai"]) - WRITE_FILE(S["cyborg_name"] , custom_names["cyborg"]) - WRITE_FILE(S["religion_name"] , custom_names["religion"]) - WRITE_FILE(S["deity_name"] , custom_names["deity"]) + + //Custom names + for(var/custom_name_id in GLOB.preferences_custom_names) + var/savefile_slot_name = custom_name_id + "_name" //TODO remove this + WRITE_FILE(S[savefile_slot_name],custom_names[custom_name_id]) + WRITE_FILE(S["prefered_security_department"] , prefered_security_department) //Jobs diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm index 2f4922fddf..bc163b1781 100644 --- a/code/modules/client/verbs/ooc.dm +++ b/code/modules/client/verbs/ooc.dm @@ -50,9 +50,7 @@ to_chat(src, "You have OOC muted.") return - - log_talk(mob,"[key_name(src)] : [raw_msg]",LOGOOC) - mob.log_message("[key]: [raw_msg]", INDIVIDUAL_OOC_LOG) + mob.log_talk(raw_msg, LOG_OOC) var/keyname = key if(prefs.unlock_content) diff --git a/code/modules/client/verbs/suicide.dm b/code/modules/client/verbs/suicide.dm index 6b273cb818..64d58cd40b 100644 --- a/code/modules/client/verbs/suicide.dm +++ b/code/modules/client/verbs/suicide.dm @@ -205,18 +205,24 @@ log_game("[key_name(src)] (job: [src.job ? "[src.job]" : "None"]) committed suicide at [AREACOORD(src)].") /mob/living/proc/canSuicide() - if(stat == CONSCIOUS) - return TRUE - else if(stat == DEAD) - to_chat(src, "You're already dead!") - else if(stat == UNCONSCIOUS) - to_chat(src, "You need to be conscious to suicide!") + switch(stat) + if(CONSCIOUS) + return TRUE + if(SOFT_CRIT) + to_chat(src, "You can't commit suicide while in a critical condition!") + if(UNCONSCIOUS) + to_chat(src, "You need to be conscious to commit suicide!") + if(DEAD) + to_chat(src, "You're already dead!") return /mob/living/carbon/canSuicide() if(!..()) return - if(!canmove || restrained()) //just while I finish up the new 'fun' suiciding verb. This is to prevent metagaming via suicide - to_chat(src, "You can't commit suicide whilst restrained! ((You can type Ghost instead however.))") + if(IsStun() || IsKnockdown()) //just while I finish up the new 'fun' suiciding verb. This is to prevent metagaming via suicide + to_chat(src, "You can't commit suicide while stunned! ((You can type Ghost instead however.))") + return + if(restrained()) + to_chat(src, "You can't commit suicide while restrained! ((You can type Ghost instead however.))") return return TRUE diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 1dde03a8ed..b9ce170dc5 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -56,6 +56,22 @@ if(M.putItemFromInventoryInHandIfPossible(src, H.held_index)) add_fingerprint(usr) +/obj/item/reagent_containers/food/snacks/clothing + name = "oops" + desc = "If you're reading this it means I messed up. This is related to moths eating clothes and I didn't know a better way to do it than making a new food object." + list_reagents = list("nutriment" = 1) + tastes = list("dust" = 1, "lint" = 1) + +/obj/item/clothing/attack(mob/M, mob/user, def_zone) + if(user.a_intent != INTENT_HARM && ismoth(M)) + var/obj/item/reagent_containers/food/snacks/clothing/clothing_as_food = new + clothing_as_food.name = name + if(clothing_as_food.attack(M, user, def_zone)) + take_damage(15, sound_effect=FALSE) + qdel(clothing_as_food) + else + return ..() + /obj/item/clothing/attackby(obj/item/W, mob/user, params) if(damaged_clothes && istype(W, /obj/item/stack/sheet/cloth)) var/obj/item/stack/sheet/cloth/C = W diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm index 0e9be23aed..817fe59dcd 100644 --- a/code/modules/clothing/gloves/_gloves.dm +++ b/code/modules/clothing/gloves/_gloves.dm @@ -13,9 +13,9 @@ /obj/item/clothing/gloves/ComponentInitialize() . = ..() - AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT), CALLBACK(src, .proc/clean_blood)) + AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT = CALLBACK(src, .proc/clean_blood))) -/obj/item/clothing/gloves/proc/clean_blood(strength) +/obj/item/clothing/gloves/proc/clean_blood(datum/source, strength) if(strength < CLEAN_STRENGTH_BLOOD) return transfer_blood = 0 diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index e27b1e9e13..6c22334f78 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -70,7 +70,7 @@ if(M.a_intent == INTENT_HARM) M.changeNext_move(CLICK_CD_RAPID) if(warcry) - M.say("[warcry]", ignore_spam = TRUE) + M.say("[warcry]", ignore_spam = TRUE, forced = "north star warcry") .= FALSE /obj/item/clothing/gloves/rapid/attack_self(mob/user) diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index ae4d80a0e4..f7c52c909e 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -79,6 +79,6 @@ clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL | BLOCK_GAS_SMOKE_EFFECT flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT cold_protection = HEAD min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index 61234b3f46..ad8fafb872 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -12,10 +12,10 @@ /obj/item/clothing/head/chefhat/suicide_act(mob/user) user.visible_message("[user] is donning [src]! It looks like [user.p_theyre()] trying to become a chef.") - user.say("Bork Bork Bork!") + user.say("Bork Bork Bork!", forced = "chef hat suicide") sleep(20) user.visible_message("[user] climbs into an imaginary oven!") - user.say("BOOORK!") + user.say("BOOORK!", forced = "chef hat suicide") playsound(user, 'sound/machines/ding.ogg', 50, 1) return(FIRELOSS) @@ -59,8 +59,8 @@ /obj/item/clothing/head/fedora/det_hat name = "detective's fedora" desc = "There's only one man who can sniff out the dirty stench of crime, and he's likely wearing this hat." - icon_state = "detective" armor = list("melee" = 25, "bullet" = 5, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) + icon_state = "detective" var/candy_cooldown = 0 pocket_storage_component_path = /datum/component/storage/concrete/pockets/small/detective dog_fashion = /datum/dog_fashion/head/detective diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index 1c23afaa82..281a48bfda 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -193,7 +193,7 @@ return 0 var/mob/living/carbon/human/H = user user.visible_message("[user] is donning [src]! It looks like [user.p_theyre()] trying to be nice to girls.") - user.say("M'lady.") + user.say("M'lady.", forced = "fedora suicide") sleep(10) H.facial_hair_style = "Neckbeard" return(BRUTELOSS) @@ -236,6 +236,7 @@ w_class = WEIGHT_CLASS_SMALL attack_verb = list("warned", "cautioned", "smashed") resistance_flags = NONE + dynamic_hair_suffix = "" /obj/item/clothing/head/santa name = "santa hat" @@ -326,6 +327,7 @@ name = "french beret" desc = "A quality beret, infused with the aroma of chain-smoking, wine-swilling Parisians. You feel less inclined to engage military conflict, for some reason." icon_state = "beretblack" + dynamic_hair_suffix = "" /obj/item/clothing/head/frenchberet/speechModification(M) if(copytext(M, 1, 2) != "*") diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index 86763e060b..36ba2fa76c 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -61,8 +61,8 @@ /obj/item/clothing/head/hardhat/cakehat/turn_on() ..() - force = 1 - throwforce = 1 + force = 15 + throwforce = 15 damtype = BURN hitsound = 'sound/items/welder.ogg' START_PROCESSING(SSobj, src) diff --git a/code/modules/clothing/masks/hailer.dm b/code/modules/clothing/masks/hailer.dm index 3f21780f15..8860650fbc 100644 --- a/code/modules/clothing/masks/hailer.dm +++ b/code/modules/clothing/masks/hailer.dm @@ -39,6 +39,8 @@ actions_types = list(/datum/action/item_action/halt) /obj/item/clothing/mask/gas/sechailer/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE switch(aggressiveness) if(1) to_chat(user, "You set the restrictor to the middle position.") diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index c0d41aeb68..4cc339a776 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -272,3 +272,24 @@ icon_state = "scarecrow_sack" item_state = "scarecrow_sack" flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR + +/obj/item/clothing/mask/gondola + name = "gondola mask" + desc = "Genuine gondola fur." + icon_state = "gondola" + item_state = "gondola" + flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR + w_class = WEIGHT_CLASS_SMALL + +/obj/item/clothing/mask/gondola/speechModification(M) + if(copytext(M, 1, 2) != "*") + M = " [M]" + var/list/spurdo_words = strings("spurdo_replacement.json", "spurdo") + for(var/key in spurdo_words) + var/value = spurdo_words[key] + if(islist(value)) + value = pick(value) + M = replacetextEx(M,regex(uppertext(key),"g"), "[uppertext(value)]") + M = replacetextEx(M,regex(capitalize(key),"g"), "[capitalize(value)]") + M = replacetextEx(M,regex(key,"g"), "[value]") + return trim(M) diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm index 88b51963e5..325c124867 100644 --- a/code/modules/clothing/outfits/standard.dm +++ b/code/modules/clothing/outfits/standard.dm @@ -424,6 +424,17 @@ mask = /obj/item/clothing/mask/breath suit_store = /obj/item/tank/internals/oxygen - - - +/datum/outfit/debug //Debug objs plus hardsuit + name = "Debug outfit" + uniform = /obj/item/clothing/under/patriotsuit + suit = /obj/item/clothing/suit/space/hardsuit/syndi/elite + shoes = /obj/item/clothing/shoes/magboots/advance + suit_store = /obj/item/tank/internals/oxygen + mask = /obj/item/clothing/mask/gas/welding + belt = /obj/item/storage/belt/utility/chief/full + gloves = /obj/item/clothing/gloves/combat + id = /obj/item/card/id/ert + glasses = /obj/item/clothing/glasses/meson/night + ears = /obj/item/radio/headset/headset_cent/commander + back = /obj/item/storage/backpack/holding + backpack_contents = list(/obj/item/card/emag=1, /obj/item/flashlight/emp/debug=1, /obj/item/construction/rcd/combat=1, /obj/item/gun/magic/wand/resurrection/debug=1, /obj/item/melee/transforming/energy/axe=1) diff --git a/code/modules/clothing/outfits/vv_outfit.dm b/code/modules/clothing/outfits/vv_outfit.dm new file mode 100644 index 0000000000..103a152ec8 --- /dev/null +++ b/code/modules/clothing/outfits/vv_outfit.dm @@ -0,0 +1,143 @@ +// This outfit preserves varedits made on the items +// Created from admin helpers. +/datum/outfit/varedit + var/list/vv_values + var/list/stored_access + +/datum/outfit/varedit/pre_equip(mob/living/carbon/human/H, visualsOnly) + H.delete_equipment() //Applying VV to wrong objects is not reccomended. + . = ..() + +/datum/outfit/varedit/proc/set_equipement_by_slot(slot,item_path) + switch(slot) + if(SLOT_W_UNIFORM) + uniform = item_path + if(SLOT_BACK) + back = item_path + if(SLOT_WEAR_SUIT) + suit = item_path + if(SLOT_BELT) + belt = item_path + if(SLOT_GLOVES) + gloves = item_path + if(SLOT_SHOES) + shoes = item_path + if(SLOT_HEAD) + head = item_path + if(SLOT_WEAR_MASK) + mask = item_path + if(SLOT_NECK) + neck = item_path + if(SLOT_EARS) + ears = item_path + if(SLOT_GLASSES) + glasses = item_path + if(SLOT_WEAR_ID) + id = item_path + if(SLOT_S_STORE) + suit_store = item_path + if(SLOT_L_STORE) + l_pocket = item_path + if(SLOT_R_STORE) + r_pocket = item_path + + +/proc/collect_vv(obj/item/I) + //Temporary/Internal stuff, do not copy these. + var/static/list/ignored_vars = list("vars","x","y","z","plane","layer","override","animate_movement","pixel_step_size","screen_loc","fingerprintslast","tip_timer") + + if(istype(I) && I.datum_flags & DF_VAR_EDITED) + var/list/vedits = list() + for(var/varname in I.vars) + if(!I.can_vv_get(varname)) + continue + if(varname in ignored_vars) + continue + var/vval = I.vars[varname] + //Does it even work ? + if(vval == initial(I.vars[varname])) + continue + //Only text/numbers and icons variables to make it less weirdness prone. + if(!istext(vval) && !isnum(vval) && !isicon(vval)) + continue + vedits[varname] = I.vars[varname] + return vedits + +/mob/living/carbon/human/proc/copy_outfit() + var/datum/outfit/varedit/O = new + + //Copy equipment + var/list/result = list() + var/list/slots_to_check = list(SLOT_W_UNIFORM,SLOT_BACK,SLOT_WEAR_SUIT,SLOT_BELT,SLOT_GLOVES,SLOT_SHOES,SLOT_HEAD,SLOT_WEAR_MASK,SLOT_NECK,SLOT_EARS,SLOT_GLASSES,SLOT_WEAR_ID,SLOT_S_STORE,SLOT_L_STORE,SLOT_R_STORE) + for(var/s in slots_to_check) + var/obj/item/I = get_item_by_slot(s) + var/vedits = collect_vv(I) + if(vedits) + result["[s]"] = vedits + if(istype(I)) + O.set_equipement_by_slot(s,I.type) + + //Copy access + O.stored_access = list() + var/obj/item/id_slot = get_item_by_slot(SLOT_WEAR_ID) + if(id_slot) + O.stored_access |= id_slot.GetAccess() + //Copy hands + if(held_items.len >= 2) //Not in the mood to let outfits transfer amputees + var/obj/item/left_hand = held_items[1] + var/obj/item/right_hand = held_items[2] + if(istype(left_hand)) + O.l_hand = left_hand.type + var/vedits = collect_vv(left_hand) + if(vedits) + result["LHAND"] = vedits + if(istype(right_hand)) + O.r_hand = right_hand.type + var/vedits = collect_vv(left_hand) + if(vedits) + result["RHAND"] = vedits + O.vv_values = result + //Copy backpack contents if exist. + var/obj/item/backpack = get_item_by_slot(SLOT_BACK) + if(istype(backpack) && SEND_SIGNAL(backpack, COMSIG_CONTAINS_STORAGE)) + var/list/bp_stuff = list() + var/list/typecounts = list() + SEND_SIGNAL(backpack, COMSIG_TRY_STORAGE_RETURN_INVENTORY, bp_stuff, FALSE) + for(var/obj/item/I in bp_stuff) + if(typecounts[I.type]) + typecounts[I.type] += 1 + else + typecounts[I.type] = 1 + O.backpack_contents = typecounts + //TODO : Copy varedits from backpack stuff too. + //Copy implants + O.implants = list() + for(var/obj/item/implant/I in implants) + O.implants |= I.type + //Copy to outfit cache + var/outfit_name = stripped_input(usr,"Enter the outfit name") + O.name = outfit_name + GLOB.custom_outfits += O + to_chat(usr,"Outfit registered, use select equipment to equip it.") + +/datum/outfit/varedit/post_equip(mob/living/carbon/human/H, visualsOnly) + . = ..() + //Apply VV + for(var/slot in vv_values) + var/list/edits = vv_values[slot] + var/obj/item/I + switch(slot) + if("LHAND") + I = H.held_items[1] + if("RHAND") + I = H.held_items[2] + else + I = H.get_item_by_slot(text2num(slot)) + for(var/vname in edits) + I.vv_edit_var(vname,edits[vname]) + //Apply access + var/obj/item/id_slot = H.get_item_by_slot(SLOT_WEAR_ID) + if(id_slot) + var/obj/item/card/id/card = id_slot.GetID() + if(istype(card)) + card.access |= stored_access \ No newline at end of file diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index e3c32cd26c..b03d71221d 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -17,7 +17,7 @@ /obj/item/clothing/shoes/ComponentInitialize() . = ..() - AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT), CALLBACK(src, .proc/clean_blood)) + AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT = CALLBACK(src, .proc/clean_blood))) /obj/item/clothing/shoes/suicide_act(mob/living/carbon/user) if(rand(2)>1) @@ -76,7 +76,7 @@ var/mob/M = loc M.update_inv_shoes() -/obj/item/clothing/shoes/proc/clean_blood(strength) +/obj/item/clothing/shoes/proc/clean_blood(datum/source, strength) if(strength < CLEAN_STRENGTH_BLOOD) return bloody_shoes = list(BLOOD_STATE_HUMAN = 0,BLOOD_STATE_XENO = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0) diff --git a/code/modules/clothing/shoes/bananashoes.dm b/code/modules/clothing/shoes/bananashoes.dm index 3dc80e0ded..b634894805 100644 --- a/code/modules/clothing/shoes/bananashoes.dm +++ b/code/modules/clothing/shoes/bananashoes.dm @@ -10,7 +10,7 @@ /obj/item/clothing/shoes/clown_shoes/banana_shoes/Initialize() . = ..() - AddComponent(/datum/component/material_container, list(MAT_BANANIUM), 200000, TRUE, list(/obj/item/stack)) + AddComponent(/datum/component/material_container, list(MAT_BANANIUM), 200000, TRUE, /obj/item/stack) AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 75) if(always_noslip) clothing_flags |= NOSLIP diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index c81505d8e8..a6bcf23a2d 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -27,7 +27,7 @@ armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 90, "acid" = 50) /obj/item/clothing/shoes/sandal - desc = "A pair of rather plain, wooden sandals." + desc = "A pair of rather plain wooden sandals." name = "sandals" icon_state = "wizard" strip_delay = 50 @@ -91,7 +91,7 @@ /obj/item/clothing/shoes/clown_shoes/jester name = "jester shoes" - desc = "A court jesters shoes, updated with modern squeaking technology." + desc = "A court jester's shoes, updated with modern squeaking technology." icon_state = "jester_shoes" /obj/item/clothing/shoes/jackboots @@ -142,8 +142,8 @@ resistance_flags = FIRE_PROOF /obj/item/clothing/shoes/cult - name = "nar-sian invoker boots" - desc = "A pair of boots worn by the followers of Nar-Sie." + name = "\improper Nar'Sien invoker boots" + desc = "A pair of boots worn by the followers of Nar'Sie." icon_state = "cult" item_state = "cult" item_color = "cult" diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 2e5896098f..0794ef29e7 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -190,7 +190,7 @@ item_color = "atmospherics" armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) heat_protection = HEAD //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT /obj/item/clothing/suit/space/hardsuit/engine/atmos name = "atmospherics hardsuit" @@ -199,7 +199,7 @@ item_state = "atmo_hardsuit" armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/atmos @@ -212,7 +212,7 @@ item_color = "white" armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 90, "fire" = 100, "acid" = 90) heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT /obj/item/clothing/suit/space/hardsuit/engine/elite icon_state = "hardsuit-white" @@ -221,7 +221,7 @@ item_state = "ce_hardsuit" armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 90, "fire" = 100, "acid" = 90) heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite jetpack = /obj/item/tank/jetpack/suit @@ -359,7 +359,7 @@ item_color = "syndielite" armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT visor_flags_inv = 0 visor_flags = 0 on = FALSE @@ -375,7 +375,7 @@ helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = FIRE_PROOF | ACID_PROOF //The Owl Hardsuit @@ -410,7 +410,7 @@ resistance_flags = FIRE_PROOF | ACID_PROOF //No longer shall our kind be foiled by lone chemists with spray bottles! armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 20, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) heat_protection = HEAD //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT /obj/item/clothing/suit/space/hardsuit/wizard icon_state = "hardsuit-wiz" @@ -422,7 +422,7 @@ armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 20, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) allowed = list(/obj/item/teleportation_scroll, /obj/item/tank/internals) heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT helmettype = /obj/item/clothing/head/helmet/space/hardsuit/wizard /obj/item/clothing/suit/space/hardsuit/wizard/Initialize() @@ -542,7 +542,7 @@ resistance_flags = FIRE_PROOF | ACID_PROOF flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR //we want to see the mask heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT actions_types = list() /obj/item/clothing/head/helmet/space/hardsuit/captain/attack_self() @@ -556,7 +556,7 @@ armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) resistance_flags = FIRE_PROOF | ACID_PROOF heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT //this needed to be added a long fucking time ago + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT //this needed to be added a long fucking time ago helmettype = /obj/item/clothing/head/helmet/space/hardsuit/captain /obj/item/clothing/suit/space/hardsuit/captain/Initialize() @@ -628,7 +628,7 @@ if (mobhook && mobhook.parent != user) QDEL_NULL(mobhook) if (!mobhook) - mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED), CALLBACK(src, .proc/on_mob_move)) + mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_mob_move))) else QDEL_NULL(mobhook) @@ -800,7 +800,7 @@ recharge_delay = 15 armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) strip_delay = 130 - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/swat dog_fashion = /datum/dog_fashion/back/deathsquad @@ -812,5 +812,5 @@ item_color = "syndi" armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) strip_delay = 130 - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT actions_types = list() diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index a4b82c8234..dcd344563a 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -22,7 +22,7 @@ Contains: item_state = "deathsquad" armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) strip_delay = 130 - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = FIRE_PROOF | ACID_PROOF actions_types = list() @@ -37,7 +37,7 @@ Contains: allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals, /obj/item/kitchen/knife/combat) armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) strip_delay = 130 - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = FIRE_PROOF | ACID_PROOF helmettype = /obj/item/clothing/head/helmet/space/hardsuit/deathsquad dog_fashion = /datum/dog_fashion/back/deathsquad @@ -62,7 +62,7 @@ Contains: flags_inv = 0 armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) strip_delay = 130 - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = FIRE_PROOF | ACID_PROOF /obj/item/clothing/suit/space/officer @@ -77,7 +77,7 @@ Contains: allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals) armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) strip_delay = 130 - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = FIRE_PROOF | ACID_PROOF //NASA Voidsuit @@ -241,7 +241,7 @@ Contains: item_state = "griffinhat" armor = list("melee" = 20, "bullet" = 40, "laser" = 30, "energy" = 25, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 80) strip_delay = 130 - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = ACID_PROOF | FIRE_PROOF /obj/item/clothing/suit/space/freedom @@ -252,7 +252,7 @@ Contains: allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals) armor = list("melee" = 20, "bullet" = 40, "laser" = 30,"energy" = 25, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 80) strip_delay = 130 - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = ACID_PROOF | FIRE_PROOF slowdown = 0 @@ -285,7 +285,7 @@ Contains: icon_state = "hardsuit0-prt" item_state = "hardsuit0-prt" item_color = "knight_grey" - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT actions_types = list() resistance_flags = FIRE_PROOF @@ -295,7 +295,7 @@ Contains: icon_state = "knight_grey" item_state = "knight_grey" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = FIRE_PROOF /obj/item/clothing/suit/space/hardsuit/ert/paranormal/Initialize() diff --git a/code/modules/clothing/suits/cloaks.dm b/code/modules/clothing/suits/cloaks.dm index 85767852e4..173d50ba64 100644 --- a/code/modules/clothing/suits/cloaks.dm +++ b/code/modules/clothing/suits/cloaks.dm @@ -81,7 +81,7 @@ hoodtype = /obj/item/clothing/head/hooded/cloakhood/drake heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = FIRE_PROOF | ACID_PROOF /obj/item/clothing/head/hooded/cloakhood/drake @@ -90,5 +90,5 @@ desc = "The skull of a dragon." armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = FIRE_PROOF | ACID_PROOF diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 858f65b64c..9d039e0cb6 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -31,6 +31,21 @@ /* * Costume */ +/obj/item/clothing/suit/hooded/flashsuit + name = "flashy costume" + desc = "What did you expect?" + icon_state = "flashsuit" + item_state = "armor" + body_parts_covered = CHEST|GROIN + hoodtype = /obj/item/clothing/head/hooded/flashsuit + +/obj/item/clothing/head/hooded/flashsuit + name = "flash button" + desc = "You will learn to fear the flash." + icon_state = "flashsuit" + body_parts_covered = HEAD + flags_inv = HIDEHAIR|HIDEEARS|HIDEFACIALHAIR|HIDEFACE|HIDEMASK + /obj/item/clothing/suit/pirate name = "pirate coat" desc = "Yarr." @@ -397,7 +412,7 @@ desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable." icon_state = "militaryjacket" item_state = "militaryjacket" - allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver, /obj/item/gun/ballistic/revolver/detective, /obj/item/radio) + allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver, /obj/item/radio) /obj/item/clothing/suit/jacket/letterman name = "letterman jacket" @@ -599,7 +614,10 @@ name = "ghost sheet" desc = "The hands float by themselves, so it's extra spooky." icon_state = "ghost_sheet" - item_state = "ghost_sheet_item" + item_state = "ghost_sheet" + throwforce = 0 + throw_speed = 1 + throw_range = 2 w_class = WEIGHT_CLASS_TINY - flags_inv = HIDEGLOVES|HIDEMASK|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR + flags_inv = HIDEGLOVES|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR alternate_worn_layer = UNDER_HEAD_LAYER diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm index e71704132a..7542a0dd59 100644 --- a/code/modules/clothing/suits/reactive_armour.dm +++ b/code/modules/clothing/suits/reactive_armour.dm @@ -221,7 +221,7 @@ if(world.time < reactivearmor_cooldown) owner.visible_message("The reactive table armor's fabricators are still on cooldown!") return - owner.visible_message("The reactive teleport system flings [H] clear of [attack_text] and slams them into a fabricated table!") + owner.visible_message("The reactive teleport system flings [H] clear of [attack_text] and slams [H.p_them()] into a fabricated table!") owner.visible_message("[H] GOES ON THE TABLE!!!") owner.Knockdown(40) var/list/turfs = new/list() diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm index 3a9a72a543..c233ce414d 100644 --- a/code/modules/clothing/suits/utility.dm +++ b/code/modules/clothing/suits/utility.dm @@ -47,7 +47,7 @@ desc = "An expensive firesuit that protects against even the most deadly of station fires. Designed to protect even if the wearer is set aflame." icon_state = "atmos_firesuit" item_state = "firesuit_atmos" - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT /* * Bomb protection diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm index 59f7a1c387..9d1a47f231 100644 --- a/code/modules/clothing/suits/wiz_robe.dm +++ b/code/modules/clothing/suits/wiz_robe.dm @@ -168,7 +168,7 @@ to_chat(usr, "\The robe's internal magic supply is still recharging!") return - usr.say("Rise, my creation! Off your page into this realm!") + usr.say("Rise, my creation! Off your page into this realm!", forced = "stickman summoning") playsound(src.loc, 'sound/magic/summon_magic.ogg', 50, 1, 1) var/mob/living/M = new /mob/living/simple_animal/hostile/stickman(get_turf(usr)) var/list/factions = usr.faction @@ -219,7 +219,7 @@ icon_state = "electricity2" /obj/item/wizard_armour_charge/afterattack(obj/item/clothing/suit/space/hardsuit/shielded/wizard/W, mob/user) - ..() + . = ..() if(!istype(W)) to_chat(user, "The rune can only be used on battlemage armour!") return diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm index 8d250ab766..dd0083a1b7 100644 --- a/code/modules/clothing/under/accessories.dm +++ b/code/modules/clothing/under/accessories.dm @@ -280,6 +280,11 @@ desc = "Fills you with the conviction of JUSTICE. Lawyers tend to want to show it to everyone they meet." icon_state = "lawyerbadge" item_color = "lawyerbadge" + +/obj/item/clothing/accessory/lawyers_badge/attack_self(mob/user) + if(prob(1)) + user.say("The testimony contradicts the evidence!", forced = "attorney's badge") + user.visible_message("[user] shows [user.p_their()] attorney's badge.", "You show your attorney's badge.") /obj/item/clothing/accessory/lawyers_badge/on_uniform_equip(obj/item/clothing/under/U, user) var/mob/living/L = user diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm index 1115d822ed..0411a67846 100644 --- a/code/modules/clothing/under/jobs/civilian.dm +++ b/code/modules/clothing/under/jobs/civilian.dm @@ -67,9 +67,63 @@ fitted = FEMALE_UNIFORM_TOP can_adjust = FALSE -/obj/item/clothing/under/rank/clown/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - playsound(loc, 'sound/items/bikehorn.ogg', 50, 1, -1) - return 0 +/obj/item/clothing/under/rank/blueclown + name = "blue clown suit" + desc = "'BLUE HONK!'" + icon_state = "blueclown" + item_state = "blueclown" + item_color = "blueclown" + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/rank/greenclown + name = "green clown suit" + desc = "'GREEN HONK!'" + icon_state = "greenclown" + item_state = "greenclown" + item_color = "greenclown" + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/rank/yellowclown + name = "yellow clown suit" + desc = "'YELLOW HONK!'" + icon_state = "yellowclown" + item_state = "yellowclown" + item_color = "yellowclown" + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/rank/purpleclown + name = "purple clown suit" + desc = "'PURPLE HONK!'" + icon_state = "purpleclown" + item_state = "purpleclown" + item_color = "purpleclown" + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/rank/orangeclown + name = "orange clown suit" + desc = "'ORANGE HONK!'" + icon_state = "orangeclown" + item_state = "orangeclown" + item_color = "orangeclown" + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/rank/rainbowclown + name = "rainbow clown suit" + desc = "'R A I N B O W HONK!'" + icon_state = "rainbowclown" + item_state = "rainbowclown" + item_color = "rainbowclown" + fitted = FEMALE_UNIFORM_TOP + can_adjust = FALSE + +/obj/item/clothing/under/rank/clown/Initialize() + . = ..() + AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg'=1), 50) /obj/item/clothing/under/rank/head_of_personnel desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm index e506f726df..f0980cae4d 100644 --- a/code/modules/clothing/under/jobs/medsci.dm +++ b/code/modules/clothing/under/jobs/medsci.dm @@ -87,6 +87,7 @@ item_color = "nursesuit" permeability_coefficient = 0.5 armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + body_parts_covered = CHEST|GROIN|ARMS fitted = NO_FEMALE_UNIFORM can_adjust = FALSE diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm index e2be42e014..b8858fdf1a 100644 --- a/code/modules/clothing/under/jobs/security.dm +++ b/code/modules/clothing/under/jobs/security.dm @@ -34,6 +34,7 @@ icon_state = "secskirt" item_state = "r_suit" item_color = "secskirt" + body_parts_covered = CHEST|GROIN|ARMS can_adjust = FALSE //you know now that i think of it if you adjust the skirt and the sprite disappears isn't that just like flashing everyone diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 57ead15de8..ce17f3741e 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -263,7 +263,7 @@ /obj/item/clothing/under/suit_jacket/white name = "white suit" - desc = "A white suit and jacket with a blue shirt. You wanna play rough? OKAY!." + desc = "A white suit and jacket with a blue shirt. You wanna play rough? OKAY!" icon_state = "white_suit" item_state = "white_suit" item_color = "white_suit" @@ -382,7 +382,7 @@ icon_state = "kilt" item_state = "kilt" item_color = "kilt" - body_parts_covered = CHEST|GROIN|FEET + body_parts_covered = CHEST|GROIN|LEGS|FEET fitted = FEMALE_UNIFORM_TOP can_adjust = FALSE @@ -473,6 +473,7 @@ icon_state = "striped_dress" item_state = "stripeddress" item_color = "striped_dress" + body_parts_covered = CHEST|GROIN|ARMS fitted = FEMALE_UNIFORM_FULL can_adjust = FALSE @@ -482,6 +483,7 @@ icon_state = "sailor_dress" item_state = "sailordress" item_color = "sailor_dress" + body_parts_covered = CHEST|GROIN|ARMS fitted = FEMALE_UNIFORM_TOP can_adjust = FALSE @@ -555,6 +557,7 @@ icon_state = "ysing" item_state = "ysing" item_color = "ysing" + body_parts_covered = CHEST|GROIN|ARMS fitted = NO_FEMALE_UNIFORM alternate_worn_layer = ABOVE_SHOES_LAYER can_adjust = FALSE @@ -565,6 +568,7 @@ icon_state = "bsing" item_state = "bsing" item_color = "bsing" + body_parts_covered = CHEST|GROIN|ARMS alternate_worn_layer = ABOVE_SHOES_LAYER fitted = FEMALE_UNIFORM_TOP can_adjust = FALSE @@ -594,6 +598,7 @@ desc = "Cute space ninja senpai not included." icon_state = "geisha" item_color = "geisha" + body_parts_covered = CHEST|GROIN|ARMS can_adjust = FALSE /obj/item/clothing/under/villain @@ -722,6 +727,14 @@ fitted = NO_FEMALE_UNIFORM can_adjust = FALSE +/obj/item/clothing/under/gondola + name = "gondola hide suit" + desc = "Now you're cooking." + icon_state = "gondola" + item_state = "lb_suit" + item_color = "gondola" + can_adjust = FALSE + /obj/item/clothing/under/skeleton name = "skeleton jumpsuit" desc = "A black jumpsuit with a white bone pattern printed on it. Spooky!" diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index 1ffda7413e..586819ed8f 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -280,7 +280,7 @@ subcategory = CAT_AMMO /datum/crafting_recipe/laserslug - name = "Laser Slug Shell" + name = "Scatter Laser Shell" result = /obj/item/ammo_casing/shotgun/laserslug reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1, /obj/item/stock_parts/capacitor/adv = 1, @@ -370,6 +370,14 @@ /obj/item/stack/rods = 12) category = CAT_MISC +/datum/crafting_recipe/mousetrap + name = "Mouse Trap" + result = /obj/item/assembly/mousetrap + time = 10 + reqs = list(/obj/item/stack/sheet/cardboard = 1, + /obj/item/stack/rods = 1) + category = CAT_MISC + /datum/crafting_recipe/papersack name = "Paper Sack" result = /obj/item/storage/box/papersack @@ -514,7 +522,7 @@ result = /obj/item/bikehorn/golden time = 20 reqs = list(/obj/item/stack/sheet/mineral/bananium = 5, - /obj/item/bikehorn) + /obj/item/bikehorn = 1) category = CAT_MISC /datum/crafting_recipe/bonedagger @@ -677,3 +685,19 @@ tools = list(TOOL_WIRECUTTER) reqs = list(/obj/item/bedsheet = 1) category = CAT_CLOTHING + +/datum/crafting_recipe/aitater + name = "intelliTater" + result = /obj/item/aicard/aitater + time = 30 + reqs = list(/obj/item/aicard = 1, + /obj/item/reagent_containers/food/snacks/grown/potato = 1) + category = CAT_MISC + +/datum/crafting_recipe/ghettojetpack + name = "Improvised Jetpack" + result = /obj/item/tank/jetpack/improvised + time = 30 + reqs = list(/obj/item/tank/internals/oxygen/red = 2, /obj/item/extinguisher = 1, /obj/item/pipe = 3, /obj/item/stack/cable_coil = 30)//red oxygen tank so it looks right + category = CAT_MISC + tools = list(TOOL_WRENCH, TOOL_WELDER, TOOL_WIRECUTTER) \ No newline at end of file diff --git a/code/modules/detectivework/evidence.dm b/code/modules/detectivework/evidence.dm index a97758e294..a3b17a4c1f 100644 --- a/code/modules/detectivework/evidence.dm +++ b/code/modules/detectivework/evidence.dm @@ -9,6 +9,7 @@ w_class = WEIGHT_CLASS_TINY /obj/item/evidencebag/afterattack(obj/item/I, mob/user,proximity) + . = ..() if(!proximity || loc == I) return evidencebagEquip(I, user) diff --git a/code/modules/detectivework/footprints_and_rag.dm b/code/modules/detectivework/footprints_and_rag.dm index 50a845a689..9f1f2bf380 100644 --- a/code/modules/detectivework/footprints_and_rag.dm +++ b/code/modules/detectivework/footprints_and_rag.dm @@ -24,23 +24,23 @@ return (OXYLOSS) /obj/item/reagent_containers/glass/rag/afterattack(atom/A as obj|turf|area, mob/user,proximity) + . = ..() if(!proximity) return if(iscarbon(A) && A.reagents && reagents.total_volume) var/mob/living/carbon/C = A var/reagentlist = pretty_string_from_reagent_list(reagents) + var/log_object = "a damp rag containing [reagentlist]" if(user.a_intent == INTENT_HARM && !C.is_mouth_covered()) reagents.reaction(C, INGEST) reagents.trans_to(C, reagents.total_volume) C.visible_message("[user] has smothered \the [C] with \the [src]!", "[user] has smothered you with \the [src]!", "You hear some struggling and muffled cries of surprise.") - log_game("[key_name(user)] smothered [key_name(A)] with a damp rag containing [reagentlist]") - log_attack("[key_name(user)] smothered [key_name(A)] with a damp rag containing [reagentlist]") + log_combat(user, C, "smothered", log_object) else reagents.reaction(C, TOUCH) reagents.clear_reagents() - log_game("[key_name(user)] touched [key_name(A)] with a damp rag containing [reagentlist]") - log_attack("[key_name(user)] touched [key_name(A)] with a damp rag containing [reagentlist]") C.visible_message("[user] has touched \the [C] with \the [src].") + log_combat(user, C, "touched", log_object) else if(istype(A) && src in user) user.visible_message("[user] starts to wipe down [A] with [src]!", "You start to wipe down [A] with [src]...") diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm index e8847adb11..ca746e74fd 100644 --- a/code/modules/detectivework/scanner.dm +++ b/code/modules/detectivework/scanner.dm @@ -59,6 +59,7 @@ scanning = 0 /obj/item/detective_scanner/afterattack(atom/A, mob/user, params) + . = ..() scan(A, user) return FALSE @@ -191,6 +192,11 @@ to_chat(user, "The scanner logs are cleared.") log = list() +/obj/item/detective_scanner/examine(mob/user) + ..() + if(LAZYLEN(log) && !scanning) + to_chat(user, "Alt-click to clear scanner logs.") + /obj/item/detective_scanner/proc/displayDetectiveScanResults(mob/living/user) // No need for can-use checks since the action button should do proper checks if(!LAZYLEN(log)) diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm index e9f5ecb988..1c26a09c30 100644 --- a/code/modules/error_handler/error_handler.dm +++ b/code/modules/error_handler/error_handler.dm @@ -1,17 +1,16 @@ GLOBAL_VAR_INIT(total_runtimes, GLOB.total_runtimes || 0) GLOBAL_VAR_INIT(total_runtimes_skipped, 0) -#ifdef DEBUG - +#ifdef USE_CUSTOM_ERROR_HANDLER #define ERROR_USEFUL_LEN 2 /world/Error(exception/E, datum/e_src) GLOB.total_runtimes++ - + if(!istype(E)) //Something threw an unusual exception log_world("uncaught runtime error: [E]") return ..() - + //this is snowflake because of a byond bug (ID:2306577), do not attempt to call non-builtin procs in this if if(copytext(E.name,1,32) == "Maximum recursion level reached") //log to world while intentionally triggering the byond bug. @@ -86,8 +85,8 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0) var/list/usrinfo = null var/locinfo if(istype(usr)) - usrinfo = list(" usr: [datum_info_line(usr)]") - locinfo = atom_loc_line(usr) + usrinfo = list(" usr: [key_name(usr)]") + locinfo = loc_name(usr) if(locinfo) usrinfo += " usr.loc: [locinfo]" // The proceeding mess will almost definitely break if error messages are ever changed @@ -128,5 +127,4 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0) // This writes the regular format (unwrapping newlines and inserting timestamps as needed). log_runtime("runtime error: [E.name]\n[E.desc]") - -#endif \ No newline at end of file +#endif diff --git a/code/modules/error_handler/error_viewer.dm b/code/modules/error_handler/error_viewer.dm index 1f93bba9a0..adcbb8fb57 100644 --- a/code/modules/error_handler/error_viewer.dm +++ b/code/modules/error_handler/error_viewer.dm @@ -7,7 +7,7 @@ // logged errors. Only one instance of this datum should ever exist, and it's // right here: -#ifdef DEBUG +#ifdef USE_CUSTOM_ERROR_HANDLER GLOBAL_DATUM_INIT(error_cache, /datum/error_viewer/error_cache, new) #else // If debugging is disabled, there's nothing useful to log, so don't bother. @@ -192,4 +192,4 @@ GLOBAL_DATUM(error_cache, /datum/error_viewer/error_cache) browse_to(user, html) /datum/error_viewer/error_entry/make_link(linktext, datum/error_viewer/back_to, linear) - return is_skip_count ? name : ..() \ No newline at end of file + return is_skip_count ? name : ..() diff --git a/code/modules/events/holiday/vday.dm b/code/modules/events/holiday/vday.dm index eb8c8340df..a4e8b276b5 100644 --- a/code/modules/events/holiday/vday.dm +++ b/code/modules/events/holiday/vday.dm @@ -88,7 +88,7 @@ "The virologist is rogue, and the only cure is a kiss from you.", "Would you spend some time in my upgraded sleeper?", "You must be a silicon, because you've unbolted my heart.", - "Are you Nar-Sie? Because there's nar-one else I sie.", + "Are you Nar'Sie? Because there's nar-one else I sie.", "If you were a taser, you'd be set to stunning.", "Do you have stamina damage from running through my dreams?", "If I were an alien, would you let me hug you?", @@ -160,7 +160,7 @@ "A heart-shaped candy that reads: ERP", "A heart-shaped candy that reads: LEWD", "A heart-shaped candy that reads: LUSTY", - "A heart-shaped candy that reads: SPESS LOVE" + "A heart-shaped candy that reads: SPESS LOVE", "A heart-shaped candy that reads: AYY LMAO", "A heart-shaped candy that reads: TABLE ME", "A heart-shaped candy that reads: HAND CUFFS", diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index 2bb75cfade..abf13bcab9 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -105,7 +105,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 /obj/effect/immovablerod/singularity_pull() return -/obj/effect/immovablerod/Collide(atom/clong) +/obj/effect/immovablerod/Bump(atom/clong) if(prob(10)) playsound(src, 'sound/effects/bang.ogg', 50, 1) audible_message("You hear a CLANG!") diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm index 8b904ae9b8..1e1a85f108 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/pirates.dm @@ -1,5 +1,3 @@ -#define LOOT_LOCATOR_COOLDOWN 150 - /datum/round_event_control/pirates name = "Space Pirates" typepath = /datum/round_event/pirates @@ -26,10 +24,9 @@ /datum/round_event/pirates/setup() ship_name = pick(strings(PIRATE_NAMES_FILE, "ship_names")) -/datum/round_event/pirates/announce() +/datum/round_event/pirates/announce(fake) priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/ai/commandreport.ogg') // CITADEL EDIT metabreak - - if(!control) //Means this is false alarm, todo : explicit checks instead of using announceWhen + if(fake) return threat = new payoff = round(SSshuttle.points * 0.80) @@ -47,7 +44,7 @@ paid_off = TRUE return else - priority_announce("Trying to cheat us ? You'll regret this!",sender_override = ship_name) + priority_announce("Trying to cheat us? You'll regret this!",sender_override = ship_name) if(!shuttle_spawned) spawn_shuttle() @@ -60,7 +57,7 @@ /datum/round_event/pirates/proc/spawn_shuttle() shuttle_spawned = TRUE - var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew ?", ROLE_TRAITOR) + var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", ROLE_TRAITOR) shuffle_inplace(candidates) var/datum/map_template/shuttle/pirate/default/ship = new @@ -120,12 +117,12 @@ gps.tracking = TRUE active = TRUE to_chat(user,"You toggle [src] [active ? "on":"off"].") - to_chat(user,"The scrambling signal can be now tracked by gps.") + to_chat(user,"The scrambling signal can be now tracked by GPS.") START_PROCESSING(SSobj,src) /obj/machinery/shuttle_scrambler/interact(mob/user) if(!active) - if(alert(user, "Turning the scrambler on will make the shuttle trackable by GPS. Are you sure you want to do it ?", "Scrambler", "Yes", "Cancel") == "Cancel") + if(alert(user, "Turning the scrambler on will make the shuttle trackable by GPS. Are you sure you want to do it?", "Scrambler", "Yes", "Cancel") == "Cancel") return if(active || !user.canUseTopic(src)) return @@ -227,43 +224,234 @@ mask_type = /obj/item/clothing/mask/breath storage_type = /obj/item/tank/internals/oxygen + /obj/machinery/loot_locator name = "Booty Locator" desc = "This sophisticated machine scans the nearby space for items of value." icon = 'icons/obj/machines/research.dmi' icon_state = "tdoppler" density = TRUE - var/cooldown = 0 - var/result_count = 3 //Show X results. - -/obj/machinery/proc/display_current_value() - var/area/current = get_area(src) - var/value = 0 - for(var/turf/T in current.contents) - value += export_item_and_contents(T,TRUE, TRUE, dry_run = TRUE) - say("Current vault value : [value] credits.") + var/cooldown = 300 + var/next_use = 0 /obj/machinery/loot_locator/interact(mob/user) - if(world.time <= cooldown) + if(world.time <= next_use) to_chat(user,"[src] is recharging.") return - cooldown = world.time + LOOT_LOCATOR_COOLDOWN - display_current_value() - var/list/results = list() - for(var/atom/movable/AM in world) - if(is_type_in_typecache(AM,GLOB.pirate_loot_cache)) - if(is_station_level(AM.z)) - if(get_area(AM) == get_area(src)) //Should this be variable ? - continue - results += AM - CHECK_TICK - if(!results.len) + next_use = world.time + cooldown + var/atom/movable/AM = find_random_loot() + if(!AM) say("No valuables located. Try again later.") else - for(var/i in 1 to result_count) - if(!results.len) - return - var/atom/movable/AM = pick_n_take(results) - say("Located: [AM.name] at [get_area_name(AM)]") + say("Located: [AM.name] at [get_area_name(AM)]") -#undef LOOT_LOCATOR_COOLDOWN \ No newline at end of file +/obj/machinery/loot_locator/proc/find_random_loot() + if(!GLOB.exports_list.len) + setupExports() + var/list/possible_loot = list() + for(var/datum/export/pirate/E in GLOB.exports_list) + possible_loot += E + var/datum/export/pirate/P + var/atom/movable/AM + while(!AM && possible_loot.len) + P = pick_n_take(possible_loot) + AM = P.find_loot() + return AM + +//Pad & Pad Terminal +/obj/machinery/piratepad + name = "cargo hold pad" + icon = 'icons/obj/telescience.dmi' + icon_state = "lpad-idle-o" + var/idle_state = "lpad-idle-o" + var/warmup_state = "lpad-idle" + var/sending_state = "lpad-beam" + var/cargo_hold_id + +/obj/machinery/piratepad/multitool_act(mob/living/user, obj/item/multitool/I) + if (istype(I)) + to_chat(user, "You register [src] in [I]s buffer.") + I.buffer = src + return TRUE + +/obj/machinery/computer/piratepad_control + name = "cargo hold control terminal" + var/status_report = "Idle" + var/obj/machinery/piratepad/pad + var/warmup_time = 100 + var/sending = FALSE + var/points = 0 + var/datum/export_report/total_report + var/sending_timer + var/cargo_hold_id + +/obj/machinery/computer/piratepad_control/Initialize() + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/computer/piratepad_control/multitool_act(mob/living/user, obj/item/multitool/I) + if (istype(I) && istype(I.buffer,/obj/machinery/piratepad)) + to_chat(user, "You link [src] with [I.buffer] in [I] buffer.") + pad = I.buffer + updateDialog() + return TRUE + +/obj/machinery/computer/piratepad_control/LateInitialize() + . = ..() + if(cargo_hold_id) + for(var/obj/machinery/piratepad/P in GLOB.machines) + if(P.cargo_hold_id == cargo_hold_id) + pad = P + return + else + pad = locate() in range(4,src) + +/obj/machinery/computer/piratepad_control/ui_interact(mob/user) + . = ..() + var/list/t = list() + t += "
Cargo Hold Control
" + t += "Current cargo value : [points]" + t += "
" + if(!pad) + t += "
No pad located.

" + else + t += "
[status_report]
" + if(!sending) + t += "Recalculate ValueSend" + else + t += "Stop sending" + + var/datum/browser/popup = new(user, "piratepad", name, 300, 500) + popup.set_content(t.Join()) + popup.open() + +/obj/machinery/computer/piratepad_control/proc/recalc() + if(sending) + return + status_report = "Predicted value:
" + var/datum/export_report/ex = new + for(var/atom/movable/AM in get_turf(pad)) + if(AM == pad) + continue + export_item_and_contents(AM, EXPORT_PIRATE | EXPORT_CARGO | EXPORT_CONTRABAND | EXPORT_EMAG, apply_elastic = FALSE, dry_run = TRUE, external_report = ex) + + for(var/datum/export/E in ex.total_amount) + status_report += E.total_printout(ex,notes = FALSE) + "
" + +/obj/machinery/computer/piratepad_control/proc/send() + if(!sending) + return + + var/datum/export_report/ex = new + + for(var/atom/movable/AM in get_turf(pad)) + if(AM == pad) + continue + export_item_and_contents(AM, EXPORT_PIRATE | EXPORT_CARGO | EXPORT_CONTRABAND | EXPORT_EMAG, apply_elastic = FALSE, delete_unsold = FALSE, external_report = ex) + + status_report = "Sold:
" + var/value = 0 + for(var/datum/export/E in ex.total_amount) + var/export_text = E.total_printout(ex,notes = FALSE) //Don't want nanotrasen messages, makes no sense here. + if(!export_text) + continue + + status_report += export_text + "
" + value += ex.total_value[E] + + if(!total_report) + total_report = ex + else + total_report.exported_atoms += ex.exported_atoms + for(var/datum/export/E in ex.total_amount) + total_report.total_amount[E] += ex.total_amount[E] + total_report.total_value[E] += ex.total_value[E] + + points += value + + pad.visible_message("[pad] activates!") + flick(pad.sending_state,pad) + pad.icon_state = pad.idle_state + sending = FALSE + updateDialog() + +/obj/machinery/computer/piratepad_control/proc/start_sending() + if(sending) + return + sending = TRUE + status_report = "Sending..." + pad.visible_message("[pad] starts charging up.") + pad.icon_state = pad.warmup_state + sending_timer = addtimer(CALLBACK(src,.proc/send),warmup_time, TIMER_STOPPABLE) + +/obj/machinery/computer/piratepad_control/proc/stop_sending() + if(!sending) + return + sending = FALSE + status_report = "Idle" + pad.icon_state = pad.idle_state + deltimer(sending_timer) + +/obj/machinery/computer/piratepad_control/Topic(href, href_list) + if(..()) + return + if(pad) + if(href_list["recalc"]) + recalc() + if(href_list["send"]) + start_sending() + if(href_list["stop"]) + stop_sending() + updateDialog() + else + updateDialog() + +/datum/export/pirate + export_category = EXPORT_PIRATE + +//Attempts to find the thing on station +/datum/export/pirate/proc/find_loot() + return + +/datum/export/pirate/ransom + cost = 3000 + unit_name = "hostage" + export_types = list(/mob/living/carbon/human) + +/datum/export/pirate/ransom/find_loot() + var/list/head_minds = SSjob.get_living_heads() + var/list/head_mobs = list() + for(var/datum/mind/M in head_minds) + head_mobs += M.current + if(head_mobs.len) + return pick(head_mobs) + +/datum/export/pirate/ransom/get_cost(atom/movable/AM) + var/mob/living/carbon/human/H = AM + if(H.stat != CONSCIOUS || !H.mind || !H.mind.assigned_role) //mint condition only + return 0 + else + if(H.mind.assigned_role in GLOB.command_positions) + return 3000 + else + return 1000 + +/datum/export/pirate/parrot + cost = 2000 + unit_name = "alive parrot" + export_types = list(/mob/living/simple_animal/parrot) + +/datum/export/pirate/parrot/find_loot() + for(var/mob/living/simple_animal/parrot/P in GLOB.alive_mob_list) + var/turf/T = get_turf(P) + if(T && is_station_level(T.z)) + return P + +/datum/export/pirate/cash + cost = 1 + unit_name = "bills" + export_types = list(/obj/item/stack/spacecash) + +/datum/export/pirate/cash/get_amount(obj/O) + var/obj/item/stack/spacecash/C = O + return ..() * C.amount * C.value \ No newline at end of file diff --git a/code/modules/events/shuttle_loan.dm b/code/modules/events/shuttle_loan.dm index b46419e68a..cec897a61e 100644 --- a/code/modules/events/shuttle_loan.dm +++ b/code/modules/events/shuttle_loan.dm @@ -4,6 +4,8 @@ #define DEPARTMENT_RESUPPLY 4 #define ANTIDOTE_NEEDED 5 #define PIZZA_DELIVERY 6 +#define ITS_HIP_TO 7 +#define MY_GOD_JC 8 /datum/round_event_control/shuttle_loan @@ -21,7 +23,7 @@ var/thanks_msg = "The cargo shuttle should return in five minutes. Have some supply points for your trouble." /datum/round_event/shuttle_loan/setup() - dispatch_type = pick(HIJACK_SYNDIE, RUSKY_PARTY, SPIDER_GIFT, DEPARTMENT_RESUPPLY, ANTIDOTE_NEEDED, PIZZA_DELIVERY) + dispatch_type = pick(HIJACK_SYNDIE, RUSKY_PARTY, SPIDER_GIFT, DEPARTMENT_RESUPPLY, ANTIDOTE_NEEDED, PIZZA_DELIVERY, ITS_HIP_TO, MY_GOD_JC) /datum/round_event/shuttle_loan/announce(fake) SSshuttle.shuttle_loan = src @@ -70,8 +72,6 @@ P.name = "Cargo Report" P.info = "Cargo: Seems we've ordered doubles of our department resupply packages this month. Can we send them to you?" P.update_icon() - thanks_msg = "The cargo shuttle should return in 5 minutes." - bonus_points = 0 if(ANTIDOTE_NEEDED) if(prob(50)) priority_announce("Cargo: Your station has been chosen for an epidemiological research project. Send us your cargo shuttle to receive your research samples.", "CentCom Research Initiatives") @@ -94,6 +94,30 @@ P.name = "Cargo Report" P.info = "Cargo: It looks like a neighbouring station accidentally delivered their pizza to you instead." P.update_icon() + if(ITS_HIP_TO) + if(prob(50)) + priority_announce("Cargo: One of our freighters carrying a bee shipment has been attacked by eco-terrorists. Can you clean up the mess for us?", "CentCom Janitorial Division") + else + priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/ai/commandreport.ogg') // CITADEL EDIT metabreak + for(var/obj/machinery/computer/communications/C in GLOB.machines) + if(!(C.stat & (BROKEN|NOPOWER)) && is_station_level(C.z)) + var/obj/item/paper/P = new(C.loc) + P.name = "Cargo Report" + P.info = "Cargo: One of our freighters carrying a bee shipment has been attacked by eco-terrorists. Can you clean up the mess for us?." + P.update_icon() + bonus_points = 20000 //Toxin bees can be unbeelievably lethal + if(MY_GOD_JC) + if(prob(50)) + priority_announce("Cargo: We have discovered an active Syndicate bomb near our VIP shuttle's fuel lines. If you feel up to the task, we will pay you for defusing it.", "CentCom Security Division") + else + priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/ai/commandreport.ogg') // CITADEL EDIT metabreak + for(var/obj/machinery/computer/communications/C in GLOB.machines) + if(!(C.stat & (BROKEN|NOPOWER)) && is_station_level(C.z)) + var/obj/item/paper/P = new(C.loc) + P.name = "Cargo Report" + P.info = "Cargo: We have discovered an active Syndicate bomb near our VIP shuttle's fuel lines. If you feel up to the task, we will pay you for defusing it." + P.update_icon() + bonus_points = 45000 //If you mess up, people die and the shuttle gets turned into swiss cheese /datum/round_event/shuttle_loan/proc/loan_shuttle() priority_announce(thanks_msg, "Cargo shuttle commandeered by CentCom.") @@ -119,6 +143,10 @@ SSshuttle.centcom_message += "Virus samples incoming." if(PIZZA_DELIVERY) SSshuttle.centcom_message += "Pizza delivery for [station_name()]" + if(ITS_HIP_TO) + SSshuttle.centcom_message += "Biohazard cleanup incoming." + if(MY_GOD_JC) + SSshuttle.centcom_message += "Live explosive ordnance incoming. Exercise extreme caution." /datum/round_event/shuttle_loan/tick() if(dispatched) @@ -149,12 +177,12 @@ var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/emergency/specialops] pack.generate(pick_n_take(empty_shuttle_turfs)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate) + shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator) + shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator) if(prob(75)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate) + shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator) if(prob(50)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate) + shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator) if(RUSKY_PARTY) var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/service/party] @@ -224,15 +252,44 @@ var/decal = pick(/obj/effect/decal/cleanable/flour, /obj/effect/decal/cleanable/robot_debris, /obj/effect/decal/cleanable/oil) new decal(pick_n_take(empty_shuttle_turfs)) if(PIZZA_DELIVERY) - shuttle_spawns.Add(/obj/item/pizzabox/margherita) - shuttle_spawns.Add(/obj/item/pizzabox/margherita) - shuttle_spawns.Add(/obj/item/pizzabox/meat) - shuttle_spawns.Add(/obj/item/pizzabox/meat) - shuttle_spawns.Add(/obj/item/pizzabox/vegetable) - if(prob(10)) - shuttle_spawns.Add(/obj/item/pizzabox/bomb) + var/naughtypizza = list(/obj/item/pizzabox/bomb,/obj/item/pizzabox/margherita/robo) //oh look another blaklist, for pizza nonetheless! + var/nicepizza = list(/obj/item/pizzabox/margherita, /obj/item/pizzabox/meat, /obj/item/pizzabox/vegetable, /obj/item/pizzabox/mushroom) + for(var/i in 1 to 6) + shuttle_spawns.Add(pick(prob(5) ? naughtypizza : nicepizza)) + if(ITS_HIP_TO) + var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/organic/hydroponics/beekeeping_fullkit] + pack.generate(pick_n_take(empty_shuttle_turfs)) + + shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/bee_terrorist) + shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/cargo_tech) + shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/cargo_tech) + shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/nanotrasensoldier) + shuttle_spawns.Add(/obj/item/gun/ballistic/automatic/pistol/no_mag) + shuttle_spawns.Add(/obj/item/gun/ballistic/automatic/pistol/m1911/no_mag) + shuttle_spawns.Add(/obj/item/honey_frame) + shuttle_spawns.Add(/obj/item/honey_frame) + shuttle_spawns.Add(/obj/item/honey_frame) + shuttle_spawns.Add(/obj/structure/beebox/unwrenched) + shuttle_spawns.Add(/obj/item/queen_bee/bought) + shuttle_spawns.Add(/obj/structure/closet/crate/hydroponics) + + for(var/i in 1 to 8) + shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/bees/toxin) + + for(var/i in 1 to 5) + var/decal = pick(/obj/effect/decal/cleanable/blood, /obj/effect/decal/cleanable/insectguts) + new decal(pick_n_take(empty_shuttle_turfs)) + + for(var/i in 1 to 10) + var/casing = /obj/item/ammo_casing/spent + new casing(pick_n_take(empty_shuttle_turfs)) + + if(MY_GOD_JC) + shuttle_spawns.Add(/obj/machinery/syndicatebomb/shuttle_loan) + if(prob(95)) + shuttle_spawns.Add(/obj/item/paper/fluff/cargo/bomb) else - shuttle_spawns.Add(/obj/item/pizzabox/margherita) + shuttle_spawns.Add(/obj/item/paper/fluff/cargo/bomb/allyourbase) var/false_positive = 0 while(shuttle_spawns.len && empty_shuttle_turfs.len) @@ -244,9 +301,36 @@ var/spawn_type = pick_n_take(shuttle_spawns) new spawn_type(T) +//items that appear only in shuttle loan events + +/obj/item/storage/belt/fannypack/yellow/bee_terrorist/PopulateContents() + new /obj/item/grenade/plastic/c4 (src) + new /obj/item/reagent_containers/pill/cyanide(src) + new /obj/item/grenade/chem_grenade/facid(src) + +/obj/item/paper/fluff/bee_objectives + name = "Objectives of a Bee Liberation Front Operative" + info = "Objective #1. Liberate all bees on the NT transport vessel 2416/B. Success!
Objective #2. Escape alive. Failed." + +/obj/machinery/syndicatebomb/shuttle_loan/Initialize() + . = ..() + setAnchored(TRUE) + timer_set = rand(480, 600) //once the supply shuttle docks (after 5 minutes travel time), players have between 3-5 minutes to defuse the bomb + activate() + update_icon() + +/obj/item/paper/fluff/cargo/bomb + name = "hastly scribbled note" + info = "GOOD LUCK!" + +/obj/item/paper/fluff/cargo/bomb/allyourbase + info = "Somebody set us up the bomb!" + #undef HIJACK_SYNDIE #undef RUSKY_PARTY #undef SPIDER_GIFT #undef DEPARTMENT_RESUPPLY #undef ANTIDOTE_NEEDED #undef PIZZA_DELIVERY +#undef ITS_HIP_TO +#undef MY_GOD_JC diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index 0d1fb7e7d8..966d5cc0d8 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -92,7 +92,7 @@ if(issilicon(crosser)) return if(prob(severity) && istype(crosser) && !isvineimmune(crosser)) - to_chat(crosser, "You accidently touch the vine and feel a strange sensation.") + to_chat(crosser, "You accidentally touch the vine and feel a strange sensation.") crosser.adjustToxLoss(5) /datum/spacevine_mutation/toxicity/on_eat(obj/structure/spacevine/holder, mob/living/eater) diff --git a/code/modules/events/vent_clog.dm b/code/modules/events/vent_clog.dm index 84846c236a..160304b1c3 100644 --- a/code/modules/events/vent_clog.dm +++ b/code/modules/events/vent_clog.dm @@ -79,6 +79,11 @@ typepath = /datum/round_event/vent_clog/beer max_occurrences = 0 +/datum/round_event_control/vent_clog/plasma_decon + name = "Plasma decontamination" + typepath = /datum/round_event/vent_clog/plasma_decon + max_occurrences = 0 + /datum/round_event/vent_clog/beer reagentsAmount = 100 @@ -96,3 +101,14 @@ foam.set_up(200, get_turf(vent), R) foam.start() CHECK_TICK + +/datum/round_event/vent_clog/plasma_decon/announce() + priority_announce("We are deploying an experimental plasma decontamination system. Please stand away from the vents and do not breathe the smoke that comes out.", "Central Command Update") + +/datum/round_event/vent_clog/plasma_decon/start() + for(var/obj/machinery/atmospherics/components/unary/vent in vents) + if(vent && vent.loc) + var/datum/effect_system/smoke_spread/freezing/decon/smoke = new + smoke.set_up(7, get_turf(vent), 7) + smoke.start() + CHECK_TICK diff --git a/code/modules/events/wizard/departmentrevolt.dm b/code/modules/events/wizard/departmentrevolt.dm index 9a8c70c504..f37b7470fb 100644 --- a/code/modules/events/wizard/departmentrevolt.dm +++ b/code/modules/events/wizard/departmentrevolt.dm @@ -46,8 +46,8 @@ if(M.assigned_role == job) citizens += H M.add_antag_datum(/datum/antagonist/separatist,nation) - H.log_message("Was made into a separatist, long live [nation_name]!", INDIVIDUAL_ATTACK_LOG) - + H.log_message("Was made into a separatist, long live [nation_name]!", LOG_ATTACK, color="red") + if(citizens.len) var/message for(var/job in jobs_to_revolt) diff --git a/code/modules/events/wizard/greentext.dm b/code/modules/events/wizard/greentext.dm index 3dee740436..356e83757f 100644 --- a/code/modules/events/wizard/greentext.dm +++ b/code/modules/events/wizard/greentext.dm @@ -63,11 +63,11 @@ /obj/item/greentext/proc/check_winner() if(!new_holder) return - + if(is_centcom_level(new_holder.z))//you're winner! to_chat(new_holder, "At last it feels like victory is assured!") new_holder.mind.add_antag_datum(/datum/antagonist/greentext) - new_holder.log_message("Won with greentext!!!", INDIVIDUAL_ATTACK_LOG) + new_holder.log_message("won with greentext!!!", LOG_ATTACK, color="green") color_altered_mobs -= new_holder resistance_flags |= ON_FIRE qdel(src) diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm index c2bf0d5254..1c8ef95baa 100644 --- a/code/modules/events/wizard/imposter.dm +++ b/code/modules/events/wizard/imposter.dm @@ -36,5 +36,5 @@ SSticker.mode.apprentices += I.mind I.mind.special_role = "imposter" // - I.log_message("Is an imposter!", INDIVIDUAL_ATTACK_LOG) //? + I.log_message("is an imposter!", LOG_ATTACK, color="red") //? SEND_SOUND(I, sound('sound/effects/magic.ogg')) diff --git a/code/modules/events/wizard/rpgloot.dm b/code/modules/events/wizard/rpgloot.dm index d94ee0e212..420582ddab 100644 --- a/code/modules/events/wizard/rpgloot.dm +++ b/code/modules/events/wizard/rpgloot.dm @@ -34,6 +34,7 @@ var/one_use = TRUE /obj/item/upgradescroll/afterattack(obj/item/target, mob/user , proximity) + . = ..() if(!proximity || !istype(target)) return diff --git a/code/modules/fields/fields.dm b/code/modules/fields/fields.dm index 0fdd2699d6..5e34c934d9 100644 --- a/code/modules/fields/fields.dm +++ b/code/modules/fields/fields.dm @@ -305,7 +305,7 @@ to_chat(user, "You turn [src] [operating? "on":"off"].") QDEL_NULL(mobhook) if(!istype(current) && operating) - mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED), CALLBACK(src, .proc/on_mob_move)) + mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_mob_move))) setup_debug_field() else if(!operating) QDEL_NULL(current) diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 2899a190c3..e30bac4f41 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -795,7 +795,7 @@ GLOBAL_LIST_INIT(hallucination_list, list( if("alarm") target.playsound_local(source, 'sound/machines/alarm.ogg', 100, 0) if("beepsky") - target.playsound_local(source, 'sound/voice/bfreeze.ogg', 35, 0) + target.playsound_local(source, 'sound/voice/beepsky/freeze.ogg', 35, 0) if("mech") var/mech_dir = pick(GLOB.cardinals) for(var/i in 1 to rand(4,9)) @@ -1278,6 +1278,9 @@ GLOBAL_LIST_INIT(hallucination_list, list( var/list/turf/startlocs = list() for(var/turf/open/T in view(world.view+1,target)-view(world.view,target)) startlocs += T + if(!startlocs.len) + qdel(src) + return var/turf/start = pick(startlocs) var/proj_type = pick(subtypesof(/obj/item/projectile/hallucination)) feedback_details += "Type: [proj_type]" diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm index d84deb8132..9256f5bdd1 100644 --- a/code/modules/food_and_drinks/drinks/drinks.dm +++ b/code/modules/food_and_drinks/drinks/drinks.dm @@ -46,7 +46,7 @@ if(!reagents || !reagents.total_volume) return // The drink might be empty after the delay, such as by spam-feeding M.visible_message("[user] feeds the contents of [src] to [M].", "[user] feeds the contents of [src] to [M].") - add_logs(user, M, "fed", reagents.log_list()) + log_combat(user, M, "fed", reagents.log_list()) var/fraction = min(gulp_size/reagents.total_volume, 1) checkLiked(fraction, M) @@ -56,6 +56,7 @@ return 1 /obj/item/reagent_containers/food/drinks/afterattack(obj/target, mob/user , proximity) + . = ..() if(!proximity) return @@ -93,8 +94,6 @@ var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this) to_chat(user, "You fill [src] with [trans] units of the contents of [target].") - else - /obj/item/reagent_containers/food/drinks/attackby(obj/item/I, mob/user, params) var/hotness = I.is_hot() if(hotness && reagents) diff --git a/code/modules/food_and_drinks/drinks/drinks/bottle.dm b/code/modules/food_and_drinks/drinks/drinks/bottle.dm index f35bd6f937..3bc7443a9b 100644 --- a/code/modules/food_and_drinks/drinks/drinks/bottle.dm +++ b/code/modules/food_and_drinks/drinks/drinks/bottle.dm @@ -105,7 +105,7 @@ "[target] hits [target.p_them()]self with a bottle of [src.name][head_attack_message]!") //Attack logs - add_logs(user, target, "attacked", src) + log_combat(user, target, "attacked", src) //The reagents in the bottle splash all over the target, thanks for the idea Nodrak SplashReagents(target) diff --git a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm index faafcdf776..c24b22ce71 100644 --- a/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm +++ b/code/modules/food_and_drinks/drinks/drinks/drinkingglass.dm @@ -108,13 +108,14 @@ if(user.a_intent == INTENT_HARM && ismob(target) && target.reagents && reagents.total_volume) target.visible_message("[user] splashes the contents of [src] onto [target]!", \ "[user] splashes the contents of [src] onto [target]!") - add_logs(user, target, "splashed", src) + log_combat(user, target, "splashed", src) reagents.reaction(target, TOUCH) reagents.clear_reagents() return ..() /obj/item/reagent_containers/food/drinks/drinkingglass/afterattack(obj/target, mob/user, proximity) + . = ..() if((!proximity) || !check_allowed_items(target,target_self=1)) return @@ -124,5 +125,4 @@ reagents.reaction(target, TOUCH) reagents.clear_reagents() return - ..() diff --git a/code/modules/food_and_drinks/food/condiment.dm b/code/modules/food_and_drinks/food/condiment.dm index 53a0c2fb5b..d6d744f4db 100644 --- a/code/modules/food_and_drinks/food/condiment.dm +++ b/code/modules/food_and_drinks/food/condiment.dm @@ -49,7 +49,7 @@ if(!reagents || !reagents.total_volume) return // The condiment might be empty after the delay. user.visible_message("[user] feeds [M] from [src].") - add_logs(user, M, "fed", reagents.log_list()) + log_combat(user, M, "fed", reagents.log_list()) var/fraction = min(10/reagents.total_volume, 1) reagents.reaction(M, INGEST, fraction) @@ -58,6 +58,7 @@ return 1 /obj/item/reagent_containers/food/condiment/afterattack(obj/target, mob/user , proximity) + . = ..() if(!proximity) return if(istype(target, /obj/structure/reagent_dispensers)) //A dispenser. Transfer FROM it TO us. @@ -146,6 +147,7 @@ return (TOXLOSS) /obj/item/reagent_containers/food/condiment/saltshaker/afterattack(obj/target, mob/living/user, proximity) + . = ..() if(!proximity) return if(isturf(target)) @@ -156,7 +158,6 @@ reagents.remove_reagent("sodiumchloride", 2) new/obj/effect/decal/cleanable/salt(target) return - ..() /obj/item/reagent_containers/food/condiment/peppermill name = "pepper mill" @@ -241,6 +242,7 @@ return /obj/item/reagent_containers/food/condiment/pack/afterattack(obj/target, mob/user , proximity) + . = ..() if(!proximity) return diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm index a74c7c6f69..f2b195c32d 100644 --- a/code/modules/food_and_drinks/food/snacks.dm +++ b/code/modules/food_and_drinks/food/snacks.dm @@ -1,3 +1,33 @@ +/** # Snacks + +Items in the "Snacks" subcategory are food items that people actually eat. The key points are that they are created +already filled with reagents and are destroyed when empty. Additionally, they make a "munching" noise when eaten. + +Notes by Darem: Food in the "snacks" subtype can hold a maximum of 50 units. Generally speaking, you don't want to go over 40 +total for the item because you want to leave space for extra condiments. If you want effect besides healing, add a reagent for +it. Try to stick to existing reagents when possible (so if you want a stronger healing effect, just use omnizine). On use +effect (such as the old officer eating a donut code) requires a unique reagent (unless you can figure out a better way). + +The nutriment reagent and bitesize variable replace the old heal_amt and amount variables. Each unit of nutriment is equal to +2 of the old heal_amt variable. Bitesize is the rate at which the reagents are consumed. So if you have 6 nutriment and a +bitesize of 2, then it'll take 3 bites to eat. Unlike the old system, the contained reagents are evenly spread among all +the bites. No more contained reagents = no more bites. + +Here is an example of the new formatting for anyone who wants to add more food items. +``` +/obj/item/reagent_containers/food/snacks/xenoburger //Identification path for the object. + name = "Xenoburger" //Name that displays in the UI. + desc = "Smells caustic. Tastes like heresy." //Duh + icon_state = "xburger" //Refers to an icon in food.dmi +/obj/item/reagent_containers/food/snacks/xenoburger/Initialize() //Don't mess with this. | nO I WILL MESS WITH THIS + . = ..() //Same here. + reagents.add_reagent("xenomicrobes", 10) //This is what is in the food item. you may copy/paste + reagents.add_reagent("nutriment", 2) //this line of code for all the contents. + bitesize = 3 //This is the amount each bite consumes. +``` + +All foods are distributed among various categories. Use common sense. +*/ /obj/item/reagent_containers/food/snacks name = "snack" desc = "Yummy." @@ -76,11 +106,11 @@ else if(fullness > 50 && fullness < 150) user.visible_message("[user] hungrily takes a [eatverb] from \the [src].", "You hungrily take a [eatverb] from \the [src].") else if(fullness > 150 && fullness < 500) - user.visible_message("[user] takes a [eatverb] from \the [src].", "You take a [eatverb] from \the [src].") + user.visible_message("[user] takes a [eatverb] from \the [src].", "You take a [eatverb] from \the [src].") else if(fullness > 500 && fullness < 600) user.visible_message("[user] unwillingly takes a [eatverb] of a bit of \the [src].", "You unwillingly take a [eatverb] of a bit of \the [src].") else if(fullness > (600 * (1 + M.overeatduration / 2000))) // The more you eat - the more you can eat - user.visible_message("[user] cannot force any more of \the [src] to go down [user.p_their()] throat!", "You cannot force any more of \the [src] to go down your throat!") + user.visible_message("[user] cannot force any more of \the [src] to go down [user.p_their()] throat!", "You cannot force any more of \the [src] to go down your throat!") return 0 if(M.has_trait(TRAIT_VORACIOUS)) M.changeNext_move(CLICK_CD_MELEE * 0.5) //nom nom nom @@ -96,7 +126,7 @@ if(!do_mob(user, M)) return - add_logs(user, M, "fed", reagents.log_list()) + log_combat(user, M, "fed", reagents.log_list()) M.visible_message("[user] forces [M] to eat [src].", \ "[user] forces [M] to eat [src].") @@ -120,11 +150,6 @@ return 0 - -/obj/item/reagent_containers/food/snacks/afterattack(obj/target, mob/user , proximity) - return - - /obj/item/reagent_containers/food/snacks/examine(mob/user) ..() if(bitecount == 0) @@ -299,40 +324,8 @@ M.emote("me", 1, "[sattisfaction_text]") qdel(src) - -////////////////////////////////////////////////// -////////////////////////////////////////////Snacks -////////////////////////////////////////////////// -//Items in the "Snacks" subcategory are food items that people actually eat. The key points are that they are created -// already filled with reagents and are destroyed when empty. Additionally, they make a "munching" noise when eaten. - -//Notes by Darem: Food in the "snacks" subtype can hold a maximum of 50 units Generally speaking, you don't want to go over 40 -// total for the item because you want to leave space for extra condiments. If you want effect besides healing, add a reagent for -// it. Try to stick to existing reagents when possible (so if you want a stronger healing effect, just use omnizine). On use -// effect (such as the old officer eating a donut code) requires a unique reagent (unless you can figure out a better way). - -//The nutriment reagent and bitesize variable replace the old heal_amt and amount variables. Each unit of nutriment is equal to -// 2 of the old heal_amt variable. Bitesize is the rate at which the reagents are consumed. So if you have 6 nutriment and a -// bitesize of 2, then it'll take 3 bites to eat. Unlike the old system, the contained reagents are evenly spread among all -// the bites. No more contained reagents = no more bites. - -//Here is an example of the new formatting for anyone who wants to add more food items. -///obj/item/reagent_containers/food/snacks/xenoburger //Identification path for the object. -// name = "Xenoburger" //Name that displays in the UI. -// desc = "Smells caustic. Tastes like heresy." //Duh -// icon_state = "xburger" //Refers to an icon in food.dmi -///obj/item/reagent_containers/food/snacks/xenoburger/Initialize() //Don't mess with this. | nO I WILL MESS WITH THIS -// . = ..() //Same here. -// reagents.add_reagent("xenomicrobes", 10) //This is what is in the food item. you may copy/paste -// reagents.add_reagent("nutriment", 2) //this line of code for all the contents. -// bitesize = 3 //This is the amount each bite consumes. - -//All foods are distributed among various categories. Use common sense. - -/////////////////////////////////////////////////Store//////////////////////////////////////// -// All the food items that can store an item inside itself, like bread or cake. - - +// //////////////////////////////////////////////Store//////////////////////////////////////// +/// All the food items that can store an item inside itself, like bread or cake. /obj/item/reagent_containers/food/snacks/store w_class = WEIGHT_CLASS_NORMAL var/stored_item = 0 diff --git a/code/modules/food_and_drinks/food/snacks/meat.dm b/code/modules/food_and_drinks/food/snacks/meat.dm index 7cee86bd38..1a82d1b406 100644 --- a/code/modules/food_and_drinks/food/snacks/meat.dm +++ b/code/modules/food_and_drinks/food/snacks/meat.dm @@ -257,6 +257,16 @@ tastes = list("bacon" = 1) foodtype = MEAT +/obj/item/reagent_containers/food/snacks/meat/slab/gondola + name = "gondola meat" + desc = "According to legends of old, consuming raw gondola flesh grants one inner peace." + list_reagents = list("nutriment" = 3, "tranquility" = 5, "cooking_oil" = 3) + tastes = list("meat" = 4, "tranquility" = 1) + filling_color = "#9A6750" + cooked_type = /obj/item/reagent_containers/food/snacks/meat/steak/gondola + slice_path = /obj/item/reagent_containers/food/snacks/meat/rawcutlet/gondola + foodtype = RAW | MEAT + ////////////////////////////////////// MEAT STEAKS /////////////////////////////////////////////////////////// @@ -304,6 +314,10 @@ tastes = list("meat" = 1, "rock" = 1) foodtype = MEAT +/obj/item/reagent_containers/food/snacks/meat/steak/gondola + name = "gondola steak" + tastes = list("meat" = 1, "tranquility" = 1) + //////////////////////////////// MEAT CUTLETS /////////////////////////////////////////////////////// //Raw cutlets @@ -361,6 +375,11 @@ cooked_type = /obj/item/reagent_containers/food/snacks/meat/cutlet/spider tastes = list("cobwebs" = 1) +/obj/item/reagent_containers/food/snacks/meat/rawcutlet/gondola + name = "raw gondola cutlet" + cooked_type = /obj/item/reagent_containers/food/snacks/meat/cutlet/gondola + tastes = list("meat" = 1, "tranquility" = 1) + //Cooked cutlets /obj/item/reagent_containers/food/snacks/meat/cutlet @@ -396,3 +415,7 @@ /obj/item/reagent_containers/food/snacks/meat/cutlet/spider name = "spider cutlet" tastes = list("cobwebs" = 1) + +/obj/item/reagent_containers/food/snacks/meat/cutlet/gondola + name = "gondola cutlet" + tastes = list("meat" = 1, "tranquility" = 1) diff --git a/code/modules/food_and_drinks/food/snacks_bread.dm b/code/modules/food_and_drinks/food/snacks_bread.dm index 95c66f019e..0638bfad25 100644 --- a/code/modules/food_and_drinks/food/snacks_bread.dm +++ b/code/modules/food_and_drinks/food/snacks_bread.dm @@ -188,11 +188,11 @@ /obj/item/reagent_containers/food/snacks/deepfryholder/Initialize(mapload, obj/item/fried) . = ..() name = fried.name //We'll determine the other stuff when it's actually removed - icon = fried.icon - overlays = fried.copy_overlays() + appearance = fried.appearance + layer = initial(layer) + plane = initial(plane) lefthand_file = fried.lefthand_file righthand_file = fried.righthand_file - icon_state = fried.icon_state item_state = fried.item_state desc = fried.desc w_class = fried.w_class @@ -276,4 +276,4 @@ /obj/item/reagent_containers/food/snacks/butterdog/ComponentInitialize() . = ..() - AddComponent(/datum/component/slippery, 80) \ No newline at end of file + AddComponent(/datum/component/slippery, 80) diff --git a/code/modules/food_and_drinks/food/snacks_meat.dm b/code/modules/food_and_drinks/food/snacks_meat.dm index bd1dbc4f8e..c77242e69d 100644 --- a/code/modules/food_and_drinks/food/snacks_meat.dm +++ b/code/modules/food_and_drinks/food/snacks_meat.dm @@ -189,7 +189,7 @@ var/mob/living/carbon/monkey/bananas = new(drop_location(), TRUE, spammer) if (!QDELETED(bananas)) visible_message("[src] expands!") - bananas.log_message("Spawned via [src] at [AREACOORD(src)], Last attached mob: [key_name(spammer)].", INDIVIDUAL_ATTACK_LOG) + bananas.log_message("Spawned via [src] at [AREACOORD(src)], Last attached mob: [key_name(spammer)].", LOG_ATTACK) else if (!spammer) // Visible message in case there are no fingerprints visible_message("[src] fails to expand!") qdel(src) diff --git a/code/modules/food_and_drinks/food/snacks_pastry.dm b/code/modules/food_and_drinks/food/snacks_pastry.dm index 80aa588bae..1956365896 100644 --- a/code/modules/food_and_drinks/food/snacks_pastry.dm +++ b/code/modules/food_and_drinks/food/snacks_pastry.dm @@ -33,7 +33,7 @@ H.adjust_disgust(-5 + -2.5 * fraction) GET_COMPONENT_FROM(mood, /datum/component/mood, H) if(mood) - mood.add_event("fav_food", /datum/mood_event/favorite_food) + mood.add_event(null, "fav_food", /datum/mood_event/favorite_food) last_check_time = world.time return ..() @@ -89,6 +89,13 @@ icon_state = "jdonut1" extra_reagent = "cherryjelly" foodtype = JUNKFOOD | GRAIN | FRIED | FRUIT + +/obj/item/reagent_containers/food/snacks/donut/meat + bonus_reagents = list("ketchup" = 1) + list_reagents = list("nutriment" = 3, "ketchup" = 2) + tastes = list("meat" = 1) + foodtype = JUNKFOOD | MEAT | GROSS | FRIED + ////////////////////////////////////////////MUFFINS//////////////////////////////////////////// diff --git a/code/modules/food_and_drinks/food/snacks_pizza.dm b/code/modules/food_and_drinks/food/snacks_pizza.dm index a94c1a25c7..a6b026ac30 100644 --- a/code/modules/food_and_drinks/food/snacks_pizza.dm +++ b/code/modules/food_and_drinks/food/snacks_pizza.dm @@ -24,6 +24,10 @@ tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1) foodtype = GRAIN | VEGETABLES +/obj/item/reagent_containers/food/snacks/pizza/margherita/robo/Initialize() + bonus_reagents += list("nanomachines" = 70) + return ..() + /obj/item/reagent_containers/food/snacks/pizzaslice/margherita name = "margherita slice" desc = "A slice of the most cheezy pizza in galaxy." @@ -156,8 +160,59 @@ tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 2, "ham" = 2) foodtype = GRAIN | VEGETABLES | DAIRY | MEAT | FRUIT | PINEAPPLE +/obj/item/reagent_containers/food/snacks/pizza/arnold + name = "\improper Arnold pizza" + desc = "Hello, you've reached Arnold's pizza shop. I'm not here now, I'm out killing pepperoni." + icon_state = "arnoldpizza" + slice_path = /obj/item/reagent_containers/food/snacks/pizzaslice/arnold + bonus_reagents = list("nutriment" = 30, "vitamin" = 6, "iron" = 10, "omnizine" = 30) + tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pepperoni" = 2, "9 millimeter bullets" = 2) + +/obj/item/reagent_containers/food/snacks/proc/try_break_off(mob/living/M, mob/living/user) //maybe i give you a pizza maybe i break off your arm + var/obj/item/bodypart/l_arm = user.get_bodypart(BODY_ZONE_L_ARM) + var/obj/item/bodypart/r_arm = user.get_bodypart(BODY_ZONE_R_ARM) + if(prob(50) && iscarbon(user) && M == user && (r_arm || l_arm)) + user.visible_message("\The [src] breaks off [user]'s arm!!", "\The [src] breaks off your arm!") + if(l_arm) + l_arm.dismember() + else + r_arm.dismember() + playsound(user,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, TRUE, -1) + +/obj/item/reagent_containers/food/snacks/proc/i_kill_you(obj/item/I, mob/user) + if(istype(I, /obj/item/reagent_containers/food/snacks/pineappleslice)) + to_chat(user, "If you want something crazy like pineapple, I kill you.") + user.gib() //if you want something crazy like pineapple, i kill you + +/obj/item/reagent_containers/food/snacks/pizza/arnold/attack(mob/living/M, mob/living/user) + . = ..() + try_break_off(M, user) + +/obj/item/reagent_containers/food/snacks/pizza/arnold/attackby(obj/item/I, mob/user) + i_kill_you(I, user) + . = ..() + + +/obj/item/reagent_containers/food/snacks/pizzaslice/arnold + name = "\improper Arnold pizza slice" + desc = "I come over, maybe I give you a pizza, maybe I break off your arm." + icon_state = "arnoldpizzaslice" + filling_color = "#A52A2A" + tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 2, "ham" = 2) + foodtype = GRAIN | VEGETABLES | DAIRY | MEAT + +/obj/item/reagent_containers/food/snacks/pizzaslice/arnold/attack(mob/living/M, mob/living/user) + . =..() + try_break_off(M, user) + +/obj/item/reagent_containers/food/snacks/pizzaslice/arnold/attackby(obj/item/I, mob/user) + i_kill_you(I, user) + . = ..() + + /obj/item/reagent_containers/food/snacks/pizzaslice/custom name = "pizza slice" icon_state = "pizzamargheritaslice" filling_color = "#FFFFFF" foodtype = GRAIN | VEGETABLES + diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm index e6734b4b58..d8d4e843dc 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm @@ -98,7 +98,7 @@ /obj/machinery/gibber/attackby(obj/item/P, mob/user, params) if(default_deconstruction_screwdriver(user, "grinder_open", "grinder", P)) return - + else if(default_pry_open(P)) return @@ -184,7 +184,7 @@ if(typeofskin) skin = new typeofskin - add_logs(user, occupant, "gibbed") + log_combat(user, occupant, "gibbed") mob_occupant.death(1) mob_occupant.ghostize() qdel(src.occupant) @@ -215,7 +215,7 @@ /obj/machinery/gibber/autogibber var/input_dir = NORTH -/obj/machinery/gibber/autogibber/CollidedWith(atom/movable/AM) +/obj/machinery/gibber/autogibber/Bumped(atom/movable/AM) var/atom/input = get_step(src, input_dir) if(ismob(AM)) var/mob/M = AM diff --git a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm index 8c4e0eaae0..3bcc8d7659 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm @@ -9,6 +9,7 @@ idle_power_usage = 5 active_power_usage = 100 circuit = /obj/item/circuitboard/machine/microwave + pass_flags = PASSTABLE var/operating = FALSE // Is it on? var/dirty = 0 // = {0..100} Does it need cleaning? var/broken = 0 // ={0,1,2} How broken is it??? @@ -37,6 +38,11 @@ efficiency = E max_n_of_items = max_items +/obj/machinery/microwave/examine(mob/user) + ..() + if(!operating) + to_chat(user, "Alt-click [src] to turn it on.") + /******************* * Item Adding ********************/ diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index fa4168c399..b6e3b19640 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -13,8 +13,7 @@ active_power_usage = 100 circuit = /obj/item/circuitboard/machine/smartfridge var/max_n_of_items = 1500 - var/icon_on = "smartfridge" - var/icon_off = "smartfridge-off" + var/allow_ai_retrieve = FALSE var/list/initial_contents /obj/machinery/smartfridge/Initialize() @@ -39,10 +38,11 @@ update_icon() /obj/machinery/smartfridge/update_icon() + var/startstate = initial(icon_state) if(!stat) - icon_state = icon_on + icon_state = startstate else - icon_state = icon_off + icon_state = "[startstate]-off" @@ -168,6 +168,10 @@ if("Release") var/desired = 0 + if(!allow_ai_retrieve && isAI(usr)) + to_chat(usr, "[src] does not seem to be configured to respect your authority!") + return + if (params["amount"]) desired = text2num(params["amount"]) else @@ -203,12 +207,10 @@ name = "drying rack" desc = "A wooden contraption, used to dry plant products, food and leather." icon = 'icons/obj/hydroponics/equipment.dmi' - icon_state = "drying_rack_on" + icon_state = "drying_rack" use_power = IDLE_POWER_USE idle_power_usage = 5 active_power_usage = 200 - icon_on = "drying_rack_on" - icon_off = "drying_rack" var/drying = FALSE /obj/machinery/smartfridge/drying_rack/Initialize() @@ -413,6 +415,7 @@ name = "disk compartmentalizer" desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)." icon_state = "disktoaster" + pass_flags = PASSTABLE /obj/machinery/smartfridge/disks/accept_check(obj/item/O) if(istype(O, /obj/item/disk/)) diff --git a/code/modules/food_and_drinks/pizzabox.dm b/code/modules/food_and_drinks/pizzabox.dm index 34868a5000..b06a160180 100644 --- a/code/modules/food_and_drinks/pizzabox.dm +++ b/code/modules/food_and_drinks/pizzabox.dm @@ -269,9 +269,14 @@ /obj/item/pizzabox/margherita/Initialize() . = ..() - pizza = new /obj/item/reagent_containers/food/snacks/pizza/margherita(src) + AddPizza() boxtag = "Margherita Deluxe" +/obj/item/pizzabox/margherita/proc/AddPizza() + pizza = new /obj/item/reagent_containers/food/snacks/pizza/margherita(src) + +/obj/item/pizzabox/margherita/robo/AddPizza() + pizza = new /obj/item/reagent_containers/food/snacks/pizza/margherita/robo(src) /obj/item/pizzabox/vegetable/Initialize() . = ..() diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm index 403fd9c8da..3925416560 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_misc.dm @@ -13,14 +13,14 @@ subcategory = CAT_MISCFOOD /datum/crafting_recipe/food/spiderlollipop - name = "Spider Lollipop" - reqs = list(/obj/item/stack/rods = 1, - /datum/reagent/consumable/sugar = 5, - /datum/reagent/water = 5, - /obj/item/reagent_containers/food/snacks/spiderling = 1 - ) - result = /obj/item/reagent_containers/food/snacks/spiderlollipop - subcategory = CAT_MISCFOOD + name = "Spider Lollipop" + reqs = list(/obj/item/stack/rods = 1, + /datum/reagent/consumable/sugar = 5, + /datum/reagent/water = 5, + /obj/item/reagent_containers/food/snacks/spiderling = 1 + ) + result = /obj/item/reagent_containers/food/snacks/spiderlollipop + subcategory = CAT_MISCFOOD /datum/crafting_recipe/food/chococoin name = "Choco coin" diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm index a75510ae9a..e751bf887e 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm @@ -22,6 +22,16 @@ ) result = /obj/item/reagent_containers/food/snacks/donut subcategory = CAT_PASTRY + +datum/crafting_recipe/food/donut/meat + time = 15 + name = "Meat donut" + reqs = list( + /obj/item/reagent_containers/food/snacks/meat/slab = 1, + /obj/item/reagent_containers/food/snacks/pastrybase = 1 + ) + result = /obj/item/reagent_containers/food/snacks/donut/meat + subcategory = CAT_PASTRY /datum/crafting_recipe/food/jellydonut name = "Jelly donut" diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm index 27654a0702..70f4b50727 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm @@ -24,6 +24,18 @@ result = /obj/item/reagent_containers/food/snacks/pizza/meat subcategory = CAT_PIZZA +/datum/crafting_recipe/food/arnold + name = "Arnold pizza" + reqs = list( + /obj/item/reagent_containers/food/snacks/pizzabread = 1, + /obj/item/reagent_containers/food/snacks/meat/cutlet = 3, + /obj/item/ammo_casing/c9mm = 8, + /obj/item/reagent_containers/food/snacks/cheesewedge = 1, + /obj/item/reagent_containers/food/snacks/grown/tomato = 1 + ) + result = /obj/item/reagent_containers/food/snacks/pizza/arnold + subcategory = CAT_PIZZA + /datum/crafting_recipe/food/mushroompizza name = "Mushroom pizza" reqs = list( @@ -45,7 +57,7 @@ result = /obj/item/reagent_containers/food/snacks/pizza/vegetable subcategory = CAT_PIZZA -/datum/crafting_recipe/food/donpocketpizza +/datum/crafting_recipe/food/donkpocketpizza name = "Donkpocket pizza" reqs = list( /obj/item/reagent_containers/food/snacks/pizzabread = 1, diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm index 267f5f8427..76a5a64096 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_soup.dm @@ -33,7 +33,7 @@ reqs = list( /datum/reagent/water = 10, /obj/item/reagent_containers/glass/bowl = 1, - /obj/item/grown/nettle = 1, + /obj/item/reagent_containers/food/snacks/grown/nettle = 1, /obj/item/reagent_containers/food/snacks/grown/potato = 1, /obj/item/reagent_containers/food/snacks/boiledegg = 1 ) diff --git a/code/modules/goonchat/browserOutput.dm b/code/modules/goonchat/browserOutput.dm index 55d5a806bd..ba50a8396e 100644 --- a/code/modules/goonchat/browserOutput.dm +++ b/code/modules/goonchat/browserOutput.dm @@ -175,29 +175,23 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic log_world("\[[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]\] Client: [(src.owner.key ? src.owner.key : src.owner)] triggered JS error: [error]") //Global chat procs - /proc/to_chat(target, message, handle_whitespace=TRUE) if(!target) return //Ok so I did my best but I accept that some calls to this will be for shit like sound and images //It stands that we PROBABLY don't want to output those to the browser output so just handle them here - if (istype(message, /image) || istype(message, /sound) || istype(target, /savefile)) + if (istype(target, /savefile)) CRASH("Invalid message! [message]") if(!istext(message)) + if (istype(message, /image) || istype(message, /sound)) + CRASH("Invalid message! [message]") return if(target == world) target = GLOB.clients - var/list/targets - if(!islist(target)) - targets = list(target) - else - targets = target - if(!targets.len) - return var/original_message = message //Some macros remain in the string even after parsing and fuck up the eventual output message = replacetext(message, "\improper", "") @@ -206,34 +200,43 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic message = replacetext(message, "\n", "
") message = replacetext(message, "\t", "[GLOB.TAB][GLOB.TAB]") - for(var/I in targets) - //Grab us a client if possible - var/client/C - if (ismob(I)) - var/mob/M = I - if(M.client) - C = M.client - else if(istype(I, /client)) - C = I - else if(istype(I, /datum/mind)) - var/datum/mind/M = I - if(M.current && M.current.client) - C = M.current.client - + if(islist(target)) + // Do the double-encoding outside the loop to save nanoseconds + var/twiceEncoded = url_encode(url_encode(message)) + for(var/I in target) + var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible + + if (!C) + continue + + //Send it to the old style output window. + SEND_TEXT(C, original_message) + + if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file. + continue + + if(!C.chatOutput.loaded) + //Client still loading, put their messages in a queue + C.chatOutput.messageQueue += message + continue + + C << output(twiceEncoded, "browseroutput:output") + else + var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible if (!C) - continue + return //Send it to the old style output window. SEND_TEXT(C, original_message) if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file. - continue + return if(!C.chatOutput.loaded) //Client still loading, put their messages in a queue C.chatOutput.messageQueue += message - continue + return // url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript. C << output(url_encode(url_encode(message)), "browseroutput:output") diff --git a/code/modules/goonchat/browserassets/css/browserOutput.css b/code/modules/goonchat/browserassets/css/browserOutput.css index 778e16a831..fe90e8a333 100644 --- a/code/modules/goonchat/browserassets/css/browserOutput.css +++ b/code/modules/goonchat/browserassets/css/browserOutput.css @@ -10,7 +10,7 @@ html, body { color: #000000; } body { - background: #E0E0E0; /*CIT CHANGE - darkens chatbox a lil*/ + background: #fff; font-family: Verdana, sans-serif; font-size: 9pt; line-height: 1.2; @@ -264,7 +264,7 @@ em {font-style: normal; font-weight: bold;} .adminobserverooc {color: #0099cc; font-weight: bold;} .adminooc {color: #700038; font-weight: bold;} -.adminobserver {color: #996600; font-weight: bold;} +.adminsay {color: #FF4500; font-weight: bold;} .admin {color: #386aff; font-weight: bold;} .name { font-weight: bold;} diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index 701167c853..7d1e25235d 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -81,6 +81,9 @@ begin_month = FEBRUARY drone_hat = /obj/item/clothing/head/helmet/space/chronos +/datum/holiday/groundhog/getStationPrefix() + return pick("Deja Vu") //I have been to this place before + /datum/holiday/valentines name = VALENTINES begin_day = 13 @@ -149,6 +152,9 @@ /datum/holiday/no_this_is_patrick/getStationPrefix() return pick("Blarney","Green","Leprechaun","Booze") +/datum/holiday/no_this_is_patrick/greet() + return "Happy National Inebriation Day!" + /datum/holiday/april_fools name = APRIL_FOOLS begin_day = 1 @@ -168,7 +174,15 @@ begin_month = APRIL /datum/holiday/fourtwenty/getStationPrefix() - return pick("Snoop","Blunt","Toke","Dank") + return pick("Snoop","Blunt","Toke","Dank","Cheech","Chong") + +/datum/holiday/tea + name = "National Tea Day" + begin_day = 21 + begin_month = APRIL + +/datum/holiday/tea/getStationPrefix() + return pick("Crumpet","Assam","Oolong","Pu-erh","Sweet Tea","Green","Black") /datum/holiday/earth name = "Earth Day" @@ -190,6 +204,14 @@ /datum/holiday/firefighter/getStationPrefix() return pick("Burning","Blazing","Plasma","Fire") +/datum/holiday/bee + name = "Bee Day" + begin_day = 20 + begin_month = MAY + +/datum/holiday/bee/getStationPrefix() + return pick("Bee","Honey","Hive","Africanized","Mead","Buzz") + /datum/holiday/summersolstice name = "Summer Solstice" begin_day = 21 @@ -205,10 +227,18 @@ name = "UFO Day" begin_day = 2 begin_month = JULY - drone_hat = /obj/item/clothing/mask/facehugger/dead + drone_hat = /obj/item/clothing/mask/facehugger/dead /datum/holiday/UFO/getStationPrefix() //Is such a thing even possible? - return pick("Ayy","Truth","Tsoukalos","Mulder") //Yes it is! + return pick("Ayy","Truth","Tsoukalos","Mulder","Scully") //Yes it is! + +/datum/holiday/USA + name = "Independence Day" + begin_day = 4 + begin_month = JULY + +/datum/holiday/USA/getStationPrefix() + return pick("Independant","American","Burger","Bald Eagle","Star-Spangled") /datum/holiday/writer name = "Writer's Day" @@ -225,8 +255,14 @@ /datum/holiday/beer name = "Beer Day" - begin_day = 5 - begin_month = AUGUST + +/datum/holiday/beer/shouldCelebrate(dd, mm, yy, ww, ddd) + if(mm == 8 && ddd == FRIDAY && ww == 1) //First Friday in August + return TRUE + return FALSE + +/datum/holiday/beer/getStationPrefix() + return pick("Stout","Porter","Lager","Ale","Malt","Bock","Doppelbock","Hefeweizen","Pilsner","IPA","Lite") //I'm sorry for the last one /datum/holiday/pirate name = "Talk-Like-a-Pirate Day" @@ -366,6 +402,15 @@ begin_month = JUNE begin_weekday = SUNDAY +/datum/holiday/moth + name = "Moth Week" + +/datum/holiday/moth/shouldCelebrate(dd, mm, yy, ww, ddd) //National Moth Week falls on the last full week of July + return mm == JULY && (ww == 4 || (ww == 5 && ddd == SUNDAY)) + +/datum/holiday/moth/getStationPrefix() + return pick("Mothball","Lepidopteran","Lightbulb","Moth","Giant Atlas","Twin-spotted Sphynx","Madagascan Sunset","Luna","Death's Head","Emperor Gum","Polyphenus","Oleander Hawk","Io","Rosy Maple","Cecropia","Noctuidae","Giant Leopard","Dysphania Militaris","Garden Tiger") + /datum/holiday/ramadan name = "Start of Ramadan" diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm index 4590d58174..4ffc10f9c2 100644 --- a/code/modules/holodeck/computer.dm +++ b/code/modules/holodeck/computer.dm @@ -177,7 +177,7 @@ /obj/machinery/computer/holodeck/proc/generate_program_list() for(var/typekey in subtypesof(program_type)) - var/area/holodeck/A = locate(typekey) in GLOB.sortedAreas + var/area/holodeck/A = GLOB.areas_by_type[typekey] if(!A || !A.contents.len) continue var/list/info_this = list() diff --git a/code/modules/holodeck/holo_effect.dm b/code/modules/holodeck/holo_effect.dm index 4134b0516f..fee0b2b7c9 100644 --- a/code/modules/holodeck/holo_effect.dm +++ b/code/modules/holodeck/holo_effect.dm @@ -76,7 +76,7 @@ mob = new mobtype(loc) // these vars are not really standardized but all would theoretically create stuff on death - for(var/v in list("butcher_results","corpse","weapon1","weapon2","blood_volume","vore_organs") & mob.vars) //CITADEL EDIT, maybe stops ghost bellies + for(var/v in list("butcher_results","corpse","weapon1","weapon2","blood_volume") & mob.vars) mob.vars[v] = null return mob diff --git a/code/modules/holodeck/turfs.dm b/code/modules/holodeck/turfs.dm index c2c17417fd..50496e5518 100644 --- a/code/modules/holodeck/turfs.dm +++ b/code/modules/holodeck/turfs.dm @@ -28,6 +28,7 @@ name = "lush grass" icon_state = "grass" bullet_bounce_sound = null + tiled_dirt = FALSE /turf/open/floor/holofloor/beach gender = PLURAL @@ -35,6 +36,7 @@ icon = 'icons/misc/beach.dmi' icon_state = "sand" bullet_bounce_sound = null + tiled_dirt = FALSE /turf/open/floor/holofloor/beach/coast_t gender = NEUTER @@ -54,6 +56,7 @@ /turf/open/floor/holofloor/asteroid name = "asteroid" icon_state = "asteroid0" + tiled_dirt = FALSE /turf/open/floor/holofloor/asteroid/Initialize() icon_state = "asteroid[rand(0, 12)]" @@ -63,6 +66,7 @@ gender = PLURAL name = "basalt" icon_state = "basalt0" + tiled_dirt = FALSE /turf/open/floor/holofloor/basalt/Initialize() . = ..() @@ -84,6 +88,7 @@ icon = 'icons/turf/space.dmi' icon_state = "speedspace_ns_1" bullet_bounce_sound = null + tiled_dirt = FALSE /turf/open/floor/holofloor/hyperspace/Initialize() icon_state = "speedspace_ns_[(x + 5*y + (y%2+1)*7)%15+1]" @@ -102,6 +107,7 @@ smooth = SMOOTH_TRUE canSmoothWith = null bullet_bounce_sound = null + tiled_dirt = FALSE /turf/open/floor/holofloor/carpet/Initialize() . = ..() @@ -122,6 +128,7 @@ slowdown = 2 bullet_sizzle = TRUE bullet_bounce_sound = null + tiled_dirt = FALSE /turf/open/floor/holofloor/snow/cold initial_gas_mix = "nob=7500;TEMP=2.7" @@ -131,3 +138,4 @@ name = "asteroid sand" icon = 'icons/turf/floors.dmi' icon_state = "asteroid" + tiled_dirt = FALSE diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm new file mode 100644 index 0000000000..b88e6e1ebb --- /dev/null +++ b/code/modules/hydroponics/fermenting_barrel.dm @@ -0,0 +1,77 @@ +/obj/structure/fermenting_barrel + name = "wooden barrel" + desc = "A large wooden barrel. You can ferment fruits and such inside it, or just use it to hold liquid." + icon = 'icons/obj/objects.dmi' + icon_state = "barrel" + density = TRUE + anchored = FALSE + container_type = DRAINABLE | AMOUNT_VISIBLE + pressure_resistance = 2 * ONE_ATMOSPHERE + max_integrity = 300 + var/open = FALSE + var/speed_multiplier = 1 //How fast it distills. Defaults to 100% (1.0). Lower is better. + +/obj/structure/fermenting_barrel/Initialize() + create_reagents(300) //Bluespace beakers, but without the portability or efficiency in circuits. + . = ..() + +/obj/structure/fermenting_barrel/examine(mob/user) + . = ..() + to_chat(user, "It is currently [open?"open, letting you pour liquids in.":"closed, letting you draw liquids from the tap."]") + +/obj/structure/fermenting_barrel/proc/makeWine(obj/item/reagent_containers/food/snacks/grown/fruit) + if(fruit.reagents) + fruit.reagents.trans_to(src, fruit.reagents.total_volume) + var/amount = fruit.seed.potency / 4 + if(fruit.distill_reagent) + reagents.add_reagent(fruit.distill_reagent, amount) + else + var/data = list() + data["names"] = list("[initial(fruit.name)]" = 1) + data["color"] = fruit.filling_color + data["boozepwr"] = fruit.wine_power + if(fruit.wine_flavor) + data["tastes"] = list(fruit.wine_flavor = 1) + else + data["tastes"] = list(fruit.tastes[1] = 1) + reagents.add_reagent("fruit_wine", amount, data) + qdel(fruit) + playsound(src, 'sound/effects/bubbles.ogg', 50, TRUE) + +/obj/structure/fermenting_barrel/attackby(obj/item/I, mob/user, params) + var/obj/item/reagent_containers/food/snacks/grown/fruit = I + if(istype(fruit)) + if(!fruit.can_distill) + to_chat(user, "You can't distill this into anything...") + return TRUE + else if(!user.transferItemToLoc(I,src)) + to_chat(user, "[I] is stuck to your hand!") + return TRUE + to_chat(user, "You place [I] into [src] to start the fermentation process.") + addtimer(CALLBACK(src, .proc/makeWine, fruit), rand(80, 120) * speed_multiplier) + return TRUE + else + return ..() + +/obj/structure/fermenting_barrel/attack_hand(mob/user) + open = !open + if(open) + container_type = REFILLABLE | AMOUNT_VISIBLE + to_chat(user, "You open [src], letting you fill it.") + else + container_type = DRAINABLE | AMOUNT_VISIBLE + to_chat(user, "You close [src], letting you draw from its tap.") + update_icon() + +/obj/structure/fermenting_barrel/update_icon() + if(open) + icon_state = "barrel_open" + else + icon_state = "barrel" + +/datum/crafting_recipe/fermenting_barrel + name = "Wooden Barrel" + result = /obj/structure/fermenting_barrel + reqs = list(/obj/item/stack/sheet/mineral/wood = 30) + time = 50 + category = CAT_PRIMAL diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm index 7e541f2471..d6eacfe0e1 100644 --- a/code/modules/hydroponics/gene_modder.dm +++ b/code/modules/hydroponics/gene_modder.dm @@ -5,6 +5,7 @@ icon_state = "dnamod" density = TRUE circuit = /obj/item/circuitboard/machine/plantgenes + pass_flags = PASSTABLE var/obj/item/seeds/seed var/obj/item/disk/plantgene/disk diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 5b9c1f4e70..096c7b5b2b 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -16,6 +16,10 @@ // If you don't want a plant to be driable (watermelons) set this to null in the time definition. resistance_flags = FLAMMABLE var/dry_grind = FALSE //If TRUE, this object needs to be dry to be ground up + var/can_distill = TRUE //If FALSE, this object cannot be distilled into an alcohol. + var/distill_reagent //If NULL and this object can be distilled, it uses a generic fruit_wine reagent and adjusts its variables. + var/wine_flavor //If NULL, this is automatically set to the fruit's flavor. Determines the flavor of the wine if distill_reagent is NULL. + var/wine_power = 10 //Determines the boozepwr of the wine if distill_reagent is NULL. /obj/item/reagent_containers/food/snacks/grown/Initialize(mapload, obj/item/seeds/new_seed) . = ..() diff --git a/code/modules/hydroponics/grown/ambrosia.dm b/code/modules/hydroponics/grown/ambrosia.dm index 5883b4dd64..a3b93db1ea 100644 --- a/code/modules/hydroponics/grown/ambrosia.dm +++ b/code/modules/hydroponics/grown/ambrosia.dm @@ -31,6 +31,7 @@ seed = /obj/item/seeds/ambrosia name = "ambrosia vulgaris branch" desc = "This is a plant containing various healing chemicals." + wine_power = 30 // Ambrosia Deus /obj/item/seeds/ambrosia/deus @@ -50,6 +51,7 @@ desc = "Eating this makes you feel immortal!" icon_state = "ambrosiadeus" filling_color = "#008B8B" + wine_power = 50 //Ambrosia Gaia /obj/item/seeds/ambrosia/gaia @@ -73,3 +75,5 @@ filling_color = rgb(255, 175, 0) light_range = 3 seed = /obj/item/seeds/ambrosia/gaia + wine_power = 70 + wine_flavor = "the earthmother's blessing" diff --git a/code/modules/hydroponics/grown/apple.dm b/code/modules/hydroponics/grown/apple.dm index 47d288b669..007c0b2454 100644 --- a/code/modules/hydroponics/grown/apple.dm +++ b/code/modules/hydroponics/grown/apple.dm @@ -26,6 +26,7 @@ foodtype = FRUIT juice_results = list("applejuice" = 0) tastes = list("apple" = 1) + distill_reagent = "hcider" // Gold Apple /obj/item/seeds/apple/gold @@ -47,3 +48,5 @@ desc = "Emblazoned upon the apple is the word 'Kallisti'." icon_state = "goldapple" filling_color = "#FFD700" + distill_reagent = null + wine_power = 50 diff --git a/code/modules/hydroponics/grown/banana.dm b/code/modules/hydroponics/grown/banana.dm index 4e29094df4..251d9de5c0 100644 --- a/code/modules/hydroponics/grown/banana.dm +++ b/code/modules/hydroponics/grown/banana.dm @@ -25,6 +25,7 @@ bitesize = 5 foodtype = FRUIT juice_results = list("banana" = 0) + distill_reagent = "bananahonk" /obj/item/reagent_containers/food/snacks/grown/banana/suicide_act(mob/user) user.visible_message("[user] is aiming [src] at [user.p_them()]self! It looks like [user.p_theyre()] trying to commit suicide!") @@ -32,7 +33,7 @@ sleep(25) if(!user) return (OXYLOSS) - user.say("BANG!") + user.say("BANG!", forced = "banana") sleep(25) if(!user) return (OXYLOSS) @@ -79,6 +80,7 @@ icon_state = "mimana" trash = /obj/item/grown/bananapeel/mimanapeel filling_color = "#FFFFEE" + distill_reagent = "silencer" /obj/item/grown/bananapeel/mimanapeel seed = /obj/item/seeds/banana/mime @@ -109,6 +111,8 @@ trash = /obj/item/grown/bananapeel/bluespace filling_color = "#0000FF" tastes = list("banana" = 1) + wine_power = 60 + wine_flavor = "slippery hypercubes" /obj/item/grown/bananapeel/bluespace seed = /obj/item/seeds/banana/bluespace diff --git a/code/modules/hydroponics/grown/beans.dm b/code/modules/hydroponics/grown/beans.dm index b9363d5a12..4338e3b070 100644 --- a/code/modules/hydroponics/grown/beans.dm +++ b/code/modules/hydroponics/grown/beans.dm @@ -28,6 +28,7 @@ foodtype = VEGETABLES grind_results = list("soymilk" = 0) tastes = list("soy" = 1) + wine_power = 20 // Koibean /obj/item/seeds/soya/koi @@ -51,3 +52,4 @@ bitesize_mod = 2 foodtype = VEGETABLES tastes = list("koi" = 1) + wine_power = 40 diff --git a/code/modules/hydroponics/grown/berries.dm b/code/modules/hydroponics/grown/berries.dm index e8e6c52b7a..19abdacf3a 100644 --- a/code/modules/hydroponics/grown/berries.dm +++ b/code/modules/hydroponics/grown/berries.dm @@ -28,6 +28,7 @@ foodtype = FRUIT juice_results = list("berryjuice" = 0) tastes = list("berry" = 1) + distill_reagent = "gin" // Poison Berries /obj/item/seeds/berry/poison @@ -50,6 +51,8 @@ foodtype = FRUIT | TOXIC juice_results = list("poisonberryjuice" = 0) tastes = list("poison-berry" = 1) + distill_reagent = null + wine_power = 35 // Death Berries /obj/item/seeds/berry/death @@ -73,6 +76,8 @@ filling_color = "#708090" foodtype = FRUIT | TOXIC tastes = list("death-berry" = 1) + distill_reagent = null + wine_power = 50 // Glow Berries /obj/item/seeds/berry/glow @@ -97,6 +102,8 @@ filling_color = "#7CFC00" foodtype = FRUIT tastes = list("glow-berry" = 1) + distill_reagent = null + wine_power = 60 // Cherries /obj/item/seeds/cherry @@ -129,6 +136,7 @@ foodtype = FRUIT grind_results = list("cherryjelly" = 0) tastes = list("cherry" = 1) + wine_power = 30 // Blue Cherries /obj/item/seeds/cherry/blue @@ -152,6 +160,7 @@ foodtype = FRUIT grind_results = list("bluecherryjelly" = 0) tastes = list("blue cherry" = 1) + wine_power = 50 // Grapes /obj/item/seeds/grape @@ -185,6 +194,7 @@ foodtype = FRUIT juice_results = list("grapejuice" = 0) tastes = list("grape" = 1) + distill_reagent = "wine" // Green Grapes /obj/item/seeds/grape/green @@ -204,3 +214,4 @@ icon_state = "greengrapes" filling_color = "#7FFF00" tastes = list("green grape" = 1) + distill_reagent = "cognac" diff --git a/code/modules/hydroponics/grown/cannabis.dm b/code/modules/hydroponics/grown/cannabis.dm index c5bf77f6f9..67c5e61dcf 100644 --- a/code/modules/hydroponics/grown/cannabis.dm +++ b/code/modules/hydroponics/grown/cannabis.dm @@ -92,25 +92,28 @@ bitesize_mod = 2 foodtype = VEGETABLES //i dont really know what else weed could be to be honest tastes = list("cannabis" = 1) - + wine_power = 20 /obj/item/reagent_containers/food/snacks/grown/cannabis/rainbow seed = /obj/item/seeds/cannabis/rainbow name = "rainbow cannabis leaf" desc = "Is it supposed to be glowing like that...?" icon_state = "megacannabis" + wine_power = 60 /obj/item/reagent_containers/food/snacks/grown/cannabis/death seed = /obj/item/seeds/cannabis/death name = "death cannabis leaf" desc = "Looks a bit dark. Oh well." icon_state = "blackcannabis" + wine_power = 40 /obj/item/reagent_containers/food/snacks/grown/cannabis/white seed = /obj/item/seeds/cannabis/white name = "white cannabis leaf" desc = "It feels smooth and nice to the touch." icon_state = "whitecannabis" + wine_power = 10 /obj/item/reagent_containers/food/snacks/grown/cannabis/ultimate seed = /obj/item/seeds/cannabis/ultimate @@ -118,3 +121,4 @@ desc = "You feel dizzy looking at it. What the fuck?" icon_state = "ocannabis" volume = 420 + wine_power = 90 diff --git a/code/modules/hydroponics/grown/cereals.dm b/code/modules/hydroponics/grown/cereals.dm index acd3203ecc..c53cd6718a 100644 --- a/code/modules/hydroponics/grown/cereals.dm +++ b/code/modules/hydroponics/grown/cereals.dm @@ -24,6 +24,7 @@ foodtype = GRAIN grind_results = list("flour" = 0) tastes = list("wheat" = 1) + distill_reagent = "beer" // Oat /obj/item/seeds/wheat/oat @@ -46,6 +47,7 @@ foodtype = GRAIN grind_results = list("flour" = 0) tastes = list("oat" = 1) + distill_reagent = "ale" // Rice /obj/item/seeds/wheat/rice @@ -69,6 +71,7 @@ foodtype = GRAIN grind_results = list("rice" = 0) tastes = list("rice" = 1) + distill_reagent = "sake" //Meatwheat - grows into synthetic meat /obj/item/seeds/wheat/meat @@ -91,6 +94,7 @@ foodtype = MEAT | GRAIN grind_results = list("flour" = 0, "blood" = 0) tastes = list("meatwheat" = 1) + can_distill = FALSE /obj/item/reagent_containers/food/snacks/grown/meatwheat/attack_self(mob/living/user) user.visible_message("[user] crushes [src] into meat.", "You crush [src] into something that resembles meat.") diff --git a/code/modules/hydroponics/grown/chili.dm b/code/modules/hydroponics/grown/chili.dm index c01f3ba6ef..6325daacdc 100644 --- a/code/modules/hydroponics/grown/chili.dm +++ b/code/modules/hydroponics/grown/chili.dm @@ -26,6 +26,7 @@ filling_color = "#FF0000" bitesize_mod = 2 foodtype = FRUIT + wine_power = 20 // Ice Chili /obj/item/seeds/chili/ice @@ -50,6 +51,7 @@ filling_color = "#0000CD" bitesize_mod = 2 foodtype = FRUIT + wine_power = 30 // Ghost Chili /obj/item/seeds/chili/ghost @@ -76,6 +78,7 @@ filling_color = "#F8F8FF" bitesize_mod = 4 foodtype = FRUIT + wine_power = 50 /obj/item/reagent_containers/food/snacks/grown/ghost_chili/attack_hand(mob/user) . = ..() diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm index 30b4865a9d..97b30aec06 100644 --- a/code/modules/hydroponics/grown/citrus.dm +++ b/code/modules/hydroponics/grown/citrus.dm @@ -6,6 +6,7 @@ icon_state = "lime" bitesize_mod = 2 foodtype = FRUIT + wine_power = 30 // Lime /obj/item/seeds/lime @@ -58,6 +59,7 @@ icon_state = "orange" filling_color = "#FFA500" juice_results = list("orangejuice" = 0) + distill_reagent = "triple_sec" // Lemon /obj/item/seeds/lemon @@ -109,6 +111,7 @@ icon_state = "firelemon" bitesize_mod = 2 foodtype = FRUIT + wine_power = 70 /obj/item/reagent_containers/food/snacks/grown/firelemon/attack_self(mob/living/user) user.visible_message("[user] primes [src]!", "You prime [src]!") diff --git a/code/modules/hydroponics/grown/cocoa_vanilla.dm b/code/modules/hydroponics/grown/cocoa_vanilla.dm index 12e235b1c3..ea1b9716bd 100644 --- a/code/modules/hydroponics/grown/cocoa_vanilla.dm +++ b/code/modules/hydroponics/grown/cocoa_vanilla.dm @@ -27,6 +27,7 @@ bitesize_mod = 2 foodtype = FRUIT tastes = list("cocoa" = 1) + distill_reagent = "creme_de_cacao" // Vanilla Pod /obj/item/seeds/cocoapod/vanillapod @@ -48,3 +49,4 @@ filling_color = "#FFD700" foodtype = FRUIT tastes = list("vanilla" = 1) + distill_reagent = "vanilla" //Takes longer, but you can get even more vanilla from it. diff --git a/code/modules/hydroponics/grown/corn.dm b/code/modules/hydroponics/grown/corn.dm index 312dda8c26..c259baaf2d 100644 --- a/code/modules/hydroponics/grown/corn.dm +++ b/code/modules/hydroponics/grown/corn.dm @@ -27,6 +27,7 @@ foodtype = VEGETABLES juice_results = list("corn_starch" = 0) tastes = list("corn" = 1) + distill_reagent = "whiskey" /obj/item/grown/corncob name = "corn cob" diff --git a/code/modules/hydroponics/grown/eggplant.dm b/code/modules/hydroponics/grown/eggplant.dm index 9615b7acb2..7c01a68f0a 100644 --- a/code/modules/hydroponics/grown/eggplant.dm +++ b/code/modules/hydroponics/grown/eggplant.dm @@ -23,12 +23,15 @@ filling_color = "#800080" bitesize_mod = 2 foodtype = FRUIT + wine_power = 20 // Egg-Plant /obj/item/seeds/eggplant/eggy + name = "pack of egg-plant seeds" desc = "These seeds grow to produce berries that look a lot like eggs." icon_state = "seed-eggy" species = "eggy" + plantname = "Egg-Plants" product = /obj/item/reagent_containers/food/snacks/grown/shell/eggy lifespan = 75 production = 12 @@ -44,3 +47,4 @@ filling_color = "#F8F8FF" bitesize_mod = 2 foodtype = MEAT + distill_reagent = "eggnog" diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm index 62c3c0b064..4e1718f853 100644 --- a/code/modules/hydroponics/grown/flowers.dm +++ b/code/modules/hydroponics/grown/flowers.dm @@ -26,6 +26,7 @@ filling_color = "#FF6347" bitesize_mod = 3 foodtype = VEGETABLES | GROSS + distill_reagent = "vermouth" // Lily /obj/item/seeds/poppy/lily @@ -61,7 +62,6 @@ icon_state = "geranium" filling_color = "#008B8B" - // Harebell /obj/item/seeds/harebell name = "pack of harebell seeds" @@ -89,7 +89,7 @@ slot_flags = ITEM_SLOT_HEAD filling_color = "#E6E6FA" bitesize_mod = 3 - + distill_reagent = "vermouth" // Sunflower /obj/item/seeds/sunflower @@ -152,6 +152,7 @@ slot_flags = ITEM_SLOT_HEAD filling_color = "#E6E6FA" bitesize_mod = 2 + distill_reagent = "absinthe" //It's made from flowers. // Novaflower /obj/item/seeds/sunflower/novaflower @@ -199,6 +200,7 @@ log_game("[key_name(user)] set [key_name(M)] on fire with [src] at [AREACOORD(user)]") /obj/item/grown/novaflower/afterattack(atom/A as mob|obj, mob/user,proximity) + . = ..() if(!proximity) return if(force > 0) diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm index af466f5186..1a1c2ac07f 100644 --- a/code/modules/hydroponics/grown/grass_carpet.dm +++ b/code/modules/hydroponics/grown/grass_carpet.dm @@ -16,7 +16,7 @@ icon_dead = "grass-dead" genes = list(/datum/plant_gene/trait/repeated_harvest) mutatelist = list(/obj/item/seeds/grass/carpet) - reagents_add = list("nutriment" = 0.02, "hydrogen" = 0.05, "oxygen" = 0.03) //CITADEL CHANGE - adds 0.03 oxygen to grass + reagents_add = list("nutriment" = 0.02, "hydrogen" = 0.05) /obj/item/reagent_containers/food/snacks/grown/grass seed = /obj/item/seeds/grass @@ -27,6 +27,7 @@ bitesize_mod = 2 var/stacktype = /obj/item/stack/tile/grass var/tile_coefficient = 0.02 // 1/50 + wine_power = 15 /obj/item/reagent_containers/food/snacks/grown/grass/attack_self(mob/user) to_chat(user, "You prepare the astroturf.") @@ -56,3 +57,4 @@ desc = "The textile industry's dark secret." icon_state = "carpetclump" stacktype = /obj/item/stack/tile/carpet + can_distill = FALSE diff --git a/code/modules/hydroponics/grown/kudzu.dm b/code/modules/hydroponics/grown/kudzu.dm index e9dec09799..6ceb69536d 100644 --- a/code/modules/hydroponics/grown/kudzu.dm +++ b/code/modules/hydroponics/grown/kudzu.dm @@ -104,3 +104,4 @@ bitesize_mod = 2 foodtype = VEGETABLES | GROSS tastes = list("kudzu" = 1) + wine_power = 20 diff --git a/code/modules/hydroponics/grown/melon.dm b/code/modules/hydroponics/grown/melon.dm index 7497a10ffb..29691c3289 100644 --- a/code/modules/hydroponics/grown/melon.dm +++ b/code/modules/hydroponics/grown/melon.dm @@ -34,6 +34,7 @@ bitesize_mod = 3 foodtype = FRUIT juice_results = list("watermelonjuice" = 0) + wine_power = 40 // Holymelon /obj/item/seeds/watermelon/holy @@ -54,6 +55,8 @@ icon_state = "holymelon" filling_color = "#FFD700" dried_type = null + wine_power = 70 //Water to wine, baby. + wine_flavor = "divinity" /obj/item/reagent_containers/food/snacks/grown/holymelon/Initialize() . = ..() diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm index 82ff56e7c9..107a6a94f9 100644 --- a/code/modules/hydroponics/grown/misc.dm +++ b/code/modules/hydroponics/grown/misc.dm @@ -55,7 +55,7 @@ filling_color = "#90EE90" bitesize_mod = 2 foodtype = VEGETABLES - + wine_power = 20 // Sugarcane /obj/item/seeds/sugarcane @@ -81,7 +81,7 @@ filling_color = "#FFD700" bitesize_mod = 2 foodtype = VEGETABLES | SUGAR - + distill_reagent = "rum" // Gatfruit /obj/item/seeds/gatfruit @@ -112,6 +112,7 @@ bitesize_mod = 2 foodtype = FRUIT tastes = list("gunpowder" = 1) + wine_power = 90 //It burns going down, too. //Cherry Bombs /obj/item/seeds/cherry/bomb @@ -134,6 +135,7 @@ bitesize_mod = 2 volume = 125 //Gives enough room for the black powder at max potency max_integrity = 40 + wine_power = 80 /obj/item/reagent_containers/food/snacks/grown/cherry_bomb/attack_self(mob/living/user) user.visible_message("[user] plucks the stem from [src]!", "You pluck the stem from [src], which begins to hiss loudly!") diff --git a/code/modules/hydroponics/grown/mushrooms.dm b/code/modules/hydroponics/grown/mushrooms.dm index fddcb5b1fc..fe2beb896c 100644 --- a/code/modules/hydroponics/grown/mushrooms.dm +++ b/code/modules/hydroponics/grown/mushrooms.dm @@ -2,7 +2,7 @@ name = "mushroom" bitesize_mod = 2 foodtype = VEGETABLES - + wine_power = 40 // Reishi /obj/item/seeds/reishi @@ -56,7 +56,6 @@ icon_state = "amanita" filling_color = "#FF0000" - // Destroying Angel /obj/item/seeds/angel name = "pack of destroying angel mycelium" @@ -83,7 +82,7 @@ desc = "Amanita Virosa: Deadly poisonous basidiomycete fungus filled with alpha amatoxins." icon_state = "angel" filling_color = "#C0C0C0" - + wine_power = 60 // Liberty Cap /obj/item/seeds/liberty @@ -108,7 +107,7 @@ desc = "Psilocybe Semilanceata: Liberate yourself!" icon_state = "libertycap" filling_color = "#DAA520" - + wine_power = 80 // Plump Helmet /obj/item/seeds/plump @@ -134,7 +133,7 @@ desc = "Plumus Hellmus: Plump, soft and s-so inviting~" icon_state = "plumphelmet" filling_color = "#9370DB" - + distill_reagent = "manlydorf" // Walking Mushroom /obj/item/seeds/plump/walkingmushroom @@ -159,6 +158,7 @@ desc = "Plumus Locomotus: The beginning of the great walk." icon_state = "walkingmushroom" filling_color = "#9370DB" + can_distill = FALSE /obj/item/reagent_containers/food/snacks/grown/mushroom/walkingmushroom/attack_self(mob/user) if(isspaceturf(user.loc)) @@ -228,6 +228,7 @@ icon_state = "glowshroom" filling_color = "#00FA9A" var/effect_path = /obj/structure/glowshroom + wine_power = 50 /obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom/attack_self(mob/user) if(isspaceturf(user.loc)) @@ -298,6 +299,7 @@ icon_state = "shadowshroom" effect_path = /obj/structure/glowshroom/shadowshroom tastes = list("shadow" = 1, "mushroom" = 1) + wine_power = 60 /obj/item/reagent_containers/food/snacks/grown/mushroom/glowshroom/shadowshroom/attack_self(mob/user) . = ..() diff --git a/code/modules/hydroponics/grown/nettle.dm b/code/modules/hydroponics/grown/nettle.dm index bb1a0d2f23..e3f8c254ac 100644 --- a/code/modules/hydroponics/grown/nettle.dm +++ b/code/modules/hydroponics/grown/nettle.dm @@ -4,7 +4,7 @@ icon_state = "seed-nettle" species = "nettle" plantname = "Nettles" - product = /obj/item/grown/nettle/basic + product = /obj/item/reagent_containers/food/snacks/grown/nettle lifespan = 30 endurance = 40 // tuff like a toiger yield = 4 @@ -19,7 +19,7 @@ icon_state = "seed-deathnettle" species = "deathnettle" plantname = "Death Nettles" - product = /obj/item/grown/nettle/death + product = /obj/item/reagent_containers/food/snacks/grown/nettle/death endurance = 25 maturation = 8 yield = 2 @@ -28,7 +28,8 @@ reagents_add = list("facid" = 0.5, "sacid" = 0.5) rarity = 20 -/obj/item/grown/nettle //abstract type +/obj/item/reagent_containers/food/snacks/grown/nettle // "snack" + seed = /obj/item/seeds/nettle name = "nettle" desc = "It's probably not wise to touch it with bare hands..." icon = 'icons/obj/items_and_weapons.dmi' @@ -43,13 +44,12 @@ throw_speed = 1 throw_range = 3 attack_verb = list("stung") - grind_results = list("sacid" = 0) -/obj/item/grown/nettle/suicide_act(mob/user) +/obj/item/reagent_containers/food/snacks/grown/nettle/suicide_act(mob/user) user.visible_message("[user] is eating some of [src]! It looks like [user.p_theyre()] trying to commit suicide!") return (BRUTELOSS|TOXLOSS) -/obj/item/grown/nettle/pickup(mob/living/user) +/obj/item/reagent_containers/food/snacks/grown/nettle/pickup(mob/living/user) ..() if(!iscarbon(user)) return FALSE @@ -66,7 +66,8 @@ to_chat(C, "The nettle burns your bare hand!") return TRUE -/obj/item/grown/nettle/afterattack(atom/A as mob|obj, mob/user,proximity) +/obj/item/reagent_containers/food/snacks/grown/nettle/afterattack(atom/A as mob|obj, mob/user,proximity) + . = ..() if(!proximity) return if(force > 0) @@ -75,38 +76,37 @@ to_chat(usr, "All the leaves have fallen off the nettle from violent whacking.") qdel(src) -/obj/item/grown/nettle/basic +/obj/item/reagent_containers/food/snacks/grown/nettle/basic seed = /obj/item/seeds/nettle -/obj/item/grown/nettle/basic/add_juice() +/obj/item/reagent_containers/food/snacks/grown/nettle/basic/add_juice() ..() force = round((5 + seed.potency / 5), 1) -/obj/item/grown/nettle/death +/obj/item/reagent_containers/food/snacks/grown/nettle/death seed = /obj/item/seeds/nettle/death name = "deathnettle" desc = "The glowing nettle incites rage in you just from looking at it!" icon_state = "deathnettle" force = 30 throwforce = 15 - grind_results = list("facid" = 1, "sacid" = 1) -/obj/item/grown/nettle/death/add_juice() +/obj/item/reagent_containers/food/snacks/grown/nettle/death/add_juice() ..() force = round((5 + seed.potency / 2.5), 1) -/obj/item/grown/nettle/death/pickup(mob/living/carbon/user) +/obj/item/reagent_containers/food/snacks/grown/nettle/death/pickup(mob/living/carbon/user) if(..()) if(prob(50)) user.Knockdown(100) to_chat(user, "You are stunned by the Deathnettle as you try picking it up!") -/obj/item/grown/nettle/death/attack(mob/living/carbon/M, mob/user) +/obj/item/reagent_containers/food/snacks/grown/nettle/death/attack(mob/living/carbon/M, mob/user) if(!..()) return if(isliving(M)) to_chat(M, "You are stunned by the powerful acid of the Deathnettle!") - add_logs(user, M, "attacked", src) + log_combat(user, M, "attacked", src) M.adjust_blurriness(force/7) if(prob(20)) diff --git a/code/modules/hydroponics/grown/onion.dm b/code/modules/hydroponics/grown/onion.dm index 9b957ae744..9cb8d1a63c 100644 --- a/code/modules/hydroponics/grown/onion.dm +++ b/code/modules/hydroponics/grown/onion.dm @@ -26,6 +26,7 @@ tastes = list("onions" = 1) slice_path = /obj/item/reagent_containers/food/snacks/onion_slice slices_num = 2 + wine_power = 30 /obj/item/seeds/onion/red name = "pack of red onion seeds" @@ -44,6 +45,7 @@ icon_state = "onion_red" filling_color = "#C29ACF" slice_path = /obj/item/reagent_containers/food/snacks/onion_slice/red + wine_power = 60 /obj/item/reagent_containers/food/snacks/grown/onion/slice(accuracy, obj/item/W, mob/user) var/datum/effect_system/smoke_spread/chem/S = new //Since the onion is destroyed when it's sliced, diff --git a/code/modules/hydroponics/grown/pineapple.dm b/code/modules/hydroponics/grown/pineapple.dm index 7b58c7553d..e52c261217 100644 --- a/code/modules/hydroponics/grown/pineapple.dm +++ b/code/modules/hydroponics/grown/pineapple.dm @@ -31,3 +31,4 @@ w_class = WEIGHT_CLASS_NORMAL foodtype = FRUIT | PINEAPPLE tastes = list("pineapple" = 1) + wine_power = 40 diff --git a/code/modules/hydroponics/grown/potato.dm b/code/modules/hydroponics/grown/potato.dm index 4d1d916572..15378b368e 100644 --- a/code/modules/hydroponics/grown/potato.dm +++ b/code/modules/hydroponics/grown/potato.dm @@ -27,7 +27,7 @@ bitesize = 100 foodtype = VEGETABLES juice_results = list("potato" = 0) - + distill_reagent = "vodka" /obj/item/reagent_containers/food/snacks/grown/potato/wedges name = "potato wedges" @@ -64,3 +64,4 @@ name = "sweet potato" desc = "It's sweet." icon_state = "sweetpotato" + distill_reagent = "sbiten" diff --git a/code/modules/hydroponics/grown/pumpkin.dm b/code/modules/hydroponics/grown/pumpkin.dm index 2b0ffddfe7..644dedff3d 100644 --- a/code/modules/hydroponics/grown/pumpkin.dm +++ b/code/modules/hydroponics/grown/pumpkin.dm @@ -25,6 +25,7 @@ bitesize_mod = 2 foodtype = FRUIT juice_results = list("pumpkinjuice" = 0) + wine_power = 20 /obj/item/reagent_containers/food/snacks/grown/pumpkin/attackby(obj/item/W as obj, mob/user as mob, params) if(W.is_sharp()) @@ -56,3 +57,4 @@ bitesize_mod = 2 foodtype = FRUIT juice_results = list("blumpkinjuice" = 0) + wine_power = 50 diff --git a/code/modules/hydroponics/grown/random.dm b/code/modules/hydroponics/grown/random.dm index 04b3ff29fa..75505202da 100644 --- a/code/modules/hydroponics/grown/random.dm +++ b/code/modules/hydroponics/grown/random.dm @@ -26,4 +26,10 @@ name = "strange plant" desc = "What could this even be?" icon_state = "crunchy" - bitesize_mod = 2 \ No newline at end of file + bitesize_mod = 2 + +/obj/item/reagent_containers/food/snacks/grown/random/Initialize() + . = ..() + wine_power = rand(10,150) + if(prob(1)) + wine_power = 200 diff --git a/code/modules/hydroponics/grown/root.dm b/code/modules/hydroponics/grown/root.dm index fd78fa6ffa..be0a209c9f 100644 --- a/code/modules/hydroponics/grown/root.dm +++ b/code/modules/hydroponics/grown/root.dm @@ -23,6 +23,7 @@ bitesize_mod = 2 foodtype = VEGETABLES juice_results = list("carrotjuice" = 0) + wine_power = 30 /obj/item/reagent_containers/food/snacks/grown/carrot/attackby(obj/item/I, mob/user, params) if(I.is_sharp()) @@ -53,6 +54,7 @@ icon_state = "parsnip" bitesize_mod = 2 foodtype = VEGETABLES + wine_power = 35 // White-Beet @@ -79,6 +81,7 @@ filling_color = "#F4A460" bitesize_mod = 2 foodtype = VEGETABLES + wine_power = 40 // Red Beet /obj/item/seeds/redbeet @@ -103,3 +106,4 @@ icon_state = "redbeet" bitesize_mod = 2 foodtype = VEGETABLES + wine_power = 60 diff --git a/code/modules/hydroponics/grown/tea_coffee.dm b/code/modules/hydroponics/grown/tea_coffee.dm index fc2ed221c5..fc84617ed8 100644 --- a/code/modules/hydroponics/grown/tea_coffee.dm +++ b/code/modules/hydroponics/grown/tea_coffee.dm @@ -23,6 +23,7 @@ filling_color = "#008000" grind_results = list("teapowder" = 0) dry_grind = TRUE + can_distill = FALSE // Tea Astra /obj/item/seeds/tea/astra @@ -71,6 +72,7 @@ bitesize_mod = 2 dry_grind = TRUE grind_results = list("coffeepowder" = 0) + distill_reagent = "kahlua" // Coffee Robusta /obj/item/seeds/coffee/robusta diff --git a/code/modules/hydroponics/grown/tobacco.dm b/code/modules/hydroponics/grown/tobacco.dm index 3258e7ea4f..684271fa6b 100644 --- a/code/modules/hydroponics/grown/tobacco.dm +++ b/code/modules/hydroponics/grown/tobacco.dm @@ -21,6 +21,7 @@ desc = "Dry them out to make some smokes." icon_state = "tobacco_leaves" filling_color = "#008000" + distill_reagent = "creme_de_menthe" //Menthol, I guess. // Space Tobacco /obj/item/seeds/tobacco/space @@ -38,4 +39,6 @@ seed = /obj/item/seeds/tobacco/space name = "space tobacco leaves" desc = "Dry them out to make some space-smokes." - icon_state = "stobacco_leaves" \ No newline at end of file + icon_state = "stobacco_leaves" + distill_reagent = null + wine_power = 50 \ No newline at end of file diff --git a/code/modules/hydroponics/grown/tomato.dm b/code/modules/hydroponics/grown/tomato.dm index 0f5b969b3b..767de8011a 100644 --- a/code/modules/hydroponics/grown/tomato.dm +++ b/code/modules/hydroponics/grown/tomato.dm @@ -25,6 +25,7 @@ foodtype = FRUIT grind_results = list("ketchup" = 0) juice_results = list("tomatojuice" = 0) + distill_reagent = "enzyme" // Blood Tomato /obj/item/seeds/tomato/blood @@ -47,7 +48,7 @@ filling_color = "#FF0000" foodtype = FRUIT | GROSS grind_results = list("ketchup" = 0, "blood" = 0) - + distill_reagent = "bloodymary" // Blue Tomato /obj/item/seeds/tomato/blue @@ -71,7 +72,7 @@ icon_state = "bluetomato" splat_type = /obj/effect/decal/cleanable/oil filling_color = "#0000FF" - + distill_reagent = "laughter" // Bluespace Tomato /obj/item/seeds/tomato/blue/bluespace @@ -92,7 +93,8 @@ name = "bluespace tomato" desc = "So lubricated, you might slip through space-time." icon_state = "bluespacetomato" - + distill_reagent = null + wine_power = 80 // Killer Tomato /obj/item/seeds/tomato/killer @@ -118,6 +120,7 @@ icon_state = "killertomato" var/awakening = 0 filling_color = "#FF0000" + distill_reagent = "demonsblood" /obj/item/reagent_containers/food/snacks/grown/tomato/killer/attack(mob/M, mob/user, def_zone) if(awakening) diff --git a/code/modules/integrated_electronics/core/analyzer.dm b/code/modules/integrated_electronics/core/analyzer.dm index 89b990f352..7e30529bf4 100644 --- a/code/modules/integrated_electronics/core/analyzer.dm +++ b/code/modules/integrated_electronics/core/analyzer.dm @@ -7,6 +7,7 @@ w_class = WEIGHT_CLASS_SMALL /obj/item/integrated_electronics/analyzer/afterattack(var/atom/A, var/mob/living/user) + . = ..() if(istype(A, /obj/item/electronic_assembly)) var/saved = "[A.name] analyzed! On circuit printers with cloning enabled, you may use the code below to clone the circuit:

[SScircuit.save_electronic_assembly(A)]" if(saved) @@ -14,5 +15,3 @@ user << browse(saved, "window=circuit_scan;size=500x600;border=1;can_resize=1;can_close=1;can_minimize=1") else to_chat(user, "[A] is not complete enough to be encoded!") - else - ..() diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm index d39163b547..1a4bb1fb1b 100644 --- a/code/modules/integrated_electronics/core/assemblies.dm +++ b/code/modules/integrated_electronics/core/assemblies.dm @@ -59,6 +59,11 @@ COLOR_ASSEMBLY_PURPLE ) +/obj/item/electronic_assembly/New() + ..() + src.max_components = round(max_components) + src.max_complexity = round(max_complexity) + /obj/item/electronic_assembly/GenerateTag() tag = "assembly_[next_assembly_id++]" @@ -70,12 +75,18 @@ to_chat(user, "The maintenance panel [opened ? "can be" : "is"] screwed in place.") if((isobserver(user) && ckeys_allowed_to_scan[user.ckey]) || IsAdminGhost(user)) - to_chat(user, "You can scan this circuit."); + to_chat(user, "You can scan this circuit.") + + for(var/I in assembly_components) + var/obj/item/integrated_circuit/IC = I + IC.external_examine(user) + if(opened) + interact(user) /obj/item/electronic_assembly/proc/check_interactivity(mob/user) return user.canUseTopic(src, BE_CLOSE) -/obj/item/electronic_assembly/Collide(atom/AM) +/obj/item/electronic_assembly/Bump(atom/AM) collw = AM .=..() if((istype(collw, /obj/machinery/door/airlock) || istype(collw, /obj/machinery/door/window)) && (!isnull(access_card))) @@ -306,14 +317,6 @@ detail_overlay.color = detail_color add_overlay(detail_overlay) -/obj/item/electronic_assembly/examine(mob/user) - ..() - for(var/I in assembly_components) - var/obj/item/integrated_circuit/IC = I - IC.external_examine(user) - if(opened) - interact(user) - /obj/item/electronic_assembly/proc/return_total_complexity() . = 0 var/obj/item/integrated_circuit/part @@ -420,12 +423,15 @@ /obj/item/electronic_assembly/afterattack(atom/target, mob/user, proximity) + . = ..() for(var/obj/item/integrated_circuit/input/S in assembly_components) if(S.sense(target,user,proximity)) visible_message(" [user] waves [src] around [target].") /obj/item/electronic_assembly/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE I.play_tool_sound(src) opened = !opened to_chat(user, "You [opened ? "open" : "close"] the maintenance hatch of [src].") @@ -439,7 +445,6 @@ if(!user.canUnEquip(I)) return FALSE if(try_add_component(I, user)) - interact(user) return TRUE else for(var/obj/item/integrated_circuit/input/S in assembly_components) @@ -465,24 +470,50 @@ for(var/obj/item/integrated_circuit/input/S in assembly_components) S.attackby_react(I,user,user.a_intent) return ..() - var/obj/item/stock_parts/cell = I - user.transferItemToLoc(I, loc) - cell.forceMove(src) - battery = cell + I.forceMove(src) + battery = I diag_hud_set_circuitstat() //update diagnostic hud playsound(get_turf(src), 'sound/items/Deconstruct.ogg', 50, 1) - to_chat(user, "You slot \the [cell] inside \the [src]'s power supplier.") - interact(user) + to_chat(user, "You slot the [I] inside \the [src]'s power supplier.") return TRUE else if(istype(I, /obj/item/integrated_electronics/detailer)) var/obj/item/integrated_electronics/detailer/D = I detail_color = D.detail_color update_icon() else - for(var/obj/item/integrated_circuit/input/S in assembly_components) - S.attackby_react(I,user,user.a_intent) if(user.a_intent != INTENT_HELP) return ..() + var/list/input_selection = list() + //Check all the components asking for an input + for(var/obj/item/integrated_circuit/input in assembly_components) + if((input.demands_object_input && opened) || (input.demands_object_input && input.can_input_object_when_closed)) + var/i = 0 + //Check if there is another component with the same name and append a number for identification + for(var/s in input_selection) + var/obj/item/integrated_circuit/s_circuit = input_selection[s] + if(s_circuit.name == input.name && s_circuit.displayed_name == input.displayed_name && s_circuit != input) + i++ + var/disp_name= "[input.displayed_name] \[[input]\]" + if(i) + disp_name += " ([i+1])" + //Associative lists prevent me from needing another list and using a Find proc + input_selection[disp_name] = input + + var/obj/item/integrated_circuit/choice + if(input_selection) + if(input_selection.len == 1) + choice = input_selection[input_selection[1]] + else + var/selection = input(user, "Where do you want to insert that item?", "Interaction") as null|anything in input_selection + if(!check_interactivity(user)) + return ..() + if(selection) + choice = input_selection[selection] + if(choice) + choice.additem(I, user) + for(var/obj/item/integrated_circuit/input/S in assembly_components) + S.attackby_react(I,user,user.a_intent) + return ..() /obj/item/electronic_assembly/attack_self(mob/user) @@ -492,30 +523,33 @@ interact(user) var/list/input_selection = list() - var/list/available_inputs = list() + //Check all the components asking for an input for(var/obj/item/integrated_circuit/input/input in assembly_components) if(input.can_be_asked_input) - available_inputs.Add(input) var/i = 0 - for(var/obj/item/integrated_circuit/s in available_inputs) - if(s.name == input.name && s.displayed_name == input.displayed_name && s != input) + //Check if there is another component with the same name and append a number for identification + for(var/s in input_selection) + var/obj/item/integrated_circuit/s_circuit = input_selection[s] + if(s_circuit.name == input.name && s_circuit.displayed_name == input.displayed_name && s_circuit != input) i++ var/disp_name= "[input.displayed_name] \[[input]\]" if(i) disp_name += " ([i+1])" - input_selection.Add(disp_name) + //Associative lists prevent me from needing another list and using a Find proc + input_selection[disp_name] = input var/obj/item/integrated_circuit/input/choice - if(available_inputs) - if(available_inputs.len ==1) - choice = available_inputs[1] + + + if(input_selection) + if(input_selection.len ==1) + choice = input_selection[input_selection[1]] else var/selection = input(user, "What do you want to interact with?", "Interaction") as null|anything in input_selection if(!check_interactivity(user)) return if(selection) - var/index = input_selection.Find(selection) - choice = available_inputs[index] + choice = input_selection[selection] if(choice) choice.ask_for_input(user) @@ -609,6 +643,37 @@ icon_state = "setup_small_pda" desc = "It's a case, for building small electronics with. This one resembles a PDA." +/obj/item/electronic_assembly/small + name = "electronic device" + icon_state = "setup_device" + desc = "It's a case, for building tiny-sized electronics with." + w_class = WEIGHT_CLASS_TINY + max_components = IC_MAX_SIZE_BASE / 2 + max_complexity = IC_COMPLEXITY_BASE / 2 + +/obj/item/electronic_assembly/small/default + name = "type-a electronic device" + +/obj/item/electronic_assembly/small/cylinder + name = "type-b electronic device" + icon_state = "setup_device_cylinder" + desc = "It's a case, for building tiny-sized electronics with. This one has a cylindrical design." + +/obj/item/electronic_assembly/small/scanner + name = "type-c electronic device" + icon_state = "setup_device_scanner" + desc = "It's a case, for building tiny-sized electronics with. This one has a scanner-like design." + +/obj/item/electronic_assembly/small/hook + name = "type-d electronic device" + icon_state = "setup_device_hook" + desc = "It's a case, for building tiny-sized electronics with. This one looks like it has a belt clip, but it's purely decorative." + +/obj/item/electronic_assembly/small/box + name = "type-e electronic device" + icon_state = "setup_device_box" + desc = "It's a case, for building tiny-sized electronics with. This one has a boxy design." + /obj/item/electronic_assembly/medium name = "electronic mechanism" icon_state = "setup_medium" @@ -750,6 +815,14 @@ max_components = IC_MAX_SIZE_BASE max_complexity = IC_COMPLEXITY_BASE +/obj/item/electronic_assembly/wallmount/tiny + name = "tiny wall-mounted electronic assembly" + icon_state = "setup_wallmount_tiny" + desc = "It's a case, for building tiny electronics with. It has a magnetized backing to allow it to stick to walls, but you'll still need to wrench the anchoring bolts in place to keep it on." + w_class = WEIGHT_CLASS_TINY + max_components = IC_MAX_SIZE_BASE / 2 + max_complexity = IC_COMPLEXITY_BASE / 2 + /obj/item/electronic_assembly/wallmount/proc/mount_assembly(turf/on_wall, mob/user) //Yeah, this is admittedly just an abridged and kitbashed version of the wallframe attach procs. if(get_dist(on_wall,user)>1) return diff --git a/code/modules/integrated_electronics/core/debugger.dm b/code/modules/integrated_electronics/core/debugger.dm index d6f6a551ad..5d414de5ee 100644 --- a/code/modules/integrated_electronics/core/debugger.dm +++ b/code/modules/integrated_electronics/core/debugger.dm @@ -9,9 +9,10 @@ w_class = WEIGHT_CLASS_SMALL var/data_to_write = null var/accepting_refs = FALSE + var/copy_values = FALSE /obj/item/integrated_electronics/debugger/attack_self(mob/user) - var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref", "null") + var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref","copy","null") if(!user.IsAdvancedToolUser()) return @@ -19,43 +20,63 @@ switch(type_to_use) if("string") accepting_refs = FALSE + copy_values = FALSE new_data = stripped_input(user, "Now type in a string.","[src] string writing", no_trim = TRUE) if(istext(new_data) && user.IsAdvancedToolUser()) data_to_write = new_data to_chat(user, "You set \the [src]'s memory to \"[new_data]\".") if("number") accepting_refs = FALSE + copy_values = FALSE new_data = input(user, "Now type in a number.","[src] number writing") as null|num if(isnum(new_data) && user.IsAdvancedToolUser()) data_to_write = new_data to_chat(user, "You set \the [src]'s memory to [new_data].") if("ref") accepting_refs = TRUE + copy_values = FALSE to_chat(user, "You turn \the [src]'s ref scanner on. Slide it across \ an object for a ref of that object to save it in memory.") + if("copy") + accepting_refs = FALSE + copy_values = TRUE + to_chat(user, "You turn \the [src]'s value copier on. Use it on a pin \ + to save its current value in memory.") if("null") data_to_write = null + copy_values = FALSE to_chat(user, "You set \the [src]'s memory to absolutely nothing.") /obj/item/integrated_electronics/debugger/afterattack(atom/target, mob/living/user, proximity) + . = ..() if(accepting_refs && proximity) data_to_write = WEAKREF(target) visible_message("[user] slides \a [src]'s over \the [target].") to_chat(user, "You set \the [src]'s memory to a reference to [target.name] \[Ref\]. The ref scanner is \ now off.") accepting_refs = FALSE - else - return ..() /obj/item/integrated_electronics/debugger/proc/write_data(var/datum/integrated_io/io, mob/user) + //If the pin can take data: if(io.io_type == DATA_CHANNEL) + //If the debugger is set to copy, copy the data in the pin onto it + if(copy_values) + data_to_write = io.data + to_chat(user, "You let the debugger copy the data.") + copy_values = FALSE + return + + //Else, write the data to the pin io.write_data_to_pin(data_to_write) var/data_to_show = data_to_write + //This is only to convert a weakref into a name for better output if(isweakref(data_to_write)) var/datum/weakref/w = data_to_write var/atom/A = w.resolve() data_to_show = A.name to_chat(user, "You write '[data_to_write ? data_to_show : "NULL"]' to the '[io]' pin of \the [io.holder].") + + //If the pin can only be pulsed else if(io.io_type == PULSE_CHANNEL) io.holder.check_then_do_work(io.ord,ignore_power = TRUE) to_chat(user, "You pulse \the [io.holder]'s [io].") diff --git a/code/modules/integrated_electronics/core/integrated_circuit.dm b/code/modules/integrated_electronics/core/integrated_circuit.dm index 57ad917246..60b2d0486e 100644 --- a/code/modules/integrated_electronics/core/integrated_circuit.dm +++ b/code/modules/integrated_electronics/core/integrated_circuit.dm @@ -24,6 +24,9 @@ var/category_text = "NO CATEGORY THIS IS A BUG" // To show up on circuit printer, and perhaps other places. var/removable = TRUE // Determines if a circuit is removable from the assembly. var/displayed_name = "" + var/demands_object_input = FALSE + var/can_input_object_when_closed = FALSE + /* Integrated circuits are essentially modular machines. Each circuit has a specific function, and combining them inside Electronic Assemblies allows @@ -35,6 +38,10 @@ a creative player the means to solve many problems. Circuits are held inside an external_examine(user) . = ..() +// Can be called via electronic_assembly/attackby() +/obj/item/integrated_circuit/proc/additem(var/obj/item/I, var/mob/living/user) + attackby(I, user) + // This should be used when someone is examining while the case is opened. /obj/item/integrated_circuit/proc/internal_examine(mob/user) to_chat(user, "This board has [inputs.len] input pin\s, [outputs.len] output pin\s and [activators.len] activation pin\s.") diff --git a/code/modules/integrated_electronics/core/printer.dm b/code/modules/integrated_electronics/core/printer.dm index f8d1424160..de3ade389f 100644 --- a/code/modules/integrated_electronics/core/printer.dm +++ b/code/modules/integrated_electronics/core/printer.dm @@ -38,6 +38,7 @@ /obj/item/integrated_circuit_printer/proc/print_program(mob/user) if(!cloning) return + visible_message("[src] has finished printing its assembly!") playsound(src, 'sound/items/poster_being_created.ogg', 50, TRUE) var/obj/item/electronic_assembly/assembly = SScircuit.load_electronic_assembly(get_turf(src), program) @@ -52,7 +53,6 @@ return TRUE to_chat(user, "You install [O] into [src]. ") upgraded = TRUE - interact(user) return TRUE if(istype(O, /obj/item/disk/integrated_circuit/upgrade/clone)) @@ -61,7 +61,6 @@ return TRUE to_chat(user, "You install [O] into [src]. Circuit cloning will now be instant. ") fast_clone = TRUE - interact(user) return TRUE if(istype(O, /obj/item/electronic_assembly)) @@ -108,11 +107,17 @@ interact(user) /obj/item/integrated_circuit_printer/interact(mob/user) + if(!(in_range(src, user) || issilicon(user))) + return + if(isnull(current_category)) current_category = SScircuit.circuit_fabricator_recipe_list[1] var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + //Preparing the browser + var/datum/browser/popup = new(user, "printernew", "Integrated Circuit Printer", 800, 630) // Set up the popup browser window + var/HTML = "

Integrated Circuit Printer


" if(debug) HTML += "

DEBUG PRINTER -- Infinite materials. Cloning available.

" @@ -132,13 +137,13 @@ if(!cloning) HTML += " {Load Program} " else - HTML += " {Load Program}" + HTML += " Load Program" if(!program) - HTML += " {[fast_clone ? "Print" : "Begin Printing"] Assembly}" + HTML += " [fast_clone ? "Print" : "Begin Printing"] Assembly" else if(cloning) - HTML += " {Cancel Print}" + HTML += " Cancel Print" else - HTML += " {[fast_clone ? "Print" : "Begin Printing"] Assembly}" + HTML += " [fast_clone ? "Print" : "Begin Printing"] Assembly" HTML += "

" HTML += "Categories:" @@ -159,11 +164,12 @@ if((initial(IC.spawn_flags) & IC_SPAWN_RESEARCH) && (!(initial(IC.spawn_flags) & IC_SPAWN_DEFAULT)) && !upgraded) can_build = FALSE if(can_build) - HTML += "\[[initial(O.name)]\]: [initial(O.desc)]
" + HTML += "[initial(O.name)]: [initial(O.desc)]
" else - HTML += "\[[initial(O.name)]\]: [initial(O.desc)]
" + HTML += "[initial(O.name)]: [initial(O.desc)]
" - user << browse(HTML, "window=integrated_printer;size=600x500;border=1;can_resize=1;can_close=1;can_minimize=1") + popup.set_content(HTML) + popup.open() /obj/item/integrated_circuit_printer/Topic(href, href_list) if(!check_interactivity(usr)) diff --git a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm index 461965f254..f64e15f225 100644 --- a/code/modules/integrated_electronics/core/special_pins/ref_pin.dm +++ b/code/modules/integrated_electronics/core/special_pins/ref_pin.dm @@ -11,4 +11,14 @@ holder.on_data_written() /datum/integrated_io/ref/display_pin_type() - return IC_FORMAT_REF \ No newline at end of file + return IC_FORMAT_REF + +/datum/integrated_io/ref/connect_pin(datum/integrated_io/pin) + ..(pin) + if(istype(pin,/datum/integrated_io/selfref)) + write_data_to_pin(pin.data) + +/datum/integrated_io/ref/disconnect_pin(datum/integrated_io/pin) + ..(pin) + if(istype(pin,/datum/integrated_io/selfref)) + write_data_to_pin(null) diff --git a/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm b/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm new file mode 100644 index 0000000000..79dcf9e6c8 --- /dev/null +++ b/code/modules/integrated_electronics/core/special_pins/selfref_pin.dm @@ -0,0 +1,27 @@ +// This pin only contains its own weakref and can't be changed +/datum/integrated_io/selfref + name = "selfref pin" + +/datum/integrated_io/selfref/New() + ..() + write_data_to_pin(src) + +/datum/integrated_io/selfref/ask_for_pin_data(mob/user) // You can't clear it, it's self reference. + + +/datum/integrated_io/selfref/write_data_to_pin(var/new_data) // You can't write anything else but itself onto it + if(data) + return + data = WEAKREF(holder) + holder.on_data_written() + +/datum/integrated_io/selfref/display_pin_type() + return IC_FORMAT_REF + +/datum/integrated_io/selfref/connect_pin(datum/integrated_io/pin) + pin.write_data_to_pin(data) + ..(pin) + +/datum/integrated_io/selfref/disconnect_pin(datum/integrated_io/pin) + ..(pin) + pin.write_data_to_pin(null) diff --git a/code/modules/integrated_electronics/subtypes/access.dm b/code/modules/integrated_electronics/subtypes/access.dm index 9012cd1089..5e03ea1394 100644 --- a/code/modules/integrated_electronics/subtypes/access.dm +++ b/code/modules/integrated_electronics/subtypes/access.dm @@ -14,17 +14,13 @@ "on read" = IC_PINTYPE_PULSE_OUT ) -/obj/item/integrated_circuit/input/card_reader/old - name = "card reader" - spawn_flags = 0 - /obj/item/integrated_circuit/input/card_reader/attackby_react(obj/item/I, mob/living/user, intent) var/obj/item/card/id/card = I.GetID() var/list/access = I.GetAccess() var/passkey = strtohex(XorEncrypt(json_encode(access), SScircuit.cipherkey)) if(assembly) - assembly.access_card.access = access + assembly.access_card.access |= access if(card) // An ID card. set_pin_data(IC_OUTPUT, 1, card.registered_name) diff --git a/code/modules/integrated_electronics/subtypes/arithmetic.dm b/code/modules/integrated_electronics/subtypes/arithmetic.dm index d4b854268b..6a6ee27ae1 100644 --- a/code/modules/integrated_electronics/subtypes/arithmetic.dm +++ b/code/modules/integrated_electronics/subtypes/arithmetic.dm @@ -311,3 +311,33 @@ set_pin_data(IC_OUTPUT, 1, result) push_data() activate_pin(2) + +// -Max- // +/obj/item/integrated_circuit/arithmetic/max + name = "max circuit" + desc = "This circuit sends out the highest number." + extended_desc = "The highest number is put out. Null is ignored." + icon_state = "addition" + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + var/min_comparision = FALSE + +/obj/item/integrated_circuit/arithmetic/max/do_work() + var/result + for(var/k in 1 to inputs.len) + var/I = get_pin_data(IC_INPUT, k) + if(!isnum(I)) + continue + if(!isnum(result) || (!min_comparision && I > result) || (min_comparision && I < result)) + result = I + if(!isnum(result)) + result = 0 + set_pin_data(IC_OUTPUT, 1, result) + push_data() + activate_pin(2) + +// -Min- // +/obj/item/integrated_circuit/arithmetic/max/min + name = "min circuit" + desc = "This circuit sends out the smallest number." + extended_desc = "The smallest number is put out. Null is ignored. In case no number is found, 0 is given out." + min_comparision = TRUE diff --git a/code/modules/integrated_electronics/subtypes/converters.dm b/code/modules/integrated_electronics/subtypes/converters.dm index 9382f70066..668f49c98f 100644 --- a/code/modules/integrated_electronics/subtypes/converters.dm +++ b/code/modules/integrated_electronics/subtypes/converters.dm @@ -357,6 +357,7 @@ /obj/item/integrated_circuit/converter/abs_to_rel_coords name = "abs to rel coordinate converter" desc = "Easily convert absolute coordinates to relative coordinates with this." + extended_desc = "Keep in mind that both sets of input coordinates should be absolute." complexity = 1 inputs = list( "X1" = IC_PINTYPE_NUMBER, @@ -385,6 +386,71 @@ push_data() activate_pin(2) +/obj/item/integrated_circuit/converter/rel_to_abs_coords + name = "rel to abs coordinate converter" + desc = "Convert relative coordinates to absolute coordinates with this." + extended_desc = "Keep in mind that only one set of input coordinates should be absolute, and the other relative. \ + The output coordinates will be the absolute form of the input relative coordinates." + complexity = 1 + inputs = list( + "X1" = IC_PINTYPE_NUMBER, + "Y1" = IC_PINTYPE_NUMBER, + "X2" = IC_PINTYPE_NUMBER, + "Y2" = IC_PINTYPE_NUMBER + ) + outputs = list( + "X" = IC_PINTYPE_NUMBER, + "Y" = IC_PINTYPE_NUMBER + ) + activators = list("compute abs coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/abs_to_rel_coords/do_work() + var/x1 = get_pin_data(IC_INPUT, 1) + var/y1 = get_pin_data(IC_INPUT, 2) + + var/x2 = get_pin_data(IC_INPUT, 3) + var/y2 = get_pin_data(IC_INPUT, 4) + + if(!isnull(x1) && !isnull(y1) && !isnull(x2) && !isnull(y2)) + set_pin_data(IC_OUTPUT, 1, x1 + x2) + set_pin_data(IC_OUTPUT, 2, y1 + y2) + + push_data() + activate_pin(2) + +/obj/item/integrated_circuit/converter/adv_rel_to_abs_coords + name = "advanced rel to abs coordinate converter" + desc = "Easily convert relative coordinates to absolute coordinates with this." + extended_desc = "This circuit only requires a single set of relative inputs to output absolute coordinates." + complexity = 2 + inputs = list( + "X" = IC_PINTYPE_NUMBER, + "Y" = IC_PINTYPE_NUMBER, + ) + outputs = list( + "X" = IC_PINTYPE_NUMBER, + "Y" = IC_PINTYPE_NUMBER + ) + activators = list("compute abs coordinates" = IC_PINTYPE_PULSE_IN, "on convert" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + +/obj/item/integrated_circuit/converter/abs_to_rel_coords/do_work() + var/turf/T = get_turf(src) + + if(!T) + return + + var/x1 = get_pin_data(IC_INPUT, 1) + var/y1 = get_pin_data(IC_INPUT, 2) + + if(!isnull(x1) && !isnull(y1)) + set_pin_data(IC_OUTPUT, 1, T.x + x1) + set_pin_data(IC_OUTPUT, 2, T.y + y1) + + push_data() + activate_pin(2) + /obj/item/integrated_circuit/converter/hsv2hex name = "hsv to hexadecimal" desc = "This circuit can convert a HSV (Hue, Saturation, and Value) color to a Hexadecimal RGB color." diff --git a/code/modules/integrated_electronics/subtypes/data_transfer.dm b/code/modules/integrated_electronics/subtypes/data_transfer.dm index 682d982373..8e1c715d83 100644 --- a/code/modules/integrated_electronics/subtypes/data_transfer.dm +++ b/code/modules/integrated_electronics/subtypes/data_transfer.dm @@ -145,6 +145,55 @@ w_class = WEIGHT_CLASS_SMALL number_of_pins = 16 +/obj/item/integrated_circuit/transfer/pulsemultiplexer + name = "two pulse multiplexer" + desc = "Pulse in pins to choose the pin value to be sent." + extended_desc = "The input pulses are used to select which of the input pins has its data moved to the output." + complexity = 2 + icon_state = "dmux2" + inputs = list() + outputs = list("output" = IC_PINTYPE_ANY) + activators = list("on selected" = IC_PINTYPE_PULSE_OUT) + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + power_draw_per_use = 4 + var/number_of_pins = 2 + +/obj/item/integrated_circuit/transfer/pulsemultiplexer/Initialize() + for(var/i = 1 to number_of_pins) + inputs["input [i]"] = IC_PINTYPE_ANY + for(var/i = 1 to number_of_pins) + activators["input [i]"] = IC_PINTYPE_PULSE_IN + complexity = number_of_pins + + . = ..() + desc += " It has [number_of_pins] pulse in pins and [number_of_pins] output pins." + extended_desc += " This pulse multiplexer has a range from 1 to [activators.len - 1]." + +/obj/item/integrated_circuit/transfer/pulsemultiplexer/do_work(ord) + var/input_index = ord - 2 + + if(!isnull(input_index) && (input_index >= 0 && input_index < inputs.len)) + set_pin_data(IC_OUTPUT, 1,get_pin_data(IC_INPUT, input_index + 1)) + push_data() + activate_pin(1) + +/obj/item/integrated_circuit/transfer/pulsemultiplexer/medium + name = "four pulse multiplexer" + icon_state = "dmux4" + number_of_pins = 4 + +/obj/item/integrated_circuit/transfer/pulsemultiplexer/large + name = "eight pulse multiplexer" + icon_state = "dmux8" + w_class = WEIGHT_CLASS_SMALL + number_of_pins = 8 + +/obj/item/integrated_circuit/transfer/pulsemultiplexer/huge + name = "sixteen pulse multiplexer" + icon_state = "dmux16" + w_class = WEIGHT_CLASS_SMALL + number_of_pins = 16 + /obj/item/integrated_circuit/transfer/wire_node name = "wire node" desc = "Just a wire node to make wiring easier. Transfers the pulse from in to out." @@ -156,4 +205,4 @@ size = 0.1 /obj/item/integrated_circuit/transfer/wire_node/do_work() - activate_pin(2) \ No newline at end of file + activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index a32be12b6d..e963bf9262 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -162,11 +162,6 @@ push_data() activate_pin(2) -//please delete at a later date after people stop using the old named circuit -/obj/item/integrated_circuit/input/adv_med_scanner/old - name = "integrated advanced medical analyser" - spawn_flags = 0 - /obj/item/integrated_circuit/input/slime_scanner name = "slime_scanner" desc = "A very small version of the xenobio analyser. This allows the machine to know every needed properties of slime. Output mutation list is non-associative." @@ -729,8 +724,7 @@ inputs = list( "target NTNet addresses"= IC_PINTYPE_STRING, "data to send" = IC_PINTYPE_STRING, - "secondary text" = IC_PINTYPE_STRING, - "passkey" = IC_PINTYPE_STRING, //No this isn't a real passkey encryption scheme but that's why you keep your nodes secure so no one can find it out! + "secondary text" = IC_PINTYPE_STRING ) outputs = list( "address received" = IC_PINTYPE_STRING, @@ -756,11 +750,10 @@ var/target_address = get_pin_data(IC_INPUT, 1) var/message = get_pin_data(IC_INPUT, 2) var/text = get_pin_data(IC_INPUT, 3) - var/key = get_pin_data(IC_INPUT, 4) var/datum/netdata/data = new data.recipient_ids = splittext(target_address, ";") - data.standard_format_data(message, text, key) + data.standard_format_data(message, text, assembly ? strtohex(XorEncrypt(json_encode(assembly.access_card.access), SScircuit.cipherkey)) : null) ntnet_send(data) /obj/item/integrated_circuit/input/ntnet_receive(datum/netdata/data) @@ -777,9 +770,9 @@ name = "Low level NTNet transreceiver" desc = "Enables the sending and receiving of messages over NTNet via packet data protocol. Allows advanced control of message contents and signalling. Must use associative lists. Outputs associative list. Has a slower transmission rate than normal NTNet circuits, due to increased data processing complexity." extended_desc = "Data can be sent or received using the second pin on each side, \ - with additonal data reserved for the third pin. When a message is received, the second activation pin \ - will pulse whatever is connected to it. Pulsing the first activation pin will send a message. Messages \ - can be sent to multiple recepients. Addresses must be separated with a semicolon, like this: Address1;Address2;Etc." + When a message is received, the second activation pin will pulse whatever is connected to it. \ + Pulsing the first activation pin will send a message. Messages can be sent to multiple recepients. \ + Addresses must be separated with a semicolon, like this: Address1;Address2;Etc." icon_state = "signal" complexity = 4 cooldown_per_use = 10 @@ -809,6 +802,7 @@ var/datum/netdata/data = new data.recipient_ids = splittext(target_address, ";") data.data = message + data.passkey = assembly.access_card.access ntnet_send(data) /obj/item/integrated_circuit/input/ntnet_advanced/ntnet_receive(datum/netdata/data) @@ -821,11 +815,11 @@ /obj/item/integrated_circuit/input/gps name = "global positioning system" desc = "This allows you to easily know the position of a machine containing this device." - extended_desc = "The coordinates that the GPS outputs are absolute, not relative." + extended_desc = "The coordinates that the GPS outputs are absolute, not relative. The full coords output has the coords separated by commas and is in string format." icon_state = "gps" complexity = 4 inputs = list() - outputs = list("X"= IC_PINTYPE_NUMBER, "Y" = IC_PINTYPE_NUMBER, "Z" = IC_PINTYPE_NUMBER) + outputs = list("X"= IC_PINTYPE_NUMBER, "Y" = IC_PINTYPE_NUMBER, "Z" = IC_PINTYPE_NUMBER, "full coords" = IC_PINTYPE_STRING) activators = list("get coordinates" = IC_PINTYPE_PULSE_IN, "on get coordinates" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH power_draw_per_use = 30 @@ -836,13 +830,14 @@ set_pin_data(IC_OUTPUT, 1, null) set_pin_data(IC_OUTPUT, 2, null) set_pin_data(IC_OUTPUT, 3, null) + set_pin_data(IC_OUTPUT, 4, null) if(!T) return set_pin_data(IC_OUTPUT, 1, T.x) set_pin_data(IC_OUTPUT, 2, T.y) set_pin_data(IC_OUTPUT, 3, T.z) - + set_pin_data(IC_OUTPUT, 4, "[T.x],[T.y],[T.z]") push_data() activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/logic.dm b/code/modules/integrated_electronics/subtypes/logic.dm index e9dca8c330..37bc93e887 100644 --- a/code/modules/integrated_electronics/subtypes/logic.dm +++ b/code/modules/integrated_electronics/subtypes/logic.dm @@ -203,7 +203,7 @@ return A.data > B.data /obj/item/integrated_circuit/logic/binary/greater_than_or_equal - name = "greater_than or equal gate" + name = "greater than or equal gate" desc = "This will output TRUE if the first input is greater than, or equal to the second input." icon_state = "greater_than_or_equal" spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH diff --git a/code/modules/integrated_electronics/subtypes/manipulation.dm b/code/modules/integrated_electronics/subtypes/manipulation.dm index 6267f2b622..1c76c7648c 100644 --- a/code/modules/integrated_electronics/subtypes/manipulation.dm +++ b/code/modules/integrated_electronics/subtypes/manipulation.dm @@ -34,11 +34,13 @@ var/lethal_projectile = null //lethal mode projectile type var/lethal_projectile_sound + demands_object_input = TRUE // You can put stuff in once the circuit is in assembly,passed down from additem and handled by attackby() + /obj/item/integrated_circuit/manipulation/weapon_firing/Destroy() qdel(installed_gun) - ..() + return ..() /obj/item/integrated_circuit/manipulation/weapon_firing/attackby(var/obj/O, var/mob/user) if(istype(O, /obj/item/gun/energy)) @@ -129,6 +131,7 @@ //Shooting Code: A.preparePixelProjectile(target, src) A.fire() + log_attack("[assembly] [REF(assembly)] has fired [installed_gun].") return A /obj/item/integrated_circuit/manipulation/locomotion @@ -184,6 +187,7 @@ action_flags = IC_ACTION_COMBAT var/obj/item/grenade/attached_grenade var/pre_attached_grenade_type + demands_object_input = TRUE // You can put stuff in once the circuit is in assembly,passed down from additem and handled by attackby() /obj/item/integrated_circuit/manipulation/grenade/Initialize() . = ..() @@ -350,7 +354,7 @@ name = "grabber" desc = "A circuit with its own inventory for items. Used to grab and store things." icon_state = "grabber" - extended_desc = "This circuit accepts a reference to an object to be grabbed, and can store up to 10 objects. Modes: 1 to grab, 0 to eject the first object, and -1 to eject all objects. If you throw something from a grabber's inventory with a thrower, the grabber will update its outputs accordingly." + extended_desc = "This circuit accepts a reference to an object to be grabbed, and can store up to 10 objects. Modes: 1 to grab, 0 to eject the first object, -1 to eject all objects, and -2 to eject the target. If you throw something from a grabber's inventory with a thrower, the grabber will update its outputs accordingly." w_class = WEIGHT_CLASS_SMALL size = 3 cooldown_per_use = 5 @@ -359,37 +363,47 @@ outputs = list("first" = IC_PINTYPE_REF, "last" = IC_PINTYPE_REF, "amount" = IC_PINTYPE_NUMBER,"contents" = IC_PINTYPE_LIST) activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_RESEARCH + action_flags = IC_ACTION_COMBAT power_draw_per_use = 50 var/max_items = 10 /obj/item/integrated_circuit/manipulation/grabber/do_work() - var/max_w_class = assembly.w_class - var/atom/movable/acting_object = get_object() - var/turf/T = get_turf(acting_object) var/obj/item/AM = get_pin_data_as_type(IC_INPUT, 1, /obj/item) if(!QDELETED(AM) && !istype(AM, /obj/item/electronic_assembly) && !istype(AM, /obj/item/transfer_valve) && !istype(AM, /obj/item/twohanded) && !istype(assembly.loc, /obj/item/implant/storage)) var/mode = get_pin_data(IC_INPUT, 2) - if(mode == 1) - if(check_target(AM)) - var/weightcheck = FALSE - if (AM.w_class <= max_w_class) - weightcheck = TRUE - else - weightcheck = FALSE - if((contents.len < max_items) && (weightcheck)) - AM.forceMove(src) - if(mode == 0) - if(contents.len) - var/obj/item/U = contents[1] - U.forceMove(T) - if(mode == -1) - if(contents.len) - var/obj/item/U - for(U in contents) - U.forceMove(T) + switch(mode) + if(1) + grab(AM) + if(0) + if(contents.len) + drop(contents[1]) + if(-1) + drop_all() + if(-2) + drop(AM) update_outputs() activate_pin(2) +/obj/item/integrated_circuit/manipulation/grabber/proc/grab(obj/item/AM) + var/max_w_class = assembly.w_class + if(check_target(AM)) + if(contents.len < max_items && AM.w_class <= max_w_class) + var/atom/A = get_object() + A.investigate_log("picked up ([AM]) with [src].", INVESTIGATE_CIRCUIT) + AM.forceMove(src) + +/obj/item/integrated_circuit/manipulation/grabber/proc/drop(obj/item/AM, turf/T = drop_location()) + var/atom/A = get_object() + A.investigate_log("dropped ([AM]) from [src].", INVESTIGATE_CIRCUIT) + AM.forceMove(T) + +/obj/item/integrated_circuit/manipulation/grabber/proc/drop_all() + if(contents.len) + var/turf/T = drop_location() + var/obj/item/U + for(U in src) + drop(U, T) + /obj/item/integrated_circuit/manipulation/grabber/proc/update_outputs() if(contents.len) set_pin_data(IC_OUTPUT, 1, WEAKREF(contents[1])) @@ -402,11 +416,7 @@ push_data() /obj/item/integrated_circuit/manipulation/grabber/attack_self(var/mob/user) - if(contents.len) - var/turf/T = get_turf(src) - var/obj/item/U - for(U in contents) - U.forceMove(T) + drop_all() update_outputs() push_data() @@ -436,6 +446,7 @@ mode = CLAMP(mode, GRAB_PASSIVE, max_grab) if(AM) if(check_target(AM, exclude_contents = TRUE)) + acting_object.investigate_log("grabbed ([AM]) using [src].", INVESTIGATE_CIRCUIT) acting_object.start_pulling(AM,mode) if(acting_object.pulling) set_pin_data(IC_OUTPUT, 1, TRUE) @@ -528,16 +539,24 @@ var/x_abs = CLAMP(T.x + target_x_rel, 0, world.maxx) var/y_abs = CLAMP(T.y + target_y_rel, 0, world.maxy) var/range = round(CLAMP(sqrt(target_x_rel*target_x_rel+target_y_rel*target_y_rel),0,8),1) - + //remove damage + A.throwforce = 0 + A.embedding = list("embed_chance" = 0) + //throw it assembly.visible_message("[assembly] has thrown [A]!") log_attack("[assembly] [REF(assembly)] has thrown [A].") A.forceMove(drop_location()) - A.throw_at(locate(x_abs, y_abs, T.z), range, 3) + A.throw_at(locate(x_abs, y_abs, T.z), range, 3, , , , CALLBACK(src, .proc/post_throw, A)) // If the item came from a grabber now we can update the outputs since we've thrown it. - if(G) + if(istype(G)) G.update_outputs() +/obj/item/integrated_circuit/manipulation/thrower/proc/post_throw(obj/item/A) + //return damage + A.throwforce = initial(A.throwforce) + A.embedding = initial(A.embedding) + /obj/item/integrated_circuit/manipulation/matman name = "material manager" desc = "This circuit is designed for automatic storage and distribution of materials." @@ -593,7 +612,7 @@ /obj/item/integrated_circuit/manipulation/matman/Initialize() var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), 0, - FALSE, list(/obj/item/stack), CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) + FALSE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) materials.max_amount =100000 materials.precise_insertion = TRUE .=..() @@ -657,4 +676,4 @@ /obj/item/integrated_circuit/manipulation/matman/Destroy() GET_COMPONENT(materials, /datum/component/material_container) materials.retrieve_all() - .=..() \ No newline at end of file + .=..() diff --git a/code/modules/integrated_electronics/subtypes/memory.dm b/code/modules/integrated_electronics/subtypes/memory.dm index fe74657532..93d800807a 100644 --- a/code/modules/integrated_electronics/subtypes/memory.dm +++ b/code/modules/integrated_electronics/subtypes/memory.dm @@ -129,6 +129,7 @@ to_chat(user, "You set \the [src]'s memory to absolutely nothing.") /obj/item/integrated_circuit/memory/constant/afterattack(atom/target, mob/living/user, proximity) + . = ..() if(accepting_refs && proximity) var/datum/integrated_io/O = outputs[1] O.data = WEAKREF(target) diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm index b887617ea8..0ad2714e28 100644 --- a/code/modules/integrated_electronics/subtypes/output.dm +++ b/code/modules/integrated_electronics/subtypes/output.dm @@ -2,7 +2,7 @@ category_text = "Output" /obj/item/integrated_circuit/output/screen - name = "small screen" + name = "screen" extended_desc = " use <br> to start a new line" desc = "Takes any data type as an input, and displays it to the user upon examining." icon_state = "screen" @@ -34,14 +34,24 @@ else stuff_to_display = replacetext("[I.data]", eol , "
") -/obj/item/integrated_circuit/output/screen/medium - name = "screen" - desc = "Takes any data type as an input and displays it to the user upon examining, and to adjacent beings when pulsed." +/obj/item/integrated_circuit/output/screen/large + name = "large screen" + desc = "Takes any data type as an input and displays it to anybody near the device when pulsed. \ + It can also be examined to see the last thing it displayed." icon_state = "screen_medium" power_draw_per_use = 20 -/obj/item/integrated_circuit/output/screen/medium/do_work() +/obj/item/integrated_circuit/output/screen/large/do_work() ..() + + if(isliving(assembly.loc))//this whole block just returns if the assembly is neither in a mobs hands or on the ground + var/mob/living/H = assembly.loc + if(H.get_active_held_item() != assembly && H.get_inactive_held_item() != assembly) + return + else + if(!isturf(assembly.loc)) + return + var/list/nearby_things = range(0, get_turf(src)) for(var/mob/M in nearby_things) var/obj/O = assembly ? assembly : src @@ -51,22 +61,6 @@ else investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT) -/obj/item/integrated_circuit/output/screen/large - name = "large screen" - desc = "Takes any data type as an input and displays it to the user upon examining, and to all nearby beings when pulsed." - icon_state = "screen_large" - power_draw_per_use = 40 - cooldown_per_use = 10 - -/obj/item/integrated_circuit/output/screen/large/do_work() - ..() - var/obj/O = assembly ? get_turf(assembly) : loc - O.visible_message("[icon2html(O.icon, world, O.icon_state)] [stuff_to_display]") - if(assembly) - assembly.investigate_log("displayed \"[html_encode(stuff_to_display)]\" with [type].", INVESTIGATE_CIRCUIT) - else - investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT) - /obj/item/integrated_circuit/output/light name = "light" desc = "A basic light which can be toggled on/off when pulsed." @@ -88,7 +82,7 @@ /obj/item/integrated_circuit/output/light/proc/update_lighting() if(light_toggled) if(assembly) - assembly.set_light(l_range = light_brightness, l_power = light_brightness, l_color = light_rgb) + assembly.set_light(l_range = light_brightness, l_power = 1, l_color = light_rgb) else if(assembly) assembly.set_light(0) @@ -118,7 +112,7 @@ var/brightness = get_pin_data(IC_INPUT, 2) if(new_color && isnum(brightness)) - brightness = CLAMP(brightness, 0, 6) + brightness = CLAMP(brightness, 0, 4) light_rgb = new_color light_brightness = brightness @@ -159,10 +153,8 @@ return vol = CLAMP(vol ,0 , 100) playsound(get_turf(src), selected_sound, vol, freq, -1) - if(assembly) - assembly.investigate_log("played a sound ([selected_sound]) with [type].", INVESTIGATE_CIRCUIT) - else - investigate_log("played a sound ([selected_sound]) as [type].", INVESTIGATE_CIRCUIT) + var/atom/A = get_object() + A.investigate_log("played a sound ([selected_sound]) as [type].", INVESTIGATE_CIRCUIT) /obj/item/integrated_circuit/output/sound/on_data_written() power_draw_per_use = get_pin_data(IC_INPUT, 2) * 15 @@ -186,14 +178,14 @@ name = "securitron sound circuit" desc = "Takes a sound name as an input, and will play said sound when pulsed. This circuit is similar to those used in Securitrons." sounds = list( - "creep" = 'sound/voice/bcreep.ogg', - "criminal" = 'sound/voice/bcriminal.ogg', - "freeze" = 'sound/voice/bfreeze.ogg', - "god" = 'sound/voice/bgod.ogg', - "i am the law" = 'sound/voice/biamthelaw.ogg', - "insult" = 'sound/voice/binsult.ogg', - "radio" = 'sound/voice/bradio.ogg', - "secure day" = 'sound/voice/bsecureday.ogg', + "creep" = 'sound/voice/beepsky/creep.ogg', + "criminal" = 'sound/voice/beepsky/criminal.ogg', + "freeze" = 'sound/voice/beepsky/freeze.ogg', + "god" = 'sound/voice/beepsky/god.ogg', + "i am the law" = 'sound/voice/beepsky/iamthelaw.ogg', + "insult" = 'sound/voice/beepsky/insult.ogg', + "radio" = 'sound/voice/beepsky/radio.ogg', + "secure day" = 'sound/voice/beepsky/secureday.ogg', ) spawn_flags = IC_SPAWN_RESEARCH @@ -201,21 +193,21 @@ name = "medbot sound circuit" desc = "Takes a sound name as an input, and will play said sound when pulsed. This circuit is often found in medical robots." sounds = list( - "surgeon" = 'sound/voice/msurgeon.ogg', - "radar" = 'sound/voice/mradar.ogg', - "feel better" = 'sound/voice/mfeelbetter.ogg', - "patched up" = 'sound/voice/mpatchedup.ogg', - "injured" = 'sound/voice/minjured.ogg', - "insult" = 'sound/voice/minsult.ogg', - "coming" = 'sound/voice/mcoming.ogg', - "help" = 'sound/voice/mhelp.ogg', - "live" = 'sound/voice/mlive.ogg', - "lost" = 'sound/voice/mlost.ogg', - "flies" = 'sound/voice/mflies.ogg', - "catch" = 'sound/voice/mcatch.ogg', - "delicious" = 'sound/voice/mdelicious.ogg', - "apple" = 'sound/voice/mapple.ogg', - "no" = 'sound/voice/mno.ogg', + "surgeon" = 'sound/voice/medbot/surgeon.ogg', + "radar" = 'sound/voice/medbot/radar.ogg', + "feel better" = 'sound/voice/medbot/feelbetter.ogg', + "patched up" = 'sound/voice/medbot/patchedup.ogg', + "injured" = 'sound/voice/medbot/injured.ogg', + "insult" = 'sound/voice/medbot/insult.ogg', + "coming" = 'sound/voice/medbot/coming.ogg', + "help" = 'sound/voice/medbot/help.ogg', + "live" = 'sound/voice/medbot/live.ogg', + "lost" = 'sound/voice/medbot/lost.ogg', + "flies" = 'sound/voice/medbot/flies.ogg', + "catch" = 'sound/voice/medbot/catch.ogg', + "delicious" = 'sound/voice/medbot/delicious.ogg', + "apple" = 'sound/voice/medbot/apple.ogg', + "no" = 'sound/voice/medbot/no.ogg', ) spawn_flags = IC_SPAWN_RESEARCH @@ -250,21 +242,22 @@ A.say(sanitized_text) if (assembly) log_say("[assembly] [REF(assembly)] : [sanitized_text]") - else + else log_say("[name] ([type]) : [sanitized_text]") /obj/item/integrated_circuit/output/video_camera name = "video camera circuit" - desc = "Takes a string as a name and a boolean to determine whether it is on, and uses this to be a camera linked to the research network." - extended_desc = "The camera is linked to the Research camera network." + desc = "Takes a string as a name and a boolean to determine whether it is on, and uses this to be a camera linked to a list of networks you choose." + extended_desc = "The camera is linked to a list of camera networks of your choosing. Common choices are 'rd' for the research network, 'ss13' for the main station network (visible to AI), 'mine' for the mining network, and 'thunder' for the thunderdome network (viewable from bar)." icon_state = "video_camera" - w_class = WEIGHT_CLASS_SMALL + w_class = WEIGHT_CLASS_TINY complexity = 10 inputs = list( "camera name" = IC_PINTYPE_STRING, - "camera active" = IC_PINTYPE_BOOLEAN + "camera active" = IC_PINTYPE_BOOLEAN, + "camera network" = IC_PINTYPE_LIST ) - inputs_default = list("1" = "video camera circuit") + inputs_default = list("1" = "video camera circuit", "3" = list("rd")) outputs = list() activators = list() spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH @@ -296,8 +289,11 @@ if(camera) var/cam_name = get_pin_data(IC_INPUT, 1) var/cam_active = get_pin_data(IC_INPUT, 2) + var/list/new_network = get_pin_data(IC_INPUT, 3) if(!isnull(cam_name)) camera.c_tag = cam_name + if(!isnull(new_network)) + camera.network = new_network set_camera_status(cam_active) /obj/item/integrated_circuit/output/video_camera/power_fail() diff --git a/code/modules/integrated_electronics/subtypes/reagents.dm b/code/modules/integrated_electronics/subtypes/reagents.dm index 2a6b4a1a80..b8f326f389 100644 --- a/code/modules/integrated_electronics/subtypes/reagents.dm +++ b/code/modules/integrated_electronics/subtypes/reagents.dm @@ -16,59 +16,6 @@ set_pin_data(IC_OUTPUT, 1, reagents.total_volume) push_data() -/obj/item/integrated_circuit/reagent/smoke - name = "smoke generator" - desc = "Unlike most electronics, creating smoke is completely intentional." - icon_state = "smoke" - extended_desc = "This smoke generator creates clouds of smoke on command. It can also hold liquids inside, which will go \ - into the smoke clouds when activated. The reagents are consumed when the smoke is made." - ext_cooldown = 1 - container_type = OPENCONTAINER - volume = 100 - - complexity = 20 - cooldown_per_use = 1 SECONDS - inputs = list() - outputs = list( - "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_REF - ) - activators = list( - "create smoke" = IC_PINTYPE_PULSE_IN, - "on smoked" = IC_PINTYPE_PULSE_OUT, - "push ref" = IC_PINTYPE_PULSE_IN - ) - spawn_flags = IC_SPAWN_RESEARCH - power_draw_per_use = 20 - var/smoke_radius = 5 - var/notified = FALSE - -/obj/item/integrated_circuit/reagent/smoke/on_reagent_change(changetype) - //reset warning only if we have reagents now - if(changetype == ADD_REAGENT) - notified = FALSE - push_vol() - -/obj/item/integrated_circuit/reagent/smoke/do_work(ord) - switch(ord) - if(1) - if(!reagents || (reagents.total_volume < IC_SMOKE_REAGENTS_MINIMUM_UNITS)) - return - var/location = get_turf(src) - var/datum/effect_system/smoke_spread/chem/S = new - S.attach(location) - playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3) - if(S) - S.set_up(reagents, smoke_radius, location, notified) - if(!notified) - notified = TRUE - S.start() - reagents.clear_reagents() - activate_pin(2) - if(3) - set_pin_data(IC_OUTPUT, 2, WEAKREF(src)) - push_data() - // Hydroponics trays have no reagents holder and handle reagents in their own snowflakey way. // This is a dirty hack to make injecting reagents into them work. // TODO: refactor that. @@ -118,7 +65,7 @@ ) outputs = list( "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_REF + "self reference" = IC_PINTYPE_SELFREF ) activators = list( "inject" = IC_PINTYPE_PULSE_IN, @@ -186,7 +133,7 @@ //Always log attemped injections for admins var/contained = reagents.log_list() - add_logs(src, L, "attempted to inject", addition="which had [contained]") + log_combat(src, L, "attempted to inject", addition="which had [contained]") L.visible_message("[acting_object] is trying to inject [L]!", \ "[acting_object] is trying to inject you!") busy = TRUE @@ -194,7 +141,7 @@ var/fraction = min(transfer_amount/reagents.total_volume, 1) reagents.reaction(L, INJECT, fraction) reagents.trans_to(L, transfer_amount) - add_logs(src, L, "injected", addition="which had [contained]") + log_combat(src, L, "injected", addition="which had [contained]") L.visible_message("[acting_object] injects [L] with its needle!", \ "[acting_object] injects you with its needle!") else @@ -320,7 +267,7 @@ inputs = list() outputs = list( "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_REF + "self reference" = IC_PINTYPE_SELFREF ) activators = list("push ref" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH @@ -367,7 +314,7 @@ ) outputs = list( "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_REF + "self reference" = IC_PINTYPE_SELFREF ) activators = list( "grind" = IC_PINTYPE_PULSE_IN, @@ -414,7 +361,7 @@ ) outputs = list( "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_REF + "self reference" = IC_PINTYPE_SELFREF ) activators = list( "juice" = IC_PINTYPE_PULSE_IN, @@ -459,7 +406,7 @@ complexity = 8 outputs = list( "volume used" = IC_PINTYPE_NUMBER, - "self reference" = IC_PINTYPE_REF, + "self reference" = IC_PINTYPE_SELFREF, "list of reagents" = IC_PINTYPE_LIST ) activators = list( @@ -561,7 +508,7 @@ "on" = IC_PINTYPE_BOOLEAN ) inputs_default = list("1" = 300) - outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_REF,"temperature" = IC_PINTYPE_NUMBER) + outputs = list("volume used" = IC_PINTYPE_NUMBER,"self reference" = IC_PINTYPE_SELFREF,"temperature" = IC_PINTYPE_NUMBER) spawn_flags = IC_SPAWN_RESEARCH var/heater_coefficient = 0.1 diff --git a/code/modules/integrated_electronics/subtypes/smart.dm b/code/modules/integrated_electronics/subtypes/smart.dm index 4445c1e1f3..bd93ae8314 100644 --- a/code/modules/integrated_electronics/subtypes/smart.dm +++ b/code/modules/integrated_electronics/subtypes/smart.dm @@ -95,12 +95,7 @@ if(!assembly) activate_pin(3) return - var/Ps = get_pin_data(IC_INPUT, 4) - if(!Ps) - return - var/list/Pl = json_decode(XorEncrypt(hextostr(Ps, TRUE), SScircuit.cipherkey)) - if(Pl&&islist(Pl)) - idc.access = Pl + idc.access = assembly.access_card.access var/turf/a_loc = get_turf(assembly) var/list/P = cir_get_path_to(assembly, locate(get_pin_data(IC_INPUT, 1),get_pin_data(IC_INPUT, 2),a_loc.z), /turf/proc/Distance_cardinal, 0, 200, id=idc, exclude=get_turf(get_pin_data_as_type(IC_INPUT,3, /atom)), simulated_only = 0) diff --git a/code/modules/integrated_electronics/subtypes/text.dm b/code/modules/integrated_electronics/subtypes/text.dm new file mode 100644 index 0000000000..bcc3d94766 --- /dev/null +++ b/code/modules/integrated_electronics/subtypes/text.dm @@ -0,0 +1,29 @@ +/obj/item/integrated_circuit/text + name = "text thingy" + desc = "Does text-processing related things." + category_text = "Text" + complexity = 1 + +// - Text Replacer - // +/obj/item/integrated_circuit/text/text_replacer + name = "find-replace circuit" + desc = "Replaces all of one bit of text with another" + extended_desc = "Takes a string(haystack) and puts out the string while having a certain word(needle) replaced with another." + spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH + inputs = list( + "haystack" = IC_PINTYPE_STRING, + "needle" = IC_PINTYPE_STRING, + "replacement" = IC_PINTYPE_STRING + ) + activators = list( + "replace" = IC_PINTYPE_PULSE_IN, + "on replaced" = IC_PINTYPE_PULSE_OUT + ) + outputs = list( + "replaced string" = IC_PINTYPE_STRING + ) + +/obj/item/integrated_circuit/text/text_replacer/do_work() + set_pin_data(IC_OUTPUT, 1,replacetext(get_pin_data(IC_INPUT, 1), get_pin_data(IC_INPUT, 2), get_pin_data(IC_INPUT, 3))) + push_data() + activate_pin(2) diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm index f72f5dbd74..9f3265ed40 100644 --- a/code/modules/integrated_electronics/subtypes/time.dm +++ b/code/modules/integrated_electronics/subtypes/time.dm @@ -142,25 +142,44 @@ power_draw_per_use = 2 /obj/item/integrated_circuit/time/clock - name = "integrated clock" - desc = "Tells you what the local time is, specific to your station or planet." + name = "integrated clock (NT Common Time)" + desc = "Tells you what the time is, in Nanotrasen Common Time." //round time icon_state = "clock" inputs = list() outputs = list( "time" = IC_PINTYPE_STRING, "hours" = IC_PINTYPE_NUMBER, "minutes" = IC_PINTYPE_NUMBER, - "seconds" = IC_PINTYPE_NUMBER + "seconds" = IC_PINTYPE_NUMBER, + "absolute decisecond elapsed time" = IC_PINTYPE_NUMBER ) activators = list("get time" = IC_PINTYPE_PULSE_IN, "on time got" = IC_PINTYPE_PULSE_OUT) spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH - power_draw_per_use = 4 + power_draw_per_use = 2 + +/obj/item/integrated_circuit/time/clock/proc/get_time() + return world.time /obj/item/integrated_circuit/time/clock/do_work() - var/wtime = world.time - set_pin_data(IC_OUTPUT, 1, time2text(wtime, "hh:mm:ss") ) - set_pin_data(IC_OUTPUT, 2, text2num(time2text(wtime, "hh") ) ) - set_pin_data(IC_OUTPUT, 3, text2num(time2text(wtime, "mm") ) ) - set_pin_data(IC_OUTPUT, 4, text2num(time2text(wtime, "ss") ) ) + var/current_time = get_time() + set_pin_data(IC_OUTPUT, 1, time2text(current_time, "hh:mm:ss") ) + set_pin_data(IC_OUTPUT, 2, text2num(time2text(current_time, "hh") ) ) + set_pin_data(IC_OUTPUT, 3, text2num(time2text(current_time, "mm") ) ) + set_pin_data(IC_OUTPUT, 4, text2num(time2text(current_time, "ss") ) ) + set_pin_data(IC_OUTPUT, 5, current_time) push_data() activate_pin(2) + +/obj/item/integrated_circuit/time/clock/station + name = "integrated clock (Station Time)" + desc = "Tells you what the time is, in terms and adjusted for your local station or planet" + +/obj/item/integrated_circuit/time/clock/station/get_time() + return station_time() + +/obj/item/integrated_circuit/time/clock/bluespace + name = "integrated clock (Bluespace Absolute Time)" + desc = "Tells you what the time is, in Bluespace Absolute Time, unaffected by local time dilation or other phenomenon." + +/obj/item/integrated_circuit/time/clock/bluespace/get_time() + return REALTIMEOFDAY diff --git a/code/modules/jobs/access.dm b/code/modules/jobs/access.dm index 63e6962a74..e88a80514a 100644 --- a/code/modules/jobs/access.dm +++ b/code/modules/jobs/access.dm @@ -11,7 +11,7 @@ if(IsAdminGhost(M)) //Access can't stop the abuse return TRUE - else if(SEND_SIGNAL(M, COMSIG_MOB_ALLOWED, src)) + else if(istype(M) && SEND_SIGNAL(M, COMSIG_MOB_ALLOWED, src)) return TRUE else if(ishuman(M)) var/mob/living/carbon/human/H = M diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm index 6a5a857ef0..4b7b175240 100644 --- a/code/modules/jobs/job_exp.dm +++ b/code/modules/jobs/job_exp.dm @@ -155,7 +155,7 @@ GLOBAL_PROTECT(exp_to_update) if(!SSdbcore.Connect()) return -1 var/datum/DBQuery/exp_read = SSdbcore.NewQuery("SELECT job, minutes FROM [format_table_name("role_time")] WHERE ckey = '[sanitizeSQL(ckey)]'") - if(!exp_read.Execute()) + if(!exp_read.Execute(async = TRUE)) qdel(exp_read) return -1 var/list/play_records = list() @@ -172,7 +172,6 @@ GLOBAL_PROTECT(exp_to_update) prefs.exp = play_records - //updates player db flags /client/proc/update_flag_db(newflag, state = FALSE) @@ -269,7 +268,7 @@ GLOBAL_PROTECT(exp_to_update) var/datum/DBQuery/flags_read = SSdbcore.NewQuery("SELECT flags FROM [format_table_name("player")] WHERE ckey='[ckey]'") - if(!flags_read.Execute()) + if(!flags_read.Execute(async = TRUE)) qdel(flags_read) return FALSE diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm index 988eca631c..94cf27749e 100755 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -52,6 +52,13 @@ Captain chameleon_extras = list(/obj/item/gun/energy/e_gun, /obj/item/stamp/captain) +/datum/outfit/job/captain/hardsuit + name = "Captain (Hardsuit)" + + mask = /obj/item/clothing/mask/gas/sechailer + suit = /obj/item/clothing/suit/space/hardsuit/captain + suit_store = /obj/item/tank/internals/oxygen + /* Head of Personnel */ diff --git a/code/modules/jobs/job_types/civilian.dm b/code/modules/jobs/job_types/civilian.dm index 8ec25e5f5e..944499470a 100644 --- a/code/modules/jobs/job_types/civilian.dm +++ b/code/modules/jobs/job_types/civilian.dm @@ -154,7 +154,6 @@ Curator return H.grant_all_languages(omnitongue=TRUE) - H.gain_trauma(/datum/brain_trauma/mild/phobia, TRAUMA_RESILIENCE_SURGERY, "snakes") //why does it have to be snakes... /* Lawyer */ diff --git a/code/modules/jobs/job_types/engineering.dm b/code/modules/jobs/job_types/engineering.dm index 171a437bb1..f28e5f1afc 100644 --- a/code/modules/jobs/job_types/engineering.dm +++ b/code/modules/jobs/job_types/engineering.dm @@ -57,6 +57,7 @@ Chief Engineer suit = /obj/item/clothing/suit/space/hardsuit/engine/elite shoes = /obj/item/clothing/shoes/magboots/advance suit_store = /obj/item/tank/internals/oxygen + glasses = /obj/item/clothing/glasses/meson/engine gloves = /obj/item/clothing/gloves/color/yellow head = null internals_slot = SLOT_S_STORE diff --git a/code/modules/jobs/job_types/job.dm b/code/modules/jobs/job_types/job.dm index 92f4ccbdd9..ea5573b89f 100644 --- a/code/modules/jobs/job_types/job.dm +++ b/code/modules/jobs/job_types/job.dm @@ -38,9 +38,6 @@ //If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. var/req_admin_notify - //Allows defining arbitrary spawn text for the job - var/custom_spawn_text - //If you have the use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) var/minimal_player_age = 0 @@ -72,12 +69,12 @@ return TRUE /datum/job/proc/GetAntagRep() - . = CONFIG_GET(keyed_number_list/antag_rep)[lowertext(title)] + . = CONFIG_GET(keyed_list/antag_rep)[lowertext(title)] if(. == null) return antag_rep //Don't override this unless the job transforms into a non-human (Silicons do this for example) -/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE) +/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, datum/outfit/outfit_override = null) if(!H) return FALSE @@ -85,13 +82,12 @@ if(H.dna.species.id != "human") H.set_species(/datum/species/human) H.apply_pref_name("human", H.client) - purrbation_remove(H, silent=TRUE) //Equip the rest of the gear H.dna.species.before_equip_job(src, H, visualsOnly) - if(outfit) - H.equipOutfit(outfit, visualsOnly) + if(outfit_override || outfit) + H.equipOutfit(outfit_override ? outfit_override : outfit, visualsOnly) H.dna.species.after_equip_job(src, H, visualsOnly) diff --git a/code/modules/jobs/job_types/medical.dm b/code/modules/jobs/job_types/medical.dm index 2ab5da36aa..5a926f490a 100644 --- a/code/modules/jobs/job_types/medical.dm +++ b/code/modules/jobs/job_types/medical.dm @@ -48,6 +48,14 @@ Chief Medical Officer chameleon_extras = list(/obj/item/gun/syringe, /obj/item/stamp/cmo) +/datum/outfit/job/cmo/hardsuit + name = "Chief Medical Officer (Hardsuit)" + + mask = /obj/item/clothing/mask/breath + suit = /obj/item/clothing/suit/space/hardsuit/medical + suit_store = /obj/item/tank/internals/oxygen + r_pocket = /obj/item/flashlight/pen + /* Medical Doctor */ diff --git a/code/modules/jobs/job_types/security.dm b/code/modules/jobs/job_types/security.dm index 252b51301c..c69a5873b1 100644 --- a/code/modules/jobs/job_types/security.dm +++ b/code/modules/jobs/job_types/security.dm @@ -62,6 +62,14 @@ Head of Security chameleon_extras = list(/obj/item/gun/energy/e_gun/hos, /obj/item/stamp/hos) +/datum/outfit/job/hos/hardsuit + name = "Head of Security (Hardsuit)" + + mask = /obj/item/clothing/mask/gas/sechailer + suit = /obj/item/clothing/suit/space/hardsuit/security/hos + suit_store = /obj/item/tank/internals/oxygen + backpack_contents = list(/obj/item/melee/baton/loaded=1, /obj/item/gun/energy/e_gun=1) + /* Warden */ @@ -162,7 +170,8 @@ Detective /datum/outfit/job/detective/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) ..() var/obj/item/clothing/mask/cigarette/cig = H.wear_mask - cig.light("") + if(istype(cig)) //Some species specfic changes can mess this up (plasmamen) + cig.light("") if(visualsOnly) return diff --git a/code/modules/jobs/job_types/silicon.dm b/code/modules/jobs/job_types/silicon.dm index 0738f20b24..662f666cf1 100644 --- a/code/modules/jobs/job_types/silicon.dm +++ b/code/modules/jobs/job_types/silicon.dm @@ -17,7 +17,7 @@ AI exp_type_department = EXP_TYPE_SILICON var/do_special_check = TRUE -/datum/job/ai/equip(mob/living/carbon/human/H, visualsOnly, announce, latejoin) +/datum/job/ai/equip(mob/living/carbon/human/H, visualsOnly, announce, latejoin, outfit_override) . = H.AIize(latejoin) /datum/job/ai/after_spawn(mob/H, mob/M, latejoin) @@ -81,8 +81,9 @@ Cyborg exp_requirements = 120 exp_type = EXP_TYPE_CREW -/datum/job/cyborg/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE) +/datum/job/cyborg/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE, latejoin = FALSE, outfit_override = null) return H.Robotize(FALSE, latejoin) /datum/job/cyborg/after_spawn(mob/living/silicon/robot/R, mob/M) R.updatename(M.client) + R.gender = NEUTER diff --git a/code/modules/keybindings/readme.md b/code/modules/keybindings/readme.md index 6c0369d7c7..f57d8d55ff 100644 --- a/code/modules/keybindings/readme.md +++ b/code/modules/keybindings/readme.md @@ -1,7 +1,8 @@ # In-code keypress handling system This whole system is heavily based off of forum_account's keyboard library. -Thanks to forum_account for saving the day, the library can be found [here](https://secure.byond.com/developer/Forum_account/Keyboard)! +Thanks to forum_account for saving the day, the library can be found +[here](https://secure.byond.com/developer/Forum_account/Keyboard)! .dmf macros have some very serious shortcomings. For example, they do not allow reusing parts of one macro in another, so giving cyborgs their own shortcuts to swap active module couldn't @@ -30,10 +31,11 @@ pressed. No client-set keybindings at this time, but it shouldn't be too hard if someone wants. -Notes about certain keys -`Tab` has client-sided behavior but acts normally -`T`, `O`, and `M` move focus to the input when pressed. This fires the keyUp macro right away. -`\` needs to be escaped in the dmf so any usage is `\\` +Notes about certain keys: + +* `Tab` has client-sided behavior but acts normally +* `T`, `O`, and `M` move focus to the input when pressed. This fires the keyUp macro right away. +* `\` needs to be escaped in the dmf so any usage is `\\` You cannot `TICK_CHECK` or check `world.tick_usage` inside of procs called by key down and up events. They happen outside of a byond tick and have no meaning there. Key looping diff --git a/code/modules/language/narsian.dm b/code/modules/language/narsian.dm index 70966ad880..42d0a06531 100644 --- a/code/modules/language/narsian.dm +++ b/code/modules/language/narsian.dm @@ -1,6 +1,6 @@ /datum/language/narsie - name = "Nar-Sian" - desc = "The ancient, blood-soaked, impossibly complex language of Nar-Sian cultists." + name = "Nar'Sian" + desc = "The ancient, blood-soaked, impossibly complex language of Nar'Sian cultists." speech_verb = "intones" ask_verb = "inquires" exclaim_verb = "invokes" diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 066ce22831..ee563f3100 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -149,7 +149,7 @@ /obj/structure/bookcase/manuals/medical/Initialize() . = ..() - new /obj/item/book/manual/medical_cloning(src) + new /obj/item/book/manual/wiki/medical_cloning(src) update_icon() @@ -159,11 +159,10 @@ /obj/structure/bookcase/manuals/engineering/Initialize() . = ..() new /obj/item/book/manual/wiki/engineering_construction(src) - new /obj/item/book/manual/engineering_particle_accelerator(src) new /obj/item/book/manual/wiki/engineering_hacking(src) new /obj/item/book/manual/wiki/engineering_guide(src) - new /obj/item/book/manual/engineering_singularity_safety(src) - new /obj/item/book/manual/robotics_cyborgs(src) + new /obj/item/book/manual/wiki/engineering_singulo_tesla(src) + new /obj/item/book/manual/wiki/robotics_cyborgs(src) update_icon() @@ -172,7 +171,7 @@ /obj/structure/bookcase/manuals/research_and_development/Initialize() . = ..() - new /obj/item/book/manual/research_and_development(src) + new /obj/item/book/manual/wiki/research_and_development(src) update_icon() diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 5f686699d2..8ae63a8a76 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -166,11 +166,12 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums /obj/machinery/computer/libraryconsole/bookmanagement name = "book inventory management console" desc = "Librarian's command station." - var/arcanecheckout = 0 screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book verb_say = "beeps" verb_ask = "beeps" verb_exclaim = "beeps" + pass_flags = PASSTABLE + var/arcanecheckout = 0 var/buffer_book var/buffer_mob var/upload_category = "Fiction" @@ -424,7 +425,7 @@ GLOBAL_LIST(cachedbooks) // List of our cached book datums var/sqlcategory = sanitizeSQL(upload_category) var/sqlckey = sanitizeSQL(usr.ckey) var/msg = "[key_name(usr)] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs" - var/datum/DBQuery/query_library_upload = SSdbcore.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, datetime, round_id_created) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory], '[sqlckey]', Now(), '[GLOB.round_id]')") + var/datum/DBQuery/query_library_upload = SSdbcore.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, datetime, round_id_created) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[sqlckey]', Now(), '[GLOB.round_id]')") if(!query_library_upload.Execute()) qdel(query_library_upload) alert("Database error encountered uploading to Archive") diff --git a/code/modules/library/soapstone.dm b/code/modules/library/soapstone.dm index e51103937f..88d1248a28 100644 --- a/code/modules/library/soapstone.dm +++ b/code/modules/library/soapstone.dm @@ -19,6 +19,7 @@ to_chat(user, "It has [remaining_uses] uses left.") /obj/item/soapstone/afterattack(atom/target, mob/user, proximity) + . = ..() var/turf/T = get_turf(target) if(!proximity) return diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm index 89ec6a3a7e..4a617ab073 100644 --- a/code/modules/lighting/lighting_atom.dm +++ b/code/modules/lighting/lighting_atom.dm @@ -92,14 +92,17 @@ switch (var_name) if ("light_range") set_light(l_range=var_value) - return + datum_flags |= DF_VAR_EDITED + return TRUE if ("light_power") set_light(l_power=var_value) - return + datum_flags |= DF_VAR_EDITED + return TRUE if ("light_color") set_light(l_color=var_value) - return + datum_flags |= DF_VAR_EDITED + return TRUE return ..() diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index 9b562a276d..1de896520d 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -138,6 +138,6 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, if (!force) return QDEL_HINT_LETMELIVE - stack_trace("Ok, Look, TG, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is 100% retarded and needs his head checked. Thanks. Send them my regards by the way.") + stack_trace("Ok, Look, /tg/, I need you to find whatever fucker decided to call qdel on a fucking lighting corner, then tell him very nicely and politely that he is 100% retarded and needs his head checked. Thanks. Send them my regards by the way.") return ..() diff --git a/code/modules/mapping/README.txt b/code/modules/mapping/README.txt new file mode 100644 index 0000000000..75e57efa8b --- /dev/null +++ b/code/modules/mapping/README.txt @@ -0,0 +1,52 @@ +The code in this module originally evolved from dmm_suite and has since been +specialized for SS13 and otherwise tweaked to fit /tg/station's needs. + +dmm_suite version 1.0 + Released January 30th, 2011. + +NOTE: Map saving functionality removed + +defines the object /dmm_suite + - Provides the proc load_map() + - Loads the specified map file onto the specified z-level. + - provides the proc write_map() + - Returns a text string of the map in dmm format + ready for output to a file. + - provides the proc save_map() + - Returns a .dmm file if map is saved + - Returns FALSE if map fails to save + +The dmm_suite provides saving and loading of map files in BYOND's native DMM map +format. It approximates the map saving and loading processes of the Dream Maker +and Dream Seeker programs so as to allow editing, saving, and loading of maps at +runtime. + +------------------------ + +To save a map at runtime, create an instance of /dmm_suite, and then call +write_map(), which accepts three arguments: + - A turf representing one corner of a three dimensional grid (Required). + - Another turf representing the other corner of the same grid (Required). + - Any, or a combination, of several bit flags (Optional, see documentation). + +The order in which the turfs are supplied does not matter, the /dmm_writer will +determine the grid containing both, in much the same way as DM's block() function. +write_map() will then return a string representing the saved map in dmm format; +this string can then be saved to a file, or used for any other purose. + +------------------------ + +To load a map at runtime, create an instance of /dmm_suite, and then call load_map(), +which accepts two arguments: + - A .dmm file to load (Required). + - A number representing the z-level on which to start loading the map (Optional). + +The /dmm_suite will load the map file starting on the specified z-level. If no +z-level was specified, world.maxz will be increased so as to fit the map. Note +that if you wish to load a map onto a z-level that already has objects on it, +you will have to handle the removal of those objects. Otherwise the new map will +simply load the new objects on top of the old ones. + +Also note that all type paths specified in the .dmm file must exist in the world's +code, and that the /dmm_reader trusts that files to be loaded are in fact valid +.dmm files. Errors in the .dmm format will cause runtime errors. diff --git a/code/modules/mapping/map_template.dm b/code/modules/mapping/map_template.dm index 2cc5a74b6b..ceba29baec 100644 --- a/code/modules/mapping/map_template.dm +++ b/code/modules/mapping/map_template.dm @@ -4,24 +4,28 @@ var/height = 0 var/mappath = null var/loaded = 0 // Times loaded this round - var/static/dmm_suite/maploader = new + var/datum/parsed_map/cached_map + var/keep_cached_map = FALSE -/datum/map_template/New(path = null, rename = null) +/datum/map_template/New(path = null, rename = null, cache = FALSE) if(path) mappath = path if(mappath) - preload_size(mappath) + preload_size(mappath, cache) if(rename) name = rename -/datum/map_template/proc/preload_size(path) - var/bounds = maploader.load_map(file(path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE) +/datum/map_template/proc/preload_size(path, cache = FALSE) + var/datum/parsed_map/parsed = new(file(path)) + var/bounds = parsed?.bounds if(bounds) width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1 height = bounds[MAP_MAXY] + if(cache) + cached_map = parsed return bounds -/datum/map_template/proc/initTemplateBounds(var/list/bounds) +/datum/parsed_map/proc/initTemplateBounds() var/list/obj/machinery/atmospherics/atmos_machines = list() var/list/obj/structure/cable/cables = list() var/list/atom/atoms = list() @@ -53,14 +57,15 @@ var/y = round((world.maxy - height)/2) var/datum/space_level/level = SSmapping.add_new_zlevel(name, list(ZTRAIT_AWAY = TRUE)) - var/list/bounds = maploader.load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE) + var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE) + var/list/bounds = parsed.bounds if(!bounds) return FALSE repopulate_sorted_areas() //initialize things that are normally initialized after map load - initTemplateBounds(bounds) + parsed.initTemplateBounds() smooth_zlevel(world.maxz) log_game("Z-level [name] loaded at at [x],[y],[world.maxz]") @@ -76,7 +81,13 @@ if(T.y+height > world.maxy) return - var/list/bounds = maploader.load_map(file(mappath), T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE) + // Accept cached maps, but don't save them automatically - we don't want + // ruins clogging up memory for the whole round. + var/datum/parsed_map/parsed = cached_map || new(file(mappath)) + cached_map = keep_cached_map ? parsed : null + if(!parsed.load(T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=TRUE)) + return + var/list/bounds = parsed.bounds if(!bounds) return @@ -84,7 +95,7 @@ repopulate_sorted_areas() //initialize things that are normally initialized after map load - initTemplateBounds(bounds) + parsed.initTemplateBounds() log_game("[name] loaded at at [T.x],[T.y],[T.z]") return bounds diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index 8a7f5cb91e..0ddbe8e1e4 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -133,6 +133,21 @@ else log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]") +/obj/effect/mapping_helpers/airlock/unres + name = "airlock unresctricted side helper" + icon_state = "airlock_unres_helper" + +/obj/effect/mapping_helpers/airlock/unres/Initialize(mapload) + . = ..() + if(!mapload) + log_world("### MAP WARNING, [src] spawned outside of mapload!") + return + var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc + if(airlock) + airlock.unres_sides ^= dir + else + log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]") + //needs to do its thing before spawn_rivers() is called INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava) @@ -145,17 +160,15 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava) var/turf/T = get_turf(src) T.flags_1 |= NO_LAVA_GEN_1 -//Contains the list of planetary z-levels defined by the planet_z helper. -GLOBAL_LIST_EMPTY(z_is_planet) - -/obj/effect/mapping_helpers/planet_z //adds the map it is on to the z_is_planet list +/// Adds the map it is on to the z_is_planet list +/obj/effect/mapping_helpers/planet_z name = "planet z helper" layer = POINT_LAYER /obj/effect/mapping_helpers/planet_z/Initialize() . = ..() - var/turf/T = get_turf(src) - GLOB.z_is_planet["[T.z]"] = TRUE + var/datum/space_level/S = SSmapping.get_level(z) + S.traits[ZTRAIT_PLANET] = TRUE //This helper applies components to things on the map directly. diff --git a/code/modules/mapping/preloader.dm b/code/modules/mapping/preloader.dm new file mode 100644 index 0000000000..e6fa2421a0 --- /dev/null +++ b/code/modules/mapping/preloader.dm @@ -0,0 +1,31 @@ +// global datum that will preload variables on atoms instanciation +GLOBAL_VAR_INIT(use_preloader, FALSE) +GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new) + +/// Preloader datum +/datum/map_preloader + parent_type = /datum + var/list/attributes + var/target_path + +/datum/map_preloader/proc/setup(list/the_attributes, path) + if(the_attributes.len) + GLOB.use_preloader = TRUE + attributes = the_attributes + target_path = path + +/datum/map_preloader/proc/load(atom/what) + GLOB.use_preloader = FALSE + for(var/attribute in attributes) + var/value = attributes[attribute] + if(islist(value)) + value = deepCopyList(value) + what.vars[attribute] = value + +/area/template_noop + name = "Area Passthrough" + +/turf/template_noop + name = "Turf Passthrough" + icon_state = "noop" + bullet_bounce_sound = null diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm index dfc75c32ee..22be4aa246 100644 --- a/code/modules/mapping/reader.dm +++ b/code/modules/mapping/reader.dm @@ -1,66 +1,65 @@ /////////////////////////////////////////////////////////////// //SS13 Optimized Map loader ////////////////////////////////////////////////////////////// +#define SPACE_KEY "space" -//global datum that will preload variables on atoms instanciation -GLOBAL_VAR_INIT(use_preloader, FALSE) -GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new) +/datum/grid_set + var/xcrd + var/ycrd + var/zcrd + var/gridLines -/dmm_suite - // /"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}/g - var/static/regex/dmmRegex = new/regex({""(\[a-zA-Z]+)" = \\(((?:.|\n)*?)\\)\n(?!\t)|\\((\\d+),(\\d+),(\\d+)\\) = \\{"(\[a-zA-Z\n]*)"\\}"}, "g") - // /^[\s\n]+"?|"?[\s\n]+$|^"|"$/g - var/static/regex/trimQuotesRegex = new/regex({"^\[\\s\n]+"?|"?\[\\s\n]+$|^"|"$"}, "g") - // /^[\s\n]+|[\s\n]+$/ - var/static/regex/trimRegex = new/regex("^\[\\s\n]+|\[\\s\n]+$", "g") - var/static/list/modelCache = list() - var/static/space_key - #ifdef TESTING - var/static/turfsSkipped - #endif - -/** - * Construct the model map and control the loading process - * - * WORKING : - * - * 1) Makes an associative mapping of model_keys with model - * e.g aa = /turf/unsimulated/wall{icon_state = "rock"} - * 2) Read the map line by line, parsing the result (using parse_grid) - * - */ -/dmm_suite/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, no_changeturf as num, lower_crop_x as num, lower_crop_y as num, upper_crop_x as num, upper_crop_y as num, placeOnTop as num) - //How I wish for RAII - Master.StartLoadingMap() - space_key = null - #ifdef TESTING - turfsSkipped = 0 - #endif - . = load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, lower_crop_x, upper_crop_x, lower_crop_y, upper_crop_y, placeOnTop) - #ifdef TESTING - if(turfsSkipped) - testing("Skipped loading [turfsSkipped] default turfs") - #endif - Master.StopLoadingMap() - -/dmm_suite/proc/load_map_impl(dmm_file, x_offset, y_offset, z_offset, cropMap, measureOnly, no_changeturf, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, placeOnTop = FALSE) - var/tfile = dmm_file//the map file we're creating - if(isfile(tfile)) - tfile = file2text(tfile) - - if(!x_offset) - x_offset = 1 - if(!y_offset) - y_offset = 1 - if(!z_offset) - z_offset = world.maxz + 1 - - var/list/bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF) - var/list/grid_models = list() +/datum/parsed_map + var/original_path var/key_len = 0 + var/list/grid_models = list() + var/list/gridSets = list() + var/list/modelCache + + /// Unoffset bounds. Null on parse failure. + var/list/parsed_bounds + /// Offset bounds. Same as parsed_bounds until load(). + var/list/bounds + + // raw strings used to represent regexes more accurately + // '' used to avoid confusing syntax highlighting + var/static/regex/dmmRegex = new(@'"([a-zA-Z]+)" = \(((?:.|\n)*?)\)\n(?!\t)|\((\d+),(\d+),(\d+)\) = \{"([a-zA-Z\n]*)"\}', "g") + var/static/regex/trimQuotesRegex = new(@'^[\s\n]+"?|"?[\s\n]+$|^"|"$', "g") + var/static/regex/trimRegex = new(@'^[\s\n]+|[\s\n]+$', "g") + + #ifdef TESTING + var/turfsSkipped = 0 + #endif + +/// Shortcut function to parse a map and apply it to the world. +/// +/// - `dmm_file`: A .dmm file to load (Required). +/// - `x_offset`, `y_offset`, `z_offset`: Positions representign where to load the map (Optional). +/// - `cropMap`: When true, the map will be cropped to fit the existing world dimensions (Optional). +/// - `measureOnly`: When true, no changes will be made to the world (Optional). +/// - `no_changeturf`: When true, [turf/AfterChange] won't be called on loaded turfs +/// - `x_lower`, `x_upper`, `y_lower`, `y_upper`: Coordinates (relative to the map) to crop to (Optional). +/// - `placeOnTop`: Whether to use [turf/PlaceOnTop] rather than [turf/ChangeTurf] (Optional). +/proc/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, no_changeturf as num, x_lower = -INFINITY as num, x_upper = INFINITY as num, y_lower = -INFINITY as num, y_upper = INFINITY as num, placeOnTop = FALSE as num) + var/datum/parsed_map/parsed = new(dmm_file, x_lower, x_upper, y_lower, y_upper, measureOnly) + if(parsed.bounds && !measureOnly) + parsed.load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop) + return parsed + +/// Parse a map, possibly cropping it. +/datum/parsed_map/New(tfile, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper=INFINITY, measureOnly=FALSE) + if(isfile(tfile)) + original_path = "[tfile]" + tfile = file2text(tfile) + else if(isnull(tfile)) + // create a new datum without loading a map + return + + bounds = parsed_bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF) var/stored_index = 1 + //multiz lool while(dmmRegex.Find(tfile, stored_index)) stored_index = dmmRegex.next @@ -73,41 +72,33 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new) if(!key_len) key_len = length(key) else - throw EXCEPTION("Inconsistent key length in DMM") + CRASH("Inconsistent key length in DMM") if(!measureOnly) grid_models[key] = dmmRegex.group[2] // (1,1,1) = {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"} else if(dmmRegex.group[3]) // Coords if(!key_len) - throw EXCEPTION("Coords before model definition in DMM") + CRASH("Coords before model definition in DMM") var/curr_x = text2num(dmmRegex.group[3]) if(curr_x < x_lower || curr_x > x_upper) continue - var/xcrdStart = curr_x + x_offset - 1 + var/datum/grid_set/gridSet = new + + gridSet.xcrd = curr_x //position of the currently processed square - var/xcrd - var/ycrd = text2num(dmmRegex.group[4]) + y_offset - 1 - var/zcrd = text2num(dmmRegex.group[5]) + z_offset - 1 + gridSet.ycrd = text2num(dmmRegex.group[4]) + gridSet.zcrd = text2num(dmmRegex.group[5]) - var/zexpansion = zcrd > world.maxz - if(zexpansion && !measureOnly) - if(cropMap) - continue - else - while (zcrd > world.maxz) //create a new z_level if needed - world.incrementMaxZ() - if(!no_changeturf) - WARNING("Z-level expansion occurred without no_changeturf set, this may cause problems when /turf/AfterChange is called") - - bounds[MAP_MINX] = min(bounds[MAP_MINX], CLAMP(xcrdStart, x_lower, x_upper)) - bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd) - bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd) + bounds[MAP_MINX] = min(bounds[MAP_MINX], CLAMP(gridSet.xcrd, x_lower, x_upper)) + bounds[MAP_MINZ] = min(bounds[MAP_MINZ], gridSet.zcrd) + bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], gridSet.zcrd) var/list/gridLines = splittext(dmmRegex.group[6], "\n") + gridSet.gridLines = gridLines var/leadingBlanks = 0 while(leadingBlanks < gridLines.len && gridLines[++leadingBlanks] == "") @@ -117,125 +108,144 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new) if(!gridLines.len) // Skip it if only blank lines exist. continue + gridSets += gridSet + if(gridLines.len && gridLines[gridLines.len] == "") gridLines.Cut(gridLines.len) // Remove only one blank line at the end. - bounds[MAP_MINY] = min(bounds[MAP_MINY], CLAMP(ycrd, y_lower, y_upper)) - ycrd += gridLines.len - 1 // Start at the top and work down + bounds[MAP_MINY] = min(bounds[MAP_MINY], CLAMP(gridSet.ycrd, y_lower, y_upper)) + gridSet.ycrd += gridLines.len - 1 // Start at the top and work down + bounds[MAP_MAXY] = max(bounds[MAP_MAXY], CLAMP(gridSet.ycrd, y_lower, y_upper)) - if(!cropMap && ycrd > world.maxy) - if(!measureOnly) - world.maxy = ycrd // Expand Y here. X is expanded in the loop below - bounds[MAP_MAXY] = max(bounds[MAP_MAXY], CLAMP(ycrd, y_lower, y_upper)) + var/maxx = gridSet.xcrd + if(gridLines.len) //Not an empty map + maxx = max(maxx, gridSet.xcrd + length(gridLines[1]) / key_len - 1) + + bounds[MAP_MAXX] = CLAMP(max(bounds[MAP_MAXX], maxx), x_lower, x_upper) + CHECK_TICK + + // Indicate failure to parse any coordinates by nulling bounds + if(bounds[1] == 1.#INF) + bounds = null + parsed_bounds = bounds + +/// Load the parsed map into the world. See [/proc/load_map] for arguments. +/datum/parsed_map/proc/load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop) + //How I wish for RAII + Master.StartLoadingMap() + . = _load_impl(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop) + Master.StopLoadingMap() + +// Do not call except via load() above. +/datum/parsed_map/proc/_load_impl(x_offset = 1, y_offset = 1, z_offset = world.maxz + 1, cropMap = FALSE, no_changeturf = FALSE, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, placeOnTop = FALSE) + var/list/areaCache = list() + var/list/modelCache = build_cache(no_changeturf) + var/space_key = modelCache[SPACE_KEY] + var/list/bounds + src.bounds = bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF) + + for(var/I in gridSets) + var/datum/grid_set/gset = I + var/ycrd = gset.ycrd + y_offset - 1 + var/zcrd = gset.zcrd + z_offset - 1 + if(!cropMap && ycrd > world.maxy) + world.maxy = ycrd // Expand Y here. X is expanded in the loop below + var/zexpansion = zcrd > world.maxz + if(zexpansion) + if(cropMap) + continue else - bounds[MAP_MAXY] = max(bounds[MAP_MAXY], CLAMP(min(ycrd, world.maxy), y_lower, y_upper)) + while (zcrd > world.maxz) //create a new z_level if needed + world.incrementMaxZ() + if(!no_changeturf) + WARNING("Z-level expansion occurred without no_changeturf set, this may cause problems when /turf/AfterChange is called") - var/maxx = xcrdStart - if(measureOnly) - for(var/line in gridLines) - maxx = max(maxx, xcrdStart + length(line) / key_len - 1) - else - for(var/line in gridLines) - if((ycrd - y_offset + 1) < y_lower || (ycrd - y_offset + 1) > y_upper) //Reverse operation and check if it is out of bounds of cropping. - --ycrd - continue - if(ycrd <= world.maxy && ycrd >= 1) - xcrd = xcrdStart - for(var/tpos = 1 to length(line) - key_len + 1 step key_len) - if((xcrd - x_offset + 1) < x_lower || (xcrd - x_offset + 1) > x_upper) //Same as above. - ++xcrd - continue //X cropping. - if(xcrd > world.maxx) - if(cropMap) - break - else - world.maxx = xcrd + for(var/line in gset.gridLines) + if((ycrd - y_offset + 1) < y_lower || (ycrd - y_offset + 1) > y_upper) //Reverse operation and check if it is out of bounds of cropping. + --ycrd + continue + if(ycrd <= world.maxy && ycrd >= 1) + var/xcrd = gset.xcrd + x_offset - 1 + for(var/tpos = 1 to length(line) - key_len + 1 step key_len) + if((xcrd - x_offset + 1) < x_lower || (xcrd - x_offset + 1) > x_upper) //Same as above. + ++xcrd + continue //X cropping. + if(xcrd > world.maxx) + if(cropMap) + break + else + world.maxx = xcrd - if(xcrd >= 1) - var/model_key = copytext(line, tpos, tpos + key_len) - var/no_afterchange = no_changeturf || zexpansion - if(!no_afterchange || (model_key != space_key)) - if(!grid_models[model_key]) - throw EXCEPTION("Undefined model key in DMM.") - parse_grid(grid_models[model_key], model_key, xcrd, ycrd, zcrd, no_changeturf || zexpansion, placeOnTop) - #ifdef TESTING - else - ++turfsSkipped - #endif - CHECK_TICK - maxx = max(maxx, xcrd) - ++xcrd - --ycrd + if(xcrd >= 1) + var/model_key = copytext(line, tpos, tpos + key_len) + var/no_afterchange = no_changeturf || zexpansion + if(!no_afterchange || (model_key != space_key)) + var/list/cache = modelCache[model_key] + if(!cache) + CRASH("Undefined model key in DMM: [model_key]") + build_coordinate(areaCache, cache, locate(xcrd, ycrd, zcrd), no_afterchange, placeOnTop) - bounds[MAP_MAXX] = CLAMP(max(bounds[MAP_MAXX], cropMap ? min(maxx, world.maxx) : maxx), x_lower, x_upper) + // only bother with bounds that actually exist + bounds[MAP_MINX] = min(bounds[MAP_MINX], xcrd) + bounds[MAP_MINY] = min(bounds[MAP_MINY], ycrd) + bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd) + bounds[MAP_MAXX] = max(bounds[MAP_MAXX], xcrd) + bounds[MAP_MAXY] = max(bounds[MAP_MAXY], ycrd) + bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], zcrd) + #ifdef TESTING + else + ++turfsSkipped + #endif + CHECK_TICK + ++xcrd + --ycrd CHECK_TICK - if(bounds[1] == 1.#INF) // Shouldn't need to check every item - return null - else - if(!measureOnly) - if(!no_changeturf) - for(var/t in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ]))) - var/turf/T = t - //we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs - T.AfterChange(CHANGETURF_IGNORE_AIR) - return bounds + if(!no_changeturf) + for(var/t in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]), locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ]))) + var/turf/T = t + //we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs + T.AfterChange(CHANGETURF_IGNORE_AIR) -/** - * Fill a given tile with its area/turf/objects/mobs - * Variable model is one full map line (e.g /turf/unsimulated/wall{icon_state = "rock"}, /area/mine/explored) - * - * WORKING : - * - * 1) Read the model string, member by member (delimiter is ',') - * - * 2) Get the path of the atom and store it into a list - * - * 3) a) Check if the member has variables (text within '{' and '}') - * - * 3) b) Construct an associative list with found variables, if any (the atom index in members is the same as its variables in members_attributes) - * - * 4) Instanciates the atom with its variables - * - */ -/dmm_suite/proc/parse_grid(model as text, model_key as text, xcrd as num,ycrd as num,zcrd as num, no_changeturf as num, placeOnTop as num) - /*Method parse_grid() - - Accepts a text string containing a comma separated list of type paths of the - same construction as those contained in a .dmm file, and instantiates them. - */ + #ifdef TESTING + if(turfsSkipped) + testing("Skipped loading [turfsSkipped] default turfs") + #endif - var/list/members //will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/explored) - var/list/members_attributes //will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list()) - var/list/cached = modelCache[model] - var/index + return TRUE - if(cached) - members = cached[1] - members_attributes = cached[2] - else +/datum/parsed_map/proc/build_cache(no_changeturf, bad_paths=null) + if(modelCache && !bad_paths) + return modelCache + . = modelCache = list() + var/list/grid_models = src.grid_models + for(var/model_key in grid_models) + var/model = grid_models[model_key] + var/list/members = list() //will contain all members (paths) in model (in our example : /turf/unsimulated/wall and /area/mine/explored) + var/list/members_attributes = list() //will contain lists filled with corresponding variables, if any (in our example : list(icon_state = "rock") and list()) ///////////////////////////////////////////////////////// //Constructing members and corresponding variables lists //////////////////////////////////////////////////////// - members = list() - members_attributes = list() - index = 1 - + var/index = 1 var/old_position = 1 var/dpos - do + while(dpos != 0) //finding next member (e.g /turf/unsimulated/wall{icon_state = "rock"} or /area/mine/explored) dpos = find_next_delimiter_position(model, old_position, ",", "{", "}") //find next delimiter (comma here) that's not within {...} var/full_def = trim_text(copytext(model, old_position, dpos)) //full definition, e.g : /obj/foo/bar{variables=derp} var/variables_start = findtext(full_def, "{") - var/atom_def = text2path(trim_text(copytext(full_def, 1, variables_start))) //path definition, e.g /obj/foo/bar + var/path_text = trim_text(copytext(full_def, 1, variables_start)) + var/atom_def = text2path(path_text) //path definition, e.g /obj/foo/bar old_position = dpos + 1 - if(!atom_def) // Skip the item if the path does not exist. Fix your crap, mappers! + if(!ispath(atom_def, /atom)) // Skip the item if the path does not exist. Fix your crap, mappers! + if(bad_paths) + LAZYOR(bad_paths[path_text], model_key) continue members.Add(atom_def) @@ -258,7 +268,6 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new) members_attributes[index++] = fields CHECK_TICK - while(dpos != 0) //check and see if we can just skip this turf //So you don't have to understand this horrid statement, we can do this if @@ -269,33 +278,42 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new) // 5. and the members are world.turf and world.area // Basically, if we find an entry like this: "XXX" = (/turf/default, /area/default) // We can skip calling this proc every time we see XXX - if(no_changeturf && !space_key && members.len == 2 && members_attributes.len == 2 && length(members_attributes[1]) == 0 && length(members_attributes[2]) == 0 && (world.area in members) && (world.turf in members)) - space_key = model_key - return + if(no_changeturf \ + && !(.[SPACE_KEY]) \ + && members.len == 2 \ + && members_attributes.len == 2 \ + && length(members_attributes[1]) == 0 \ + && length(members_attributes[2]) == 0 \ + && (world.area in members) \ + && (world.turf in members)) + + .[SPACE_KEY] = model_key + continue - modelCache[model] = list(members, members_attributes) + .[model_key] = list(members, members_attributes) +/datum/parsed_map/proc/build_coordinate(list/areaCache, list/model, turf/crds, no_changeturf as num, placeOnTop as num) + var/index + var/list/members = model[1] + var/list/members_attributes = model[2] //////////////// //Instanciation //////////////// //The next part of the code assumes there's ALWAYS an /area AND a /turf on a given tile - var/turf/crds = locate(xcrd,ycrd,zcrd) - //first instance the /area and remove it from the members list index = members.len if(members[index] != /area/template_noop) - var/atom/instance GLOB._preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation var/atype = members[index] - for(var/area/A in world) - if(A.type == atype) - instance = A - break - if(!instance) - instance = new atype(null) + var/atom/instance = areaCache[atype] + if (!instance) + instance = GLOB.areas_by_type[atype] + if (!instance) + instance = new atype(null) + areaCache[atype] = instance if(crds) instance.contents.Add(crds) @@ -335,7 +353,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new) //////////////// //Instance an atom at (x,y,z) and gives it the variables in attributes -/dmm_suite/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop) +/datum/parsed_map/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop) GLOB._preloader.setup(attributes, path) if(crds) @@ -358,13 +376,13 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new) stoplag() SSatoms.map_loader_begin() -/dmm_suite/proc/create_atom(path, crds) +/datum/parsed_map/proc/create_atom(path, crds) set waitfor = FALSE . = new path (crds) //text trimming (both directions) helper proc //optionally removes quotes before and after the text (for variable name) -/dmm_suite/proc/trim_text(what as text,trim_quotes=0) +/datum/parsed_map/proc/trim_text(what as text,trim_quotes=0) if(trim_quotes) return trimQuotesRegex.Replace(what, "") else @@ -373,7 +391,7 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new) //find the position of the next delimiter,skipping whatever is comprised between opening_escape and closing_escape //returns 0 if reached the last delimiter -/dmm_suite/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape="\"",closing_escape="\"") +/datum/parsed_map/proc/find_next_delimiter_position(text as text,initial_position as num, delimiter=",",opening_escape="\"",closing_escape="\"") var/position = initial_position var/next_delimiter = findtext(text,delimiter,position,0) var/next_opening = findtext(text,opening_escape,position,0) @@ -388,90 +406,69 @@ GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new) //build a list from variables in text form (e.g {var1="derp"; var2; var3=7} => list(var1="derp", var2, var3=7)) //return the filled list -/dmm_suite/proc/readlist(text as text, delimiter=",") - - var/list/to_return = list() +/datum/parsed_map/proc/readlist(text as text, delimiter=",") + . = list() + if (!text) + return var/position var/old_position = 1 - do - //find next delimiter that is not within "..." + while(position != 0) + // find next delimiter that is not within "..." position = find_next_delimiter_position(text,old_position,delimiter) - //check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo",var2=7)) + // check if this is a simple variable (as in list(var1, var2)) or an associative one (as in list(var1="foo",var2=7)) var/equal_position = findtext(text,"=",old_position, position) - var/trim_left = trim_text(copytext(text,old_position,(equal_position ? equal_position : position)),1)//the name of the variable, must trim quotes to build a BYOND compliant associatives list + var/trim_left = trim_text(copytext(text,old_position,(equal_position ? equal_position : position))) + var/left_constant = delimiter == ";" ? trim_left : parse_constant(trim_left) old_position = position + 1 - if(equal_position)//associative var, so do the association - var/trim_right = trim_text(copytext(text,equal_position+1,position))//the content of the variable + if(equal_position && !isnum(left_constant)) + // Associative var, so do the association. + // Note that numbers cannot be keys - the RHS is dropped if so. + var/trim_right = trim_text(copytext(text,equal_position+1,position)) + var/right_constant = parse_constant(trim_right) + .[left_constant] = right_constant - //Check for string - if(findtext(trim_right,"\"",1,2)) - trim_right = copytext(trim_right,2,findtext(trim_right,"\"",3,0)) + else // simple var + . += list(left_constant) - //Check for number - else if(isnum(text2num(trim_right))) - trim_right = text2num(trim_right) +/datum/parsed_map/proc/parse_constant(text) + // number + var/num = text2num(text) + if(isnum(num)) + return num - //Check for null - else if(trim_right == "null") - trim_right = null + // string + if(findtext(text,"\"",1,2)) + return copytext(text,2,findtext(text,"\"",3,0)) - //Check for list - else if(copytext(trim_right,1,5) == "list") - trim_right = readlist(copytext(trim_right,6,length(trim_right))) + // list + if(copytext(text,1,6) == "list(") + return readlist(copytext(text,6,length(text))) - //Check for file - else if(copytext(trim_right,1,2) == "'") - trim_right = file(copytext(trim_right,2,length(trim_right))) + // typepath + var/path = text2path(text) + if(ispath(path)) + return path - //Check for path - else if(ispath(text2path(trim_right))) - trim_right = text2path(trim_right) + // file + if(copytext(text,1,2) == "'") + return file(copytext(text,2,length(text))) - to_return[trim_left] = trim_right + // null + if(text == "null") + return null - else//simple var - to_return[trim_left] = null + // not parsed: + // - pops: /obj{name="foo"} + // - new(), newlist(), icon(), matrix(), sound() - while(position != 0) + // fallback: string + return text - return to_return - -/dmm_suite/Destroy() +/datum/parsed_map/Destroy() ..() return QDEL_HINT_HARDDEL_NOW - -////////////////// -//Preloader datum -////////////////// - -/dmm_suite/preloader - parent_type = /datum - var/list/attributes - var/target_path - -/dmm_suite/preloader/proc/setup(list/the_attributes, path) - if(the_attributes.len) - GLOB.use_preloader = TRUE - attributes = the_attributes - target_path = path - -/dmm_suite/preloader/proc/load(atom/what) - for(var/attribute in attributes) - var/value = attributes[attribute] - if(islist(value)) - value = deepCopyList(value) - what.vars[attribute] = value - GLOB.use_preloader = FALSE - -/area/template_noop - name = "Area Passthrough" - -/turf/template_noop - name = "Turf Passthrough" - icon_state = "noop" - bullet_bounce_sound = null diff --git a/code/modules/mapping/verify.dm b/code/modules/mapping/verify.dm new file mode 100644 index 0000000000..a9834e3709 --- /dev/null +++ b/code/modules/mapping/verify.dm @@ -0,0 +1,98 @@ +/// An error report generated by [parsed_map/check_for_errors]. +/datum/map_report + var/original_path + var/list/bad_paths = list() + var/list/bad_keys = list() + /// Whether this map can be loaded safely despite the errors. + var/loadable = TRUE + var/crashed = TRUE + + var/static/tag_number = 0 + +/datum/map_report/New(datum/parsed_map/map) + original_path = map.original_path || "Untitled" + +/// Show a rendered version of this report to a client. +/datum/map_report/proc/show_to(client/C) + var/list/html = list() + html += "

Report for map file [original_path]

" + if(crashed) + html += "

Validation crashed: check the runtime logs.

" + if(!loadable) + html += "

Not loadable: some tiles are missing their turfs or areas.

" + + if(bad_paths.len) + html += "

Bad paths:

    " + for(var/path in bad_paths) + var/list/keys = bad_paths[path] + html += "
  1. [path]: used in ([keys.len]): [keys.Join(", ")]" + html += "

" + + if(bad_keys.len) + html += "

Bad keys:

    " + for(var/key in bad_keys) + var/list/messages = bad_keys[key] + html += "
  • [key]" + if(messages.len == 1) + html += ": [bad_keys[key][1]]" + else + html += "
    • [messages.Join("
    • ")]
    " + html += "
  • " + html += "

" + C << browse(html.Join(), "window=[tag];size=600x400") + +/datum/map_report/Topic(href, href_list) + . = ..() + if(. || !check_rights(R_ADMIN, FALSE) || !usr.client.holder.CheckAdminHref(href, href_list)) + return + + if (href_list["show"]) + show_to(usr) + + +/// Check a parsed but not yet loaded map for errors. +/// +/// Returns a [/datum/map_report] if there are errors or `FALSE` otherwise. +/datum/parsed_map/proc/check_for_errors() + var/datum/map_report/report = new(src) + . = report + + // build_cache will check bad paths for us + var/list/modelCache = build_cache(TRUE, report.bad_paths) + + for(var/path in report.bad_paths) + if(copytext(path, 1, 7) == "/turf/" || copytext(path, 1, 7) == "/area/") + report.loadable = FALSE + + // check for tiles with the wrong number of turfs or areas + for(var/key in modelCache) + if(key == SPACE_KEY) + continue + var/model = modelCache[key] + var/list/members = model[1] + + var/turfs = 0 + var/areas = 0 + for(var/i in 1 to members.len) + var/atom/path = members[i] + + turfs += ispath(path, /turf) + areas += ispath(path, /area) + + if(turfs == 0) + report.loadable = FALSE + LAZYADD(report.bad_keys[key], "no turf") + else if(turfs > 1) + LAZYADD(report.bad_keys[key], "[turfs] stacked turfs") + + if(areas != 1) + report.loadable = FALSE + LAZYADD(report.bad_keys[key], "[areas] areas instead of 1") + + // return the report + if(report.bad_paths.len || report.bad_keys.len || !report.loadable) + // keep the report around so it can be referenced later + report.tag = "mapreport_[++report.tag_number]" + report.crashed = FALSE + else + return FALSE diff --git a/code/modules/mining/aux_base.dm b/code/modules/mining/aux_base.dm index 2c001571a1..91eab536c9 100644 --- a/code/modules/mining/aux_base.dm +++ b/code/modules/mining/aux_base.dm @@ -134,7 +134,7 @@ interface with the mining shuttle at the landing site if a mobile beacon is also shuttleId = "mining" //The base can only be dropped once, so this gives the console a new purpose. possible_destinations = "mining_home;mining_away;landing_zone_dock;mining_public" -/obj/machinery/computer/auxillary_base/proc/set_landing_zone(turf/T, mob/user, var/no_restrictions) +/obj/machinery/computer/auxillary_base/proc/set_landing_zone(turf/T, mob/user, no_restrictions) var/obj/docking_port/mobile/auxillary_base/base_dock = locate(/obj/docking_port/mobile/auxillary_base) in SSshuttle.mobile if(!base_dock) //Not all maps have an Aux base. This object is useless in that case. to_chat(user, "This station is not equipped with an auxillary base. Please contact your Nanotrasen contractor.") @@ -151,8 +151,8 @@ interface with the mining shuttle at the landing site if a mobile beacon is also if(!is_mining_level(T.z)) return BAD_ZLEVEL - var/colony_radius = CEILING(max(base_dock.width, base_dock.height)*0.5, 1) - var/list/colony_turfs = block(locate(T.x - colony_radius, T.y - colony_radius, T.z), locate(T.x + colony_radius, T.y + colony_radius, T.z)) + + var/list/colony_turfs = base_dock.return_ordered_turfs(T.x,T.y,T.z,base_dock.dir) for(var/i in 1 to colony_turfs.len) CHECK_TICK var/turf/place = colony_turfs[i] @@ -239,7 +239,6 @@ interface with the mining shuttle at the landing site if a mobile beacon is also /obj/docking_port/mobile/auxillary_base name = "auxillary base" id = "colony_drop" - timid = FALSE //Reminder to map-makers to set these values equal to the size of your base. dheight = 4 dwidth = 4 @@ -355,7 +354,7 @@ interface with the mining shuttle at the landing site if a mobile beacon is also qdel(Mport) return - if(!mining_shuttle.canDock(Mport)) + if(mining_shuttle.canDock(Mport) != SHUTTLE_CAN_DOCK) to_chat(user, "Unable to secure a valid docking zone. Please try again in an open area near, but not within the aux. mining base.") SSshuttle.stationary.Remove(Mport) qdel(Mport) diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm index 61d439667d..a905a3baa9 100644 --- a/code/modules/mining/equipment/explorer_gear.dm +++ b/code/modules/mining/equipment/explorer_gear.dm @@ -61,7 +61,7 @@ icon_state = "hostile_env" item_state = "hostile_env" clothing_flags = THICKMATERIAL //not spaceproof - max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT resistance_flags = FIRE_PROOF | LAVA_PROOF slowdown = 0 armor = list("melee" = 70, "bullet" = 40, "laser" = 10, "energy" = 10, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) @@ -91,7 +91,7 @@ icon_state = "hostile_env" item_state = "hostile_env" w_class = WEIGHT_CLASS_NORMAL - max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT clothing_flags = THICKMATERIAL // no space protection armor = list("melee" = 70, "bullet" = 40, "laser" = 10, "energy" = 10, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) resistance_flags = FIRE_PROOF | LAVA_PROOF diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 7cf16f6064..fb6e9eb6fe 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -72,6 +72,7 @@ C.total_damage += target_health - target.health //we did some damage, but let's not assume how much we did /obj/item/twohanded/required/kinetic_crusher/afterattack(atom/target, mob/living/user, proximity_flag, clickparams) + . = ..() if(!proximity_flag && charged)//Mark a target, or mine a tile. var/turf/proj_turf = user.loc if(!isturf(proj_turf)) diff --git a/code/modules/mining/equipment/lazarus_injector.dm b/code/modules/mining/equipment/lazarus_injector.dm index 6947b5b23f..b2a2e3357d 100644 --- a/code/modules/mining/equipment/lazarus_injector.dm +++ b/code/modules/mining/equipment/lazarus_injector.dm @@ -16,6 +16,7 @@ var/revive_type = SENTIENCE_ORGANIC //So you can't revive boss monsters or robots with it /obj/item/lazarus_injector/afterattack(atom/target, mob/user, proximity_flag) + . = ..() if(!loaded) return if(isliving(target) && proximity_flag) diff --git a/code/modules/mining/equipment/regenerative_core.dm b/code/modules/mining/equipment/regenerative_core.dm index 43be61dd77..d6e99f7361 100644 --- a/code/modules/mining/equipment/regenerative_core.dm +++ b/code/modules/mining/equipment/regenerative_core.dm @@ -7,6 +7,7 @@ w_class = WEIGHT_CLASS_TINY /obj/item/hivelordstabilizer/afterattack(obj/item/organ/M, mob/user) + . = ..() var/obj/item/organ/regenerative_core/C = M if(!istype(C, /obj/item/organ/regenerative_core)) to_chat(user, "The stabilizer only works on certain types of monster organs, generally regenerative in nature.") @@ -62,10 +63,11 @@ /obj/item/organ/regenerative_core/on_life() ..() - if(owner.health < HEALTH_THRESHOLD_CRIT) + if(owner.health < owner.crit_threshold) ui_action_click() /obj/item/organ/regenerative_core/afterattack(atom/target, mob/user, proximity_flag) + . = ..() if(proximity_flag && ishuman(target)) var/mob/living/carbon/human/H = target if(inert) @@ -83,7 +85,6 @@ SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "self")) H.revive(full_heal = 1) qdel(src) - ..() /obj/item/organ/regenerative_core/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE) . = ..() diff --git a/code/modules/mining/equipment/resonator.dm b/code/modules/mining/equipment/resonator.dm index 57efc0f7f5..e23c3deb58 100644 --- a/code/modules/mining/equipment/resonator.dm +++ b/code/modules/mining/equipment/resonator.dm @@ -100,7 +100,7 @@ playsound(T,'sound/weapons/resonator_blast.ogg',50,1) for(var/mob/living/L in T) if(creator) - add_logs(creator, L, "used a resonator field on", "resonator") + log_combat(creator, L, "used a resonator field on", "resonator") to_chat(L, "[src] ruptured with you in it!") L.apply_damage(resonance_damage, BRUTE) qdel(src) diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm index 3005a29dcd..715ecc906a 100644 --- a/code/modules/mining/equipment/survival_pod.dm +++ b/code/modules/mining/equipment/survival_pod.dm @@ -175,8 +175,6 @@ desc = "A heated storage unit." icon_state = "donkvendor" icon = 'icons/obj/lavaland/donkvendor.dmi' - icon_on = "donkvendor" - icon_off = "donkvendor" light_range = 5 light_power = 1.2 light_color = "#DDFFD3" @@ -185,6 +183,9 @@ flags_1 = NODECONSTRUCT_1 var/empty = FALSE +/obj/machinery/smartfridge/survival_pod/update_icon() + return + /obj/machinery/smartfridge/survival_pod/Initialize(mapload) . = ..() if(empty) @@ -247,11 +248,6 @@ . = ..() air_update_turf(1) -/obj/structure/fans/Destroy() - var/turf/T = loc - . = ..() - T.air_update_turf(1) - //Inivisible, indestructible fans /obj/structure/fans/tiny/invisible name = "air flow blocker" diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm index d6ea55855d..f97d3612a8 100644 --- a/code/modules/mining/fulton.dm +++ b/code/modules/mining/fulton.dm @@ -38,6 +38,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons) to_chat(user, "You link the extraction pack to the beacon system.") /obj/item/extraction_pack/afterattack(atom/movable/A, mob/living/carbon/human/user, flag, params) + . = ..() if(!beacon) to_chat(user, "[src] is not linked to a beacon, and cannot be used.") return diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index 765f9d92e0..621a6afe23 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -135,6 +135,7 @@ GLOBAL_LIST(labor_sheet_values) /obj/machinery/mineral/stacking_machine/laborstacker + force_connect = TRUE var/points = 0 //The unclaimed value of ore stacked. /obj/machinery/mineral/stacking_machine/laborstacker/process_sheet(obj/item/stack/sheet/inp) diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm index 988c224279..540a012df1 100644 --- a/code/modules/mining/lavaland/ash_flora.dm +++ b/code/modules/mining/lavaland/ash_flora.dm @@ -152,6 +152,7 @@ resistance_flags = FLAMMABLE max_integrity = 100 seed = /obj/item/seeds/lavaland/polypore + wine_power = 20 /obj/item/reagent_containers/food/snacks/grown/ash_flora/Initialize() . = ..() @@ -167,7 +168,7 @@ list_reagents = list("nutriment" = 3, "vitfro" = 2, "nicotine" = 2) icon_state = "mushroom_leaf" seed = /obj/item/seeds/lavaland/porcini - + wine_power = 40 /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_cap name = "mushroom cap" @@ -175,7 +176,7 @@ list_reagents = list("mindbreaker" = 2, "entpoly" = 4, "mushroomhallucinogen" = 2) icon_state = "mushroom_cap" seed = /obj/item/seeds/lavaland/inocybe - + wine_power = 70 /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_stem name = "mushroom stem" @@ -184,6 +185,7 @@ icon_state = "mushroom_stem" light_range = 1 seed = /obj/item/seeds/lavaland/ember + wine_power = 60 /obj/item/reagent_containers/food/snacks/grown/ash_flora/cactus_fruit name = "cactus fruit" @@ -191,6 +193,7 @@ desc = "A cactus fruit covered in a thick, reddish skin. And some ash." icon_state = "cactus_fruit" seed = /obj/item/seeds/lavaland/cactus + wine_power = 50 /obj/item/reagent_containers/glass/bowl/mushroom_bowl name = "mushroom bowl" diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 5345410005..23823e1e18 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -164,22 +164,22 @@ var/failText = "The snake seems unsatisfied with your incomplete oath and returns to it's previous place on the rod, returning to its dormant, wooden state. You must stand still while completing your oath!" to_chat(itemUser, "The wooden snake that was carved into the rod seems to suddenly come alive and begins to slither down your arm! The compulsion to help others grows abnormally strong...") if(do_after(itemUser, 40, target = itemUser)) - itemUser.say("I swear to fulfill, to the best of my ability and judgment, this covenant:") + itemUser.say("I swear to fulfill, to the best of my ability and judgment, this covenant:", forced = "hippocratic oath") else to_chat(itemUser, failText) return if(do_after(itemUser, 20, target = itemUser)) - itemUser.say("I will apply, for the benefit of the sick, all measures that are required, avoiding those twin traps of overtreatment and therapeutic nihilism.") + itemUser.say("I will apply, for the benefit of the sick, all measures that are required, avoiding those twin traps of overtreatment and therapeutic nihilism.", forced = "hippocratic oath") else to_chat(itemUser, failText) return if(do_after(itemUser, 30, target = itemUser)) - itemUser.say("I will remember that I remain a member of society, with special obligations to all my fellow human beings, those sound of mind and body as well as the infirm.") + itemUser.say("I will remember that I remain a member of society, with special obligations to all my fellow human beings, those sound of mind and body as well as the infirm.", forced = "hippocratic oath") else to_chat(itemUser, failText) return if(do_after(itemUser, 30, target = itemUser)) - itemUser.say("If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.") + itemUser.say("If I do not violate this oath, may I enjoy life and art, respected while I live and remembered with affection thereafter. May I always act so as to preserve the finest traditions of my calling and may I long experience the joy of healing those who seek my help.", forced = "hippocratic oath") else to_chat(itemUser, failText) return @@ -434,10 +434,11 @@ //Immortality Talisman /obj/item/immortality_talisman - name = "Immortality Talisman" + name = "\improper Immortality Talisman" desc = "A dread talisman that can render you completely invulnerable." icon = 'icons/obj/lavaland/artefacts.dmi' icon_state = "talisman" + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF actions_types = list(/datum/action/item_action/immortality) var/cooldown = 0 @@ -448,12 +449,6 @@ /datum/action/item_action/immortality name = "Immortality" -/obj/item/immortality_talisman/Destroy(force) - if(force) - . = ..() - else - return QDEL_HINT_LETMELIVE - /obj/item/immortality_talisman/attack_self(mob/user) if(cooldown < world.time) SSblackbox.record_feedback("amount", "immortality_talisman_uses", 1) @@ -467,6 +462,8 @@ user.notransform = 1 user.status_flags |= GODMODE addtimer(CALLBACK(src, .proc/return_to_reality, user, Z), 100) + else + to_chat(user, "[src] is not ready yet!") /obj/item/immortality_talisman/proc/return_to_reality(mob/user, obj/effect/immortality_talisman/Z) user.status_flags &= ~GODMODE @@ -899,7 +896,7 @@ var/static/list/banned_turfs = typecacheof(list(/turf/open/space/transit, /turf/closed)) /obj/item/lava_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters) - ..() + . = ..() if(timer > world.time) return @@ -975,7 +972,7 @@ to_chat(user, "You shatter the bottle!") playsound(user.loc, 'sound/effects/glassbr1.ogg', 100, 1) message_admins("[ADMIN_LOOKUPFLW(user)] has activated a bottle of mayhem!") - add_logs(user, null, "activated a bottle of mayhem", src) + log_combat(user, null, "activated a bottle of mayhem", src) qdel(src) /obj/item/blood_contract @@ -1019,7 +1016,7 @@ var/datum/objective/survive/survive = new survive.owner = L.mind L.mind.objectives += survive - add_logs(user, L, "took out a blood contract on", src) + log_combat(user, L, "took out a blood contract on", src) to_chat(L, "You've been marked for death! Don't let the demons get you! KILL THEM ALL!") L.add_atom_colour("#FF0000", ADMIN_COLOUR_PRIORITY) var/obj/effect/mine/pickup/bloodbath/B = new(L) @@ -1082,7 +1079,7 @@ to_chat(user, "The[beacon ? " beacon is not currently":"re is a beacon"] attached.") /obj/item/hierophant_club/suicide_act(mob/living/user) - say("Xverwpsgexmrk...") + say("Xverwpsgexmrk...", forced = "hierophant club suicide") user.visible_message("[user] holds [src] into the air! It looks like [user.p_theyre()] trying to commit suicide!") new/obj/effect/temp_visual/hierophant/telegraph(get_turf(user)) playsound(user,'sound/machines/airlockopen.ogg', 75, TRUE) @@ -1097,7 +1094,7 @@ qdel(user) /obj/item/hierophant_club/afterattack(atom/target, mob/user, proximity_flag, click_parameters) - ..() + . = ..() var/turf/T = get_turf(target) if(!T || timer > world.time) return @@ -1105,7 +1102,7 @@ timer = world.time + CLICK_CD_MELEE //by default, melee attacks only cause melee blasts, and have an accordingly short cooldown if(proximity_flag) INVOKE_ASYNC(src, .proc/aoe_burst, T, user) - add_logs(user, target, "fired 3x3 blast at", src) + log_combat(user, target, "fired 3x3 blast at", src) else if(ismineralturf(target) && get_dist(user, target) < 6) //target is minerals, we can hit it(even if we can't see it) INVOKE_ASYNC(src, .proc/cardinal_blasts, T, user) @@ -1117,10 +1114,10 @@ var/obj/effect/temp_visual/hierophant/chaser/C = new(get_turf(user), user, target, chaser_speed, friendly_fire_check) C.damage = 30 C.monster_damage_boost = FALSE - add_logs(user, target, "fired a chaser at", src) + log_combat(user, target, "fired a chaser at", src) else INVOKE_ASYNC(src, .proc/cardinal_blasts, T, user) //otherwise, just do cardinal blast - add_logs(user, target, "fired cardinal blast at", src) + log_combat(user, target, "fired cardinal blast at", src) else to_chat(user, "That target is out of range!" ) timer = world.time @@ -1232,7 +1229,7 @@ INVOKE_ASYNC(src, .proc/prepare_icon_update) beacon.icon_state = "hierophant_tele_off" return - add_logs(user, beacon, "teleported self from [AREACOORD(source)] to") + user.log_message("teleported self from [AREACOORD(source)] to [beacon]") new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, user) new /obj/effect/temp_visual/hierophant/telegraph/teleport(source, user) for(var/t in RANGE_TURFS(1, T)) @@ -1279,7 +1276,7 @@ return M.visible_message("[M] fades in!") if(user != M) - add_logs(user, M, "teleported", null, "from [AREACOORD(source)]") + log_combat(user, M, "teleported", null, "from [AREACOORD(source)]") /obj/item/hierophant_club/proc/cardinal_blasts(turf/T, mob/living/user) //fire cardinal cross blasts with a delay if(!T) diff --git a/code/modules/mining/lavaland/ruins/gym.dm b/code/modules/mining/lavaland/ruins/gym.dm index 634c2df859..1c535fd9ab 100644 --- a/code/modules/mining/lavaland/ruins/gym.dm +++ b/code/modules/mining/lavaland/ruins/gym.dm @@ -16,17 +16,20 @@ playsound(loc, pick(hit_sounds), 25, 1, -1) if(isliving(user)) var/mob/living/L = user + SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "exercise", /datum/mood_event/exercise) L.apply_status_effect(STATUS_EFFECT_EXERCISED) -/obj/structure/stacklifter +/obj/structure/weightmachine name = "Weight Machine" desc = "Just looking at this thing makes you feel tired." - icon = 'goon/icons/obj/fitness.dmi' - icon_state = "fitnesslifter" density = TRUE anchored = TRUE + var/icon_state_inuse -/obj/structure/stacklifter/attack_hand(mob/living/user) +/obj/structure/weightmachine/proc/AnimateMachine(mob/living/user) + return + +/obj/structure/weightmachine/attack_hand(mob/living/user) . = ..() if(.) return @@ -35,76 +38,58 @@ return else obj_flags |= IN_USE - icon_state = "fitnesslifter2" + icon_state = icon_state_inuse user.setDir(SOUTH) user.Stun(80) user.forceMove(src.loc) var/bragmessage = pick("pushing it to the limit","going into overdrive","burning with determination","rising up to the challenge", "getting strong now","getting ripped") user.visible_message("[user] is [bragmessage]!") - var/lifts = 0 - while (lifts++ < 6) - if (user.loc != src.loc) - break - sleep(3) - animate(user, pixel_y = -2, time = 3) - sleep(3) - animate(user, pixel_y = -4, time = 3) - sleep(3) - playsound(user, 'goon/sound/effects/spring.ogg', 60, 1) + AnimateMachine(user) playsound(user, 'sound/machines/click.ogg', 60, 1) obj_flags &= ~IN_USE user.pixel_y = 0 var/finishmessage = pick("You feel stronger!","You feel like you can take on the world!","You feel robust!","You feel indestructible!") - icon_state = "fitnesslifter" + SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "exercise", /datum/mood_event/exercise) + icon_state = initial(icon_state) to_chat(user, finishmessage) user.apply_status_effect(STATUS_EFFECT_EXERCISED) -/obj/structure/weightlifter - name = "Weight Machine" - desc = "Just looking at this thing makes you feel tired." +/obj/structure/weightmachine/stacklifter + icon = 'goon/icons/obj/fitness.dmi' + icon_state = "fitnesslifter" + icon_state_inuse = "fitnesslifter2" + +/obj/structure/weightmachine/stacklifter/AnimateMachine(mob/living/user) + var/lifts = 0 + while (lifts++ < 6) + if (user.loc != src.loc) + break + sleep(3) + animate(user, pixel_y = -2, time = 3) + sleep(3) + animate(user, pixel_y = -4, time = 3) + sleep(3) + playsound(user, 'goon/sound/effects/spring.ogg', 60, 1) + +/obj/structure/weightmachine/weightlifter icon = 'goon/icons/obj/fitness.dmi' icon_state = "fitnessweight" - density = TRUE - anchored = TRUE + icon_state_inuse = "fitnessweight-c" -/obj/structure/weightlifter/attack_hand(mob/living/user) - . = ..() - if(.) - return - if(obj_flags & IN_USE) - to_chat(user, "It's already in use - wait a bit.") - return - else - obj_flags |= IN_USE - icon_state = "fitnessweight-c" - user.setDir(SOUTH) - user.Stun(80) - user.forceMove(src.loc) - var/mutable_appearance/swole_overlay = mutable_appearance(icon, "fitnessweight-w", WALL_OBJ_LAYER) - add_overlay(swole_overlay) - var/bragmessage = pick("pushing it to the limit","going into overdrive","burning with determination","rising up to the challenge", "getting strong now","getting ripped") - user.visible_message("[user] is [bragmessage]!") - var/reps = 0 - user.pixel_y = 5 - while (reps++ < 6) - if (user.loc != src.loc) - break - - for (var/innerReps = max(reps, 1), innerReps > 0, innerReps--) - sleep(3) - animate(user, pixel_y = (user.pixel_y == 3) ? 5 : 3, time = 3) - - playsound(user, 'goon/sound/effects/spring.ogg', 60, 1) - - sleep(3) - animate(user, pixel_y = 2, time = 3) - sleep(3) - playsound(user, 'sound/machines/click.ogg', 60, 1) - obj_flags &= ~IN_USE - animate(user, pixel_y = 0, time = 3) - var/finishmessage = pick("You feel stronger!","You feel like you can take on the world!","You feel robust!","You feel indestructible!") - icon_state = "fitnessweight" - cut_overlay(swole_overlay) - to_chat(user, "[finishmessage]") - user.apply_status_effect(STATUS_EFFECT_EXERCISED) \ No newline at end of file +/obj/structure/weightmachine/weightlifter/AnimateMachine(mob/living/user) + var/mutable_appearance/swole_overlay = mutable_appearance(icon, "fitnessweight-w", WALL_OBJ_LAYER) + add_overlay(swole_overlay) + var/reps = 0 + user.pixel_y = 5 + while (reps++ < 6) + if (user.loc != src.loc) + break + for (var/innerReps = max(reps, 1), innerReps > 0, innerReps--) + sleep(3) + animate(user, pixel_y = (user.pixel_y == 3) ? 5 : 3, time = 3) + playsound(user, 'goon/sound/effects/spring.ogg', 60, 1) + sleep(3) + animate(user, pixel_y = 2, time = 3) + sleep(3) + cut_overlay(swole_overlay) \ No newline at end of file diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index 026170da09..a004a6ae0d 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -82,7 +82,7 @@ /obj/machinery/mineral/processing_unit/Initialize() . = ..() proximity_monitor = new(src, 1) - AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), INFINITY, TRUE, list(/obj/item/stack)) + AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), INFINITY, TRUE, /obj/item/stack) stored_research = new /datum/techweb/specialized/autounlocking/smelter /obj/machinery/mineral/processing_unit/Destroy() diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index 3529214609..ce569ec055 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -13,7 +13,6 @@ speed_process = TRUE circuit = /obj/item/circuitboard/machine/ore_redemption layer = BELOW_OBJ_LAYER - var/req_access_reclaim = ACCESS_MINING_STATION var/obj/item/card/id/inserted_id var/points = 0 var/ore_pickup_rate = 15 @@ -24,11 +23,12 @@ var/list/ore_buffer = list() var/datum/techweb/stored_research var/obj/item/disk/design_disk/inserted_disk + var/datum/component/remote_materials/materials -/obj/machinery/mineral/ore_redemption/Initialize() +/obj/machinery/mineral/ore_redemption/Initialize(mapload) . = ..() - AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE),INFINITY, FALSE, list(/obj/item/stack)) stored_research = new /datum/techweb/specialized/autounlocking/smelter + materials = AddComponent(/datum/component/remote_materials, "orm", mapload) /obj/machinery/mineral/ore_redemption/Destroy() QDEL_NULL(stored_research) @@ -49,35 +49,43 @@ sheet_per_ore = sheet_per_ore_temp /obj/machinery/mineral/ore_redemption/proc/smelt_ore(obj/item/stack/ore/O) + var/datum/component/material_container/mat_container = materials.mat_container + if (!mat_container) + return ore_buffer -= O if(O && O.refined_type) points += O.points * point_upgrade * O.amount - GET_COMPONENT(materials, /datum/component/material_container) - var/material_amount = materials.get_item_material_amount(O) + var/material_amount = mat_container.get_item_material_amount(O) if(!material_amount) qdel(O) //no materials, incinerate it - else if(!materials.has_space(material_amount * sheet_per_ore * O.amount)) //if there is no space, eject it + else if(!mat_container.has_space(material_amount * sheet_per_ore * O.amount)) //if there is no space, eject it unload_mineral(O) else - materials.insert_item(O, sheet_per_ore) //insert it + var/mats = O.materials & mat_container.materials + var/amount = O.amount + var/id = inserted_id && inserted_id.registered_name + if (id) + id = " (ID: [id])" + mat_container.insert_item(O, sheet_per_ore) //insert it + materials.silo_log(src, "smelted", amount, "ores[id]", mats) qdel(O) /obj/machinery/mineral/ore_redemption/proc/can_smelt_alloy(datum/design/D) - if(D.make_reagents.len) + var/datum/component/material_container/mat_container = materials.mat_container + if(!mat_container || D.make_reagents.len) return FALSE var/build_amount = 0 - GET_COMPONENT(materials, /datum/component/material_container) for(var/mat_id in D.materials) var/M = D.materials[mat_id] - var/datum/material/redemption_mat = materials.materials[mat_id] + var/datum/material/redemption_mat = mat_container.materials[mat_id] if(!M || !redemption_mat) return FALSE @@ -102,17 +110,18 @@ smelt_ore(ore) /obj/machinery/mineral/ore_redemption/proc/send_console_message() - if(!is_station_level(z)) + var/datum/component/material_container/mat_container = materials.mat_container + if(!mat_container || !is_station_level(z)) return message_sent = TRUE + var/area/A = get_area(src) var/msg = "Now available in [A]:
" var/has_minerals = FALSE - GET_COMPONENT(materials, /datum/component/material_container) - for(var/mat_id in materials.materials) - var/datum/material/M = materials.materials[mat_id] + for(var/mat_id in mat_container.materials) + var/datum/material/M = mat_container.materials[mat_id] var/mineral_amount = M.amount / MINERAL_MATERIAL_AMOUNT if(mineral_amount) has_minerals = TRUE @@ -126,7 +135,7 @@ D.createmessage("Ore Redemption Machine", "New minerals available!", msg, 1, 0) /obj/machinery/mineral/ore_redemption/process() - if(panel_open || !powered()) + if(!materials.mat_container || panel_open || !powered()) return var/atom/input = get_step(src, input_dir) var/obj/structure/ore_box/OB = locate() in input @@ -147,10 +156,6 @@ send_console_message() /obj/machinery/mineral/ore_redemption/attackby(obj/item/W, mob/user, params) - GET_COMPONENT(materials, /datum/component/material_container) - if(default_pry_open(W)) - materials.retrieve_all() - return if(default_unfasten_wrench(user, W)) return if(default_deconstruction_screwdriver(user, "ore_redemption-open", "ore_redemption", W)) @@ -170,22 +175,18 @@ interact(user) return - if(istype(W, /obj/item/multitool) && panel_open) - input_dir = turn(input_dir, -90) - output_dir = turn(output_dir, -90) - to_chat(user, "You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)].") - return - if(istype(W, /obj/item/disk/design_disk)) if(user.transferItemToLoc(W, src)) inserted_disk = W return TRUE return ..() -/obj/machinery/mineral/ore_redemption/on_deconstruction() - GET_COMPONENT(materials, /datum/component/material_container) - materials.retrieve_all() - ..() +/obj/machinery/mineral/ore_redemption/multitool_act(mob/living/user, obj/item/multitool/I) + if (panel_open) + input_dir = turn(input_dir, -90) + output_dir = turn(output_dir, -90) + to_chat(user, "You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)].") + return TRUE /obj/machinery/mineral/ore_redemption/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) @@ -201,16 +202,25 @@ data["claimedPoints"] = inserted_id.mining_points data["materials"] = list() - GET_COMPONENT(materials, /datum/component/material_container) - for(var/mat_id in materials.materials) - var/datum/material/M = materials.materials[mat_id] - var/sheet_amount = M.amount ? M.amount / MINERAL_MATERIAL_AMOUNT : "0" - data["materials"] += list(list("name" = M.name, "id" = M.id, "amount" = sheet_amount, "value" = ore_values[M.id] * point_upgrade)) + var/datum/component/material_container/mat_container = materials.mat_container + if (mat_container) + for(var/mat_id in mat_container.materials) + var/datum/material/M = mat_container.materials[mat_id] + var/sheet_amount = M.amount ? M.amount / MINERAL_MATERIAL_AMOUNT : "0" + data["materials"] += list(list("name" = M.name, "id" = M.id, "amount" = sheet_amount, "value" = ore_values[M.id] * point_upgrade)) + + data["alloys"] = list() + for(var/v in stored_research.researched_designs) + var/datum/design/D = stored_research.researched_designs[v] + data["alloys"] += list(list("name" = D.name, "id" = D.id, "amount" = can_smelt_alloy(D))) + + if (!mat_container) + data["disconnected"] = "local mineral storage is unavailable" + else if (!materials.silo) + data["disconnected"] = "no ore silo connection is available; storing locally" + else if (materials.on_hold()) + data["disconnected"] = "mineral withdrawal is on hold" - data["alloys"] = list() - for(var/v in stored_research.researched_designs) - var/datum/design/D = stored_research.researched_designs[v] - data["alloys"] += list(list("name" = D.name, "id" = D.id, "amount" = can_smelt_alloy(D))) data["diskDesigns"] = list() if(inserted_disk) data["hasDisk"] = TRUE @@ -225,7 +235,7 @@ /obj/machinery/mineral/ore_redemption/ui_act(action, params) if(..()) return - GET_COMPONENT(materials, /datum/component/material_container) + var/datum/component/material_container/mat_container = materials.mat_container switch(action) if("Eject") if(!inserted_id) @@ -248,12 +258,17 @@ points = 0 return TRUE if("Release") - - if(check_access(inserted_id) || allowed(usr)) //Check the ID inside, otherwise check the user + if(!mat_container) + return + if(materials.on_hold()) + to_chat(usr, "Mineral access is on hold, please contact the quartermaster.") + else if(!check_access(inserted_id) && !allowed(usr)) //Check the ID inside, otherwise check the user + to_chat(usr, "Required access not found.") + else var/mat_id = params["id"] - if(!materials.materials[mat_id]) + if(!mat_container.materials[mat_id]) return - var/datum/material/mat = materials.materials[mat_id] + var/datum/material/mat = mat_container.materials[mat_id] var/stored_amount = mat.amount / MINERAL_MATERIAL_AMOUNT if(!stored_amount) @@ -266,10 +281,10 @@ desired = input("How many sheets?", "How many sheets would you like to smelt?", 1) as null|num var/sheets_to_remove = round(min(desired,50,stored_amount)) - materials.retrieve_sheets(sheets_to_remove, mat_id, get_step(src, output_dir)) - - else - to_chat(usr, "Required access not found.") + var/count = mat_container.retrieve_sheets(sheets_to_remove, mat_id, get_step(src, output_dir)) + var/list/mats = list() + mats[mat_id] = MINERAL_MATERIAL_AMOUNT + materials.silo_log(src, "released", -count, "sheets", mats) return TRUE if("diskInsert") var/obj/item/disk/design_disk/disk = usr.get_active_held_item() @@ -291,6 +306,11 @@ stored_research.add_design(inserted_disk.blueprints[n]) return TRUE if("Smelt") + if(!mat_container) + return + if(materials.on_hold()) + to_chat(usr, "Mineral access is on hold, please contact the quartermaster.") + return var/alloy_id = params["id"] var/datum/design/alloy = stored_research.isDesignResearchedID(alloy_id) if((check_access(inserted_id) || allowed(usr)) && alloy) @@ -301,7 +321,8 @@ else desired = input("How many sheets?", "How many sheets would you like to smelt?", 1) as null|num var/amount = round(min(desired,50,smelt_amount)) - materials.use_amount(alloy.materials, amount) + mat_container.use_amount(alloy.materials, amount) + materials.silo_log(src, "released", -amount, "sheets", alloy.materials) var/output if(ispath(alloy.build_path, /obj/item/stack/sheet)) output = new alloy.build_path(src, amount) @@ -311,20 +332,6 @@ else to_chat(usr, "Required access not found.") return TRUE - if("SmeltAll") - var/alloy_id = params["id"] - var/datum/design/alloy = stored_research.isDesignResearchedID(alloy_id) - if((check_access(inserted_id) || allowed(usr)) && alloy) - var/smelt_amount = can_smelt_alloy(alloy) - while(smelt_amount > 0) - materials.use_amount(alloy.materials) - smelt_amount-- - var/output = new alloy.build_path(src) - unload_mineral(output) - CHECK_TICK - else - to_chat(usr, "Required access not found.") - return TRUE /obj/machinery/mineral/ore_redemption/ex_act(severity, target) do_sparks(5, TRUE, src) diff --git a/code/modules/mining/machine_silo.dm b/code/modules/mining/machine_silo.dm new file mode 100644 index 0000000000..bd1da0a90f --- /dev/null +++ b/code/modules/mining/machine_silo.dm @@ -0,0 +1,234 @@ +GLOBAL_DATUM(ore_silo_default, /obj/machinery/ore_silo) +GLOBAL_LIST_EMPTY(silo_access_logs) + +/obj/machinery/ore_silo + name = "ore silo" + desc = "An all-in-one bluespace storage and transmission system for the station's mineral distribution needs." + icon = 'icons/obj/mining.dmi' + icon_state = "silo" + density = TRUE + circuit = /obj/item/circuitboard/machine/ore_silo + + var/list/holds = list() + var/list/datum/component/remote_materials/connected = list() + var/log_page = 1 + +/obj/machinery/ore_silo/Initialize(mapload) + . = ..() + AddComponent(/datum/component/material_container, + list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE, MAT_PLASTIC), + INFINITY, + FALSE, + /obj/item/stack, + null, + null, + TRUE) + if (!GLOB.ore_silo_default && mapload && is_station_level(z)) + GLOB.ore_silo_default = src + +/obj/machinery/ore_silo/Destroy() + if (GLOB.ore_silo_default == src) + GLOB.ore_silo_default = null + + for(var/C in connected) + var/datum/component/remote_materials/mats = C + mats.disconnect_from(src) + + GET_COMPONENT(materials, /datum/component/material_container) + materials.retrieve_all() + + return ..() + +/obj/machinery/ore_silo/proc/remote_attackby(obj/machinery/M, mob/user, obj/item/stack/I) + GET_COMPONENT(materials, /datum/component/material_container) + // stolen from /datum/component/material_container/proc/OnAttackBy + if(user.a_intent != INTENT_HELP) + return + if(I.item_flags & ABSTRACT) + return + if(!istype(I) || (I.flags_1 & HOLOGRAM_1) || (I.item_flags & NO_MAT_REDEMPTION)) + to_chat(user, "[M] won't accept [I]!") + return + var/item_mats = I.materials & materials.materials + if(!length(item_mats)) + to_chat(user, "[I] does not contain sufficient materials to be accepted by [M].") + return + // assumes unlimited space... + var/amount = I.amount + materials.user_insert(I, user) + silo_log(M, "deposited", amount, "sheets", item_mats) + return TRUE + +/obj/machinery/ore_silo/attackby(obj/item/W, mob/user, params) + if (istype(W, /obj/item/stack)) + return remote_attackby(src, user, W) + return ..() + +/obj/machinery/ore_silo/ui_interact(mob/user) + user.set_machine(src) + var/datum/browser/popup = new(user, "ore_silo", null, 600, 550) + popup.set_content(generate_ui()) + popup.open() + +/obj/machinery/ore_silo/proc/generate_ui() + GET_COMPONENT(materials, /datum/component/material_container) + var/list/ui = list("Ore Silo

Stored Material:

") + var/any = FALSE + for(var/M in materials.materials) + var/datum/material/mat = materials.materials[M] + var/sheets = round(mat.amount) / MINERAL_MATERIAL_AMOUNT + if (sheets) + if (sheets >= 1) + ui += "Eject" + else + ui += "Eject" + if (sheets >= 20) + ui += "20x" + else + ui += "20x" + ui += "[mat.name]: [sheets] sheets
" + any = TRUE + if(!any) + ui += "Nothing!" + + ui += "

Connected Machines:

" + for(var/C in connected) + var/datum/component/remote_materials/mats = C + var/atom/parent = mats.parent + var/hold_key = "[get_area(parent)]/[mats.category]" + ui += "Remove" + ui += "[holds[hold_key] ? "Allow" : "Hold"]" + ui += " [parent.name] in [get_area_name(parent, TRUE)]
" + if(!connected.len) + ui += "Nothing!" + + ui += "

Access Logs:

" + var/list/logs = GLOB.silo_access_logs[REF(src)] + var/len = LAZYLEN(logs) + var/num_pages = 1 + round((len - 1) / 30) + var/page = CLAMP(log_page, 1, num_pages) + if(num_pages > 1) + for(var/i in 1 to num_pages) + if(i == page) + ui += "[i]" + else + ui += "[i]" + + ui += "
    " + any = FALSE + for(var/i in (page - 1) * 30 + 1 to min(page * 30, len)) + var/datum/ore_silo_log/entry = logs[i] + ui += "
  1. [entry.formatted]
  2. " + any = TRUE + if (!any) + ui += "
  3. Nothing!
  4. " + + ui += "
" + return ui.Join() + +/obj/machinery/ore_silo/Topic(href, href_list) + if(..()) + return + add_fingerprint(usr) + usr.set_machine(src) + + if(href_list["remove"]) + var/datum/component/remote_materials/mats = locate(href_list["remove"]) in connected + if (mats) + mats.disconnect_from(src) + connected -= mats + updateUsrDialog() + return TRUE + else if(href_list["hold1"]) + holds[href_list["hold1"]] = TRUE + updateUsrDialog() + return TRUE + else if(href_list["hold0"]) + holds -= href_list["hold0"] + updateUsrDialog() + return TRUE + else if(href_list["ejectsheet"]) + var/eject_sheet = href_list["ejectsheet"] + GET_COMPONENT(materials, /datum/component/material_container) + var/count = materials.retrieve_sheets(text2num(href_list["eject_amt"]), eject_sheet, drop_location()) + var/list/matlist = list() + matlist[eject_sheet] = MINERAL_MATERIAL_AMOUNT + silo_log(src, "ejected", -count, "sheets", matlist) + return TRUE + else if(href_list["page"]) + log_page = text2num(href_list["page"]) || 1 + updateUsrDialog() + return TRUE + +/obj/machinery/ore_silo/multitool_act(mob/living/user, obj/item/multitool/I) + if (istype(I)) + to_chat(user, "You log [src] in the multitool's buffer.") + I.buffer = src + return TRUE + +/obj/machinery/ore_silo/proc/silo_log(obj/machinery/M, action, amount, noun, list/mats) + if (!length(mats)) + return + var/datum/ore_silo_log/entry = new(M, action, amount, noun, mats) + + var/list/logs = GLOB.silo_access_logs[REF(src)] + if(!LAZYLEN(logs)) + GLOB.silo_access_logs[REF(src)] = logs = list(entry) + else if(!logs[1].merge(entry)) + logs.Insert(1, entry) + + updateUsrDialog() + flick("silo_active", src) + +/obj/machinery/ore_silo/examine(mob/user) + ..() + to_chat(user, "[src] can be linked to techfabs, circuit printers and protolathes with a multitool.") + +/datum/ore_silo_log + var/name // for VV + var/formatted // for display + + var/timestamp + var/machine_name + var/area_name + var/action + var/noun + var/amount + var/list/materials + +/datum/ore_silo_log/New(obj/machinery/M, _action, _amount, _noun, list/mats=list()) + timestamp = station_time_timestamp() + machine_name = M.name + area_name = get_area_name(M, TRUE) + action = _action + amount = _amount + noun = _noun + materials = mats.Copy() + for(var/each in materials) + materials[each] *= abs(_amount) + format() + +/datum/ore_silo_log/proc/merge(datum/ore_silo_log/other) + if (other == src || action != other.action || noun != other.noun) + return FALSE + if (machine_name != other.machine_name || area_name != other.area_name) + return FALSE + + timestamp = other.timestamp + amount += other.amount + for(var/each in other.materials) + materials[each] += other.materials[each] + format() + return TRUE + +/datum/ore_silo_log/proc/format() + name = "[machine_name]: [action] [amount]x [noun]" + + var/list/msg = list("([timestamp]) [machine_name] in [area_name]
[action] [abs(amount)]x [noun]
") + var/sep = "" + for(var/key in materials) + var/val = round(materials[key]) / MINERAL_MATERIAL_AMOUNT + msg += sep + sep = ", " + msg += "[amount < 0 ? "-" : "+"][val] [copytext(key, 2)]" + formatted = msg.Join() diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index dcc9034e2d..aa3ab240d8 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -71,42 +71,51 @@ desc = "A machine that automatically stacks acquired materials. Controlled by a nearby console." density = TRUE circuit = /obj/item/circuitboard/machine/stacking_machine + input_dir = EAST + output_dir = WEST var/obj/machinery/mineral/stacking_unit_console/CONSOLE var/stk_types = list() var/stk_amt = list() var/stack_list[0] //Key: Type. Value: Instance of type. - var/stack_amt = 50; //ammount to stack before releassing - input_dir = EAST - output_dir = WEST + var/stack_amt = 50 //amount to stack before releassing + var/datum/component/remote_materials/materials + var/force_connect = FALSE -/obj/machinery/mineral/stacking_machine/Initialize() +/obj/machinery/mineral/stacking_machine/Initialize(mapload) . = ..() proximity_monitor = new(src, 1) + materials = AddComponent(/datum/component/remote_materials, "stacking", mapload, FALSE, mapload && force_connect) /obj/machinery/mineral/stacking_machine/HasProximity(atom/movable/AM) if(istype(AM, /obj/item/stack/sheet) && AM.loc == get_step(src, input_dir)) process_sheet(AM) -/obj/machinery/mineral/stacking_machine/multitool_act(mob/living/user, obj/item/I) - if(istype(I, /obj/item/multitool)) - var/obj/item/multitool/M = I - if(!istype(M.buffer, /obj/machinery/mineral/stacking_unit_console)) - to_chat(user, "The [I] has no linkage data in its buffer.") - return FALSE - else +/obj/machinery/mineral/stacking_machine/multitool_act(mob/living/user, obj/item/multitool/M) + if(istype(M)) + if(istype(M.buffer, /obj/machinery/mineral/stacking_unit_console)) CONSOLE = M.buffer CONSOLE.machine = src - to_chat(user, "You link [src] to the console in [I]'s buffer.") + to_chat(user, "You link [src] to the console in [M]'s buffer.") return TRUE /obj/machinery/mineral/stacking_machine/proc/process_sheet(obj/item/stack/sheet/inp) - if(!(inp.type in stack_list)) //It's the first of this sheet added - var/obj/item/stack/sheet/s = new inp.type(src, 0) - stack_list[inp.type] = s - var/obj/item/stack/sheet/storage = stack_list[inp.type] + var/key = inp.merge_type + var/obj/item/stack/sheet/storage = stack_list[key] + if(!storage) //It's the first of this sheet added + stack_list[key] = storage = new inp.type(src, 0) storage.amount += inp.amount //Stack the sheets - while(storage.amount > stack_amt) //Get rid of excessive stackage + qdel(inp) + + if(materials.silo && !materials.on_hold()) //Dump the sheets to the silo + var/matlist = storage.materials & materials.mat_container.materials + if (length(matlist)) + var/inserted = materials.mat_container.insert_stack(storage) + materials.silo_log(src, "collected", inserted, "sheets", matlist) + if (QDELETED(storage)) + stack_list -= key + return + + while(storage.amount >= stack_amt) //Get rid of excessive stackage var/obj/item/stack/sheet/out = new inp.type(null, stack_amt) unload_mineral(out) storage.amount -= stack_amt - qdel(inp) //Let the old sheet garbage collect diff --git a/code/modules/mining/machine_vending.dm b/code/modules/mining/machine_vending.dm index a9230195ac..47e248f681 100644 --- a/code/modules/mining/machine_vending.dm +++ b/code/modules/mining/machine_vending.dm @@ -234,7 +234,7 @@ /obj/item/card/mining_point_card name = "mining points card" desc = "A small card preloaded with mining points. Swipe your ID card over it to transfer the points, then discard." - icon_state = "data" + icon_state = "data_1" var/points = 500 /obj/item/card/mining_point_card/attackby(obj/item/I, mob/user, params) @@ -256,9 +256,10 @@ /obj/item/card/mining_access_card name = "mining access card" desc = "A small card, that when used on any ID, will add mining access." - icon_state = "data" + icon_state = "data_1" /obj/item/card/mining_access_card/afterattack(atom/movable/AM, mob/user, proximity) + . = ..() if(istype(AM, /obj/item/card/id) && proximity) var/obj/item/card/id/I = AM I.access |= ACCESS_MINING @@ -267,7 +268,6 @@ I.access |= ACCESS_CARGO to_chat(user, "You upgrade [I] with mining access.") qdel(src) - ..() /obj/item/storage/backpack/duffelbag/mining_conscript name = "mining conscription kit" diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm index 3809514f02..15ff372c47 100644 --- a/code/modules/mining/minebot.dm +++ b/code/modules/mining/minebot.dm @@ -270,6 +270,7 @@ icon = 'icons/obj/module.dmi' /obj/item/mine_bot_upgrade/afterattack(mob/living/simple_animal/hostile/mining_drone/M, mob/user, proximity) + . = ..() if(!istype(M) || !proximity) return upgrade_bot(M, user) diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm index 66b6b94d6c..6b03be610d 100644 --- a/code/modules/mining/mint.dm +++ b/code/modules/mining/mint.dm @@ -15,7 +15,7 @@ /obj/machinery/mineral/mint/Initialize() . = ..() - AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_PLASMA, MAT_SILVER, MAT_GOLD, MAT_URANIUM, MAT_DIAMOND, MAT_BANANIUM), MINERAL_MATERIAL_AMOUNT * 50, FALSE, list(/obj/item/stack)) + AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_PLASMA, MAT_SILVER, MAT_GOLD, MAT_URANIUM, MAT_DIAMOND, MAT_BANANIUM), MINERAL_MATERIAL_AMOUNT * 50, FALSE, /obj/item/stack) /obj/machinery/mineral/mint/process() var/turf/T = get_step(src, input_dir) diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 9e4e1bbb53..69361e8685 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -335,7 +335,6 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ /obj/item/coin/proc/manual_suicide(mob/living/user) var/index = sideslist.Find(coinflip) - message_admins("coinflip landed on [coinflip] which is [index] in sideslist") if (index==2)//tails user.visible_message("\the [src] lands on [coinflip]! [user] promptly falls over, dead!") user.adjustOxyLoss(200) diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/camera/camera.dm index 5f99cd8aa2..883f5a034f 100644 --- a/code/modules/mob/camera/camera.dm +++ b/code/modules/mob/camera/camera.dm @@ -28,5 +28,5 @@ /mob/camera/forceMove(atom/destination) loc = destination -/mob/camera/emote(act, m_type=1, message = null) +/mob/camera/emote(act, m_type=1, message = null, intentional = FALSE) return diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm index 30173c0a32..d886a41b0f 100644 --- a/code/modules/mob/dead/dead.dm +++ b/code/modules/mob/dead/dead.dm @@ -4,6 +4,7 @@ INITIALIZE_IMMEDIATE(/mob/dead) /mob/dead sight = SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF + throwforce = 0 /mob/dead/Initialize() if(flags_1 & INITIALIZED_1) @@ -14,12 +15,12 @@ INITIALIZE_IMMEDIATE(/mob/dead) prepare_huds() - if(length(CONFIG_GET(keyed_string_list/cross_server))) + if(length(CONFIG_GET(keyed_list/cross_server))) verbs += /mob/dead/proc/server_hop set_focus(src) return INITIALIZE_HINT_NORMAL -/mob/dead/dust() //ghosts can't be vaporised. +/mob/dead/dust(just_ash, drop_items, force) //ghosts can't be vaporised. return /mob/dead/gib() //ghosts can't be gibbed. @@ -29,6 +30,10 @@ INITIALIZE_IMMEDIATE(/mob/dead) return /mob/dead/forceMove(atom/destination) + var/turf/old_turf = get_turf(src) + var/turf/new_turf = get_turf(destination) + if (old_turf?.z != new_turf?.z) + onTransitZ(old_turf?.z, new_turf?.z) loc = destination /mob/dead/Stat() @@ -36,7 +41,7 @@ INITIALIZE_IMMEDIATE(/mob/dead) if(!statpanel("Status")) return - //stat(null, "Game Mode: [SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]") CIT CHANGE - obfuscates gamemode from player view + //stat(null, "Game Mode: [SSticker.hide_mode ? "Secret" : "[GLOB.master_mode]"]") if(SSticker.HasRoundStarted()) return @@ -59,7 +64,7 @@ INITIALIZE_IMMEDIATE(/mob/dead) set desc= "Jump to the other server" if(notransform) return - var/list/csa = CONFIG_GET(keyed_string_list/cross_server) + var/list/csa = CONFIG_GET(keyed_list/cross_server) var/pick switch(csa.len) if(0) @@ -92,3 +97,28 @@ INITIALIZE_IMMEDIATE(/mob/dead) winset(src, null, "command=.options") //other wise the user never knows if byond is downloading resources C << link("[addr]?server_hop=[key]") + +/mob/dead/proc/update_z(new_z) // 1+ to register, null to unregister + if (registered_z != new_z) + if (registered_z) + SSmobs.dead_players_by_zlevel[registered_z] -= src + if (client) + if (new_z) + SSmobs.dead_players_by_zlevel[new_z] += src + registered_z = new_z + else + registered_z = null + +/mob/dead/Login() + . = ..() + var/turf/T = get_turf(src) + if (isturf(T)) + update_z(T.z) + +/mob/dead/Logout() + update_z(null) + return ..() + +/mob/dead/onTransitZ(old_z,new_z) + ..() + update_z(new_z) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 14bd0ffaab..d5646c2fa4 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -53,7 +53,7 @@ var/isadmin = 0 if(src.client && src.client.holder) isadmin = 1 - var/datum/DBQuery/query_get_new_polls = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[ckey]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[ckey]\")") + var/datum/DBQuery/query_get_new_polls = SSdbcore.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[sanitizeSQL(ckey)]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[sanitizeSQL(ckey)]\")") var/rs = REF(src) if(query_get_new_polls.Execute()) var/newpoll = 0 diff --git a/code/modules/mob/dead/new_player/poll.dm b/code/modules/mob/dead/new_player/poll.dm index 5ac1b4adb5..04a28f6b5a 100644 --- a/code/modules/mob/dead/new_player/poll.dm +++ b/code/modules/mob/dead/new_player/poll.dm @@ -103,23 +103,20 @@ var/output = "
Player poll
" output += "Question: [pollquestion]
" output += "Feedback gathering runs from [pollstarttime] until [pollendtime]

" - if(!vote_text) - output += "" - output += "" - output += "" - output += "" - output += "Please provide feedback below. You can use any letters of the English alphabet, numbers and the symbols: . , ! ? : ; -
" - output += "" - output += "

" - output += "
" - output += "" - output += "" - output += "" - output += "" - output += "" - else - vote_text = replacetext(vote_text, "\n", "
") - output += "[vote_text]" + output += "
" + output += "" + output += "" + output += "" + output += "Please provide feedback below. You can use any letters of the English alphabet, numbers and the symbols: . , ! ? : ; -
" + output += "" + output += "

" + output += "
" + output += "" + output += "" + output += "" + output += "" + output += "" + src << browse(null ,"window=playerpolllist") src << browse(output,"window=playerpoll;size=500x500") if(POLLTYPE_RATING) @@ -348,7 +345,8 @@ src << browse(output,"window=playerpoll;size=500x500") return -/mob/dead/new_player/proc/poll_check_voted(pollid, text = FALSE) +//Returns null on failure, TRUE if already voted, FALSE if not voted yet. +/mob/dead/new_player/proc/poll_check_voted(pollid, text = FALSE, silent = FALSE) var/table = "poll_vote" if (text) table = "poll_textreply" @@ -361,13 +359,17 @@ return if(query_hasvoted.NextRow()) qdel(query_hasvoted) - to_chat(usr, "You've already replied to this poll.") - return + if(!silent) + to_chat(usr, "You've already replied to this poll.") + return TRUE qdel(query_hasvoted) + return FALSE + +//Returns adminrank for use in polls. +/mob/dead/new_player/proc/poll_rank() . = "Player" if(client.holder) . = client.holder.rank.name - return . /mob/dead/new_player/proc/vote_rig_check() @@ -487,7 +489,10 @@ //validate the poll if (!vote_valid_check(pollid, client.holder, POLLTYPE_OPTION)) return 0 - var/adminrank = sanitizeSQL(poll_check_voted(pollid)) + var/voted = poll_check_voted(pollid) + if(isnull(voted) || voted) //Failed or already voted. + return + var/adminrank = sanitizeSQL(poll_rank()) if(!adminrank) return var/datum/DBQuery/query_option_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]')") @@ -513,14 +518,21 @@ if(!replytext) to_chat(usr, "The text you entered was blank. Please correct the text and submit again.") return - var/adminrank = sanitizeSQL(poll_check_voted(pollid, TRUE)) + var/voted = poll_check_voted(pollid, text = TRUE, silent = TRUE) + if(isnull(voted)) + return + var/adminrank = sanitizeSQL(poll_rank()) if(!adminrank) return replytext = sanitizeSQL(replytext) if(!(length(replytext) > 0) || !(length(replytext) <= 8000)) to_chat(usr, "The text you entered was invalid or too long. Please correct the text and submit again.") return - var/datum/DBQuery/query_text_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_textreply")] (datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (Now(), [pollid], '[ckey]', INET_ATON('[client.address]'), '[replytext]', '[adminrank]')") + var/datum/DBQuery/query_text_vote + if(!voted) + query_text_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_textreply")] (datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (Now(), [pollid], '[ckey]', INET_ATON('[client.address]'), '[replytext]', '[adminrank]')") + else + query_text_vote = SSdbcore.NewQuery("UPDATE [format_table_name("poll_textreply")] SET datetime = Now(), ip = INET_ATON('[client.address]'), replytext = '[replytext]' WHERE pollid = '[pollid]' AND ckey = '[ckey]'") if(!query_text_vote.warn_execute()) qdel(query_text_vote) return diff --git a/code/modules/mob/dead/new_player/sprite_accessories.dm b/code/modules/mob/dead/new_player/sprite_accessories.dm index 45ff7e40c8..ca9119eb43 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories.dm @@ -64,59 +64,197 @@ ////////////////////// // Hair Definitions // ////////////////////// - -#define VHAIR(_name, new_state) /datum/sprite_accessory/hair/##new_state/icon_state=#new_state;/datum/sprite_accessory/hair/##new_state/name = "Virgo - " + #_name - /datum/sprite_accessory/hair icon = 'icons/mob/human_face.dmi' // default icon for all hairs -/datum/sprite_accessory/hair/short - name = "Short Hair" // try to capitalize the names please~ // try to spell - icon_state = "hair_a" // you do not need to define _s or _l sub-states, game automatically does this for you + // please make sure they're sorted alphabetically and, where needed, categorized + // try to capitalize the names please~ + // try to spell + // you do not need to define _s or _l sub-states, game automatically does this for you -/datum/sprite_accessory/hair/shorthair2 - name = "Short Hair 2" - icon_state = "hair_shorthair2" +/datum/sprite_accessory/hair/afro + name = "Afro" + icon_state = "hair_afro" -/datum/sprite_accessory/hair/shorthair3 - name = "Short Hair 3" - icon_state = "hair_shorthair3" +/datum/sprite_accessory/hair/afro2 + name = "Afro 2" + icon_state = "hair_afro2" + +/datum/sprite_accessory/hair/afro_large + name = "Afro (Large)" + icon_state = "hair_bigafro" + +/datum/sprite_accessory/hair/antenna + name = "Ahoge" + icon_state = "hair_antenna" + +/datum/sprite_accessory/hair/bald + name = "Bald" + icon_state = null + +/datum/sprite_accessory/hair/balding + name = "Balding Hair" + icon_state = "hair_e" + +/datum/sprite_accessory/hair/bedhead + name = "Bedhead" + icon_state = "hair_bedhead" + +/datum/sprite_accessory/hair/bedhead2 + name = "Bedhead 2" + icon_state = "hair_bedheadv2" + +/datum/sprite_accessory/hair/bedhead3 + name = "Bedhead 3" + icon_state = "hair_bedheadv3" + +/datum/sprite_accessory/hair/beehive + name = "Beehive" + icon_state = "hair_beehive" + +/datum/sprite_accessory/hair/beehive2 + name = "Beehive 2" + icon_state = "hair_beehivev2" + +/datum/sprite_accessory/hair/bob + name = "Bob Hair" + icon_state = "hair_bob" + +/datum/sprite_accessory/hair/bob2 + name = "Bob Hair 2" + icon_state = "hair_bob2" + +/datum/sprite_accessory/hair/bob3 + name = "Bob Hair 3" + icon_state = "hair_bobcut" + +/datum/sprite_accessory/hair/bobcurl + name = "Bobcurl" + icon_state = "hair_bobcurl" + +/datum/sprite_accessory/hair/boddicker + name = "Boddicker" + icon_state = "hair_boddicker" + +/datum/sprite_accessory/hair/bowl + name = "Bowl" + icon_state = "hair_bowlcut" + +/datum/sprite_accessory/hair/braid + name = "Braid (Floorlength)" + icon_state = "hair_braid" + +/datum/sprite_accessory/hair/braided + name = "Braided" + icon_state = "hair_braided" + +/datum/sprite_accessory/hair/front_braid + name = "Braided Front" + icon_state = "hair_braidfront" + +/datum/sprite_accessory/hair/not_floorlength_braid + name = "Braid (High)" + icon_state = "hair_braid2" + +/datum/sprite_accessory/hair/lowbraid + name = "Braid (Low)" + icon_state = "hair_hbraid" + +/datum/sprite_accessory/hair/shortbraid + name = "Braid (Short)" + icon_state = "hair_shortbraid" + +/datum/sprite_accessory/hair/braidtail + name = "Braided Tail" + icon_state = "hair_braidtail" + +/datum/sprite_accessory/hair/bun + name = "Bun Head" + icon_state = "hair_bun" + +/datum/sprite_accessory/hair/bun2 + name = "Bun Head 2" + icon_state = "hair_bunhead2" + +/datum/sprite_accessory/hair/largebun + name = "Bun (Large)" + icon_state = "hair_largebun" + +/datum/sprite_accessory/hair/business + name = "Business Hair" + icon_state = "hair_business" + +/datum/sprite_accessory/hair/business2 + name = "Business Hair 2" + icon_state = "hair_business2" + +/datum/sprite_accessory/hair/business3 + name = "Business Hair 3" + icon_state = "hair_business3" + +/datum/sprite_accessory/hair/business4 + name = "Business Hair 4" + icon_state = "hair_business4" + +/datum/sprite_accessory/hair/buzz + name = "Buzzcut" + icon_state = "hair_buzzcut" + +/datum/sprite_accessory/hair/combover + name = "Combover" + icon_state = "hair_combover" + +/datum/sprite_accessory/hair/crew + name = "Crewcut" + icon_state = "hair_crewcut" + +/datum/sprite_accessory/hair/curls + name = "Curls" + icon_state = "hair_curls" /datum/sprite_accessory/hair/cut name = "Cut Hair" icon_state = "hair_c" -/datum/sprite_accessory/hair/long - name = "Shoulder-length Hair" - icon_state = "hair_b" +/datum/sprite_accessory/hair/devillock + name = "Devil Lock" + icon_state = "hair_devilock" -/datum/sprite_accessory/hair/longer - name = "Long Hair" - icon_state = "hair_vlong" +/datum/sprite_accessory/hair/dreadlocks + name = "Dreadlocks" + icon_state = "hair_dreads" -/datum/sprite_accessory/hair/over_eye - name = "Over Eye" - icon_state = "hair_shortovereye" +/datum/sprite_accessory/hair/drillhair + name = "Drill Hair" + icon_state = "hair_drillhair" -/datum/sprite_accessory/hair/long_over_eye - name = "Long Over Eye" - icon_state = "hair_longovereye" +/datum/sprite_accessory/hair/drillhairextended + name = "Drill Hair (Extended)" + icon_state = "hair_drillhairextended" -/datum/sprite_accessory/hair/longest2 - name = "Very Long Over Eye" - icon_state = "hair_longest2" +/datum/sprite_accessory/hair/emo + name = "Emo" + icon_state = "hair_emo" -/datum/sprite_accessory/hair/longest - name = "Very Long Hair" - icon_state = "hair_longest" +/datum/sprite_accessory/hair/feather + name = "Feather" + icon_state = "hair_feather" -/datum/sprite_accessory/hair/longfringe - name = "Long Fringe" - icon_state = "hair_longfringe" +/datum/sprite_accessory/hair/sargeant + name = "Flat Top" + icon_state = "hair_sargeant" -/datum/sprite_accessory/hair/longestalt - name = "Longer Fringe" - icon_state = "hair_vlongfringe" +/datum/sprite_accessory/hair/bigflattop + name = "Flat Top (Big)" + icon_state = "hair_bigflattop" + +/datum/sprite_accessory/hair/fag + name = "Flow Hair" + icon_state = "hair_f" + +/datum/sprite_accessory/hair/gelled + name = "Gelled Back" + icon_state = "hair_gelled" /datum/sprite_accessory/hair/gentle name = "Gentle" @@ -130,6 +268,134 @@ name = "Half-banged Hair 2" icon_state = "hair_halfbang2" +/datum/sprite_accessory/hair/hedgehog + name = "Hedgehog Hair" + icon_state = "hair_hedgehog" + +/datum/sprite_accessory/hair/himecut + name = "Hime Cut" + icon_state = "hair_himecut" + +/datum/sprite_accessory/hair/himecut2 + name = "Hime Cut 2" + icon_state = "hair_himecut2" + +/datum/sprite_accessory/hair/himeup + name = "Hime Updo" + icon_state = "hair_himeup" + +/datum/sprite_accessory/hair/hitop + name = "Hitop" + icon_state = "hair_hitop" + +/datum/sprite_accessory/hair/jensen + name = "Jensen Hair" + icon_state = "hair_jensen" + +/datum/sprite_accessory/hair/keanu + name = "Keanu Hair" + icon_state = "hair_keanu" + +/datum/sprite_accessory/hair/kusangi + name = "Kusanagi Hair" + icon_state = "hair_kusanagi" + +/datum/sprite_accessory/hair/long + name = "Long Hair 1" + icon_state = "hair_long" + +/datum/sprite_accessory/hair/long2 + name = "Long Hair 2" + icon_state = "hair_long2" + +/datum/sprite_accessory/hair/long3 + name = "Long Hair 3" + icon_state = "hair_long3" + +/datum/sprite_accessory/hair/long_over_eye + name = "Long Over Eye" + icon_state = "hair_longovereye" + +/datum/sprite_accessory/hair/longbangs + name = "Long Bangs" + icon_state = "hair_lbangs" + +/datum/sprite_accessory/hair/longemo + name = "Long Emo" + icon_state = "hair_longemo" + +/datum/sprite_accessory/hair/longfringe + name = "Long Fringe" + icon_state = "hair_longfringe" + +/datum/sprite_accessory/hair/sidepartlongalt + name = "Long Side Part" + icon_state = "hair_longsidepart" + +/datum/sprite_accessory/hair/megaeyebrows + name = "Mega Eyebrows" + icon_state = "hair_megaeyebrows" + +/datum/sprite_accessory/hair/messy + name = "Messy" + icon_state = "hair_messy" + +/datum/sprite_accessory/hair/mohawk + name = "Mohawk" + icon_state = "hair_d" + +/datum/sprite_accessory/hair/reversemohawk + name = "Mohawk (Reverse)" + icon_state = "hair_reversemohawk" + +/datum/sprite_accessory/hair/odango + name = "Odango" + icon_state = "hair_odango" + +/datum/sprite_accessory/hair/ombre + name = "Ombre" + icon_state = "hair_ombre" + +/datum/sprite_accessory/hair/oneshoulder + name = "One Shoulder" + icon_state = "hair_oneshoulder" + +/datum/sprite_accessory/hair/over_eye + name = "Over Eye" + icon_state = "hair_shortovereye" + +/datum/sprite_accessory/hair/parted + name = "Parted" + icon_state = "hair_parted" + +/datum/sprite_accessory/hair/partedside + name = "Parted (Side)" + icon_state = "hair_part" + +/datum/sprite_accessory/hair/kagami + name = "Pigtails" + icon_state = "hair_kagami" + +/datum/sprite_accessory/hair/pigtail + name = "Pigtails 2" + icon_state = "hair_pigtails" + +/datum/sprite_accessory/hair/pigtail2 + name = "Pigtails 3" + icon_state = "hair_pigtails2" + +/datum/sprite_accessory/hair/pixie + name = "Pixie Cut" + icon_state = "hair_pixie" + +/datum/sprite_accessory/hair/pompadour + name = "Pompadour" + icon_state = "hair_pompadour" + +/datum/sprite_accessory/hair/bigpompadour + name = "Pompadour (Big)" + icon_state = "hair_bigpompadour" + /datum/sprite_accessory/hair/ponytail1 name = "Ponytail" icon_state = "hair_ponytail" @@ -150,161 +416,61 @@ name = "Ponytail 5" icon_state = "hair_ponytail5" +/datum/sprite_accessory/hair/highponytail + name = "Ponytail (High)" + icon_state = "hair_highponytail" + +/datum/sprite_accessory/hair/longponytail + name = "Ponytail (Long)" + icon_state = "hair_longstraightponytail" + /datum/sprite_accessory/hair/sidetail - name = "Side Pony" + name = "Ponytail (Side)" icon_state = "hair_sidetail" /datum/sprite_accessory/hair/sidetail2 - name = "Side Pony 2" + name = "Ponytail (Side) 2" icon_state = "hair_sidetail2" /datum/sprite_accessory/hair/sidetail3 - name = "Side Pony 3" + name = "Ponytail (Side) 3" icon_state = "hair_sidetail3" /datum/sprite_accessory/hair/sidetail4 - name = "Side Pony 4" + name = "Ponytail (Side) 4" icon_state = "hair_sidetail4" -/datum/sprite_accessory/hair/oneshoulder - name = "One Shoulder" - icon_state = "hair_oneshoulder" - -/datum/sprite_accessory/hair/tressshoulder - name = "Tress Shoulder" - icon_state = "hair_tressshoulder" - -/datum/sprite_accessory/hair/parted - name = "Parted" - icon_state = "hair_parted" - -/datum/sprite_accessory/hair/pompadour - name = "Pompadour" - icon_state = "hair_pompadour" - -/datum/sprite_accessory/hair/bigpompadour - name = "Big Pompadour" - icon_state = "hair_bigpompadour" - /datum/sprite_accessory/hair/quiff name = "Quiff" icon_state = "hair_quiff" -/datum/sprite_accessory/hair/bedhead - name = "Bedhead" - icon_state = "hair_bedhead" +/datum/sprite_accessory/hair/short + name = "Short Hair" + icon_state = "hair_a" -/datum/sprite_accessory/hair/bedhead2 - name = "Bedhead 2" - icon_state = "hair_bedheadv2" +/datum/sprite_accessory/hair/shorthair2 + name = "Short Hair 2" + icon_state = "hair_shorthair2" -/datum/sprite_accessory/hair/bedhead3 - name = "Bedhead 3" - icon_state = "hair_bedheadv3" +/datum/sprite_accessory/hair/shorthair3 + name = "Short Hair 3" + icon_state = "hair_shorthair3" -/datum/sprite_accessory/hair/messy - name = "Messy" - icon_state = "hair_messy" +/datum/sprite_accessory/hair/shoulderlength + name = "Shoulder-length Hair" + icon_state = "hair_b" -/datum/sprite_accessory/hair/beehive - name = "Beehive" - icon_state = "hair_beehive" +/datum/sprite_accessory/hair/sidecut + name = "Sidecut" + icon_state = "hair_sidecut" -/datum/sprite_accessory/hair/beehive2 - name = "Beehive 2" - icon_state = "hair_beehivev2" +/datum/sprite_accessory/hair/skinhead + name = "Skinhead" + icon_state = "hair_skinhead" -/datum/sprite_accessory/hair/bobcurl - name = "Bobcurl" - icon_state = "hair_bobcurl" - -/datum/sprite_accessory/hair/bob - name = "Bob" - icon_state = "hair_bobcut" - -/datum/sprite_accessory/hair/bowl - name = "Bowl" - icon_state = "hair_bowlcut" - -/datum/sprite_accessory/hair/buzz - name = "Buzzcut" - icon_state = "hair_buzzcut" - -/datum/sprite_accessory/hair/crew - name = "Crewcut" - icon_state = "hair_crewcut" - -/datum/sprite_accessory/hair/combover - name = "Combover" - icon_state = "hair_combover" - -/datum/sprite_accessory/hair/devillock - name = "Devil Lock" - icon_state = "hair_devilock" - -/datum/sprite_accessory/hair/drillhairextended - name = "Extended Drill Hair" - icon_state = "hair_drillhairextended" - -/datum/sprite_accessory/hair/dreadlocks - name = "Dreadlocks" - icon_state = "hair_dreads" - -/datum/sprite_accessory/hair/curls - name = "Curls" - icon_state = "hair_curls" - -/datum/sprite_accessory/hair/afro - name = "Afro" - icon_state = "hair_afro" - -/datum/sprite_accessory/hair/afro2 - name = "Afro 2" - icon_state = "hair_afro2" - -/datum/sprite_accessory/hair/afro_large - name = "Big Afro" - icon_state = "hair_bigafro" - -/datum/sprite_accessory/hair/sargeant - name = "Flat Top" - icon_state = "hair_sargeant" - -/datum/sprite_accessory/hair/emo - name = "Emo" - icon_state = "hair_emo" - -/datum/sprite_accessory/hair/longemo - name = "Long Emo" - icon_state = "hair_longemo" - -/datum/sprite_accessory/hair/fag - name = "Flow Hair" - icon_state = "hair_f" - -/datum/sprite_accessory/hair/feather - name = "Feather" - icon_state = "hair_feather" - -/datum/sprite_accessory/hair/hitop - name = "Hitop" - icon_state = "hair_hitop" - -/datum/sprite_accessory/hair/mohawk - name = "Mohawk" - icon_state = "hair_d" - -/datum/sprite_accessory/hair/reversemohawk - name = "Reverse Mohawk" - icon_state = "hair_reversemohawk" - -/datum/sprite_accessory/hair/jensen - name = "Jensen Hair" - icon_state = "hair_jensen" - -/datum/sprite_accessory/hair/gelled - name = "Gelled Back" - icon_state = "hair_gelled" +/datum/sprite_accessory/hair/protagonist + name = "Slightly Long Hair" + icon_state = "hair_protagonist" /datum/sprite_accessory/hair/spiky name = "Spiky" @@ -318,122 +484,6 @@ name = "Spiky 3" icon_state = "hair_spiky2" -/datum/sprite_accessory/hair/protagonist - name = "Slightly Long" - icon_state = "hair_protagonist" - -/datum/sprite_accessory/hair/kusangi - name = "Kusanagi Hair" - icon_state = "hair_kusanagi" - -/datum/sprite_accessory/hair/kagami - name = "Pigtails" - icon_state = "hair_kagami" - -/datum/sprite_accessory/hair/pigtail - name = "Pigtails 2" - icon_state = "hair_pigtails" - -/datum/sprite_accessory/hair/pigtail - name = "Pigtails 3" - icon_state = "hair_pigtails2" - -/datum/sprite_accessory/hair/himecut - name = "Hime Cut" - icon_state = "hair_himecut" - -/datum/sprite_accessory/hair/himecut2 - name = "Hime Cut 2" - icon_state = "hair_himecut2" - -/datum/sprite_accessory/hair/himeup - name = "Hime Updo" - icon_state = "hair_himeup" - -/datum/sprite_accessory/hair/antenna - name = "Ahoge" - icon_state = "hair_antenna" - -/datum/sprite_accessory/hair/front_braid - name = "Braided front" - icon_state = "hair_braidfront" - -/datum/sprite_accessory/hair/lowbraid - name = "Low Braid" - icon_state = "hair_hbraid" - -/datum/sprite_accessory/hair/not_floorlength_braid - name = "High Braid" - icon_state = "hair_braid2" - -/datum/sprite_accessory/hair/shortbraid - name = "Short Braid" - icon_state = "hair_shortbraid" - -/datum/sprite_accessory/hair/braid - name = "Floorlength Braid" - icon_state = "hair_braid" - -/datum/sprite_accessory/hair/odango - name = "Odango" - icon_state = "hair_odango" - -/datum/sprite_accessory/hair/ombre - name = "Ombre" - icon_state = "hair_ombre" - -/datum/sprite_accessory/hair/updo - name = "Updo" - icon_state = "hair_updo" - -/datum/sprite_accessory/hair/skinhead - name = "Skinhead" - icon_state = "hair_skinhead" - -/datum/sprite_accessory/hair/longbangs - name = "Long Bangs" - icon_state = "hair_lbangs" - -/datum/sprite_accessory/hair/balding - name = "Balding Hair" - icon_state = "hair_e" - -/datum/sprite_accessory/hair/bald - name = "Bald" - icon_state = null - -/datum/sprite_accessory/hair/parted - name = "Side Part" - icon_state = "hair_part" - -/datum/sprite_accessory/hair/braided - name = "Braided" - icon_state = "hair_braided" - -/datum/sprite_accessory/hair/bun - name = "Bun Head" - icon_state = "hair_bun" - -/datum/sprite_accessory/hair/bun2 - name = "Bun Head 2" - icon_state = "hair_bunhead2" - -/datum/sprite_accessory/hair/braidtail - name = "Braided Tail" - icon_state = "hair_braidtail" - -/datum/sprite_accessory/hair/bigflattop - name = "Big Flat Top" - icon_state = "hair_bigflattop" - -/datum/sprite_accessory/hair/drillhair - name = "Drill Hair" - icon_state = "hair_drillhair" - -/datum/sprite_accessory/hair/keanu - name = "Keanu Hair" - icon_state = "hair_keanu" - /datum/sprite_accessory/hair/swept name = "Swept Back Hair" icon_state = "hair_swept" @@ -442,369 +492,121 @@ name = "Swept Back Hair 2" icon_state = "hair_swept2" -/datum/sprite_accessory/hair/business - name = "Business Hair" - icon_state = "hair_business" +/datum/sprite_accessory/hair/tressshoulder + name = "Tress Shoulder" + icon_state = "hair_tressshoulder" -/datum/sprite_accessory/hair/business2 - name = "Business Hair 2" - icon_state = "hair_business2" +/datum/sprite_accessory/hair/updo + name = "Updo" + icon_state = "hair_updo" -/datum/sprite_accessory/hair/business3 - name = "Business Hair 3" - icon_state = "hair_business3" +/datum/sprite_accessory/hair/longer + name = "Very Long Hair" + icon_state = "hair_vlong" -/datum/sprite_accessory/hair/business4 - name = "Business Hair 4" - icon_state = "hair_business4" +/datum/sprite_accessory/hair/longest + name = "Very Long Hair 2" + icon_state = "hair_longest" -/datum/sprite_accessory/hair/hedgehog - name = "Hedgehog Hair" - icon_state = "hair_hedgehog" +/datum/sprite_accessory/hair/longest2 + name = "Very Long Over Eye" + icon_state = "hair_longest2" -/datum/sprite_accessory/hair/bob - name = "Bob Hair" - icon_state = "hair_bob" - -/datum/sprite_accessory/hair/bob2 - name = "Bob Hair 2" - icon_state = "hair_bob2" - -/datum/sprite_accessory/hair/boddicker - name = "Boddicker" - icon_state = "hair_boddicker" - -/datum/sprite_accessory/hair/long - name = "Long Hair 1" - icon_state = "hair_long" - -/datum/sprite_accessory/hair/long2 - name = "Long Hair 2" - icon_state = "hair_long2" - -/datum/sprite_accessory/hair/long3 - name = "Long Hair 3" - icon_state = "hair_long3" - -/datum/sprite_accessory/hair/pixie - name = "Pixie Cut" - icon_state = "hair_pixie" - -/datum/sprite_accessory/hair/megaeyebrows - name = "Mega Eyebrows" - icon_state = "hair_megaeyebrows" - -/datum/sprite_accessory/hair/highponytail - name = "High Ponytail" - icon_state = "hair_highponytail" - -/datum/sprite_accessory/hair/longponytail - name = "Long Ponytail" - icon_state = "hair_longstraightponytail" - -/datum/sprite_accessory/hair/sidepartlongalt - name = "Long Side Part" - icon_state = "hair_longsidepart" - -/datum/sprite_accessory/hair/sidecut - name = "Sidecut" - icon_state = "hair_sidecut" - -/datum/sprite_accessory/hair/largebun - name = "Large Bun" - icon_state = "hair_largebun" - - -//VIRGO PORTED HAIRS -VHAIR(Short Hair Rosa, hair_rosa_s) -VHAIR(Short Hair 80s, hair_80s_s) -VHAIR(Long Bedhead, hair_long_bedhead_s) -VHAIR(Dave, hair_dave_s) -VHAIR(Country, hair_country_s) -VHAIR(Shy, hair_shy_s) -VHAIR(Unshaven Mohawk, hair_unshaven_mohawk_s) -VHAIR(Manbun, hair_manbun_s) -VHAIR(Longer Bedhead, hair_longer_bedhead_s) -VHAIR(Ponytail, hair_ponytail_s) -VHAIR(Ziegler, hair_ziegler_s) -VHAIR(Emo Fringe, hair_emofringe_s) -VHAIR(Very Short Over Eye Alt, hair_veryshortovereyealternate_s) -VHAIR(Shorthime, hair_shorthime_s) -VHAIR(High Tight, hair_hightight_s) -VHAIR(Thinning Front, hair_thinningfront_s) -VHAIR(Big Afro, hair_bigafro_s) -VHAIR(Afro, hair_afro_s) -VHAIR(High Braid, hair_hbraid_s) -VHAIR(Braid, hair_braid_s) -VHAIR(Sargeant, hair_sargeant_s) -VHAIR(Gelled, hair_gelled_s) -VHAIR(Kagami, hair_kagami_s) -VHAIR(ShortTail, hair_stail_s) -VHAIR(Gentle, hair_gentle_s) -VHAIR(Grande, hair_grande_s) -VHAIR(Bobcurl, hair_bobcurl_s) -VHAIR(Pompadeur, hair_pompadour_s) -VHAIR(Plait, hair_plait_s) -VHAIR(Long, hair_long_s) -VHAIR(Rattail, hair_rattail_s) -VHAIR(Tajspiky, hair_tajspiky_s) -VHAIR(Messy, hair_messy_s) -VHAIR(Bangs, hair_bangs_s) -VHAIR(TBraid, hair_tbraid_s) -VHAIR(Toriyama2, hair_toriyama2_s) -VHAIR(CIA, hair_cia_s) -VHAIR(Mulder, hair_mulder_s) -VHAIR(Scully, hair_scully_s) -VHAIR(Nitori, hair_nitori_s) -VHAIR(Joestar, hair_joestar_s) -VHAIR(Ponytail4, hair_ponytail4_s) -VHAIR(Ponytail5, hair_ponytail5_s) -VHAIR(Beehive2, hair_beehive2_s) -VHAIR(Short Braid, hair_shortbraid_s) -VHAIR(Reverse Mohawk, hair_reversemohawk_s) -VHAIR(SHort Bangs, hair_shortbangs_s) -VHAIR(Half Shaved, hair_halfshaved_s) -VHAIR(Longer Alt 2, hair_longeralt2_s) -VHAIR(Bun, hair_bun_s) -VHAIR(Curly, hair_curly_s) -VHAIR(Victory, hair_victory_s) -VHAIR(Ponytail6, hair_ponytail6_s) -VHAIR(Undercut3, hair_undercut3_s) -VHAIR(Bobcut Alt, hair_bobcultalt_s) -VHAIR(Fingerwave, hair_fingerwave_s) -VHAIR(Oxton, hair_oxton_s) -VHAIR(Poofy2, hair_poofy2_s) -VHAIR(Fringe Tail, hair_fringetail_s) -VHAIR(Bun3, hair_bun3_s) -VHAIR(Wisp, hair_wisp_s) -VHAIR(Undercut2, hair_undercut2_s) -VHAIR(TBob, hair_tbob_s) -VHAIR(Spiky Ponytail, hair_spikyponytail_s) -VHAIR(Rowbun, hair_rowbun_s) -VHAIR(Rowdualtail, hair_rowdualtail_s) -VHAIR(Rowbraid, hair_rowbraid_s) -VHAIR(Shaved Mohawk, hair_shavedmohawk_s) -VHAIR(Topknot, hair_topknot_s) -VHAIR(Ronin, hair_ronin_s) -VHAIR(Bowlcut2, hair_bowlcut2_s) -VHAIR(Thinning Rear, hair_thinningrear_s) -VHAIR(Thinning, hair_thinning_s) -VHAIR(Jade, hair_jade_s) -VHAIR(Bedhead, hair_bedhead_s) -VHAIR(Dreadlocks, hair_dreads_s) -VHAIR(Very Long, hair_vlong_s) -VHAIR(Jensen, hair_jensen_s) -VHAIR(Halfbang, hair_halfbang_s) -VHAIR(Kusangi, hair_kusangi_s) -VHAIR(Ponytail, hair_ponytail_s) -VHAIR(Ponytail3, hair_ponytail3_s) -VHAIR(Halfbang Alt, hair_halfbang_alt_s) -VHAIR(Bedhead V2, hair_bedheadv2_s) -VHAIR(Long Fringe, hair_longfringe_s) -VHAIR(Flair, hair_flair_s) -VHAIR(Bedhead V3, hair_bedheadv3_s) -VHAIR(Himecut, hair_himecut_s) -VHAIR(Curls, hair_curls_s) -VHAIR(Very Long Fringe, hair_vlongfringe_s) -VHAIR(Longest, hair_longest_s) -VHAIR(Father, hair_father_s) -VHAIR(Emo Long, hair_emolong_s) -VHAIR(Short Hair 3, hair_shorthair3_s) -VHAIR(Double Bun, hair_doublebun_s) -VHAIR(Sleeze, hair_sleeze_s) -VHAIR(Twintail, hair_twintail_s) -VHAIR(Emo 2, hair_emo2_s) -VHAIR(Low Fade, hair_lowfade_s) -VHAIR(Med Fade, hair_medfade_s) -VHAIR(High Fade, hair_highfade_s) -VHAIR(Bald Fade, hair_baldfade_s) -VHAIR(No Fade, hair_nofade_s) -VHAIR(Trim Flat, hair_trimflat_s) -VHAIR(Shaved, hair_shaved_s) -VHAIR(Trimmed, hair_trimmed_s) -VHAIR(Tight Bun, hair_tightbun_s) -VHAIR(Short Hair 4, hair_d_s) -VHAIR(Short Hair 5, hair_e_s) -VHAIR(Short Hair 6, hair_f_s) -VHAIR(Skinhead, hair_skinhead_s) -VHAIR(Afro2, hair_afro2_s) -VHAIR(Bobcut, hair_bobcut_s) -VHAIR(Emo, hair_emo_s) -VHAIR(Long Over Eye, hair_longovereye_s) -VHAIR(Feather, hair_feather_s) -VHAIR(Hitop, hair_hitop_s) -VHAIR(Short Over Eye, hair_shortoverye_s) -VHAIR(Straight, hair_straight_s) -VHAIR(Buzzcut, hair_buzzcut_s) -VHAIR(Combover, hair_combover_s) -VHAIR(Crewcut, hair_crewcut_s) -VHAIR(Devillock, hair_devilock_s) -VHAIR(Clean, hair_clean_s) -VHAIR(Shaggy, hair_shaggy_s) -VHAIR(Updo, hair_updo_s) -VHAIR(Mohawk, hair_mohawk_s) -VHAIR(Odango, hair_odango_s) -VHAIR(Ombre, hair_ombre_s) -VHAIR(Parted, hair_parted_s) -VHAIR(Quiff, hair_quiff_s) -VHAIR(Volaju, hair_volaju_s) -VHAIR(Bun2, hair_bun2_s) -VHAIR(Rows1, hair_rows1_s) -VHAIR(Rows2, hair_rows2_s) -VHAIR(Dandy Pompadour, hair_dandypompadour_s) -VHAIR(Poofy, hair_poofy_s) -VHAIR(Toriyama, hair_toriyama_s) -VHAIR(Drillruru, hair_drillruru_s) -VHAIR(Bowlcut, hair_bowlcut_s) -VHAIR(Coffee House, hair_coffeehouse_s) -VHAIR(Family Man, hair_thefamilyman_s) -VHAIR(Shaved Part, hair_shavedpart_s) -VHAIR(Modern, hair_modern_s) -VHAIR(One Shoulder, hair_oneshoulder_s) -VHAIR(Very Short Over Eye, hair_veryshortovereye_s) -VHAIR(Unkept, hair_unkept_s) -VHAIR(Wife, hair_wife_s) -VHAIR(Nia, hair_nia_s) -VHAIR(Undercut, hair_undercut_s) -VHAIR(Bobcut Alt, hair_bobcutalt_s) -VHAIR(Short Hair 4 alt, hair_shorthair4_s) -VHAIR(Tressshoulder, hair_tressshoulder_s) - -//END - -#undef VHAIR +/datum/sprite_accessory/hair/longestalt + name = "Very Long with Fringe" + icon_state = "hair_vlongfringe" ///////////////////////////// // Facial Hair Definitions // ///////////////////////////// -#define VFACE(_name, new_state) /datum/sprite_accessory/facial_hair/##new_state/icon_state=#new_state;;/datum/sprite_accessory/facial_hair/##new_state/name="Virgo" + #_name - /datum/sprite_accessory/facial_hair icon = 'icons/mob/human_face.dmi' gender = MALE // barf (unless you're a dorf, dorfs dig chix w/ beards :P) +// please make sure they're sorted alphabetically and categorized + +/datum/sprite_accessory/facial_hair/abe + name = "Beard (Abraham Lincoln)" + icon_state = "facial_abe" + +/datum/sprite_accessory/facial_hair/brokenman + name = "Beard (Broken Man)" + icon_state = "facial_brokenman" + +/datum/sprite_accessory/facial_hair/chinstrap + name = "Beard (Chinstrap)" + icon_state = "facial_chin" + +/datum/sprite_accessory/facial_hair/dwarf + name = "Beard (Dwarf)" + icon_state = "facial_dwarf" + +/datum/sprite_accessory/facial_hair/fiveoclock + name = "Beard (Five o Clock Shadow)" + icon_state = "facial_fiveoclock" + +/datum/sprite_accessory/facial_hair/fullbeard + name = "Beard (Full)" + icon_state = "facial_fullbeard" + +/datum/sprite_accessory/facial_hair/gt + name = "Beard (Goatee)" + icon_state = "facial_gt" + +/datum/sprite_accessory/facial_hair/hip + name = "Beard (Hipster)" + icon_state = "facial_hip" + +/datum/sprite_accessory/facial_hair/jensen + name = "Beard (Jensen)" + icon_state = "facial_jensen" + +/datum/sprite_accessory/facial_hair/neckbeard + name = "Beard (Neckbeard)" + icon_state = "facial_neckbeard" + +/datum/sprite_accessory/facial_hair/vlongbeard + name = "Beard (Very Long)" + icon_state = "facial_wise" + +/datum/sprite_accessory/facial_hair/longbeard + name = "Beard (Long)" + icon_state = "facial_longbeard" + +/datum/sprite_accessory/facial_hair/fu + name = "Moustache (Fu Manchu)" + icon_state = "facial_fumanchu" + +/datum/sprite_accessory/facial_hair/hogan + name = "Moustache (Hulk Hogan)" + icon_state = "facial_hogan" //-Neek + +/datum/sprite_accessory/facial_hair/selleck + name = "Moustache (Selleck)" + icon_state = "facial_selleck" + +/datum/sprite_accessory/facial_hair/chaplin + name = "Moustache (Square)" + icon_state = "facial_chaplin" + +/datum/sprite_accessory/facial_hair/vandyke + name = "Moustache (Van Dyke)" + icon_state = "facial_vandyke" + +/datum/sprite_accessory/facial_hair/watson + name = "Moustache (Watson)" + icon_state = "facial_watson" + /datum/sprite_accessory/facial_hair/shaved name = "Shaved" icon_state = null gender = NEUTER -/datum/sprite_accessory/facial_hair/watson - name = "Watson Mustache" - icon_state = "facial_watson" - -/datum/sprite_accessory/facial_hair/hogan - name = "Hulk Hogan Mustache" - icon_state = "facial_hogan" //-Neek - -/datum/sprite_accessory/facial_hair/vandyke - name = "Van Dyke Mustache" - icon_state = "facial_vandyke" - -/datum/sprite_accessory/facial_hair/chaplin - name = "Square Mustache" - icon_state = "facial_chaplin" - -/datum/sprite_accessory/facial_hair/selleck - name = "Selleck Mustache" - icon_state = "facial_selleck" - -/datum/sprite_accessory/facial_hair/neckbeard - name = "Neckbeard" - icon_state = "facial_neckbeard" - -/datum/sprite_accessory/facial_hair/fullbeard - name = "Full Beard" - icon_state = "facial_fullbeard" - -/datum/sprite_accessory/facial_hair/longbeard - name = "Long Beard" - icon_state = "facial_longbeard" - -/datum/sprite_accessory/facial_hair/vlongbeard - name = "Very Long Beard" - icon_state = "facial_wise" - /datum/sprite_accessory/facial_hair/elvis - name = "Elvis Sideburns" + name = "Sideburns (Elvis)" icon_state = "facial_elvis" -/datum/sprite_accessory/facial_hair/abe - name = "Abraham Lincoln Beard" - icon_state = "facial_abe" - -/datum/sprite_accessory/facial_hair/chinstrap - name = "Chinstrap" - icon_state = "facial_chin" - -/datum/sprite_accessory/facial_hair/hip - name = "Hipster Beard" - icon_state = "facial_hip" - -/datum/sprite_accessory/facial_hair/gt - name = "Goatee" - icon_state = "facial_gt" - -/datum/sprite_accessory/facial_hair/jensen - name = "Jensen Beard" - icon_state = "facial_jensen" - -/datum/sprite_accessory/facial_hair/dwarf - name = "Dwarf Beard" - icon_state = "facial_dwarf" - -/datum/sprite_accessory/facial_hair/fiveoclock - name = "Five o Clock Shadow" - icon_state = "facial_fiveoclock" - -/datum/sprite_accessory/facial_hair/fu - name = "Fu Manchu" - icon_state = "facial_fumanchu" - -/datum/sprite_accessory/facial_hair/brokenman - name = "Broken Man" - icon_state = "facial_brokenman" - -VFACE(Watson, facial_watson_s) -VFACE(Chaplin, facial_chaplin_s) -VFACE(Fullbeard, facial_fullbeard_s) -VFACE(Vandyke, facial_vandyke_s) -VFACE(Elvis, facial_elvis_s) -VFACE(Abe, facial_abe_s) -VFACE(Chin, facial_chin_s) -VFACE(GT, facial_gt_s) -VFACE(Hip, facial_hip_s) -VFACE(Hogan, facial_hogan_s) -VFACE(Selleck, facial_selleck_s) -VFACE(Neckbeard, facial_neckbeard_s) -VFACE(Longbeard, facial_longbeard_s) -VFACE(Dwarf, facial_dwarf_s) -VFACE(Sideburn, facial_sideburn_s) -VFACE(Mutton, facial_mutton_s) -VFACE(Moustache, facial_moustache_s) -VFACE(Pencilstache, facial_pencilstache_s) -VFACE(Goatee, facial_goatee_s) -VFACE(Smallstache, facial_smallstache_s) -VFACE(Volaju, facial_volaju_s) -VFACE(3 O\'clock, facial_3oclock_s) -VFACE(5 O\'clock, facial_5oclock_s) -VFACE(7 O\'clock, facial_7oclock_s) -VFACE(5 O\'clock Moustache, facial_5oclockmoustache_s) -VFACE(7 O\'clock, facial_7oclockmoustache_s) -VFACE(Walrus, facial_walrus_s) -VFACE(Muttonmus, facial_muttonmus_s) -VFACE(Wise, facial_wise_s) -VFACE(Martial Artist, facial_martialartist_s) -VFACE(Dorsalfnil, facial_dorsalfnil_s) -VFACE(Hornadorns, facial_hornadorns_s) -VFACE(Spike, facial_spike_s) -VFACE(Chinhorns, facial_chinhorns_s) -VFACE(Cropped Fullbeard, facial_croppedfullbeard_s) -VFACE(Chinless Beard, facial_chinlessbeard_s) -VFACE(Moonshiner, facial_moonshiner_s) -VFACE(Tribearder, facial_tribearder_s) - -#undef VFACE /////////////////////////// // Underwear Definitions // @@ -817,110 +619,80 @@ VFACE(Tribearder, facial_tribearder_s) icon_state = null gender = NEUTER -/datum/sprite_accessory/underwear/male_white - name = "Mens White" - icon_state = "male_white" - gender = MALE - -/datum/sprite_accessory/underwear/male_grey - name = "Mens Grey" - icon_state = "male_grey" - gender = MALE - -/datum/sprite_accessory/underwear/male_green - name = "Mens Green" - icon_state = "male_green" - gender = MALE - -/datum/sprite_accessory/underwear/male_blue - name = "Mens Blue" - icon_state = "male_blue" - gender = MALE - -/datum/sprite_accessory/underwear/male_black - name = "Mens Black" - icon_state = "male_black" - gender = MALE - /datum/sprite_accessory/underwear/male_mankini name = "Mankini" icon_state = "male_mankini" gender = MALE -/datum/sprite_accessory/underwear/male_hearts - name = "Mens Hearts Boxer" - icon_state = "male_hearts" +/datum/sprite_accessory/underwear/male_black + name = "Men's Black" + icon_state = "male_black" gender = MALE /datum/sprite_accessory/underwear/male_blackalt - name = "Mens Black Boxer" + name = "Men's Black Boxer" icon_state = "male_blackalt" gender = MALE +/datum/sprite_accessory/underwear/male_blue + name = "Men's Blue" + icon_state = "male_blue" + gender = MALE + +/datum/sprite_accessory/underwear/male_green + name = "Men's Green" + icon_state = "male_green" + gender = MALE + +/datum/sprite_accessory/underwear/male_grey + name = "Men's Grey" + icon_state = "male_grey" + gender = MALE + /datum/sprite_accessory/underwear/male_greyalt - name = "Mens Grey Boxer" + name = "Men's Grey Boxer" icon_state = "male_greyalt" gender = MALE -/datum/sprite_accessory/underwear/male_stripe - name = "Mens Striped Boxer" - icon_state = "male_stripe" - gender = MALE - -/datum/sprite_accessory/underwear/male_commie - name = "Mens Striped Commie Boxer" - icon_state = "male_commie" - gender = MALE - -/datum/sprite_accessory/underwear/male_uk - name = "Mens Striped UK Boxer" - icon_state = "male_uk" - gender = MALE - -/datum/sprite_accessory/underwear/male_usastripe - name = "Mens Striped Freedom Boxer" - icon_state = "male_assblastusa" +/datum/sprite_accessory/underwear/male_hearts + name = "Men's Hearts Boxer" + icon_state = "male_hearts" gender = MALE /datum/sprite_accessory/underwear/male_kinky - name = "Mens Kinky" + name = "Men's Kinky" icon_state = "male_kinky" gender = MALE /datum/sprite_accessory/underwear/male_red - name = "Mens Red" + name = "Men's Red" icon_state = "male_red" gender = MALE -/datum/sprite_accessory/underwear/female_red - name = "Ladies Red" - icon_state = "female_red" - gender = FEMALE +/datum/sprite_accessory/underwear/male_stripe + name = "Men's Striped Boxer" + icon_state = "male_stripe" + gender = MALE -/datum/sprite_accessory/underwear/female_white - name = "Ladies White" - icon_state = "female_white" - gender = FEMALE +/datum/sprite_accessory/underwear/male_commie + name = "Men's Striped Commie Boxer" + icon_state = "male_commie" + gender = MALE -/datum/sprite_accessory/underwear/female_yellow - name = "Ladies Yellow" - icon_state = "female_yellow" - gender = FEMALE +/datum/sprite_accessory/underwear/male_usastripe + name = "Men's Striped Freedom Boxer" + icon_state = "male_assblastusa" + gender = MALE -/datum/sprite_accessory/underwear/female_blue - name = "Ladies Blue" - icon_state = "female_blue" - gender = FEMALE +/datum/sprite_accessory/underwear/male_uk + name = "Men's Striped UK Boxer" + icon_state = "male_uk" + gender = MALE -/datum/sprite_accessory/underwear/female_black - name = "Ladies Black" - icon_state = "female_black" - gender = FEMALE - -/datum/sprite_accessory/underwear/female_thong - name = "Ladies Thong" - icon_state = "female_thong" - gender = FEMALE +/datum/sprite_accessory/underwear/male_white + name = "Men's White" + icon_state = "male_white" + gender = MALE /datum/sprite_accessory/underwear/female_babydoll name = "Babydoll" @@ -928,89 +700,119 @@ VFACE(Tribearder, facial_tribearder_s) gender = FEMALE /datum/sprite_accessory/underwear/female_babyblue - name = "Ladies Baby-Blue" + name = "Ladies' Baby-Blue" icon_state = "female_babyblue" gender = FEMALE -/datum/sprite_accessory/underwear/female_green - name = "Ladies Green" - icon_state = "female_green" - gender = FEMALE - -/datum/sprite_accessory/underwear/female_pink - name = "Ladies Pink" - icon_state = "female_pink" - gender = FEMALE - -/datum/sprite_accessory/underwear/female_kinky - name = "Ladies Kinky" - icon_state = "female_kinky" - gender = FEMALE - -/datum/sprite_accessory/underwear/female_whitealt - name = "Ladies White Sport" - icon_state = "female_whitealt" - gender = FEMALE - -/datum/sprite_accessory/underwear/female_blackalt - name = "Ladies Black Sport" - icon_state = "female_blackalt" - gender = FEMALE - -/datum/sprite_accessory/underwear/female_white_neko - name = "Ladies White Neko" - icon_state = "female_neko_white" +/datum/sprite_accessory/underwear/female_black + name = "Ladies' Black" + icon_state = "female_black" gender = FEMALE /datum/sprite_accessory/underwear/female_black_neko - name = "Ladies Black Neko" + name = "Ladies' Black Neko" icon_state = "female_neko_black" gender = FEMALE -/datum/sprite_accessory/underwear/female_usastripe - name = "Ladies Freedom" - icon_state = "female_assblastusa" +/datum/sprite_accessory/underwear/female_blackalt + name = "Ladies' Black Sport" + icon_state = "female_blackalt" gender = FEMALE -/datum/sprite_accessory/underwear/female_uk - name = "Ladies UK" - icon_state = "female_uk" +/datum/sprite_accessory/underwear/female_blue + name = "Ladies' Blue" + icon_state = "female_blue" gender = FEMALE /datum/sprite_accessory/underwear/female_commie - name = "Ladies Commie" + name = "Ladies' Commie" icon_state = "female_commie" gender = FEMALE +/datum/sprite_accessory/underwear/female_usastripe + name = "Ladies' Freedom" + icon_state = "female_assblastusa" + gender = FEMALE + +/datum/sprite_accessory/underwear/female_green + name = "Ladies' Green" + icon_state = "female_green" + gender = FEMALE + +/datum/sprite_accessory/underwear/female_kinky + name = "Ladies' Kinky" + icon_state = "female_kinky" + gender = FEMALE + +/datum/sprite_accessory/underwear/female_pink + name = "Ladies' Pink" + icon_state = "female_pink" + gender = FEMALE + +/datum/sprite_accessory/underwear/female_red + name = "Ladies' Red" + icon_state = "female_red" + gender = FEMALE + /datum/sprite_accessory/underwear/swimsuit - name = "Ladies Black Swimsuit" + name = "Ladies' Swimsuit (Black)" icon_state = "swim_black" gender = FEMALE /datum/sprite_accessory/underwear/swimsuit_blue - name = "Ladies Blue Swimsuit" + name = "Ladies' Swimsuit (Blue)" icon_state = "swim_blue" gender = FEMALE /datum/sprite_accessory/underwear/swimsuit_green - name = "Ladies Green Swimsuit" + name = "Ladies' Swimsuit (Green)" icon_state = "swim_green" gender = FEMALE /datum/sprite_accessory/underwear/swimsuit_purple - name = "Ladies Purple Swimsuit" + name = "Ladies' Swimsuit (Purple)" icon_state = "swim_purple" gender = FEMALE /datum/sprite_accessory/underwear/swimsuit_red - name = "Ladies Red Swimsuit" + name = "Ladies' Swimsuit (Red)" icon_state = "swim_red" gender = FEMALE +/datum/sprite_accessory/underwear/female_thong + name = "Ladies' Thong" + icon_state = "female_thong" + gender = FEMALE + +/datum/sprite_accessory/underwear/female_uk + name = "Ladies' UK" + icon_state = "female_uk" + gender = FEMALE + +/datum/sprite_accessory/underwear/female_white + name = "Ladies' White" + icon_state = "female_white" + gender = FEMALE + +/datum/sprite_accessory/underwear/female_white_neko + name = "Ladies' White Neko" + icon_state = "female_neko_white" + gender = FEMALE + +/datum/sprite_accessory/underwear/female_whitealt + name = "Ladies' White Sport" + icon_state = "female_whitealt" + gender = FEMALE + +/datum/sprite_accessory/underwear/female_yellow + name = "Ladies' Yellow" + icon_state = "female_yellow" + gender = FEMALE //////////////////////////// // Undershirt Definitions // //////////////////////////// + /datum/sprite_accessory/undershirt icon = 'icons/mob/underwear.dmi' @@ -1019,264 +821,186 @@ VFACE(Tribearder, facial_tribearder_s) icon_state = null gender = NEUTER -/datum/sprite_accessory/undershirt/shirt_white - name = "White Shirt" - icon_state = "shirt_white" +// please make sure they're sorted alphabetically and categorized + +/datum/sprite_accessory/undershirt/bluejersey + name = "Jersey (Blue)" + icon_state = "shirt_bluejersey" gender = NEUTER -/datum/sprite_accessory/undershirt/shirt_black - name = "Black Shirt" - icon_state = "shirt_black" - gender = NEUTER - -/datum/sprite_accessory/undershirt/shirt_grey - name = "Grey Shirt" - icon_state = "shirt_grey" - gender = NEUTER - -/datum/sprite_accessory/undershirt/tank_white - name = "White Tank Top" - icon_state = "tank_white" - gender = NEUTER - -/datum/sprite_accessory/undershirt/tank_black - name = "Black Tank Top" - icon_state = "tank_black" - gender = NEUTER - -/datum/sprite_accessory/undershirt/tank_grey - name = "Grey Tank Top" - icon_state = "tank_grey" - gender = NEUTER - -/datum/sprite_accessory/undershirt/female_midriff - name = "Midriff Tank Top" - icon_state = "tank_midriff" - gender = FEMALE - -/datum/sprite_accessory/undershirt/lover - name = "Lover Shirt" - icon_state = "lover" - gender = NEUTER - -/datum/sprite_accessory/undershirt/ian - name = "Blue Ian Shirt" - icon_state = "ian" - gender = NEUTER - -/datum/sprite_accessory/undershirt/uk - name = "UK Shirt" - icon_state = "uk" - gender = NEUTER - -/datum/sprite_accessory/undershirt/usa - name = "USA Shirt" - icon_state = "shirt_assblastusa" - gender = NEUTER - -/datum/sprite_accessory/undershirt/ilovent - name = "I Love NT Shirt" - icon_state = "ilovent" - gender = NEUTER - -/datum/sprite_accessory/undershirt/peace - name = "Peace Shirt" - icon_state = "peace" - gender = NEUTER - -/datum/sprite_accessory/undershirt/mondmondjaja - name = "Band Shirt" - icon_state = "band" - gender = NEUTER - -/datum/sprite_accessory/undershirt/pacman - name = "Pogoman Shirt" - icon_state = "pogoman" - gender = NEUTER - -/datum/sprite_accessory/undershirt/matroska - name = "Matroska Shirt" - icon_state = "matroska" - gender = NEUTER - -/datum/sprite_accessory/undershirt/whiteshortsleeve - name = "White Short-sleeved Shirt" - icon_state = "whiteshortsleeve" - gender = NEUTER - -/datum/sprite_accessory/undershirt/purpleshortsleeve - name = "Purple Short-sleeved Shirt" - icon_state = "purpleshortsleeve" - gender = NEUTER - -/datum/sprite_accessory/undershirt/blueshortsleeve - name = "Blue Short-sleeved Shirt" - icon_state = "blueshortsleeve" - gender = NEUTER - -/datum/sprite_accessory/undershirt/greenshortsleeve - name = "Green Short-sleeved Shirt" - icon_state = "greenshortsleeve" - gender = NEUTER - -/datum/sprite_accessory/undershirt/blackshortsleeve - name = "Black Short-sleeved Shirt" - icon_state = "blackshortsleeve" - gender = NEUTER - -/datum/sprite_accessory/undershirt/blueshirt - name = "Blue T-Shirt" - icon_state = "blueshirt" - gender = NEUTER - -/datum/sprite_accessory/undershirt/redshirt - name = "Red T-Shirt" - icon_state = "redshirt" - gender = NEUTER - -/datum/sprite_accessory/undershirt/yellowshirt - name = "Yellow T-Shirt" - icon_state = "yellowshirt" - gender = NEUTER - -/datum/sprite_accessory/undershirt/greenshirt - name = "Green T-Shirt" - icon_state = "greenshirt" +/datum/sprite_accessory/undershirt/redjersey + name = "Jersey (Red)" + icon_state = "shirt_redjersey" gender = NEUTER /datum/sprite_accessory/undershirt/bluepolo - name = "Blue Polo Shirt" + name = "Polo Shirt (Blue)" icon_state = "bluepolo" gender = NEUTER +/datum/sprite_accessory/undershirt/grayyellowpolo + name = "Polo Shirt (Gray-Yellow)" + icon_state = "grayyellowpolo" + gender = NEUTER + /datum/sprite_accessory/undershirt/redpolo - name = "Red Polo Shirt" + name = "Polo Shirt (Red)" icon_state = "redpolo" gender = NEUTER /datum/sprite_accessory/undershirt/whitepolo - name = "White Polo Shirt" + name = "Polo Shirt (White)" icon_state = "whitepolo" gender = NEUTER -/datum/sprite_accessory/undershirt/grayyellowpolo - name = "Gray-Yellow Polo Shirt" - icon_state = "grayyellowpolo" +/datum/sprite_accessory/undershirt/alienshirt + name = "Shirt (Alien)" + icon_state = "shirt_alien" gender = NEUTER -/datum/sprite_accessory/undershirt/redtop - name = "Red Top" - icon_state = "redtop" - gender = FEMALE - -/datum/sprite_accessory/undershirt/whitetop - name = "White Top" - icon_state = "whitetop" - gender = FEMALE - -/datum/sprite_accessory/undershirt/greenshirtsport - name = "Green Sports Shirt" - icon_state = "greenshirtsport" +/datum/sprite_accessory/undershirt/mondmondjaja + name = "Shirt (Band)" + icon_state = "band" gender = NEUTER -/datum/sprite_accessory/undershirt/redshirtsport - name = "Red Sports Shirt" - icon_state = "redshirtsport" - gender = NEUTER - -/datum/sprite_accessory/undershirt/blueshirtsport - name = "Blue Sports Shirt" - icon_state = "blueshirtsport" - gender = NEUTER - -/datum/sprite_accessory/undershirt/ss13 - name = "SS13 Shirt" - icon_state = "shirt_ss13" - gender = NEUTER - -/datum/sprite_accessory/undershirt/tankfire - name = "Fire Tank Top" - icon_state = "tank_fire" - gender = NEUTER - -/datum/sprite_accessory/undershirt/question - name = "Question Shirt" - icon_state = "shirt_question" - gender = NEUTER - -/datum/sprite_accessory/undershirt/skull - name = "Skull Shirt" - icon_state = "shirt_skull" - gender = NEUTER - -/datum/sprite_accessory/undershirt/commie - name = "Commie Shirt" - icon_state = "shirt_commie" - gender = NEUTER - -/datum/sprite_accessory/undershirt/nano - name = "Nanotrasen Shirt" - icon_state = "shirt_nano" - gender = NEUTER - -/datum/sprite_accessory/undershirt/stripe - name = "Striped Shirt" - icon_state = "shirt_stripes" +/datum/sprite_accessory/undershirt/shirt_black + name = "Shirt (Black)" + icon_state = "shirt_black" gender = NEUTER /datum/sprite_accessory/undershirt/blueshirt - name = "Blue Shirt" + name = "Shirt (Blue)" icon_state = "shirt_blue" gender = NEUTER -/datum/sprite_accessory/undershirt/redshirt - name = "Red Shirt" - icon_state = "shirt_red" - gender = NEUTER - -/datum/sprite_accessory/undershirt/tank_red - name = "Red Tank Top" - icon_state = "tank_red" - gender = NEUTER - -/datum/sprite_accessory/undershirt/greenshirt - name = "Green Shirt" - icon_state = "shirt_green" - gender = NEUTER - -/datum/sprite_accessory/undershirt/meat - name = "Meat Shirt" - icon_state = "shirt_meat" - gender = NEUTER - -/datum/sprite_accessory/undershirt/tiedye - name = "Tie-dye Shirt" - icon_state = "shirt_tiedye" - gender = NEUTER - -/datum/sprite_accessory/undershirt/redjersey - name = "Red Jersey" - icon_state = "shirt_redjersey" - gender = NEUTER - -/datum/sprite_accessory/undershirt/bluejersey - name = "Blue Jersey" - icon_state = "shirt_bluejersey" - gender = NEUTER - -/datum/sprite_accessory/undershirt/tankstripe - name = "Striped Tank Top" - icon_state = "tank_stripes" - gender = NEUTER - /datum/sprite_accessory/undershirt/clownshirt - name = "Clown Shirt" + name = "Shirt (Clown)" icon_state = "shirt_clown" gender = NEUTER -/datum/sprite_accessory/undershirt/alienshirt - name = "Alien Shirt" - icon_state = "shirt_alien" +/datum/sprite_accessory/undershirt/commie + name = "Shirt (Commie)" + icon_state = "shirt_commie" + gender = NEUTER + +/datum/sprite_accessory/undershirt/greenshirt + name = "Shirt (Green)" + icon_state = "shirt_green" + gender = NEUTER + +/datum/sprite_accessory/undershirt/shirt_grey + name = "Shirt (Grey)" + icon_state = "shirt_grey" + gender = NEUTER + +/datum/sprite_accessory/undershirt/ian + name = "Shirt (Ian)" + icon_state = "ian" + gender = NEUTER + +/datum/sprite_accessory/undershirt/ilovent + name = "Shirt (I Love NT)" + icon_state = "ilovent" + gender = NEUTER + +/datum/sprite_accessory/undershirt/lover + name = "Shirt (Lover)" + icon_state = "lover" + gender = NEUTER + +/datum/sprite_accessory/undershirt/matroska + name = "Shirt (Matroska)" + icon_state = "matroska" + gender = NEUTER + +/datum/sprite_accessory/undershirt/meat + name = "Shirt (Meat)" + icon_state = "shirt_meat" + gender = NEUTER + +/datum/sprite_accessory/undershirt/nano + name = "Shirt (Nanotrasen)" + icon_state = "shirt_nano" + gender = NEUTER + +/datum/sprite_accessory/undershirt/peace + name = "Shirt (Peace)" + icon_state = "peace" + gender = NEUTER + +/datum/sprite_accessory/undershirt/pacman + name = "Shirt (Pogoman)" + icon_state = "pogoman" + gender = NEUTER + +/datum/sprite_accessory/undershirt/question + name = "Shirt (Question)" + icon_state = "shirt_question" + gender = NEUTER + +/datum/sprite_accessory/undershirt/redshirt + name = "Shirt (Red)" + icon_state = "shirt_red" + gender = NEUTER + +/datum/sprite_accessory/undershirt/skull + name = "Shirt (Skull)" + icon_state = "shirt_skull" + gender = NEUTER + +/datum/sprite_accessory/undershirt/ss13 + name = "Shirt (SS13)" + icon_state = "shirt_ss13" + gender = NEUTER + +/datum/sprite_accessory/undershirt/stripe + name = "Shirt (Striped)" + icon_state = "shirt_stripes" + gender = NEUTER + +/datum/sprite_accessory/undershirt/tiedye + name = "Shirt (Tie-dye)" + icon_state = "shirt_tiedye" + gender = NEUTER + +/datum/sprite_accessory/undershirt/uk + name = "Shirt (UK)" + icon_state = "uk" + gender = NEUTER + +/datum/sprite_accessory/undershirt/usa + name = "Shirt (USA)" + icon_state = "shirt_assblastusa" + gender = NEUTER + +/datum/sprite_accessory/undershirt/shirt_white + name = "Shirt (White)" + icon_state = "shirt_white" + gender = NEUTER + +/datum/sprite_accessory/undershirt/blackshortsleeve + name = "Short-sleeved Shirt (Black)" + icon_state = "blackshortsleeve" + gender = NEUTER + +/datum/sprite_accessory/undershirt/blueshortsleeve + name = "Short-sleeved Shirt (Blue)" + icon_state = "blueshortsleeve" + gender = NEUTER + +/datum/sprite_accessory/undershirt/greenshortsleeve + name = "Short-sleeved Shirt (Green)" + icon_state = "greenshortsleeve" + gender = NEUTER + +/datum/sprite_accessory/undershirt/purpleshortsleeve + name = "Short-sleeved Shirt (Purple)" + icon_state = "purpleshortsleeve" + gender = NEUTER + +/datum/sprite_accessory/undershirt/whiteshortsleeve + name = "Short-sleeved Shirt (White)" + icon_state = "whiteshortsleeve" gender = NEUTER /datum/sprite_accessory/undershirt/sports_bra @@ -1285,13 +1009,94 @@ VFACE(Tribearder, facial_tribearder_s) gender = NEUTER /datum/sprite_accessory/undershirt/sports_bra2 - name = "Alt Sports Bra" + name = "Sports Bra (Alt)" icon_state = "sports_bra_alt" gender = NEUTER +/datum/sprite_accessory/undershirt/blueshirtsport + name = "Sports Shirt (Blue)" + icon_state = "blueshirtsport" + gender = NEUTER + +/datum/sprite_accessory/undershirt/greenshirtsport + name = "Sports Shirt (Green)" + icon_state = "greenshirtsport" + gender = NEUTER + +/datum/sprite_accessory/undershirt/redshirtsport + name = "Sports Shirt (Red)" + icon_state = "redshirtsport" + gender = NEUTER + +/datum/sprite_accessory/undershirt/tank_black + name = "Tank Top (Black)" + icon_state = "tank_black" + gender = NEUTER + +/datum/sprite_accessory/undershirt/tankfire + name = "Tank Top (Fire)" + icon_state = "tank_fire" + gender = NEUTER + +/datum/sprite_accessory/undershirt/tank_grey + name = "Tank Top (Grey)" + icon_state = "tank_grey" + gender = NEUTER + +/datum/sprite_accessory/undershirt/female_midriff + name = "Tank Top (Midriff)" + icon_state = "tank_midriff" + gender = FEMALE + +/datum/sprite_accessory/undershirt/tank_red + name = "Tank Top (Red)" + icon_state = "tank_red" + gender = NEUTER + +/datum/sprite_accessory/undershirt/tankstripe + name = "Tank Top (Striped)" + icon_state = "tank_stripes" + gender = NEUTER + +/datum/sprite_accessory/undershirt/tank_white + name = "Tank Top (White)" + icon_state = "tank_white" + gender = NEUTER + +/datum/sprite_accessory/undershirt/redtop + name = "Top (Red)" + icon_state = "redtop" + gender = FEMALE + +/datum/sprite_accessory/undershirt/whitetop + name = "Top (White)" + icon_state = "whitetop" + gender = FEMALE + +/datum/sprite_accessory/undershirt/tshirt_blue + name = "T-Shirt (Blue)" + icon_state = "blueshirt" + gender = NEUTER + +/datum/sprite_accessory/undershirt/tshirt_green + name = "T-Shirt (Green)" + icon_state = "greenshirt" + gender = NEUTER + +/datum/sprite_accessory/undershirt/tshirt_red + name = "T-Shirt (Red)" + icon_state = "redshirt" + gender = NEUTER + +/datum/sprite_accessory/undershirt/yellowshirt + name = "T-Shirt (Yellow)" + icon_state = "yellowshirt" + gender = NEUTER + /////////////////////// // Socks Definitions // /////////////////////// + /datum/sprite_accessory/socks icon = 'icons/mob/underwear.dmi' @@ -1299,90 +1104,92 @@ VFACE(Tribearder, facial_tribearder_s) name = "Nude" icon_state = null -/datum/sprite_accessory/socks/white_norm - name = "Normal White" - icon_state = "white_norm" - -/datum/sprite_accessory/socks/black_norm - name = "Normal Black" - icon_state = "black_norm" - -/datum/sprite_accessory/socks/white_short - name = "Short White" - icon_state = "white_short" - -/datum/sprite_accessory/socks/black_short - name = "Short Black" - icon_state = "black_short" - -/datum/sprite_accessory/socks/white_knee - name = "Knee-high White" - icon_state = "white_knee" +// please make sure they're sorted alphabetically and categorized /datum/sprite_accessory/socks/black_knee - name = "Knee-high Black" + name = "Knee-high (Black)" icon_state = "black_knee" -/datum/sprite_accessory/socks/thin_knee - name = "Knee-high Thin" - icon_state = "thin_knee" - -/datum/sprite_accessory/socks/striped_knee - name = "Knee-high Striped" - icon_state = "striped_knee" - -/datum/sprite_accessory/socks/rainbow_knee - name = "Knee-high Rainbow" - icon_state = "rainbow_knee" - -/datum/sprite_accessory/socks/white_thigh - name = "Thigh-high White" - icon_state = "white_thigh" - -/datum/sprite_accessory/socks/black_thigh - name = "Thigh-high Black" - icon_state = "black_thigh" - -/datum/sprite_accessory/socks/thin_thigh - name = "Thigh-high Thin" - icon_state = "thin_thigh" - -/datum/sprite_accessory/socks/striped_thigh - name = "Thigh-high Striped" - icon_state = "striped_thigh" - -/datum/sprite_accessory/socks/rainbow_thigh - name = "Thigh-high Rainbow" - icon_state = "rainbow_thigh" - -/datum/sprite_accessory/socks/usa_knee - name = "Knee-High Freedom Stripes" - icon_state = "assblastusa_knee" - -/datum/sprite_accessory/socks/usa_thigh - name = "Thigh-high Freedom Stripes" - icon_state = "assblastusa_thigh" - -/datum/sprite_accessory/socks/uk_knee - name = "Knee-High UK Stripes" - icon_state = "uk_knee" - -/datum/sprite_accessory/socks/uk_thigh - name = "Thigh-high UK Stripes" - icon_state = "uk_thigh" - /datum/sprite_accessory/socks/commie_knee - name = "Knee-High Commie Stripes" + name = "Knee-High (Commie)" icon_state = "commie_knee" -/datum/sprite_accessory/socks/commie_thigh - name = "Thigh-high Commie Stripes" - icon_state = "commie_thigh" +/datum/sprite_accessory/socks/usa_knee + name = "Knee-High (Freedom)" + icon_state = "assblastusa_knee" + +/datum/sprite_accessory/socks/rainbow_knee + name = "Knee-high (Rainbow)" + icon_state = "rainbow_knee" + +/datum/sprite_accessory/socks/striped_knee + name = "Knee-high (Striped)" + icon_state = "striped_knee" + +/datum/sprite_accessory/socks/thin_knee + name = "Knee-high (Thin)" + icon_state = "thin_knee" + +/datum/sprite_accessory/socks/uk_knee + name = "Knee-High (UK)" + icon_state = "uk_knee" + +/datum/sprite_accessory/socks/white_knee + name = "Knee-high (White)" + icon_state = "white_knee" + +/datum/sprite_accessory/socks/black_norm + name = "Normal (Black)" + icon_state = "black_norm" + +/datum/sprite_accessory/socks/white_norm + name = "Normal (White)" + icon_state = "white_norm" /datum/sprite_accessory/socks/pantyhose name = "Pantyhose" icon_state = "pantyhose" +/datum/sprite_accessory/socks/black_short + name = "Short (Black)" + icon_state = "black_short" + +/datum/sprite_accessory/socks/white_short + name = "Short (White)" + icon_state = "white_short" + +/datum/sprite_accessory/socks/black_thigh + name = "Thigh-high (Black)" + icon_state = "black_thigh" + +/datum/sprite_accessory/socks/commie_thigh + name = "Thigh-high (Commie)" + icon_state = "commie_thigh" + +/datum/sprite_accessory/socks/usa_thigh + name = "Thigh-high (Freedom)" + icon_state = "assblastusa_thigh" + +/datum/sprite_accessory/socks/rainbow_thigh + name = "Thigh-high (Rainbow)" + icon_state = "rainbow_thigh" + +/datum/sprite_accessory/socks/striped_thigh + name = "Thigh-high (Striped)" + icon_state = "striped_thigh" + +/datum/sprite_accessory/socks/thin_thigh + name = "Thigh-high (Thin)" + icon_state = "thin_thigh" + +/datum/sprite_accessory/socks/uk_thigh + name = "Thigh-high (UK)" + icon_state = "uk_thigh" + +/datum/sprite_accessory/socks/white_thigh + name = "Thigh-high (White)" + icon_state = "white_thigh" + //////////.////////////////// // MutantParts Definitions // ///////////////////////////// diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm index 7bba60bef9..1b328dbc69 100644 --- a/code/modules/mob/dead/observer/login.dm +++ b/code/modules/mob/dead/observer/login.dm @@ -12,5 +12,9 @@ preferred_form = client.prefs.ghost_form ghost_orbit = client.prefs.ghost_orbit + var/turf/T = get_turf(src) + if (isturf(T)) + update_z(T.z) + update_icon(preferred_form) updateghostimages() diff --git a/code/modules/mob/dead/observer/logout.dm b/code/modules/mob/dead/observer/logout.dm index ad94dbecc8..3ca6405a0c 100644 --- a/code/modules/mob/dead/observer/logout.dm +++ b/code/modules/mob/dead/observer/logout.dm @@ -1,4 +1,5 @@ /mob/dead/observer/Logout() + update_z(null) if (client) client.images -= (GLOB.ghost_images_default+GLOB.ghost_images_simple) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 7655e8e1d7..12fe984d6f 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -16,6 +16,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) see_invisible = SEE_INVISIBLE_OBSERVER see_in_dark = 100 invisibility = INVISIBILITY_OBSERVER + hud_type = /datum/hud/ghost var/can_reenter_corpse var/datum/hud/living/carbon/hud = null // hud var/bootime = 0 @@ -135,6 +136,10 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) grant_all_languages() +/mob/dead/observer/get_photo_description(obj/item/camera/camera) + if(!invisibility || camera.see_ghosts) + return "You can also see a g-g-g-g-ghooooost!" + /mob/dead/observer/narsie_act() var/old_color = color color = "#960000" @@ -267,6 +272,7 @@ Works together with spawning an observer, noted above. /* This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues. */ + /mob/living/verb/ghost() set category = "OOC" set name = "Ghost" @@ -281,6 +287,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp C.despawn_occupant() return // END EDIT + if(stat != DEAD) succumb() if(stat == DEAD) @@ -386,6 +393,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(!L || !L.len) to_chat(usr, "No area available.") + return usr.forceMove(pick(L)) update_parallax_contents() diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index 84996184ca..d521ef179f 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -1,4 +1,4 @@ -/mob/dead/observer/say(message) +/mob/dead/observer/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) if (!message) return @@ -15,7 +15,7 @@ client.dsay(message) return - log_talk(src,"Ghost/[src.key] : [message]", LOGSAY) + src.log_talk(message, LOG_SAY, tag="ghost") if(check_emote(message)) return @@ -37,3 +37,4 @@ // Recompose the message, because it's scrambled by default message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode) to_chat(src, "[link] [message]") + diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm index 235a215eff..8c335a9e8c 100644 --- a/code/modules/mob/death.dm +++ b/code/modules/mob/death.dm @@ -6,8 +6,9 @@ //This is the proc for turning a mob into ash. Mostly a copy of gib code (above). //Originally created for wizard disintegrate. I've removed the virus code since it's irrelevant here. //Dusting robots does not eject the MMI, so it's a bit more powerful than gib() /N -/mob/proc/dust() +/mob/proc/dust(just_ash, drop_items, force) return /mob/proc/death(gibbed) SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed) + return \ No newline at end of file diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index f966702f3b..625a923fe1 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -1,5 +1,5 @@ //The code execution of the emote datum is located at code/datums/emotes.dm -/mob/proc/emote(act, m_type = null, message = null) +/mob/proc/emote(act, m_type = null, message = null, intentional = FALSE) act = lowertext(act) var/param = message var/custom_param = findchar(act, " ") @@ -12,7 +12,7 @@ if(!E) to_chat(src, "Unusable emote '[act]'. Say *help for a list.") return - E.run_emote(src, param, m_type) + E.run_emote(src, param, m_type, intentional) /datum/emote/flip key = "flip" diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 494536ebfa..a6d6a7c7b6 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -170,8 +170,10 @@ return FALSE return !held_items[hand_index] -/mob/proc/put_in_hand(obj/item/I, hand_index, forced = FALSE) +/mob/proc/put_in_hand(obj/item/I, hand_index, forced = FALSE, ignore_anim = TRUE) if(forced || can_put_in_hand(I, hand_index)) + if(isturf(I.loc) && !ignore_anim) + I.do_pickup_animation(src) if(hand_index == null) return FALSE if(get_item_for_held_index(hand_index) != null) @@ -208,8 +210,8 @@ //Puts the item into our active hand if possible. returns TRUE on success. -/mob/proc/put_in_active_hand(obj/item/I, forced = FALSE) - return put_in_hand(I, active_hand_index, forced) +/mob/proc/put_in_active_hand(obj/item/I, forced = FALSE, ignore_animation = TRUE) + return put_in_hand(I, active_hand_index, forced, ignore_animation) //Puts the item into our inactive hand if possible, returns TRUE on success diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 393b939448..bd1fba44e2 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -254,7 +254,7 @@ // Only a certain number of drips (or one large splatter) can be on a given turf. var/obj/effect/decal/cleanable/blood/drip/drop = locate() in T if(drop) - if(drop.drips < 3) + if(drop.drips < 5) drop.drips++ drop.add_overlay(pick(drop.random_icon_states)) drop.transfer_mob_blood_dna(src) @@ -271,6 +271,8 @@ var/obj/effect/decal/cleanable/blood/B = locate() in T if(!B) B = new /obj/effect/decal/cleanable/blood/splatter(T, get_static_viruses()) + if (B.bloodiness < MAX_SHOE_BLOODINESS) //add more blood, up to a limit + B.bloodiness += BLOOD_AMOUNT_PER_DECAL B.transfer_mob_blood_dna(src) //give blood info to the blood decal. if(temp_blood_DNA) B.add_blood_DNA(temp_blood_DNA) diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 965ef88c03..7d1dc7d1d2 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -21,7 +21,7 @@ name = "brain" if(C.mind && C.mind.has_antag_datum(/datum/antagonist/changeling) && !no_id_transfer) //congrats, you're trapped in a body you don't control - if(brainmob && !(C.stat == DEAD || (C.has_trait(TRAIT_FAKEDEATH)))) + if(brainmob && !(C.stat == DEAD || (C.has_trait(TRAIT_DEATHCOMA)))) to_chat(brainmob, "You can't feel your body! You're still just a brain!") forceMove(C) C.update_hair() @@ -144,7 +144,7 @@ /obj/item/organ/brain/proc/get_brain_damage() var/brain_damage_threshold = max_integrity * BRAIN_DAMAGE_INTEGRITY_MULTIPLIER var/offset_integrity = obj_integrity - (max_integrity - brain_damage_threshold) - . = round((1 - (offset_integrity / brain_damage_threshold)) * BRAIN_DAMAGE_DEATH,0.1) + . = round((1 - (offset_integrity / brain_damage_threshold)) * BRAIN_DAMAGE_DEATH, DAMAGE_PRECISION) /obj/item/organ/brain/proc/adjust_brain_damage(amount, maximum) var/adjusted_amount @@ -157,11 +157,11 @@ else adjusted_amount = amount - adjusted_amount = round(adjusted_amount * BRAIN_DAMAGE_INTEGRITY_MULTIPLIER,0.1) + adjusted_amount = round(adjusted_amount * BRAIN_DAMAGE_INTEGRITY_MULTIPLIER, DAMAGE_PRECISION) if(adjusted_amount) - if(adjusted_amount >= 0.1) + if(adjusted_amount >= DAMAGE_PRECISION) take_damage(adjusted_amount) - else if(adjusted_amount <= -0.1) + else if(adjusted_amount <= -DAMAGE_PRECISION) obj_integrity = min(max_integrity, obj_integrity-adjusted_amount) . = adjusted_amount diff --git a/code/modules/mob/living/brain/say.dm b/code/modules/mob/living/brain/say.dm index de18b8ce3f..ce0a09c27f 100644 --- a/code/modules/mob/living/brain/say.dm +++ b/code/modules/mob/living/brain/say.dm @@ -1,4 +1,4 @@ -/mob/living/brain/say(message, language) +/mob/living/brain/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) if(!(container && istype(container, /obj/item/mmi))) return //No MMI, can't speak, bucko./N else diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm index b55abb11f0..6d59bc052a 100644 --- a/code/modules/mob/living/carbon/alien/alien_defense.dm +++ b/code/modules/mob/living/carbon/alien/alien_defense.dm @@ -38,7 +38,7 @@ In all, this is a lot like the monkey code. /N visible_message("[M.name] bites [src]!", \ "[M.name] bites [src]!", null, COMBAT_MESSAGE_RANGE) adjustBruteLoss(1) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") updatehealth() else to_chat(M, "[name] is too injured for that.") @@ -97,7 +97,7 @@ In all, this is a lot like the monkey code. /N if(M.is_adult) damage = rand(10, 40) adjustBruteLoss(damage) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") updatehealth() /mob/living/carbon/alien/ex_act(severity, target, origin) diff --git a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm index cdc1b08d21..323bd408cf 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm @@ -97,7 +97,7 @@ Doesn't work on other aliens/AI.*/ return 0 var/msg = sanitize(input("Message:", "Alien Whisper") as text|null) if(msg) - log_talk(user,"AlienWhisper: [key_name(user)]->[key_name(M)] : [msg]",LOGSAY) + log_directed_talk(user, M, msg, LOG_SAY, tag="alien whisper") to_chat(M, "You hear a strange, alien voice in your head...[msg]") to_chat(user, "You said: \"[msg]\" to [M]") for(var/ded in GLOB.dead_mob_list) @@ -233,19 +233,19 @@ Doesn't work on other aliens/AI.*/ /obj/effect/proc_holder/alien/neurotoxin/on_lose(mob/living/carbon/user) remove_ranged_ability() -/obj/effect/proc_holder/alien/neurotoxin/add_ranged_ability(mob/living/user, msg) +/obj/effect/proc_holder/alien/neurotoxin/add_ranged_ability(mob/living/user,msg,forced) ..() if(isalienadult(user)) var/mob/living/carbon/alien/humanoid/A = user A.drooling = 1 A.update_icons() -/obj/effect/proc_holder/alien/neurotoxin/remove_ranged_ability(mob/living/user, msg) - ..() - if(isalienadult(user)) - var/mob/living/carbon/alien/humanoid/A = user +/obj/effect/proc_holder/alien/neurotoxin/remove_ranged_ability(msg) + if(isalienadult(ranged_ability_user)) + var/mob/living/carbon/alien/humanoid/A = ranged_ability_user A.drooling = 0 A.update_icons() + ..() /obj/effect/proc_holder/alien/resin name = "Secrete Resin" diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm index b18aaacfeb..183bf07e8c 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm @@ -5,21 +5,16 @@ health = 125 icon_state = "aliend" - /mob/living/carbon/alien/humanoid/drone/Initialize() AddAbility(new/obj/effect/proc_holder/alien/evolve(null)) . = ..() - /mob/living/carbon/alien/humanoid/drone/create_internal_organs() internal_organs += new /obj/item/organ/alien/plasmavessel/large internal_organs += new /obj/item/organ/alien/resinspinner internal_organs += new /obj/item/organ/alien/acid ..() -/mob/living/carbon/alien/humanoid/drone/movement_delay() - . = ..() - /obj/effect/proc_holder/alien/evolve name = "Evolve to Praetorian" desc = "Praetorian" diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm index ada1d1f21c..fe682b5c99 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm @@ -10,11 +10,6 @@ internal_organs += new /obj/item/organ/alien/plasmavessel/small ..() -/mob/living/carbon/alien/humanoid/hunter/movement_delay() - . = -1 //hunters are sanic - . += ..() //but they still need to slow down on stun - - //Hunter verbs /mob/living/carbon/alien/humanoid/hunter/proc/toggle_leap(message = 1) @@ -26,7 +21,6 @@ else return - /mob/living/carbon/alien/humanoid/hunter/ClickOn(atom/A, params) face_atom(A) if(leap_on_click) @@ -34,7 +28,6 @@ else ..() - #define MAX_ALIEN_LEAP_DIST 7 /mob/living/carbon/alien/humanoid/hunter/proc/leap_at(atom/A) @@ -54,7 +47,7 @@ leaping = 1 weather_immunities += "lava" update_icons() - throw_at(A, MAX_ALIEN_LEAP_DIST, 1, src, FALSE, TRUE, callback = CALLBACK(src, .leap_end)) + throw_at(A, MAX_ALIEN_LEAP_DIST, 1, src, FALSE, TRUE, callback = CALLBACK(src, .proc/leap_end)) /mob/living/carbon/alien/humanoid/hunter/proc/leap_end() leaping = 0 diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/praetorian.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/praetorian.dm index fe61c2a8ff..4ec5780838 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/praetorian.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/praetorian.dm @@ -5,12 +5,8 @@ health = 250 icon_state = "alienp" - - /mob/living/carbon/alien/humanoid/royal/praetorian/Initialize() - real_name = name - AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/repulse/xeno(src)) AddAbility(new /obj/effect/proc_holder/alien/royal/praetorian/evolve()) . = ..() @@ -22,11 +18,6 @@ internal_organs += new /obj/item/organ/alien/neurotoxin ..() - -/mob/living/carbon/alien/humanoid/royal/praetorian/movement_delay() - . = ..() - . += 1 - /obj/effect/proc_holder/alien/royal/praetorian/evolve name = "Evolve" desc = "Produce an internal egg sac capable of spawning children. Only one queen can exist at a time." diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm index 375ef2318b..7c6443cfae 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm @@ -5,7 +5,6 @@ health = 150 icon_state = "aliens" - /mob/living/carbon/alien/humanoid/sentinel/Initialize() AddAbility(new /obj/effect/proc_holder/alien/sneak) . = ..() @@ -15,7 +14,3 @@ internal_organs += new /obj/item/organ/alien/acid internal_organs += new /obj/item/organ/alien/neurotoxin ..() - - -/mob/living/carbon/alien/humanoid/sentinel/movement_delay() - . = ..() diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index eb5e9664d9..8403d533c4 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm @@ -5,6 +5,7 @@ butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/xeno = 5, /obj/item/stack/sheet/animalhide/xeno = 1) possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM) limb_destroyer = 1 + hud_type = /datum/hud/alien var/obj/item/r_store = null var/obj/item/l_store = null var/caste = "" @@ -25,16 +26,8 @@ AddAbility(new/obj/effect/proc_holder/alien/regurgitate(null)) . = ..() -/mob/living/carbon/alien/humanoid/movement_delay() - . = ..() - var/static/config_alien_delay - if(isnull(config_alien_delay)) - config_alien_delay = CONFIG_GET(number/alien_delay) - . += move_delay_add + config_alien_delay + sneaking //move_delay_add is used to slow aliens with stun - /mob/living/carbon/alien/humanoid/restrained(ignore_grab) - . = handcuffed - + return handcuffed /mob/living/carbon/alien/humanoid/show_inv(mob/user) user.set_machine(src) diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm index 6c42e687a9..b3839a6033 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid_defense.dm @@ -35,7 +35,7 @@ "[M] has knocked [src] down!") var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) apply_damage(damage, BRUTE, affecting) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") else playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) visible_message("[M] has attempted to punch [src]!", \ @@ -46,7 +46,7 @@ if (prob(5)) Unconscious(40) playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - add_logs(M, src, "pushed") + log_combat(M, src, "pushed") visible_message("[M] has pushed down [src]!", \ "[M] has pushed down [src]!") else diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 9c61e8aa2d..79eed6b82b 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -40,7 +40,7 @@ AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/repulse/xeno(src)) AddAbility(new/obj/effect/proc_holder/alien/royal/queen/promote()) smallsprite.Grant(src) - ..() + return ..() /mob/living/carbon/alien/humanoid/royal/queen/create_internal_organs() internal_organs += new /obj/item/organ/alien/plasmavessel/large/queen @@ -50,10 +50,6 @@ internal_organs += new /obj/item/organ/alien/eggsac ..() -/mob/living/carbon/alien/humanoid/royal/queen/movement_delay() - . = ..() - . += 3 - //Queen verbs /obj/effect/proc_holder/alien/lay_egg name = "Lay Egg" diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index 2e2d1f4188..e1e079cbae 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -5,6 +5,7 @@ pass_flags = PASSTABLE | PASSMOB mob_size = MOB_SIZE_SMALL density = FALSE + hud_type = /datum/hud/larva maxHealth = 25 health = 25 diff --git a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm index c3cee567a2..69c1be707d 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm @@ -5,7 +5,7 @@ var/damage = rand(1, 9) if (prob(90)) playsound(loc, "punch", 25, 1, -1) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") visible_message("[M] has kicked [src]!", \ "[M] has kicked [src]!", null, COMBAT_MESSAGE_RANGE) if ((stat != DEAD) && (damage > 4.9)) diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm index 62b9fb9b1a..5961e4dd71 100644 --- a/code/modules/mob/living/carbon/alien/larva/life.dm +++ b/code/modules/mob/living/carbon/alien/larva/life.dm @@ -18,7 +18,7 @@ if(health<= -maxHealth || !getorgan(/obj/item/organ/brain)) death() return - if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (has_trait(TRAIT_FAKEDEATH)) || health <= HEALTH_THRESHOLD_CRIT) + if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (has_trait(TRAIT_DEATHCOMA)) || health <= crit_threshold) if(stat == CONSCIOUS) stat = UNCONSCIOUS blind_eyes(1) @@ -30,4 +30,4 @@ adjust_blindness(-1) update_canmove() update_damage_hud() - update_health_hud() \ No newline at end of file + update_health_hud() diff --git a/code/modules/mob/living/carbon/alien/larva/powers.dm b/code/modules/mob/living/carbon/alien/larva/powers.dm index 906ba71c96..9d5617b3e7 100644 --- a/code/modules/mob/living/carbon/alien/larva/powers.dm +++ b/code/modules/mob/living/carbon/alien/larva/powers.dm @@ -15,7 +15,7 @@ "You are now hiding.") else user.layer = MOB_LAYER - user.visible_message("[user.] slowly peeks up from the ground...", \ + user.visible_message("[user] slowly peeks up from the ground...", \ "You stop hiding.") return 1 diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm index 1a407d0ca3..155f203708 100644 --- a/code/modules/mob/living/carbon/alien/organs.dm +++ b/code/modules/mob/living/carbon/alien/organs.dm @@ -9,6 +9,10 @@ alien_powers -= A alien_powers += new A(src) +/obj/item/organ/alien/Destroy() + QDEL_LIST(alien_powers) + return ..() + /obj/item/organ/alien/Insert(mob/living/carbon/M, special = 0) ..() for(var/obj/effect/proc_holder/alien/P in alien_powers) diff --git a/code/modules/mob/living/carbon/alien/say.dm b/code/modules/mob/living/carbon/alien/say.dm index 1d35a2c134..b921aea67e 100644 --- a/code/modules/mob/living/carbon/alien/say.dm +++ b/code/modules/mob/living/carbon/alien/say.dm @@ -1,5 +1,5 @@ /mob/living/proc/alien_talk(message, shown_name = real_name) - log_talk(src,"[key_name(src)] : [message]",LOGSAY) + src.log_talk(message, LOG_SAY) message = trim(message) if(!message) return diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index c3c74b8631..e60a63616b 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -19,6 +19,9 @@ QDEL_NULL(dna) GLOB.carbon_list -= src +/mob/living/carbon/initialize_footstep() + AddComponent(/datum/component/footstep, 1, 2) + /mob/living/carbon/relaymove(mob/user, direction) if(user in src.stomach_contents) if(prob(40)) @@ -84,9 +87,9 @@ /mob/living/carbon/attackby(obj/item/I, mob/user, params) if(lying && surgeries.len) - if(user != src && user.a_intent == INTENT_HELP) + if(user != src && (user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM)) for(var/datum/surgery/S in surgeries) - if(S.next_step(user)) + if(S.next_step(user,user.a_intent)) return 1 return ..() @@ -169,7 +172,7 @@ var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors var/turf/end_T = get_turf(target) if(start_T && end_T) - add_logs(src, throwable_mob, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]") + log_combat(src, throwable_mob, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]") else if(!(I.item_flags & (NODROP | ABSTRACT))) thrown_thing = I @@ -183,7 +186,7 @@ if(thrown_thing) visible_message("[src] has thrown [thrown_thing].") - add_logs(src, thrown_thing, "thrown") + src.log_message("has thrown [thrown_thing]", LOG_ATTACK) newtonian_move(get_dir(target, src)) thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src) @@ -409,6 +412,7 @@ //dropItemToGround(I) CIT CHANGE - makes it so the item doesn't drop if the modifier rolls above 100 var/modifier = 0 + if(has_trait(TRAIT_CLUMSY)) modifier -= 40 //Clumsy people are more likely to hit themselves -Honk! @@ -419,7 +423,7 @@ if(modifier < 100) dropItemToGround(I) //END OF CIT CHANGES - + switch(rand(1,100)+modifier) //91-100=Nothing special happens if(-INFINITY to 0) //attack yourself I.attack(src,src) @@ -523,15 +527,29 @@ var/total_stamina = 0 for(var/X in bodyparts) //hardcoded to streamline things a bit var/obj/item/bodypart/BP = X - total_brute += BP.brute_dam - total_burn += BP.burn_dam - total_stamina += BP.stamina_dam - health = maxHealth - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute - staminaloss = total_stamina + total_brute += (BP.brute_dam * BP.body_damage_coeff) + total_burn += (BP.burn_dam * BP.body_damage_coeff) + total_stamina += (BP.stamina_dam * BP.stam_damage_coeff) + health = round(maxHealth - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute, DAMAGE_PRECISION) + staminaloss = round(total_stamina, DAMAGE_PRECISION) update_stat() if(((maxHealth - total_burn) < HEALTH_THRESHOLD_DEAD) && stat == DEAD ) become_husk("burn") med_hud_set_health() + if(stat == SOFT_CRIT) + add_movespeed_modifier(MOVESPEED_ID_CARBON_SOFTCRIT, TRUE, multiplicative_slowdown = SOFTCRIT_ADD_SLOWDOWN) + else + remove_movespeed_modifier(MOVESPEED_ID_CARBON_SOFTCRIT, TRUE) + +/mob/living/carbon/update_stamina() + var/stam = getStaminaLoss() + if(stam > DAMAGE_PRECISION) + var/total_health = (health - stam) + if(total_health <= crit_threshold && !stat) + if(!IsKnockdown()) + to_chat(src, "You're too exhausted to keep going...") + Knockdown(100) + update_health_hud() /mob/living/carbon/update_sight() if(!client) @@ -616,7 +634,7 @@ if(!client) return - if(health <= HEALTH_THRESHOLD_CRIT) + if(health <= crit_threshold) var/severity = 0 switch(health) if(-20 to -10) @@ -741,11 +759,11 @@ if(health <= HEALTH_THRESHOLD_DEAD && !has_trait(TRAIT_NODEATH)) death() return - if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (has_trait(TRAIT_FAKEDEATH)) || (health <= HEALTH_THRESHOLD_FULLCRIT && !has_trait(TRAIT_NOHARDCRIT))) + if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (has_trait(TRAIT_DEATHCOMA)) || (health <= HEALTH_THRESHOLD_FULLCRIT && !has_trait(TRAIT_NOHARDCRIT))) stat = UNCONSCIOUS blind_eyes(1) else - if(health <= HEALTH_THRESHOLD_CRIT && !has_trait(TRAIT_NOSOFTCRIT)) + if(health <= crit_threshold && !has_trait(TRAIT_NOSOFTCRIT)) stat = SOFT_CRIT else stat = CONSCIOUS @@ -839,7 +857,7 @@ "[src] devours you!") C.forceMove(src) stomach_contents.Add(C) - add_logs(src, C, "devoured") + log_combat(src, C, "devoured") /mob/living/carbon/proc/create_bodyparts() var/l_arm_index_next = -1 @@ -881,6 +899,7 @@ .["Modify bodypart"] = "?_src_=vars;[HrefToken()];editbodypart=[REF(src)]" .["Modify organs"] = "?_src_=vars;[HrefToken()];editorgans=[REF(src)]" .["Hallucinate"] = "?_src_=vars;[HrefToken()];hallucinate=[REF(src)]" + .["Give martial arts"] = "?_src_=vars;[HrefToken()];givemartialart=[REF(src)]" .["Give brain trauma"] = "?_src_=vars;[HrefToken()];givetrauma=[REF(src)]" .["Cure brain traumas"] = "?_src_=vars;[HrefToken()];curetraumas=[REF(src)]" diff --git a/code/modules/mob/living/carbon/carbon_movement.dm b/code/modules/mob/living/carbon/carbon_movement.dm index 7334464235..37b888a6b8 100644 --- a/code/modules/mob/living/carbon/carbon_movement.dm +++ b/code/modules/mob/living/carbon/carbon_movement.dm @@ -1,13 +1,6 @@ /mob/living/carbon/movement_delay() - var/FP = FALSE - var/obj/item/flightpack/F = get_flightpack() - if(istype(F) && F.flight) - FP = TRUE - . = ..(FP) - if(!FP) - . += grab_state * 1 //Flightpacks are too powerful to be slowed too much by the weight of a corpse. - else - . += grab_state * 3 //can't go fast while grabbing something. + . = ..() + . += grab_state * 3 //can't go fast while grabbing something. if(!get_leg_ignore()) //ignore the fact we lack legs var/leg_amount = get_num_legs() @@ -16,7 +9,6 @@ . += 6 - 3*get_num_arms() //crawling is harder with fewer arms if(legcuffed) . += legcuffed.slowdown - if(stat == SOFT_CRIT) . += SOFTCRIT_ADD_SLOWDOWN @@ -24,7 +16,7 @@ if(movement_type & FLYING) return 0 if(!(lube&SLIDE_ICE)) - add_logs(src, (O ? O : get_turf(src)), "slipped on the", null, ((lube & SLIDE) ? "(LUBE)" : null)) + log_combat(src, (O ? O : get_turf(src)), "slipped on the", null, ((lube & SLIDE) ? "(LUBE)" : null)) return loc.handle_slip(src, knockdown_amount, O, lube) /mob/living/carbon/Process_Spacemove(movement_dir = 0) @@ -33,10 +25,6 @@ if(!isturf(loc)) return 0 - var/obj/item/flightpack/F = get_flightpack() - if(istype(F) && (F.flight) && F.allow_thrust(0.01, src)) - return 1 - // Do we have a jetpack implant (and is it on)? var/obj/item/organ/cyberimp/chest/thrusters/T = getorganslot(ORGAN_SLOT_THRUSTERS) if(istype(T) && movement_dir && T.allow_thrust(0.01)) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index 8f4c6a2b05..124fd622d7 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -95,7 +95,7 @@ . = 0 for(var/X in bodyparts) var/obj/item/bodypart/BP = X - . += BP.stamina_dam + . += round(BP.stamina_dam * BP.stam_damage_coeff, DAMAGE_PRECISION) /mob/living/carbon/adjustStaminaLoss(amount, updating_health = TRUE, forced = FALSE) if(!forced && (status_flags & GODMODE)) @@ -120,7 +120,7 @@ var/list/obj/item/bodypart/parts = list() for(var/X in bodyparts) var/obj/item/bodypart/BP = X - if(!isnull(status) && (BP.status != status)) + if(status && BP.status != status) continue if((brute && BP.brute_dam) || (burn && BP.burn_dam) || (stamina && BP.stamina_dam)) parts += BP @@ -171,13 +171,14 @@ update |= picked.heal_damage(brute, burn, stamina, only_robotic, only_organic, FALSE) - brute -= (brute_was - picked.brute_dam) - burn -= (burn_was - picked.burn_dam) - stamina -= (stamina_was - picked.stamina_dam) + brute = round(brute - (brute_was - picked.brute_dam), DAMAGE_PRECISION) + burn = round(burn - (burn_was - picked.burn_dam), DAMAGE_PRECISION) + stamina = round(stamina - (stamina_was - picked.stamina_dam), DAMAGE_PRECISION) parts -= picked if(updating_health) updatehealth() + update_stamina() if(update) update_damage_overlays() update_stamina() //CIT CHANGE - makes sure update_stamina() always gets called after a health update @@ -191,9 +192,9 @@ var/update = 0 while(parts.len && (brute > 0 || burn > 0 || stamina > 0)) var/obj/item/bodypart/picked = pick(parts) - var/brute_per_part = round(brute/parts.len, 0.01) - var/burn_per_part = round(burn/parts.len, 0.01) - var/stamina_per_part = round(stamina/parts.len, 0.01) + var/brute_per_part = round(brute/parts.len, DAMAGE_PRECISION) + var/burn_per_part = round(burn/parts.len, DAMAGE_PRECISION) + var/stamina_per_part = round(stamina/parts.len, DAMAGE_PRECISION) var/brute_was = picked.brute_dam var/burn_was = picked.burn_dam @@ -202,9 +203,9 @@ update |= picked.receive_damage(brute_per_part, burn_per_part, stamina_per_part, FALSE) - brute -= (picked.brute_dam - brute_was) - burn -= (picked.burn_dam - burn_was) - stamina -= (picked.stamina_dam - stamina_was) + brute = round(brute - (picked.brute_dam - brute_was), DAMAGE_PRECISION) + burn = round(burn - (picked.burn_dam - burn_was), DAMAGE_PRECISION) + stamina = round(stamina - (picked.stamina_dam - stamina_was), DAMAGE_PRECISION) parts -= picked if(updating_health) @@ -253,4 +254,3 @@ if(B) var/adjusted_amount = amount - B.get_brain_damage() B.adjust_brain_damage(adjusted_amount, null) - diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm index 4de59d4c3e..ae0e223e08 100644 --- a/code/modules/mob/living/carbon/death.dm +++ b/code/modules/mob/living/carbon/death.dm @@ -9,6 +9,11 @@ emote("deathgasp") . = ..() + + for(var/T in get_traumas()) + var/datum/brain_trauma/BT = T + BT.on_death() + if(SSticker.mode) SSticker.mode.check_win() //Calls the rounds wincheck, mainly for wizard, malf, and changeling now diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 447b0c9f96..0db3d82777 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -77,60 +77,27 @@ if(!.) return var/mob/living/carbon/human/H = user - if(!H.is_wagging_tail()) - H.startTailWag() + if(!istype(H) || !H.dna || !H.dna.species || !H.dna.species.can_wag_tail(H)) + return + if(!H.dna.species.is_wagging_tail()) + H.dna.species.start_wagging_tail(H) else - H.endTailWag() - -/mob/living/carbon/human/proc/is_wagging_tail() - return (dna && dna.species && (("waggingtail_lizard" in dna.species.mutant_bodyparts) || ("waggingtail_human" in dna.species.mutant_bodyparts)|| ("mam_waggingtail" in dna.species.mutant_bodyparts))) + H.dna.species.stop_wagging_tail(H) /datum/emote/living/carbon/human/wag/can_run_emote(mob/user, status_check = TRUE) if(!..()) return FALSE var/mob/living/carbon/human/H = user - if(H.dna && H.dna.species && (("tail_lizard" in H.dna.species.mutant_bodyparts) || ("waggingtail_lizard" in H.dna.species.mutant_bodyparts) || ("tail_human" in H.dna.species.mutant_bodyparts) || ("waggingtail_human" in H.dna.species.mutant_bodyparts)|| ("mam_tail" in H.dna.species.mutant_bodyparts) || ("mam_waggingtail" in H.dna.species.mutant_bodyparts))) - return TRUE + return H.dna && H.dna.species && H.dna.species.can_wag_tail(user) /datum/emote/living/carbon/human/wag/select_message_type(mob/user) . = ..() var/mob/living/carbon/human/H = user - if(H.is_wagging_tail()) + if(!H.dna || !H.dna.species) + return + if(H.dna.species.is_wagging_tail()) . = null -//Don't know where else to put this, it's basically an emote -/mob/living/carbon/human/proc/startTailWag() - if(!dna || !dna.species) - return - if("tail_lizard" in dna.species.mutant_bodyparts) - dna.species.mutant_bodyparts -= "tail_lizard" - dna.species.mutant_bodyparts -= "spines" - dna.species.mutant_bodyparts |= "waggingtail_lizard" - dna.species.mutant_bodyparts |= "waggingspines" - if("tail_human" in dna.species.mutant_bodyparts) - dna.species.mutant_bodyparts -= "tail_human" - dna.species.mutant_bodyparts |= "waggingtail_human" - if("mam_tail" in dna.species.mutant_bodyparts) - dna.species.mutant_bodyparts -= "mam_tail" - dna.species.mutant_bodyparts |= "mam_waggingtail" - update_body() - -/mob/living/carbon/human/proc/endTailWag() - if(!dna || !dna.species) - return - if("waggingtail_lizard" in dna.species.mutant_bodyparts) - dna.species.mutant_bodyparts -= "waggingtail_lizard" - dna.species.mutant_bodyparts -= "waggingspines" - dna.species.mutant_bodyparts |= "tail_lizard" - dna.species.mutant_bodyparts |= "spines" - if("waggingtail_human" in dna.species.mutant_bodyparts) - dna.species.mutant_bodyparts -= "waggingtail_human" - dna.species.mutant_bodyparts |= "tail_human" - if("mam_waggingtail" in dna.species.mutant_bodyparts) - dna.species.mutant_bodyparts -= "mam_waggingtail" - dna.species.mutant_bodyparts |= "mam_tail" - update_body() - /datum/emote/living/carbon/human/wing key = "wing" key_third_person = "wings" diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index bc55487e95..77cf6fc4bb 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -1,4 +1,4 @@ -/mob/living/carbon/human/examine(mob/user) +/mob/living/carbon/human/examine(mob/user) //User is the person being examined //this is very slightly better than it was because you can use it more places. still can't do \his[src] though. var/t_He = p_they(TRUE) var/t_His = p_their(TRUE) @@ -51,7 +51,7 @@ if(gloves && !(SLOT_GLOVES in obscured)) msg += "[t_He] [t_has] [gloves.get_examine_string(user)] on [t_his] hands.\n" else if(FR && length(FR.blood_DNA)) - var/hand_number = get_num_arms() + var/hand_number = get_num_arms(FALSE) if(hand_number) msg += "[t_He] [t_has] [hand_number > 1 ? "" : "a"] blood-stained hand[hand_number > 1 ? "s" : ""]!\n" @@ -80,8 +80,11 @@ msg += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck.\n" //eyes - if(glasses && !(SLOT_GLASSES in obscured)) - msg += "[t_He] [t_has] [glasses.get_examine_string(user)] covering [t_his] eyes.\n" + if(!(SLOT_GLASSES in obscured)) + if(glasses) + msg += "[t_He] [t_has] [glasses.get_examine_string(user)] covering [t_his] eyes.\n" + else if(eye_color == BLOODCULT_EYE && iscultist(src) && has_trait(CULT_EYES)) + msg += "[t_His] eyes are glowing an unnatural red!\n" //ears if(ears && !(SLOT_EARS in obscured)) @@ -91,16 +94,17 @@ if(wear_id) msg += "[t_He] [t_is] wearing [wear_id.get_examine_string(user)].\n" -//CIT CHANGES START HERE - adds genital details to examine text + //Status effects + msg += status_effect_examines() + + //CIT CHANGES START HERE - adds genital details to examine text if(LAZYLEN(internal_organs)) for(var/obj/item/organ/genital/dicc in internal_organs) if(istype(dicc) && dicc.is_exposed()) msg += "[dicc.desc]\n" - + msg += attempt_vr(src,"examine_bellies",args) //vore Code //END OF CIT CHANGES - //Status effects - msg += status_effect_examines() //Jitters switch(jitteriness) @@ -141,12 +145,25 @@ msg += "" var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + var/list/disabled = list() for(var/X in bodyparts) var/obj/item/bodypart/BP = X + if(BP.disabled) + disabled += BP missing -= BP.body_zone for(var/obj/item/I in BP.embedded_objects) msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!\n" + for(var/X in disabled) + var/obj/item/bodypart/BP = X + var/damage_text + if(!(BP.get_damage(include_stamina = FALSE) >= BP.max_damage)) //Stamina is disabling the limb + damage_text = "limp and lifeless" + else + var/more_brute = BP.brute_dam >= BP.burn_dam + damage_text = more_brute ? "broken and mangled" : "burnt and blistered" + msg += "[capitalize(t_his)] [BP.name] is [damage_text]!\n" + //stores missing limbs var/l_limbs_missing = 0 var/r_limbs_missing = 0 @@ -321,13 +338,11 @@ msg += "\[Add crime\] " msg += "\[View comment log\] " msg += "\[Add comment\]\n" - else if(isobserver(user) && traitstring) msg += "Traits: [traitstring]
" if(print_flavor_text() && get_visible_name() != "Unknown")//Are we sure we know who this is? Don't show flavor text unless we can recognize them. Prevents certain metagaming with impersonation. msg += "[print_flavor_text()]\n" - msg += "*---------*
" to_chat(user, msg) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 48b4feec60..1633e42752 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -28,7 +28,7 @@ . = ..() - AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT), CALLBACK(src, .proc/clean_blood)) + AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT = CALLBACK(src, .proc/clean_blood))) /mob/living/carbon/human/ComponentInitialize() @@ -207,33 +207,6 @@ spreadFire(AM) -/mob/living/carbon/human/resist() - . = ..() - if(wear_suit && wear_suit.breakouttime)//added in human cuff breakout proc - return - if(.) - if(canmove && !on_fire) - for(var/obj/item/bodypart/L in bodyparts) - if(istype(L) && L.embedded_objects.len) - for(var/obj/item/I in L.embedded_objects) - if(istype(I) && I.w_class >= WEIGHT_CLASS_NORMAL) //minimum weight class to insta-ripout via resist - remove_embedded_unsafe(L, I, src, 1.5) //forcefully call the remove embedded unsafe proc but with extra pain multiplier. if you want to remove it less painfully, examine and remove it carefully. - return FALSE //Hands are occupied - return . - -/mob/living/carbon/human/proc/remove_embedded_unsafe(obj/item/bodypart/L, obj/item/I, mob/user, painmul = 1) - if(!I || !L || I.loc != src || !(I in L.embedded_objects)) - return - L.embedded_objects -= I - L.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier*I.w_class*painmul)//It hurts to rip it out, get surgery you dingus. And if you're ripping it out quickly via resist, it's gonna hurt even more - I.forceMove(get_turf(src)) - user.put_in_hands(I) - user.emote("scream") - user.visible_message("[user] rips [I] out of [user.p_their()] [L.name]!","You remove [I] from your [L.name].") - if(!has_embedded_objects()) - clear_alert("embeddedobject") - SEND_SIGNAL(user, COMSIG_CLEAR_MOOD_EVENT, "embedded") - return /mob/living/carbon/human/Topic(href, href_list) if(usr.canUseTopic(src, BE_CLOSE, NO_DEXTERY)) @@ -244,10 +217,20 @@ var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore return - var/time_taken = I.embedding.embedded_unsafe_removal_time/I.w_class + var/time_taken = I.embedding.embedded_unsafe_removal_time*I.w_class usr.visible_message("[usr] attempts to remove [I] from [usr.p_their()] [L.name].","You attempt to remove [I] from your [L.name]... (It will take [DisplayTimeText(time_taken)].)") if(do_after(usr, time_taken, needhand = 1, target = src)) - remove_embedded_unsafe(L, I, usr) + if(!I || !L || I.loc != src || !(I in L.embedded_objects)) + return + L.embedded_objects -= I + L.receive_damage(I.embedding.embedded_unsafe_removal_pain_multiplier*I.w_class)//It hurts to rip it out, get surgery you dingus. + I.forceMove(get_turf(src)) + usr.put_in_hands(I) + usr.emote("scream") + usr.visible_message("[usr] successfully rips [I] out of [usr.p_their()] [L.name]!","You successfully remove [I] from your [L.name].") + if(!has_embedded_objects()) + clear_alert("embeddedobject") + SEND_SIGNAL(usr, COMSIG_CLEAR_MOOD_EVENT, "embedded") return if(href_list["item"]) @@ -624,7 +607,7 @@ //Agent cards lower threatlevel. if(istype(idcard, /obj/item/card/id/syndicate)) - threatcount -= 5 + threatcount -= 2 return threatcount @@ -638,8 +621,8 @@ hair_style = pick("Bedhead", "Bedhead 2", "Bedhead 3") underwear = "Nude" update_body() - update_genitals() update_hair() + update_genitals() /mob/living/carbon/human/singularity_pull(S, current_size) ..() @@ -675,13 +658,13 @@ var/they_breathe = !C.has_trait(TRAIT_NOBREATH) var/they_lung = C.getorganslot(ORGAN_SLOT_LUNGS) - if(C.health > HEALTH_THRESHOLD_CRIT) + if(C.health > C.crit_threshold) return src.visible_message("[src] performs CPR on [C.name]!", "You perform CPR on [C.name].") SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "perform_cpr", /datum/mood_event/perform_cpr) C.cpr_time = world.time - add_logs(src, C, "CPRed") + log_combat(src, C, "CPRed") if(they_breathe && they_lung) var/suff = min(C.getOxyLoss(), 7) @@ -695,14 +678,14 @@ /mob/living/carbon/human/cuff_resist(obj/item/I) if(dna && dna.check_mutation(HULK)) - say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk") if(..(I, cuff_break = FAST_CUFFBREAK)) dropItemToGround(I) else if(..()) dropItemToGround(I) -/mob/living/carbon/human/proc/clean_blood(strength) +/mob/living/carbon/human/proc/clean_blood(datum/source, strength) if(strength < CLEAN_STRENGTH_BLOOD) return if(gloves) @@ -810,6 +793,8 @@ hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[BP.body_zone][icon_num]")) for(var/t in get_missing_limbs()) //Missing limbs hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]6")) + for(var/t in get_disabled_limbs()) //Disabled limbs + hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]7")) else hud_used.healthdoll.icon_state = "healthdoll_DEAD" @@ -851,13 +836,6 @@ return 1 ..() -/mob/living/carbon/human/Collide(atom/A) - ..() - var/crashdir = get_dir(src, A) - var/obj/item/flightpack/FP = get_flightpack() - if(FP) - FP.flight_impact(A, crashdir) - /mob/living/carbon/human/vv_get_dropdown() . = ..() . += "---" @@ -867,6 +845,7 @@ .["Make alien"] = "?_src_=vars;[HrefToken()];makealien=[REF(src)]" .["Make slime"] = "?_src_=vars;[HrefToken()];makeslime=[REF(src)]" .["Toggle Purrbation"] = "?_src_=vars;[HrefToken()];purrbation=[REF(src)]" + .["Copy outfit"] = "?_src_=vars;[HrefToken()];copyoutfit=[REF(src)]" /mob/living/carbon/human/MouseDrop_T(mob/living/target, mob/living/user) //If they dragged themselves and we're currently aggressively grabbing them try to piggyback diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index acf4b0734c..aba8e98583 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -156,7 +156,7 @@ else ..() -/mob/living/carbon/human/grippedby(mob/living/user) +/mob/living/carbon/human/grippedby(mob/living/user, instant = FALSE) if(w_uniform) w_uniform.add_fingerprint(user) ..() @@ -220,7 +220,7 @@ else if(!M.client || prob(5)) // only natural monkeys get to stun reliably, (they only do it occasionaly) playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) Knockdown(100) - add_logs(M, src, "tackled") + log_combat(M, src, "tackled") visible_message("[M] has tackled down [src]!", \ "[M] has tackled down [src]!") @@ -259,7 +259,7 @@ playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) visible_message("[M] has slashed at [src]!", \ "[M] has slashed at [src]!") - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") if(!dismembering_strike(M, M.zone_selected)) //Dismemberment successful return 1 apply_damage(damage, BRUTE, affecting, armor_block) @@ -273,7 +273,7 @@ else playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) Knockdown(100) - add_logs(M, src, "tackled") + log_combat(M, src, "tackled") visible_message("[M] has tackled down [src]!", \ "[M] has tackled down [src]!") @@ -357,7 +357,7 @@ visible_message("[M.name] has hit [src]!", \ "[M.name] has hit [src]!", null, COMBAT_MESSAGE_RANGE) - add_logs(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") + log_combat(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") else ..() @@ -788,7 +788,7 @@ if(wear_suit && ((wear_suit.body_parts_covered & HANDS) || (wear_suit.body_parts_covered & ARMS))) arm_clothes = wear_suit if(arm_clothes) - torn_items += arm_clothes + torn_items |= arm_clothes //LEGS & FEET// if(!def_zone || def_zone == BODY_ZONE_L_LEG || def_zone == BODY_ZONE_R_LEG) @@ -800,7 +800,7 @@ if(wear_suit && ((wear_suit.body_parts_covered & FEET) || (wear_suit.body_parts_covered & LEGS))) leg_clothes = wear_suit if(leg_clothes) - torn_items += leg_clothes + torn_items |= leg_clothes for(var/obj/item/I in torn_items) I.take_damage(damage_amount, damage_type, damage_flag, 0) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index ff0dab75a8..763487f7ed 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -1,5 +1,6 @@ /mob/living/carbon/human - hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD, NANITE_HUD, DIAG_NANITE_FULL_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD) + hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPLOYAL_HUD,IMPCHEM_HUD,IMPTRACK_HUD, NANITE_HUD, DIAG_NANITE_FULL_HUD,ANTAG_HUD,GLAND_HUD,SENTIENT_DISEASE_HUD) + hud_type = /datum/hud/human possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM) pressure_resistance = 25 can_buckle = TRUE diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 80714df0d8..6050c3e278 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -4,7 +4,7 @@ /mob/living/carbon/human/canBeHandcuffed() - if(get_num_arms() >= 2) + if(get_num_arms(FALSE) >= 2) return TRUE else return FALSE diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 5bfbb76e46..1addb3615c 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,9 +1,16 @@ +/mob/living/carbon/human/get_movespeed_modifiers() + var/list/considering = ..() + . = considering + if(has_trait(TRAIT_IGNORESLOWDOWN)) + for(var/id in .) + var/list/data = .[id] + if(data[MOVESPEED_DATA_INDEX_FLAGS] & IGNORE_NOSLOW) + .[id] = data + /mob/living/carbon/human/movement_delay() - . = 0 - var/static/config_human_delay - if(isnull(config_human_delay)) - config_human_delay = CONFIG_GET(number/human_delay) - . += ..() + config_human_delay + dna.species.movement_delay(src) + . = ..() + if(dna && dna.species) + . += dna.species.movement_delay(src) /mob/living/carbon/human/slip(knockdown_amount, obj/O, lube) if(has_trait(TRAIT_NOSLIPALL)) @@ -49,20 +56,19 @@ //Bloody footprints var/turf/T = get_turf(src) if(S.bloody_shoes && S.bloody_shoes[S.blood_state]) - var/obj/effect/decal/cleanable/blood/footprints/oldFP = locate(/obj/effect/decal/cleanable/blood/footprints) in T - if(oldFP && oldFP.blood_state == S.blood_state) - return - else - //No oldFP or it's a different kind of blood - S.bloody_shoes[S.blood_state] = max(0, S.bloody_shoes[S.blood_state] - BLOOD_LOSS_PER_STEP) - if (S.bloody_shoes[S.blood_state] > BLOOD_LOSS_IN_SPREAD) - var/obj/effect/decal/cleanable/blood/footprints/FP = new /obj/effect/decal/cleanable/blood/footprints(T) - FP.blood_state = S.blood_state - FP.entered_dirs |= dir - FP.bloodiness = S.bloody_shoes[S.blood_state] - BLOOD_LOSS_IN_SPREAD - FP.add_blood_DNA(S.return_blood_DNA()) - FP.update_icon() - update_inv_shoes() + for(var/obj/effect/decal/cleanable/blood/footprints/oldFP in T) + if (oldFP.blood_state == S.blood_state) + return + //No oldFP or they're all a different kind of blood + S.bloody_shoes[S.blood_state] = max(0, S.bloody_shoes[S.blood_state] - BLOOD_LOSS_PER_STEP) + if (S.bloody_shoes[S.blood_state] > BLOOD_LOSS_IN_SPREAD) + var/obj/effect/decal/cleanable/blood/footprints/FP = new /obj/effect/decal/cleanable/blood/footprints(T) + FP.blood_state = S.blood_state + FP.entered_dirs |= dir + FP.bloodiness = S.bloody_shoes[S.blood_state] - BLOOD_LOSS_IN_SPREAD + FP.add_blood_DNA(S.return_blood_DNA()) + FP.update_icon() + update_inv_shoes() //End bloody footprints S.step_action() diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 2216843716..8cc6d9310b 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -23,7 +23,12 @@ if (notransform) return - if(..()) //not dead + . = ..() + + if (QDELETED(src)) + return 0 + + if(.) //not dead handle_active_genes() if(stat != DEAD) @@ -44,15 +49,15 @@ /mob/living/carbon/human/calculate_affecting_pressure(pressure) - if(istype(loc, /obj/belly)) //START OF CIT CHANGES - Makes it so you don't suffocate while inside vore organs. Remind me to modularize this some time - Bhijn - return ONE_ATMOSPHERE - if(istype(loc, /obj/item/dogborg/sleeper)) - return ONE_ATMOSPHERE //END OF CIT CHANGES if (wear_suit && head && istype(wear_suit, /obj/item/clothing) && istype(head, /obj/item/clothing)) var/obj/item/clothing/CS = wear_suit var/obj/item/clothing/CH = head if (CS.clothing_flags & CH.clothing_flags & STOPSPRESSUREDAMAGE) return ONE_ATMOSPHERE + if(istype(loc, /obj/belly)) //START OF CIT CHANGES - Makes it so you don't suffocate while inside vore organs. Remind me to modularize this some time - Bhijn + return ONE_ATMOSPHERE + if(istype(loc, /obj/item/dogborg/sleeper)) + return ONE_ATMOSPHERE //END OF CIT CHANGES return pressure @@ -65,7 +70,7 @@ else if(eye_blurry) //blurry eyes heal slowly adjust_blurriness(-1) - if (getBrainLoss() >= 60 && !incapacitated(TRUE)) + if (getBrainLoss() >= 60) SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "brain_damage", /datum/mood_event/brain_damage) if(prob(3)) if(prob(25)) @@ -88,7 +93,7 @@ var/L = getorganslot(ORGAN_SLOT_LUNGS) if(!L) - if(health >= HEALTH_THRESHOLD_CRIT) + if(health >= crit_threshold) adjustOxyLoss(HUMAN_MAX_OXYLOSS + 1) else if(!has_trait(TRAIT_NOCRITDAMAGE)) adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS) @@ -320,19 +325,11 @@ HM.on_life(src) /mob/living/carbon/human/proc/handle_heart() - if(!can_heartattack()) - return - var/we_breath = !has_trait(TRAIT_NOBREATH, SPECIES_TRAIT) - if(!undergoing_cardiac_arrest()) return - // Cardiac arrest, unless heart is stabilized - if(has_trait(TRAIT_STABLEHEART)) - return - if(we_breath) adjustOxyLoss(8) Unconscious(80) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index cb0e754450..fe323ae7c1 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -78,6 +78,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/whitelisted = 0 //Is this species restricted to certain players? var/whitelist = list() //List the ckeys that can use this species, if it's whitelisted.: list("John Doe", "poopface666", "SeeALiggerPullTheTrigger") Spaces & capitalization can be included or ignored entirely for each key as it checks for both. + /////////// // PROCS // /////////// @@ -100,7 +101,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) GLOB.roundstart_races += "human" /datum/species/proc/check_roundstart_eligible() - if(id in (CONFIG_GET(keyed_flag_list/roundstart_races))) + if(id in (CONFIG_GET(keyed_list/roundstart_races))) return TRUE return FALSE @@ -247,7 +248,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/obj/item/organ/I = new path() I.Insert(C) -/datum/species/proc/on_species_gain(mob/living/carbon/C, datum/species/old_species) +/datum/species/proc/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load) // Drop the items the new species can't wear for(var/slot_id in no_equip) var/obj/item/thing = C.get_item_by_slot(slot_id) @@ -263,6 +264,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) C.Digitigrade_Leg_Swap(FALSE) C.mob_biotypes = inherent_biotypes + regenerate_organs(C,old_species) if(exotic_bloodtype && C.dna.blood_type != exotic_bloodtype) @@ -289,6 +291,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(TRAIT_VIRUSIMMUNE in inherent_traits) for(var/datum/disease/A in C.diseases) A.cure(FALSE) + SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species) //CITADEL EDIT @@ -299,18 +302,18 @@ GLOBAL_LIST_EMPTY(roundstart_races) C.canbearoused = C.client.prefs.arousable // EDIT ENDS -/datum/species/proc/on_species_loss(mob/living/carbon/C) +/datum/species/proc/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) if(C.dna.species.exotic_bloodtype) C.dna.blood_type = random_blood_type() if(DIGITIGRADE in species_traits) C.Digitigrade_Leg_Swap(TRUE) for(var/X in inherent_traits) C.remove_trait(X, SPECIES_TRAIT) + SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src) /datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour) H.remove_overlay(HAIR_LAYER) - var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD) if(!HD) //Decapitated return @@ -496,7 +499,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) else standing += mutable_appearance(undershirt.icon, undershirt.icon_state, -BODY_LAYER) - if(H.socks && H.get_num_legs() >= 2 && !(DIGITIGRADE in species_traits)) + if(H.socks && H.get_num_legs(FALSE) >= 2 && !(DIGITIGRADE in species_traits)) var/datum/sprite_accessory/socks/socks = GLOB.socks_list[H.socks] if(socks) standing += mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER) @@ -652,15 +655,15 @@ GLOBAL_LIST_EMPTY(roundstart_races) if("tail_lizard") S = GLOB.tails_list_lizard[H.dna.features["tail_lizard"]] if("waggingtail_lizard") - S.= GLOB.animated_tails_list_lizard[H.dna.features["tail_lizard"]] + S = GLOB.animated_tails_list_lizard[H.dna.features["tail_lizard"]] if("tail_human") S = GLOB.tails_list_human[H.dna.features["tail_human"]] if("waggingtail_human") - S.= GLOB.animated_tails_list_human[H.dna.features["tail_human"]] + S = GLOB.animated_tails_list_human[H.dna.features["tail_human"]] if("spines") S = GLOB.spines_list[H.dna.features["spines"]] if("waggingspines") - S.= GLOB.animated_spines_list[H.dna.features["spines"]] + S = GLOB.animated_spines_list[H.dna.features["spines"]] if("snout") S = GLOB.snouts_list[H.dna.features["snout"]] if("frills") @@ -683,18 +686,17 @@ GLOBAL_LIST_EMPTY(roundstart_races) S = GLOB.caps_list[H.dna.features["caps"]] else S = citadel_mutant_bodyparts(bodypart, H) - if(!S || S.icon_state == "none") continue var/mutable_appearance/accessory_overlay = mutable_appearance(S.icon, layer = -layer) //A little rename so we don't have to use tail_lizard or tail_human when naming the sprites. - if(bodypart == "tail_lizard" || bodypart == "tail_human" || bodypart == "mam_tail" || bodypart == "slimecoontail" || bodypart == "xenotail") + if(bodypart == "tail_lizard" || bodypart == "tail_human" || bodypart == "mam_tail" || bodypart == "xenotail") bodypart = "tail" else if(bodypart == "waggingtail_lizard" || bodypart == "waggingtail_human" || bodypart == "mam_waggingtail") bodypart = "waggingtail" - if(bodypart == "mam_ears") + if(bodypart == "mam_ears" || bodypart == "ears") bodypart = "ears" if(bodypart == "xenohead") bodypart = "xhead" @@ -703,6 +705,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) accessory_overlay.icon_state = "[g]_[bodypart]_[S.icon_state]_[layertext]" else accessory_overlay.icon_state = "m_[bodypart]_[S.icon_state]_[layertext]" + if(S.center) accessory_overlay = center_image(accessory_overlay, S.dimension_x, S.dimension_y) @@ -818,6 +821,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) extra2_accessory_overlay.color = "#[H.hair_color]" standing += extra2_accessory_overlay + H.overlays_standing[layer] = standing.Copy() standing = list() @@ -826,6 +830,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) H.apply_overlay(BODY_FRONT_LAYER) H.apply_overlay(BODY_TAUR_LAYER) // CITADEL EDIT + //This exists so sprite accessories can still be per-layer without having to include that layer's //number in their sprite name, which causes issues when those numbers change. /datum/species/proc/mutant_bodyparts_layertext(layer) @@ -841,13 +846,14 @@ GLOBAL_LIST_EMPTY(roundstart_races) return "TAUR" //END EDIT + /datum/species/proc/spec_life(mob/living/carbon/human/H) if(H.has_trait(TRAIT_NOBREATH)) H.setOxyLoss(0) H.losebreath = 0 var/takes_crit_damage = (!H.has_trait(TRAIT_NOCRITDAMAGE)) - if((H.health < HEALTH_THRESHOLD_CRIT) && takes_crit_damage) + if((H.health < H.crit_threshold) && takes_crit_damage) H.adjustBruteLoss(1) /datum/species/proc/spec_death(gibbed, mob/living/carbon/human/H) @@ -862,8 +868,8 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(!I.species_exception || !is_type_in_list(src, I.species_exception)) return FALSE - var/num_arms = H.get_num_arms() - var/num_legs = H.get_num_legs() + var/num_arms = H.get_num_arms(FALSE) + var/num_legs = H.get_num_legs(FALSE) switch(slot) if(SLOT_HANDS) @@ -1140,22 +1146,12 @@ GLOBAL_LIST_EMPTY(roundstart_races) switch(H.nutrition) if(NUTRITION_LEVEL_FULL to INFINITY) - SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/fat) H.throw_alert("nutrition", /obj/screen/alert/fat) - if(NUTRITION_LEVEL_WELL_FED to NUTRITION_LEVEL_FULL) - SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/wellfed) - H.clear_alert("nutrition") - if( NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED) - SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/fed) - H.clear_alert("nutrition") - if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED) - SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "nutrition") + if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FULL) H.clear_alert("nutrition") if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) - SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/hungry) H.throw_alert("nutrition", /obj/screen/alert/hungry) if(0 to NUTRITION_LEVEL_STARVING) - SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/starving) H.throw_alert("nutrition", /obj/screen/alert/starving) /datum/species/proc/update_health_hud(mob/living/carbon/human/H) @@ -1204,18 +1200,14 @@ GLOBAL_LIST_EMPTY(roundstart_races) /datum/species/proc/movement_delay(mob/living/carbon/human/H) . = 0 //We start at 0. var/flight = 0 //Check for flight and flying items - var/flightpack = 0 var/ignoreslow = 0 var/gravity = 0 - var/obj/item/flightpack/F = H.get_flightpack() - if(istype(F) && F.flight) - flightpack = 1 if(H.movement_type & FLYING) flight = 1 gravity = H.has_gravity() - if(!flightpack && gravity) //Check for chemicals and innate speedups and slowdowns if we're moving using our body and not a flying suit + if(gravity && !flight) //Check for chemicals and innate speedups and slowdowns if we're on the ground if(H.has_trait(TRAIT_GOTTAGOFAST)) . -= 1 if(H.has_trait(TRAIT_GOTTAGOREALLYFAST)) @@ -1237,12 +1229,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) else if(istype(T) && T.allow_thrust(0.01, H)) . -= 2 - if(flightpack && F.boost) - . -= F.boost_speed - else if(flightpack && F.brake) - . += 1 - - if(!ignoreslow && !flightpack && gravity) + if(!ignoreslow && gravity) if(H.wear_suit) . += H.wear_suit.slowdown if(H.shoes) @@ -1297,7 +1284,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(target.health >= 0 && !(target.has_trait(TRAIT_FAKEDEATH))) target.help_shake_act(user) if(target != user) - add_logs(user, target, "shaked") + log_combat(user, target, "shaked") return 1 else var/we_breathe = !user.has_trait(TRAIT_NOBREATH) @@ -1387,7 +1374,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(user.limb_destroyer) target.dismembering_strike(user, affecting.body_zone) target.apply_damage(damage, BRUTE, affecting, armor_block) - add_logs(user, target, "punched") + log_combat(user, target, "punched") if((target.stat != DEAD) && damage >= user.dna.species.punchstunthreshold) target.visible_message("[user] has knocked [target] down!", \ "[user] has knocked [target] down!", null, COMBAT_MESSAGE_RANGE) @@ -1397,7 +1384,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) target.forcesay(GLOB.hit_appends) /datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style) -// CITADEL EDIT slap mouthy gits + // CITADEL EDIT slap mouthy gits var/aim_for_mouth = user.zone_selected == "mouth" var/target_on_help_and_unarmed = target.a_intent == INTENT_HELP && !target.get_active_held_item() var/target_aiming_for_mouth = target.zone_selected == "mouth" @@ -1434,9 +1421,8 @@ GLOBAL_LIST_EMPTY(roundstart_races) "[user] has pushed [target]!", null, COMBAT_MESSAGE_RANGE) target.apply_effect(40, EFFECT_KNOCKDOWN, target.run_armor_check(affecting, "melee", "Your armor prevents your fall!", "Your armor softens your fall!")) target.forcesay(GLOB.hit_appends) - add_logs(user, target, "disarmed", " pushing them to the ground") + log_combat(user, target, "pushed over") return*/ - if(!target.combatmode) // CITADEL CHANGE randn += -10 //CITADEL CHANGE - being out of combat mode makes it easier for you to get disarmed if(user.resting) //CITADEL CHANGE @@ -1457,14 +1443,14 @@ GLOBAL_LIST_EMPTY(roundstart_races) else I = null playsound(target, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - add_logs(user, target, "disarmed", "[I ? " removing \the [I]" : ""]") + log_combat(user, target, "disarmed", "[I ? " removing \the [I]" : ""]") return playsound(target, 'sound/weapons/punchmiss.ogg', 25, 1, -1) target.visible_message("[user] attempted to disarm [target]!", \ "[user] attemped to disarm [target]!", null, COMBAT_MESSAGE_RANGE) - add_logs(user, target, "attempted to disarm") + log_combat(user, target, "attempted to disarm") /datum/species/proc/spec_hitby(atom/movable/AM, mob/living/carbon/human/H) @@ -1481,7 +1467,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(M.mind) attacker_style = M.mind.martial_art if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK)) - add_logs(M, H, "attempted to touch") + log_combat(M, H, "attempted to touch") H.visible_message("[M] attempted to touch [H]!") return 0 switch(M.a_intent) @@ -1516,7 +1502,6 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/armor_block = H.run_armor_check(affecting, "melee", "Your armor has protected your [hit_area].", "Your armor has softened a hit to your [hit_area].",I.armour_penetration) armor_block = min(90,armor_block) //cap damage reduction at 90% var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) - //CIT CHANGES START HERE - combatmode and resting checks var/totitemdamage = I.force if(iscarbon(user)) @@ -1529,7 +1514,6 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(!H.combatmode) totitemdamage *= 1.5 //CIT CHANGES END HERE - var/weakness = H.check_weakness(I, user) apply_damage(totitemdamage * weakness, I.damtype, def_zone, armor_block, H) //CIT CHANGE - replaces I.force with totitemdamage @@ -1540,7 +1524,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) //dismemberment var/probability = I.get_dismemberment_chance(affecting) - if(prob(probability) || (H.has_trait(TRAIT_EASYDISMEMBER) && prob(2*probability))) + if(prob(probability) || (H.has_trait(TRAIT_EASYDISMEMBER) && prob(probability))) //try twice if(affecting.dismember(I.damtype)) I.add_mob_blood(H) playsound(get_turf(H), I.get_dismember_sound(), 80, 1) @@ -1559,20 +1543,20 @@ GLOBAL_LIST_EMPTY(roundstart_races) switch(hit_area) if(BODY_ZONE_HEAD) - if(H.stat == CONSCIOUS && armor_block < 50) + if(!I.is_sharp() && armor_block < 50) if(prob(I.force)) - H.visible_message("[H] has been knocked senseless!", \ - "[H] has been knocked senseless!") - H.confused = max(H.confused, 20) H.adjustBrainLoss(20) - H.adjust_blurriness(10) + if(H.stat == CONSCIOUS) + H.visible_message("[H] has been knocked senseless!", \ + "[H] has been knocked senseless!") + H.confused = max(H.confused, 20) + H.adjust_blurriness(10) if(prob(10)) H.gain_trauma(/datum/brain_trauma/mild/concussion) else - if(!I.is_sharp()) - H.adjustBrainLoss(I.force * 0.2) + H.adjustBrainLoss(I.force * 0.2) - if(!I.is_sharp() && prob(I.force + ((100 - H.health) * 0.5)) && H != user) // rev deconversion through blunt trauma. + if(H.stat == CONSCIOUS && H != user && prob(I.force + ((100 - H.health) * 0.5))) // rev deconversion through blunt trauma. var/datum/antagonist/rev/rev = H.mind.has_antag_datum(/datum/antagonist/rev) if(rev) rev.remove_revolutionary(FALSE, user) @@ -1589,7 +1573,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) H.update_inv_glasses() if(BODY_ZONE_CHEST) - if(H.stat == CONSCIOUS && armor_block < 50) + if(H.stat == CONSCIOUS && !I.is_sharp() && armor_block < 50) if(prob(I.force)) H.visible_message("[H] has been knocked down!", \ "[H] has been knocked down!") @@ -1713,19 +1697,20 @@ GLOBAL_LIST_EMPTY(roundstart_races) SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "hot", /datum/mood_event/hot) var/burn_damage - switch(H.bodytemperature) - if(BODYTEMP_HEAT_DAMAGE_LIMIT to 400) - H.throw_alert("temp", /obj/screen/alert/hot, 1) - burn_damage = HEAT_DAMAGE_LEVEL_1 - if(400 to 460) - H.throw_alert("temp", /obj/screen/alert/hot, 2) - burn_damage = HEAT_DAMAGE_LEVEL_2 - else - H.throw_alert("temp", /obj/screen/alert/hot, 3) - if(H.on_fire) - burn_damage = HEAT_DAMAGE_LEVEL_3 + var/firemodifier = H.fire_stacks / 50 + if (H.on_fire) + burn_damage = max(log(2-firemodifier,(H.bodytemperature-BODYTEMP_NORMAL))-5,0) + else + firemodifier = min(firemodifier, 0) + burn_damage = max(log(2-firemodifier,(H.bodytemperature-BODYTEMP_NORMAL))-5,0) // this can go below 5 at log 2.5 + if (burn_damage) + switch(burn_damage) + if(0 to 2) + H.throw_alert("temp", /obj/screen/alert/hot, 1) + if(2 to 4) + H.throw_alert("temp", /obj/screen/alert/hot, 2) else - burn_damage = HEAT_DAMAGE_LEVEL_2 + H.throw_alert("temp", /obj/screen/alert/hot, 3) burn_damage = burn_damage * heatmod * H.physiology.heat_mod if (H.stat < UNCONSCIOUS && (prob(burn_damage) * 10) / 4) //40% for level 3 damage on humans H.emote("scream") @@ -1816,7 +1801,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(H.wear_suit && ((H.wear_suit.body_parts_covered & HANDS) || (H.wear_suit.body_parts_covered & ARMS))) arm_clothes = H.wear_suit if(arm_clothes) - burning_items += arm_clothes + burning_items |= arm_clothes //LEGS & FEET// var/obj/item/clothing/leg_clothes = null @@ -1827,7 +1812,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) if(H.wear_suit && ((H.wear_suit.body_parts_covered & FEET) || (H.wear_suit.body_parts_covered & LEGS))) leg_clothes = H.wear_suit if(leg_clothes) - burning_items += leg_clothes + burning_items |= leg_clothes for(var/X in burning_items) var/obj/item/I = X @@ -1836,7 +1821,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/thermal_protection = H.get_thermal_protection() - if(thermal_protection >= FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT && !no_protection) + if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT && !no_protection) return if(thermal_protection >= FIRE_SUIT_MAX_TEMP_PROTECT && !no_protection) H.adjust_bodytemperature(11) @@ -1869,3 +1854,17 @@ GLOBAL_LIST_EMPTY(roundstart_races) /datum/species/proc/negates_gravity(mob/living/carbon/human/H) return 0 + +//////////////// +//Tail Wagging// +//////////////// + +/datum/species/proc/can_wag_tail(mob/living/carbon/human/H) + return FALSE + +/datum/species/proc/is_wagging_tail(mob/living/carbon/human/H) + return FALSE + +/datum/species/proc/start_wagging_tail(mob/living/carbon/human/H) + +/datum/species/proc/stop_wagging_tail(mob/living/carbon/human/H) diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index d6857e1c93..da7de39a03 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -1,5 +1,5 @@ /datum/species/dullahan - name = "dullahan" + name = "Dullahan" id = "dullahan" default_color = "FFFFFF" species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS) @@ -129,6 +129,7 @@ else qdel(src) + /obj/item/dullahan_relay/Destroy() if(!QDELETED(owner)) var/mob/living/carbon/human/H = owner diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm new file mode 100644 index 0000000000..7207a4f49d --- /dev/null +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -0,0 +1,130 @@ +//Subtype of human +/datum/species/human/felinid + name = "Felinid" + id = "felinid" + limbs_id = "human" + + mutant_bodyparts = list("ears", "tail_human") + default_features = list("mcolor" = "FFF", "tail_human" = "Cat", "ears" = "Cat", "wings" = "None") + + mutantears = /obj/item/organ/ears/cat + mutanttail = /obj/item/organ/tail/cat + +/datum/species/human/felinid/qualifies_for_rank(rank, list/features) + return TRUE + +//Curiosity killed the cat's wagging tail. +/datum/species/human/felinid/spec_death(gibbed, mob/living/carbon/human/H) + if(H) + stop_wagging_tail(H) + +/datum/species/human/felinid/spec_stun(mob/living/carbon/human/H,amount) + if(H) + stop_wagging_tail(H) + . = ..() + +/datum/species/human/felinid/can_wag_tail(mob/living/carbon/human/H) + return ("tail_human" in mutant_bodyparts) || ("waggingtail_human" in mutant_bodyparts) + +/datum/species/human/felinid/is_wagging_tail(mob/living/carbon/human/H) + return ("waggingtail_human" in mutant_bodyparts) + +/datum/species/human/felinid/start_wagging_tail(mob/living/carbon/human/H) + if("tail_human" in mutant_bodyparts) + mutant_bodyparts -= "tail_human" + mutant_bodyparts |= "waggingtail_human" + H.update_body() + +/datum/species/human/felinid/stop_wagging_tail(mob/living/carbon/human/H) + if("waggingtail_human" in mutant_bodyparts) + mutant_bodyparts -= "waggingtail_human" + mutant_bodyparts |= "tail_human" + H.update_body() + +/datum/species/human/felinid/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load) + if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(!pref_load) //Hah! They got forcefully purrbation'd. Force default felinid parts on them if they have no mutant parts in those areas! + if(H.dna.features["tail_human"] == "None") + H.dna.features["tail_human"] = "Cat" + if(H.dna.features["ears"] == "None") + H.dna.features["ears"] = "Cat" + if(H.dna.features["ears"] == "Cat") + var/obj/item/organ/ears/cat/ears = new + ears.Insert(H, drop_if_replaced = FALSE) + else + mutantears = /obj/item/organ/ears + if(H.dna.features["tail_human"] == "Cat") + var/obj/item/organ/tail/cat/tail = new + tail.Insert(H, drop_if_replaced = FALSE) + else + mutanttail = null + return ..() + +/datum/species/human/felinid/on_species_loss(mob/living/carbon/H, datum/species/new_species, pref_load) + var/obj/item/organ/ears/cat/ears = H.getorgan(/obj/item/organ/ears/cat) + var/obj/item/organ/tail/cat/tail = H.getorgan(/obj/item/organ/tail/cat) + + if(ears) + var/obj/item/organ/ears/NE + if(new_species && new_species.mutantears) + // Roundstart cat ears override new_species.mutantears, reset it here. + new_species.mutantears = initial(new_species.mutantears) + if(new_species.mutantears) + NE = new new_species.mutantears + if(!NE) + // Go with default ears + NE = new /obj/item/organ/ears + NE.Insert(H, drop_if_replaced = FALSE) + + if(tail) + var/obj/item/organ/tail/NT + if(new_species && new_species.mutanttail) + // Roundstart cat tail overrides new_species.mutanttail, reset it here. + new_species.mutanttail = initial(new_species.mutanttail) + if(new_species.mutanttail) + NT = new new_species.mutanttail + if(NT) + NT.Insert(H, drop_if_replaced = FALSE) + else + tail.Remove(H) + +/proc/mass_purrbation() + for(var/M in GLOB.mob_list) + if(ishumanbasic(M)) + purrbation_apply(M) + CHECK_TICK + +/proc/mass_remove_purrbation() + for(var/M in GLOB.mob_list) + if(ishumanbasic(M)) + purrbation_remove(M) + CHECK_TICK + +/proc/purrbation_toggle(mob/living/carbon/human/H, silent = FALSE) + if(!ishumanbasic(H)) + return + if(!iscatperson(H)) + purrbation_apply(H, silent) + . = TRUE + else + purrbation_remove(H, silent) + . = FALSE + +/proc/purrbation_apply(mob/living/carbon/human/H, silent = FALSE) + if(!ishuman(H) || iscatperson(H)) + return + H.set_species(/datum/species/human/felinid) + + if(!silent) + to_chat(H, "Something is nya~t right.") + playsound(get_turf(H), 'sound/effects/meow1.ogg', 50, 1, -1) + +/proc/purrbation_remove(mob/living/carbon/human/H, silent = FALSE) + if(!ishuman(H) || !iscatperson(H)) + return + + H.set_species(/datum/species/human) + + if(!silent) + to_chat(H, "You are no longer a cat.") diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 538d0860ab..afc3fa2d04 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -27,7 +27,7 @@ var/random_eligible = TRUE //If false, the golem subtype can't be made through golem mutation toxin var/prefix = "Iron" - var/list/special_names + var/list/special_names = list("Tarkus") var/human_surname_chance = 3 var/special_name_chance = 5 @@ -68,8 +68,9 @@ meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/golem/adamantine mutant_organs = list(/obj/item/organ/adamantine_resonator, /obj/item/organ/vocal_cords/adamantine) fixed_mut_color = "4ed" - info_text = "As an Adamantine Golem, you possess special vocal cords allowing you to \"resonate\" messages to all golems." + info_text = "As an Adamantine Golem, you possess special vocal cords allowing you to \"resonate\" messages to all golems. Your unique mineral makeup makes you immune to most types of magic." prefix = "Adamantine" + special_names = null /datum/species/golem/adamantine/on_species_gain(mob/living/carbon/C, datum/species/old_species) ..() @@ -86,7 +87,7 @@ fixed_mut_color = "a3d" meat = /obj/item/stack/ore/plasma //Can burn and takes damage from heat - inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER) + inherent_traits = list(TRAIT_NOBREATH, TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER) //no RESISTHEAT, NOFIRE info_text = "As a Plasma Golem, you burn easily. Be careful, if you get hot enough while burning, you'll blow up!" heatmod = 0 //fine until they blow up prefix = "Plasma" @@ -135,7 +136,7 @@ if(H.fire_stacks) to_chat(owner, "You ignite yourself!") else - to_chat(owner, "You try ignite yourself, but fail!") + to_chat(owner, "You try to ignite yourself, but fail!") H.IgniteMob() //firestacks are already there passively //Harder to hurt @@ -159,6 +160,7 @@ meat = /obj/item/stack/ore/gold info_text = "As a Gold Golem, you are faster but less resistant than the average golem." prefix = "Golden" + special_names = list("Boy") //Heavier, thus higher chance of stunning when punching /datum/species/golem/silver @@ -167,7 +169,7 @@ fixed_mut_color = "ddd" punchstunthreshold = 9 //60% chance, from 40% meat = /obj/item/stack/ore/silver - info_text = "As a Silver Golem, your attacks are heavier and have a higher chance of stunning." + info_text = "As a Silver Golem, your attacks have a higher chance of stunning. Being made of silver, your body is immune to most types of magic." prefix = "Silver" special_names = list("Surfer", "Chariot", "Lining") @@ -194,6 +196,7 @@ attack_verb = "smash" attack_sound = 'sound/effects/meteorimpact.ogg' //hits pretty hard prefix = "Plasteel" + special_names = null //Immune to ash storms /datum/species/golem/titanium @@ -204,6 +207,7 @@ info_text = "As a Titanium Golem, you are immune to ash storms, and slightly more resistant to burn damage." burnmod = 0.9 prefix = "Titanium" + special_names = list("Dioxide") /datum/species/golem/titanium/on_species_gain(mob/living/carbon/C, datum/species/old_species) . = ..() @@ -222,6 +226,7 @@ info_text = "As a Plastitanium Golem, you are immune to both ash storms and lava, and slightly more resistant to burn damage." burnmod = 0.8 prefix = "Plastitanium" + special_names = null /datum/species/golem/plastitanium/on_species_gain(mob/living/carbon/C, datum/species/old_species) . = ..() @@ -257,10 +262,10 @@ /datum/species/golem/wood name = "Wood Golem" id = "wood golem" - fixed_mut_color = "49311c" + fixed_mut_color = "9E704B" meat = /obj/item/stack/sheet/mineral/wood //Can burn and take damage from heat - inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER) + inherent_traits = list(TRAIT_NOBREATH, TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER) armor = 30 burnmod = 1.25 heatmod = 1.5 @@ -315,6 +320,7 @@ var/last_event = 0 var/active = null prefix = "Uranium" + special_names = list("Oxide", "Rod", "Meltdown") /datum/species/golem/uranium/spec_life(mob/living/carbon/human/H) if(!active) @@ -334,9 +340,10 @@ armor = 0 burnmod = 3 //melts easily brutemod = 0.25 - info_text = "As a Sand Golem, you are immune to physical bullets and take very little brute damage, but are extremely vulnerable to burn damage. You will also turn to sand when dying, preventing any form of recovery." + info_text = "As a Sand Golem, you are immune to physical bullets and take very little brute damage, but are extremely vulnerable to burn damage and energy weapons. You will also turn to sand when dying, preventing any form of recovery." attack_sound = 'sound/effects/shovel_dig.ogg' prefix = "Sand" + special_names = list("Castle", "Bag", "Dune", "Worm", "Storm") /datum/species/golem/sand/spec_death(gibbed, mob/living/carbon/human/H) H.visible_message("[H] turns into a pile of sand!") @@ -364,9 +371,10 @@ armor = 0 brutemod = 3 //very fragile burnmod = 0.25 - info_text = "As a Glass Golem, you reflect lasers and energy weapons, and are very resistant to burn damage, but you are extremely vulnerable to brute damage. On death, you'll shatter beyond any hope of recovery." + info_text = "As a Glass Golem, you reflect lasers and energy weapons, and are very resistant to burn damage. However, you are extremely vulnerable to brute damage. On death, you'll shatter beyond any hope of recovery." attack_sound = 'sound/effects/glassbr2.ogg' prefix = "Glass" + special_names = list("Lens", "Prism", "Fiber", "Bead") /datum/species/golem/glass/spec_death(gibbed, mob/living/carbon/human/H) playsound(H, "shatter", 70, 1) @@ -397,7 +405,7 @@ id = "bluespace golem" fixed_mut_color = "33f" meat = /obj/item/stack/ore/bluespace_crystal - info_text = "As a Bluespace Golem, are spatially unstable: you will teleport when hit, and you can teleport manually at a long distance." + info_text = "As a Bluespace Golem, you are spatially unstable: You will teleport when hit, and you can teleport manually at a long distance." attack_verb = "bluespace punch" attack_sound = 'sound/effects/phasein.ogg' prefix = "Bluespace" @@ -494,10 +502,11 @@ punchdamagehigh = 1 punchstunthreshold = 2 //Harmless and can't stun meat = /obj/item/stack/ore/bananium - info_text = "As a Bananium Golem, you are made for pranking. Your body emits natural honks, and you cannot hurt people when punching them. Your skin also emits bananas when damaged." + info_text = "As a Bananium Golem, you are made for pranking. Your body emits natural honks, and you can barely even hurt people when punching them. Your skin also bleeds banana peels when damaged." attack_verb = "honk" attack_sound = 'sound/items/airhorn2.ogg' prefix = "Bananium" + special_names = null var/last_honk = 0 var/honkooldown = 0 @@ -566,16 +575,17 @@ id = "runic golem" limbs_id = "cultgolem" sexes = FALSE - info_text = "As a Runic Golem, you possess eldritch powers granted by the Elder God Nar'Sie." + info_text = "As a Runic Golem, you possess eldritch powers granted by the Elder Goddess Nar'Sie." species_traits = list(NOBLOOD,NO_UNDERWEAR,NOEYES) //no mutcolors prefix = "Runic" + special_names = null var/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/golem/phase_shift var/obj/effect/proc_holder/spell/targeted/abyssal_gaze/abyssal_gaze var/obj/effect/proc_holder/spell/targeted/dominate/dominate /datum/species/golem/runic/random_name(gender,unique,lastname) - var/edgy_first_name = pick("Razor","Blood","Dark","Evil","Cold","Pale","Black","Silent","Chaos","Deadly") + var/edgy_first_name = pick("Razor","Blood","Dark","Evil","Cold","Pale","Black","Silent","Chaos","Deadly","Coldsteel") var/edgy_last_name = pick("Edge","Night","Death","Razor","Blade","Steel","Calamity","Twilight","Shadow","Nightmare") //dammit Razor Razor var/golem_name = "[edgy_first_name] [edgy_last_name]" return golem_name @@ -619,8 +629,7 @@ id = "clockwork golem" say_mod = "clicks" limbs_id = "clockgolem" - info_text = "As a clockwork golem, you are faster than \ - other types of golem (being a machine), and are immune to electric shocks." + info_text = "As a Clockwork Golem, you are faster than other types of golems. On death, you will break down into scrap." species_traits = list(NOBLOOD,NO_UNDERWEAR,NOEYES) inherent_biotypes = list(MOB_ROBOTIC, MOB_HUMANOID) armor = 20 //Reinforced, but much less so to allow for fast movement @@ -628,9 +637,9 @@ attack_sound = 'sound/magic/clockwork/anima_fragment_attack.ogg' sexes = FALSE speedmod = 0 - siemens_coeff = 0 damage_overlay_type = "synth" prefix = "Clockwork" + special_names = list("Remnant", "Relic", "Scrap", "Vestige") //RIP Ratvar var/has_corpse /datum/species/golem/clockwork/on_species_gain(mob/living/carbon/human/H) @@ -666,14 +675,15 @@ blacklisted = TRUE dangerous_existence = TRUE random_eligible = FALSE - + info_text = "As a Clockwork Golem Servant, you are faster than other types of golems." //warcult golems leave a corpse /datum/species/golem/cloth name = "Cloth Golem" id = "cloth golem" limbs_id = "clothgolem" sexes = FALSE - info_text = "As a Cloth Golem, you are able to reform yourself after death, provided your remains aren't burned or destroyed. You are, of course, very flammable." + info_text = "As a Cloth Golem, you are able to reform yourself after death, provided your remains aren't burned or destroyed. You are, of course, very flammable. \ + Being made of cloth, your body is magic resistant and faster than that of other golems, but weaker and less resilient." species_traits = list(NOBLOOD,NO_UNDERWEAR) //no mutcolors, and can burn inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_NOBREATH,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_NOGUNS) inherent_biotypes = list(MOB_UNDEAD, MOB_HUMANOID) @@ -684,6 +694,7 @@ punchstunthreshold = 7 punchdamagehigh = 8 // not as heavy as stone prefix = "Cloth" + special_names = null /datum/species/golem/cloth/on_species_gain(mob/living/carbon/C, datum/species/old_species) ..() @@ -734,6 +745,7 @@ var/mob/living/carbon/human/cloth_golem /obj/structure/cloth_pile/Initialize(mapload, mob/living/carbon/human/H) + . = ..() if(!QDELETED(H) && is_species(H, /datum/species/golem/cloth)) H.unequip_everything() H.forceMove(src) @@ -781,11 +793,12 @@ fire_act() /datum/species/golem/plastic - name = "Plastic" + name = "Plastic Golem" id = "plastic golem" prefix = "Plastic" + special_names = null fixed_mut_color = "fff" - info_text = "As a Plastic Golem, you are capable of ventcrawling, and passing through plastic flaps." + info_text = "As a Plastic Golem, you are capable of ventcrawling and passing through plastic flaps as long as you are naked." /datum/species/golem/plastic/on_species_gain(mob/living/carbon/C, datum/species/old_species) . = ..() diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm index a266f42f4a..a7c20b981a 100644 --- a/code/modules/mob/living/carbon/human/species_types/humans.dm +++ b/code/modules/mob/living/carbon/human/species_types/humans.dm @@ -10,23 +10,32 @@ disliked_food = GROSS | RAW liked_food = JUNKFOOD | FRIED - /datum/species/human/qualifies_for_rank(rank, list/features) return TRUE //Pure humans are always allowed in all roles. -//Curiosity killed the cat's wagging tail. /datum/species/human/spec_death(gibbed, mob/living/carbon/human/H) if(H) - H.endTailWag() + stop_wagging_tail(H) /datum/species/human/spec_stun(mob/living/carbon/human/H,amount) if(H) - H.endTailWag() + stop_wagging_tail(H) . = ..() -/datum/species/human/on_species_gain(mob/living/carbon/human/H, datum/species/old_species) - if(H.dna.features["ears"] == "Cat") - mutantears = /obj/item/organ/ears/cat - if(H.dna.features["tail_human"] == "Cat") - mutanttail = /obj/item/organ/tail/cat - ..() +/datum/species/human/can_wag_tail(mob/living/carbon/human/H) + return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/human/is_wagging_tail(mob/living/carbon/human/H) + return ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/human/start_wagging_tail(mob/living/carbon/human/H) + if("tail_human" in mutant_bodyparts) + mutant_bodyparts -= "mam_tail" + mutant_bodyparts |= "mam_waggingtail" + H.update_body() + +/datum/species/human/stop_wagging_tail(mob/living/carbon/human/H) + if("mam_waggingtail" in mutant_bodyparts) + mutant_bodyparts -= "mam_waggingtail" + mutant_bodyparts |= "mam_tail" + H.update_body() diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index cbbfe2b4d7..c72534972c 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -5,9 +5,9 @@ default_color = "00FF90" say_mod = "chirps" species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,NOBLOOD) - inherent_traits = list(TRAIT_TOXINLOVER) mutant_bodyparts = list("mam_tail", "mam_ears", "taur") //CIT CHANGE default_features = list("mcolor" = "FFF", "mam_tail" = "None", "mam_ears" = "None") //CIT CHANGE + inherent_traits = list(TRAIT_TOXINLOVER) meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime exotic_blood = "slimejelly" damage_overlay_type = "" @@ -64,7 +64,7 @@ if(!limbs_to_consume.len) H.losebreath++ return - if(H.get_num_legs()) //Legs go before arms + if(H.get_num_legs(FALSE)) //Legs go before arms limbs_to_consume -= list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM) consumed_limb = H.get_bodypart(pick(limbs_to_consume)) consumed_limb.drop_limb() @@ -647,7 +647,7 @@ if(message) var/msg = "\[[species.slimelink_owner.real_name]'s Slime Link\] [H]: [message]" - log_talk(H,"SlimeLink: [key_name(H)] : [msg]",LOGSAY) + log_directed_talk(H, species.slimelink_owner, msg, LOG_SAY, "slime link") for(var/X in species.linked_mobs) var/mob/living/M = X if(QDELETED(M) || M.stat == DEAD) @@ -684,7 +684,7 @@ var/msg = sanitize(input("Message:", "Telepathy") as text|null) if(msg) - log_talk(H,"SlimeTelepathy: [key_name(H)]->[key_name(M)] : [msg]",LOGSAY) + log_directed_talk(H, M, msg, LOG_SAY, "slime telepathy") to_chat(M, "You hear an alien voice in your head... [msg]") to_chat(H, "You telepathically said: \"[msg]\" to [M]") for(var/dead in GLOB.dead_mob_list) diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index bd286e9bc2..f083e7c658 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -37,17 +37,39 @@ /datum/species/lizard/qualifies_for_rank(rank, list/features) return TRUE - + //I wag in death /datum/species/lizard/spec_death(gibbed, mob/living/carbon/human/H) if(H) - H.endTailWag() + stop_wagging_tail(H) /datum/species/lizard/spec_stun(mob/living/carbon/human/H,amount) if(H) - H.endTailWag() + stop_wagging_tail(H) . = ..() +/datum/species/lizard/can_wag_tail(mob/living/carbon/human/H) + return ("tail_lizard" in mutant_bodyparts) || ("waggingtail_lizard" in mutant_bodyparts) + +/datum/species/lizard/is_wagging_tail(mob/living/carbon/human/H) + return ("waggingtail_lizard" in mutant_bodyparts) + +/datum/species/lizard/start_wagging_tail(mob/living/carbon/human/H) + if("tail_lizard" in mutant_bodyparts) + mutant_bodyparts -= "tail_lizard" + mutant_bodyparts -= "spines" + mutant_bodyparts |= "waggingtail_lizard" + mutant_bodyparts |= "waggingspines" + H.update_body() + +/datum/species/lizard/stop_wagging_tail(mob/living/carbon/human/H) + if("waggingtail_lizard" in mutant_bodyparts) + mutant_bodyparts -= "waggingtail_lizard" + mutant_bodyparts -= "waggingspines" + mutant_bodyparts |= "tail_lizard" + mutant_bodyparts |= "spines" + H.update_body() + /* Lizard subspecies: ASHWALKERS */ diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm index 7d5dc2022f..d15d989384 100644 --- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm +++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm @@ -1,5 +1,5 @@ /datum/species/moth - name = "Mothmen" + name = "Mothman" id = "moth" say_mod = "flutters" default_color = "00FF00" diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index 22a44e3fa7..3d42fb32cf 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -180,6 +180,7 @@ AddComponent(/datum/component/butchering, 80, 70) /obj/item/light_eater/afterattack(atom/movable/AM, mob/user, proximity) + . = ..() if(!proximity) return if(isopenturf(AM)) //So you can actually melee with it diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index e3a72601ed..d079080eaa 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -7,7 +7,7 @@ sexes = 0 meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton species_traits = list(NOBLOOD) - inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT) + inherent_traits = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_FAKEDEATH) inherent_biotypes = list(MOB_UNDEAD, MOB_HUMANOID) mutanttongue = /obj/item/organ/tongue/bone damage_overlay_type = ""//let's not show bloody wounds or burns over bones. diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm index 59de0755a2..6f7f227a80 100644 --- a/code/modules/mob/living/carbon/human/species_types/vampire.dm +++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm @@ -1,5 +1,5 @@ /datum/species/vampire - name = "vampire" + name = "Vampire" id = "vampire" default_color = "FFFFFF" species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,DRINKSBLOOD) diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm index 801d0a7192..504dbb514b 100644 --- a/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -2,14 +2,14 @@ /datum/species/zombie // 1spooky - name = "High Functioning Zombie" + name = "High-Functioning Zombie" id = "zombie" say_mod = "moans" sexes = 0 blacklisted = 1 meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/zombie species_traits = list(NOBLOOD,NOZOMBIE,NOTRANSSTING) - inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NOBREATH) + inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NOBREATH,TRAIT_NODEATH,TRAIT_FAKEDEATH) inherent_biotypes = list(MOB_UNDEAD, MOB_HUMANOID) mutanttongue = /obj/item/organ/tongue/zombie var/static/list/spooks = list('sound/hallucinations/growl1.ogg','sound/hallucinations/growl2.ogg','sound/hallucinations/growl3.ogg','sound/hallucinations/veryfar_noise.ogg','sound/hallucinations/wail.ogg') @@ -29,6 +29,7 @@ armor = 20 // 120 damage to KO a zombie, which kills it speedmod = 1.6 mutanteyes = /obj/item/organ/eyes/night_vision/zombie + var/heal_rate = 1 var/regen_cooldown = 0 /datum/species/zombie/infectious/check_roundstart_eligible() @@ -46,15 +47,25 @@ /datum/species/zombie/infectious/spec_life(mob/living/carbon/C) . = ..() C.a_intent = INTENT_HARM // THE SUFFERING MUST FLOW + + //Zombies never actually die, they just fall down until they regenerate enough to rise back up. + //They must be restrained, beheaded or gibbed to stop being a threat. if(regen_cooldown < world.time) - C.heal_overall_damage(4,4) - C.adjustToxLoss(-4) - if(prob(4)) + var/heal_amt = heal_rate + if(C.InCritical()) + heal_amt *= 2 + C.heal_overall_damage(heal_amt,heal_amt) + C.adjustToxLoss(-heal_amt) + if(!C.InCritical() && prob(4)) playsound(C, pick(spooks), 50, TRUE, 10) - if(C.InCritical()) - C.death() - // Zombies only move around when not in crit, they instantly - // succumb otherwise, and will standup again soon + +//Congrats you somehow died so hard you stopped being a zombie +/datum/species/zombie/infectious/spec_death(mob/living/carbon/C) + . = ..() + var/obj/item/organ/zombie_infection/infection + infection = C.getorganslot(ORGAN_SLOT_ZOMBIE) + if(infection) + qdel(infection) /datum/species/zombie/infectious/on_species_gain(mob/living/carbon/C, datum/species/old_species) . = ..() diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index ff4b77a4ee..4621986c0c 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -11,9 +11,20 @@ if(stat != DEAD) //Reagent processing needs to come before breathing, to prevent edge cases. handle_organs() - if(..()) //not dead + . = ..() + + if (QDELETED(src)) + return + + if(.) //not dead handle_blood() + if(stat != DEAD) + var/bprv = handle_bodyparts() + if(bprv & BODYPART_LIFE_UPDATE_HEALTH) + updatehealth() + update_stamina() + if(stat != DEAD) handle_brain_damage() @@ -22,6 +33,7 @@ if(stat == DEAD) stop_sound_channel(CHANNEL_HEARTBEAT) + rot() //Updates the number of stored chemicals for powers handle_changeling() @@ -52,7 +64,7 @@ return if(ismob(loc)) return - if(istype(loc, /obj/belly)) + if(isbelly(loc)) return var/datum/gas_mixture/environment @@ -65,7 +77,7 @@ if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL)) losebreath++ //You can't breath at all when in critical or when being choked, so you're going to miss a breath - else if(health <= HEALTH_THRESHOLD_CRIT) + else if(health <= crit_threshold) losebreath += 0.25 //You're having trouble breathing in soft crit, so you'll miss a breath one in four times //Suffocate @@ -160,7 +172,7 @@ else //Enough oxygen failed_last_breath = 0 - if(health >= HEALTH_THRESHOLD_CRIT) + if(health >= crit_threshold) adjustOxyLoss(-5) oxygen_used = breath_gases[/datum/gas/oxygen][MOLES] clear_alert("not_enough_oxy") @@ -210,15 +222,59 @@ hallucination += 10 else if(bz_partialpressure > 0.01) hallucination += 5 + //TRITIUM if(breath_gases[/datum/gas/tritium]) var/tritium_partialpressure = (breath_gases[/datum/gas/tritium][MOLES]/breath.total_moles())*breath_pressure radiation += tritium_partialpressure/10 + //NITRYL - if (breath_gases[/datum/gas/nitryl]) + if(breath_gases[/datum/gas/nitryl]) var/nitryl_partialpressure = (breath_gases[/datum/gas/nitryl][MOLES]/breath.total_moles())*breath_pressure adjustFireLoss(nitryl_partialpressure/4) + //MIASMA + if(breath_gases[/datum/gas/miasma]) + var/miasma_partialpressure = (breath_gases[/datum/gas/miasma][MOLES]/breath.total_moles())*breath_pressure + + if(prob(1 * miasma_partialpressure)) + var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(2,3) + miasma_disease.name = "Unknown" + ForceContractDisease(miasma_disease, TRUE, TRUE) + + //Miasma side effects + switch(miasma_partialpressure) + if(1 to 5) + // At lower pp, give out a little warning + SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell") + if(prob(5)) + to_chat(src, "There is an unpleasant smell in the air.") + if(5 to 20) + //At somewhat higher pp, warning becomes more obvious + if(prob(15)) + to_chat(src, "You smell something horribly decayed inside this room.") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/bad_smell) + if(15 to 30) + //Small chance to vomit. By now, people have internals on anyway + if(prob(5)) + to_chat(src, "The stench of rotting carcasses is unbearable!") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench) + vomit() + if(30 to INFINITY) + //Higher chance to vomit. Let the horror start + if(prob(25)) + to_chat(src, "The stench of rotting carcasses is unbearable!") + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench) + vomit() + else + SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell") + + + //Clear all moods if no miasma at all + else + SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell") + + breath.garbage_collect() @@ -246,9 +302,46 @@ if(!.) return FALSE //to differentiate between no internals and active, but empty internals +// Make corpses rot, emitting miasma +/mob/living/carbon/proc/rot() + // Properly stored corpses shouldn't create miasma + if(istype(loc, /obj/structure/closet/crate/coffin)|| istype(loc, /obj/structure/closet/body_bag) || istype(loc, /obj/structure/bodycontainer)) + return + + // No decay if formaldehyde in corpse or when the corpse is charred + if(reagents.has_reagent("formaldehyde", 15) || has_trait(TRAIT_HUSK)) + return + + // Also no decay if corpse chilled or not organic/undead + if(bodytemperature <= T0C-10 || (!(MOB_ORGANIC in mob_biotypes) && !(MOB_UNDEAD in mob_biotypes))) + return + + // Wait a bit before decaying + if(world.time - timeofdeath < 1200) + return + + var/deceasedturf = get_turf(src) + + // Closed turfs don't have any air in them, so no gas building up + if(!istype(deceasedturf,/turf/open)) + return + + var/turf/open/miasma_turf = deceasedturf + + var/list/cached_gases = miasma_turf.air.gases + + ASSERT_GAS(/datum/gas/miasma, miasma_turf.air) + cached_gases[/datum/gas/miasma][MOLES] += 0.02 + /mob/living/carbon/proc/handle_blood() return +/mob/living/carbon/proc/handle_bodyparts() + for(var/I in bodyparts) + var/obj/item/bodypart/BP = I + if(BP.needs_processing) + . |= BP.on_life() + /mob/living/carbon/proc/handle_organs() for(var/V in internal_organs) var/obj/item/organ/O = V @@ -437,6 +530,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put if(drunkenness) drunkenness = max(drunkenness - (drunkenness * 0.04), 0) if(drunkenness >= 6) + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "drunk", /datum/mood_event/drunk) if(prob(25)) slurring += 2 jitteriness = max(jitteriness - 3, 0) @@ -457,12 +551,12 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put else ballmer_percent = (-abs(drunkenness - 13.35) / 0.9) + 1 if(prob(5)) - say(pick(GLOB.ballmer_good_msg)) + say(pick(GLOB.ballmer_good_msg), forced = "ballmer") SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = BALLMER_POINTS * ballmer_percent)) if(drunkenness > 26) // by this point you're into windows ME territory if(prob(5)) SSresearch.science_tech.remove_point_list(list(TECHWEB_POINT_TYPE_GENERIC = BALLMER_POINTS)) - say(pick(GLOB.ballmer_windows_me_msg)) + say(pick(GLOB.ballmer_windows_me_msg), forced = "ballmer") if(drunkenness >= 41) if(prob(25)) @@ -551,9 +645,9 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put reagents.metabolize(src, can_overdose=FALSE, liverless = TRUE) if(has_trait(TRAIT_STABLEHEART)) return - adjustToxLoss(8, TRUE, TRUE) + adjustToxLoss(4, TRUE, TRUE) if(prob(30)) - to_chat(src, "You feel confused and nauseated...")//actual symptoms of liver failure + to_chat(src, "You feel a stabbing pain in your abdomen!") //////////////// @@ -577,19 +671,26 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put ///////////////////////////////////// /mob/living/carbon/proc/can_heartattack() - if(dna && dna.species && (NOBLOOD in dna.species.species_traits)) //not all carbons have species! + if(!needs_heart()) return FALSE var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART) if(!heart || heart.synthetic) return FALSE return TRUE -/mob/living/carbon/proc/undergoing_cardiac_arrest() - if(!can_heartattack()) +/mob/living/carbon/proc/needs_heart() + if(has_trait(TRAIT_STABLEHEART)) return FALSE + if(dna && dna.species && (NOBLOOD in dna.species.species_traits)) //not all carbons have species! + return FALSE + return TRUE + +/mob/living/carbon/proc/undergoing_cardiac_arrest() var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART) if(istype(heart) && heart.beating) return FALSE + else if(!needs_heart()) + return FALSE return TRUE /mob/living/carbon/proc/set_heartattack(status) diff --git a/code/modules/mob/living/carbon/monkey/combat.dm b/code/modules/mob/living/carbon/monkey/combat.dm index 303057d376..901fb48b20 100644 --- a/code/modules/mob/living/carbon/monkey/combat.dm +++ b/code/modules/mob/living/carbon/monkey/combat.dm @@ -56,7 +56,7 @@ return 1 if(IsUnconscious()) return 1 - if(IsStun()) + if(IsStun() || IsKnockdown()) return 1 if(stat) return 1 @@ -397,7 +397,7 @@ /mob/living/carbon/monkey/bullet_act(obj/item/projectile/Proj) if(istype(Proj , /obj/item/projectile/beam)||istype(Proj, /obj/item/projectile/bullet)) if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE)) - if(!Proj.nodamage && Proj.damage < src.health) + if(!Proj.nodamage && Proj.damage < src.health && isliving(Proj.firer)) retaliate(Proj.firer) ..() diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm index 1ddea78bf2..55b5d67c24 100644 --- a/code/modules/mob/living/carbon/monkey/life.dm +++ b/code/modules/mob/living/carbon/monkey/life.dm @@ -12,8 +12,8 @@ if(..()) if(!client) - if(stat == CONSCIOUS) - if(on_fire || buckled || restrained() || (resting && canmove)) //CIT CHANGE - makes it so monkeys attempt to resist if they're resting + if(stat == CONSCIOUS) + if(on_fire || buckled || restrained() || (resting && canmove)) //CIT CHANGE - makes it so monkeys attempt to resist if they're resting) if(!resisting && prob(MONKEY_RESIST_PROB)) resisting = TRUE walk_to(src,0) diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index 19592f7b63..55ecebda8c 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -14,8 +14,7 @@ unique_name = TRUE bodyparts = list(/obj/item/bodypart/chest/monkey, /obj/item/bodypart/head/monkey, /obj/item/bodypart/l_arm/monkey, /obj/item/bodypart/r_arm/monkey, /obj/item/bodypart/r_leg/monkey, /obj/item/bodypart/l_leg/monkey) - - + hud_type = /datum/hud/monkey /mob/living/carbon/monkey/Initialize(mapload, cubespawned=FALSE, mob/spawner) verbs += /mob/living/proc/mob_sleep @@ -27,7 +26,6 @@ //initialize limbs create_bodyparts() - create_internal_organs() . = ..() @@ -59,26 +57,31 @@ internal_organs += new /obj/item/organ/stomach ..() -/mob/living/carbon/monkey/movement_delay() - if(reagents) - if(reagents.has_reagent("morphine")) - return -1 - - if(reagents.has_reagent("nuka_cola")) - return -1 - +/mob/living/carbon/monkey/on_reagent_change() . = ..() + remove_movespeed_modifier(MOVESPEED_ID_MONKEY_REAGENT_SPEEDMOD, TRUE) + var/amount + if(reagents.has_reagent("morphine")) + amount = -1 + if(reagents.has_reagent("nuka_cola")) + amount = -1 + if(amount) + add_movespeed_modifier(MOVESPEED_ID_MONKEY_REAGENT_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = amount) + +/mob/living/carbon/monkey/updatehealth() + . = ..() + var/slow = 0 var/health_deficiency = (100 - health) if(health_deficiency >= 45) - . += (health_deficiency / 25) + slow += (health_deficiency / 25) + add_movespeed_modifier(MOVESPEED_ID_MONKEY_HEALTH_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = slow) +/mob/living/carbon/monkey/adjust_bodytemperature(amount) + . = ..() + var/slow = 0 if (bodytemperature < 283.222) - . += (283.222 - bodytemperature) / 10 * 1.75 - - var/static/config_monkey_delay - if(isnull(config_monkey_delay)) - config_monkey_delay = CONFIG_GET(number/monkey_delay) - . += config_monkey_delay + slow += (283.222 - bodytemperature) / 10 * 1.75 + add_movespeed_modifier(MOVESPEED_ID_MONKEY_TEMPERATURE_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = amount) /mob/living/carbon/monkey/Stat() ..() diff --git a/code/modules/mob/living/carbon/monkey/monkey_defense.dm b/code/modules/mob/living/carbon/monkey/monkey_defense.dm index f6b8e19468..df90dd56fd 100644 --- a/code/modules/mob/living/carbon/monkey/monkey_defense.dm +++ b/code/modules/mob/living/carbon/monkey/monkey_defense.dm @@ -54,7 +54,7 @@ if(!affecting) affecting = get_bodypart(BODY_ZONE_CHEST) apply_damage(damage, BRUTE, affecting) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") else playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) @@ -66,7 +66,7 @@ if (prob(25)) Knockdown(40) playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - add_logs(M, src, "pushed") + log_combat(M, src, "pushed") visible_message("[M] has pushed down [src]!", \ "[M] has pushed down [src]!", null, COMBAT_MESSAGE_RANGE) else if(dropItemToGround(get_active_held_item())) @@ -90,7 +90,7 @@ "[M] has slashed [name]!", null, COMBAT_MESSAGE_RANGE) var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") if(!affecting) affecting = get_bodypart(BODY_ZONE_CHEST) if(!dismembering_strike(M, affecting.body_zone)) //Dismemberment successful @@ -115,7 +115,7 @@ visible_message("[M] has disarmed [name]!", "[M] has disarmed [name]!", null, COMBAT_MESSAGE_RANGE) else I = null - add_logs(M, src, "disarmed", "[I ? " removing \the [I]" : ""]") + log_combat(M, src, "disarmed", "[I ? " removing \the [I]" : ""]") updatehealth() diff --git a/code/modules/mob/living/carbon/say.dm b/code/modules/mob/living/carbon/say.dm index d4a222abdc..f6e43f487f 100644 --- a/code/modules/mob/living/carbon/say.dm +++ b/code/modules/mob/living/carbon/say.dm @@ -45,4 +45,4 @@ return for(var/T in get_traumas()) var/datum/brain_trauma/trauma = T - message = trauma.on_hear(message, speaker, message_language, raw_message, radio_freq) + message = trauma.on_hear(message, speaker, message_language, raw_message, radio_freq) \ No newline at end of file diff --git a/code/modules/mob/living/carbon/status_procs.dm b/code/modules/mob/living/carbon/status_procs.dm index d850efcee6..766bd376b1 100644 --- a/code/modules/mob/living/carbon/status_procs.dm +++ b/code/modules/mob/living/carbon/status_procs.dm @@ -45,7 +45,7 @@ if(druggy) overlay_fullscreen("high", /obj/screen/fullscreen/high) throw_alert("high", /obj/screen/alert/high) - SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "high", /datum/mood_event/drugs/high) + SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "high", /datum/mood_event/high) else clear_fullscreen("high") clear_alert("high") diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index bb16aeb7bf..f6888a21ec 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -26,14 +26,14 @@ /mob/living/proc/spread_bodyparts() return -/mob/living/dust(just_ash = FALSE, drop_items = FALSE) +/mob/living/dust(just_ash, drop_items, force) death(TRUE) if(drop_items) unequip_everything() if(buckled) - buckled.unbuckle_mob(src,force=1) + buckled.unbuckle_mob(src, force = TRUE) dust_animation() spawn_dust(just_ash) @@ -74,9 +74,12 @@ update_canmove() med_hud_set_health() med_hud_set_status() - addtimer(CALLBACK(src, .proc/med_hud_set_status), (DEFIB_TIME_LIMIT * 10) + 1) + if(!gibbed && !QDELETED(src)) + addtimer(CALLBACK(src, .proc/med_hud_set_status), (DEFIB_TIME_LIMIT * 10) + 1) stop_pulling() + SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed) + if (client) client.move_delay = initial(client.move_delay) diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 458c19edfb..320b75b052 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -1,7 +1,7 @@ /* EMOTE DATUMS */ /datum/emote/living - mob_type_allowed_typecache = list(/mob/living) + mob_type_allowed_typecache = /mob/living mob_type_blacklist_typecache = list(/mob/living/simple_animal/slime, /mob/living/brain) /datum/emote/living/blush @@ -60,7 +60,7 @@ /datum/emote/living/cough/can_run_emote(mob/user, status_check = TRUE) . = ..() - if(user.reagents.get_reagent("menthol") || user.reagents.get_reagent("peppermint_patty")) + if(user.reagents && (user.reagents.get_reagent("menthol") || user.reagents.get_reagent("peppermint_patty"))) return FALSE /datum/emote/living/dance diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 40b11c14ce..c55252ddb6 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -43,7 +43,10 @@ //Breathing, if applicable handle_breathing(times_fired) - handle_diseases() // DEAD check is in the proc itself; we want it to spread even if the mob is dead, but to handle its disease-y properties only if you're not. + handle_diseases()// DEAD check is in the proc itself; we want it to spread even if the mob is dead, but to handle its disease-y properties only if you're not. + + if (QDELETED(src)) // diseases can qdel the mob via transformations + return if(stat != DEAD) //Random events (vomiting etc) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 7647f9664e..684484ed68 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -9,6 +9,10 @@ diag_hud.add_to_hud(src) faction += "[REF(src)]" GLOB.mob_living_list += src + initialize_footstep() + +/mob/living/proc/initialize_footstep() + AddComponent(/datum/component/footstep) /mob/living/prepare_huds() ..() @@ -39,31 +43,31 @@ /mob/living/proc/OpenCraftingMenu() return -//Generic Collide(). Override MobCollide() and ObjCollide() instead of this. -/mob/living/Collide(atom/A) +//Generic Bump(). Override MobBump() and ObjBump() instead of this. +/mob/living/Bump(atom/A) if(..()) //we are thrown onto something return if (buckled || now_pushing) return if(ismob(A)) var/mob/M = A - if(MobCollide(M)) + if(MobBump(M)) return if(isobj(A)) var/obj/O = A - if(ObjCollide(O)) + if(ObjBump(O)) return if(ismovableatom(A)) var/atom/movable/AM = A if(PushAM(AM)) return -/mob/living/CollidedWith(atom/movable/AM) +/mob/living/Bumped(atom/movable/AM) ..() last_bumped = world.time //Called when we bump onto a mob -/mob/living/proc/MobCollide(mob/M) +/mob/living/proc/MobBump(mob/M) //Even if we don't push/swap places, we "touched" them, so spread fire spreadFire(M) @@ -96,6 +100,7 @@ if(!(world.time % 5)) to_chat(src, "[L] is restraining [P], you cannot push past.") return 1 + //CIT CHANGES START HERE - makes it so resting stops you from moving through standing folks without a short delay if(resting && !L.resting) if(attemptingcrawl) @@ -116,6 +121,7 @@ attemptingcrawl = FALSE return TRUE //END OF CIT CHANGES + if(moving_diagonally)//no mob swap during diagonal moves. return 1 @@ -170,8 +176,24 @@ if(prob(I.block_chance*2)) return 1 +/mob/living/get_photo_description(obj/item/camera/camera) + var/list/mob_details = list() + var/list/holding = list() + var/len = length(held_items) + if(len) + for(var/obj/item/I in held_items) + if(!holding.len) + holding += "They are holding \a [I]" + else if(held_items.Find(I) == len) + holding += ", and \a [I]." + else + holding += ", \a [I]" + holding += "." + mob_details += "You can also see [src] on the photo[health < (maxHealth * 0.75) ? ", looking a bit hurt":""][holding ? ". [holding.Join("")]":"."]." + return mob_details.Join("") + //Called when we bump onto an obj -/mob/living/proc/ObjCollide(obj/O) +/mob/living/proc/ObjBump(obj/O) return //Called when we want to push an atom/movable @@ -223,7 +245,7 @@ if(AM.pulledby) if(!supress_message) visible_message("[src] has pulled [AM] from [AM.pulledby]'s grip.") - add_logs(AM, AM.pulledby, "pulled from", src) + log_combat(AM, AM.pulledby, "pulled from", src) AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once. pulling = AM @@ -235,7 +257,7 @@ if(ismob(AM)) var/mob/M = AM - add_logs(src, M, "grabbed", addition="passive grab") + log_combat(src, M, "grabbed", addition="passive grab") if(!supress_message) visible_message("[src] has grabbed [M][(zone_selected == "l_arm" || zone_selected == "r_arm")? " by their hands":" passively"]!") //Cit change - And they thought ERP was bad. if(!iscarbon(src)) @@ -279,7 +301,7 @@ /mob/living/pointed(atom/A as mob|obj|turf in view()) if(incapacitated()) return FALSE - if(has_trait(TRAIT_FAKEDEATH)) + if(has_trait(TRAIT_DEATHCOMA)) return FALSE if(!..()) return FALSE @@ -289,7 +311,7 @@ /mob/living/verb/succumb(whispered as null) set hidden = TRUE if (InCritical()) - log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", INDIVIDUAL_ATTACK_LOG) + log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", LOG_ATTACK) adjustOxyLoss(health - HEALTH_THRESHOLD_DEAD) updatehealth() if(!whispered) @@ -301,7 +323,7 @@ return TRUE /mob/living/proc/InCritical() - return (health <= HEALTH_THRESHOLD_CRIT && (stat == SOFT_CRIT || stat == UNCONSCIOUS)) + return (health <= crit_threshold && (stat == SOFT_CRIT || stat == UNCONSCIOUS)) /mob/living/proc/InFullCritical() return (health <= HEALTH_THRESHOLD_FULLCRIT && stat == UNCONSCIOUS) @@ -363,6 +385,7 @@ to_chat(src, "You are now [resting ? "resting" : "getting up"].") update_canmove() */ + //Recursive function to find everything a mob is holding. Really shitty proc tbh. /mob/living/get_contents() var/list/ret = list() @@ -487,27 +510,6 @@ if(lying && !buckled && prob(getBruteLoss()*200/maxHealth)) makeTrail(newloc, T, old_direction) -/mob/living/movement_delay(ignorewalk = 0) - . = 0 - if(isopenturf(loc) && !is_flying()) - var/turf/open/T = loc - . += T.slowdown - var/static/datum/config_entry/number/run_delay/config_run_delay - var/static/datum/config_entry/number/walk_delay/config_walk_delay - if(isnull(config_run_delay)) - config_run_delay = CONFIG_GET(number/run_delay) - config_walk_delay = CONFIG_GET(number/walk_delay) - if(ignorewalk) - . += config_run_delay.value_cache - else - switch(m_intent) - if(MOVE_INTENT_RUN) - if(drowsyness > 0) - . += 6 - . += config_run_delay.value_cache - if(MOVE_INTENT_WALK) - . += config_walk_delay.value_cache - /mob/living/proc/makeTrail(turf/target_turf, turf/start, direction) if(!has_gravity()) return @@ -594,8 +596,8 @@ //resisting grabs (as if it helps anyone...) if(!restrained(ignore_grab = 1) && pulledby) visible_message("[src] resists against [pulledby]'s grip!") + log_combat(src, pulledby, "resisted grab") resist_grab() - add_logs(pulledby, src, "resisted grab") return //unbuckling yourself @@ -631,7 +633,7 @@ if(pulledby.grab_state) if(prob(30/pulledby.grab_state)) visible_message("[src] has broken free of [pulledby]'s grip!") - add_logs(pulledby, src, "broke grab") + log_combat(pulledby, src, "broke grab") pulledby.stop_pulling() return 0 if(moving_resist && client) //we resisted by trying to move @@ -698,10 +700,10 @@ var/list/L = where if(what == who.get_item_for_held_index(L[2])) if(who.dropItemToGround(what)) - add_logs(src, who, "stripped [what] off") + log_combat(src, who, "stripped [what] off") if(what == who.get_item_by_slot(where)) if(who.dropItemToGround(what)) - add_logs(src, who, "stripped [what] off") + log_combat(src, who, "stripped [what] off") // The src mob is trying to place an item on someone // Override if a certain mob should be behave differently when placing items (can't, for example) @@ -822,16 +824,6 @@ /mob/living/proc/update_stamina() return -/* -/mob/living/carbon/update_stamina() - var/stam = getStaminaLoss() - if(stam) - var/total_health = (health - stam) - if(total_health <= HEALTH_THRESHOLD_CRIT && !stat) - to_chat(src, "You're too exhausted to keep going...") - Knockdown(100) - setStaminaLoss(health - 2, FALSE, FALSE) - update_health_hud() */ //CITADEL OVERRIDE /mob/living/carbon/alien/update_stamina() return @@ -944,7 +936,7 @@ ExtinguishMob() //Share fire evenly between the two mobs -//Called in MobCollide() and Crossed() +//Called in MobBump() and Crossed() /mob/living/proc/spreadFire(mob/living/L) if(!istype(L)) return @@ -986,7 +978,7 @@ //Updates canmove, lying and icons. Could perhaps do with a rename but I can't think of anything to describe it. //Robots, animals and brains have their own version so don't worry about them /mob/living/proc/update_canmove() - var/ko = IsKnockdown() || IsUnconscious() || (stat && (stat != SOFT_CRIT || pulledby)) || (has_trait(TRAIT_FAKEDEATH)) + var/ko = IsKnockdown() || IsUnconscious() || (stat && (stat != SOFT_CRIT || pulledby)) || (has_trait(TRAIT_DEATHCOMA)) var/move_and_fall = stat == SOFT_CRIT && !pulledby var/chokehold = pulledby && pulledby.grab_state >= GRAB_NECK var/buckle_lying = !(buckled && !buckled.buckle_lying) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 3b010ed8ec..f5b44db70f 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -88,7 +88,7 @@ var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].",I.armour_penetration) apply_damage(I.throwforce, dtype, zone, armor) if(I.thrownby) - add_logs(I.thrownby, src, "threw and hit", I) + log_combat(I.thrownby, src, "threw and hit", I) else return 1 else @@ -116,10 +116,10 @@ updatehealth() visible_message("[M.name] has hit [src]!", \ "[M.name] has hit [src]!", null, COMBAT_MESSAGE_RANGE) - add_logs(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") + log_combat(M.occupant, src, "attacked", M, "(INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") else step_away(src,M) - add_logs(M.occupant, src, "pushed", M) + log_combat(M.occupant, src, "pushed", M) visible_message("[M] pushes [src] out of the way.", null, null, 5) /mob/living/fire_act() @@ -144,17 +144,21 @@ grippedby(user) //proc to upgrade a simple pull into a more aggressive grab. -/mob/living/proc/grippedby(mob/living/carbon/user) +/mob/living/proc/grippedby(mob/living/carbon/user, instant = FALSE) if(user.grab_state < GRAB_KILL) user.changeNext_move(CLICK_CD_GRABBING) playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) if(user.grab_state) //only the first upgrade is instantaneous var/old_grab_state = user.grab_state - var/grab_upgrade_time = 30 + var/grab_upgrade_time = instant ? 0 : 30 visible_message("[user] starts to tighten [user.p_their()] grip on [src]!", \ "[user] starts to tighten [user.p_their()] grip on you!") - add_logs(user, src, "attempted to strangle", addition="grab") + switch(user.grab_state) + if(GRAB_AGGRESSIVE) + log_combat(user, src, "attempted to neck grab", addition="neck grab") + if(GRAB_NECK) + log_combat(user, src, "attempted to strangle", addition="kill grab") if(!do_mob(user, src, grab_upgrade_time)) return 0 if(!user.pulling || user.pulling != src || user.grab_state != old_grab_state || user.a_intent != INTENT_GRAB) @@ -162,20 +166,20 @@ user.grab_state++ switch(user.grab_state) if(GRAB_AGGRESSIVE) - add_logs(user, src, "grabbed", addition="aggressive grab") + log_combat(user, src, "grabbed", addition="aggressive grab") visible_message("[user] has grabbed [src] aggressively!", \ "[user] has grabbed [src] aggressively!") drop_all_held_items() stop_pulling() if(GRAB_NECK) - add_logs(user, src, "grabbed", addition="neck grab") + log_combat(user, src, "grabbed", addition="neck grab") visible_message("[user] has grabbed [src] by the neck!",\ "[user] has grabbed you by the neck!") update_canmove() //we fall down if(!buckled && !density) Move(user.loc) if(GRAB_KILL) - add_logs(user, src, "strangled", addition="grab") + log_combat(user, src, "strangled", addition="kill grab") visible_message("[user] is strangling [src]!", \ "[user] is strangling you!") update_canmove() //we fall down @@ -199,7 +203,7 @@ return FALSE if (stat != DEAD) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") M.do_attack_animation(src) visible_message("The [M.name] glomps [src]!", \ "The [M.name] glomps [src]!", null, COMBAT_MESSAGE_RANGE) @@ -220,7 +224,7 @@ M.do_attack_animation(src) visible_message("\The [M] [M.attacktext] [src]!", \ "\The [M] [M.attacktext] [src]!", null, COMBAT_MESSAGE_RANGE) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") return TRUE @@ -239,7 +243,7 @@ return FALSE M.do_attack_animation(src, ATTACK_EFFECT_BITE) if (prob(75)) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1) visible_message("[M.name] bites [src]!", \ "[M.name] bites [src]!", null, COMBAT_MESSAGE_RANGE) @@ -262,7 +266,7 @@ L.do_attack_animation(src) if(prob(90)) - add_logs(L, src, "attacked") + log_combat(L, src, "attacked") visible_message("[L.name] bites [src]!", \ "[L.name] bites [src]!", null, COMBAT_MESSAGE_RANGE) playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1) @@ -335,7 +339,7 @@ return if(is_servant_of_ratvar(src) && !stat) - to_chat(src, "You resist Nar-Sie's influence... but not all of it. Run!") + to_chat(src, "You resist Nar'Sie's influence... but not all of it. Run!") adjustBruteLoss(35) if(src && reagents) reagents.add_reagent("heparin", 5) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 5ee9b8f921..0ba5b4c56d 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -20,6 +20,7 @@ var/fireloss = 0 //Burn damage caused by being way too hot, too cold or burnt. var/cloneloss = 0 //Damage caused by being cloned or ejected from the cloner early. slimes also deal cloneloss damage to victims var/staminaloss = 0 //Stamina damage, or exhaustion. You recover it slowly naturally, and are knocked down if it gets too high. Holodeck and hallucinations deal this. + var/crit_threshold = HEALTH_THRESHOLD_CRIT // when the mob goes from "normal" to crit var/confused = 0 //Makes the mob move in random directions. @@ -38,7 +39,7 @@ var/list/surgeries = list() //a list of surgery datums. generally empty, they're added when the player wants them. - var/now_pushing = null //used by living/Collide() and living/PushAM() to prevent potential infinite loop. + var/now_pushing = null //used by living/Bump() and living/PushAM() to prevent potential infinite loop. var/cameraFollow = null @@ -101,7 +102,6 @@ var/list/obj/effect/proc_holder/abilities = list() - var/registered_z var/can_be_held = FALSE //whether this can be picked up and held. var/radiation = 0 //If the mob is irradiated. diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm new file mode 100644 index 0000000000..9566edc2ed --- /dev/null +++ b/code/modules/mob/living/living_movement.dm @@ -0,0 +1,27 @@ +/mob/living/Moved() + . = ..() + update_turf_movespeed(loc) + +/mob/living/toggle_move_intent() + . = ..() + update_move_intent_slowdown() + +/mob/living/update_config_movespeed() + update_move_intent_slowdown() + return ..() + +/mob/living/proc/update_move_intent_slowdown() + var/mod = 0 + if(m_intent == MOVE_INTENT_WALK) + mod = CONFIG_GET(number/movedelay/walk_delay) + else + mod = CONFIG_GET(number/movedelay/run_delay) + if(!isnum(mod)) + mod = 1 + add_movespeed_modifier(MOVESPEED_ID_MOB_WALK_RUN_CONFIG_SPEED, TRUE, 100, override = TRUE, multiplicative_slowdown = mod) + +/mob/living/proc/update_turf_movespeed(turf/open/T) + if(isopenturf(T) && !is_flying()) + add_movespeed_modifier(MOVESPEED_ID_LIVING_TURF_SPEEDMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = T.slowdown) + else + remove_movespeed_modifier(MOVESPEED_ID_LIVING_TURF_SPEEDMOD) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index a7398493b0..c4754599d1 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -60,29 +60,28 @@ GLOBAL_LIST_INIT(department_radio_keys, list( )) /mob/living/proc/Ellipsis(original_msg, chance = 50, keep_words) - if(chance <= 0) - return "..." - if(chance >= 100) - return original_msg + if(chance <= 0) + return "..." + if(chance >= 100) + return original_msg - var/list - words = splittext(original_msg," ") - new_words = list() + var/list/words = splittext(original_msg," ") + var/list/new_words = list() - var/new_msg = "" + var/new_msg = "" - for(var/w in words) - if(prob(chance)) - new_words += "..." - if(!keep_words) - continue - new_words += w + for(var/w in words) + if(prob(chance)) + new_words += "..." + if(!keep_words) + continue + new_words += w - new_msg = jointext(new_words," ") + new_msg = jointext(new_words," ") - return new_msg + return new_msg -/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE) +/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE) var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE) var/talk_key = get_key(message) @@ -164,7 +163,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list( if((InCritical() && !fullcrit) || message_mode == MODE_WHISPER) message_range = 1 message_mode = MODE_WHISPER - log_talk(src,"[key_name(src)] : [message]",LOGWHISPER) + src.log_talk(message, LOG_WHISPER) if(fullcrit) var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health) // If we cut our message short, abruptly end it with a-.. @@ -175,7 +174,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list( message_mode = MODE_WHISPER_CRIT succumbed = TRUE else - log_talk(src,"[name]/[key] : [message]",LOGSAY) + src.log_talk(message, LOG_SAY, forced_by=forced) message = treat_message(message) if(!message) @@ -187,9 +186,6 @@ GLOBAL_LIST_INIT(department_radio_keys, list( var/datum/language/L = GLOB.language_datum_instances[language] spans |= L.spans - //Log what we've said with an associated timestamp, using the list's len for safety/to prevent overwriting messages - log_message(message, INDIVIDUAL_SAY_LOG) - var/radio_return = radio(message, message_mode, spans, language) if(radio_return & ITALICS) spans |= SPAN_ITALICS @@ -242,11 +238,9 @@ GLOBAL_LIST_INIT(department_radio_keys, list( eavesdrop_range = EAVESDROP_EXTRA_RANGE var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source) var/list/the_dead = list() - var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas if(say_test(message) == "2") //CIT CHANGE - ditto yellareas = get_areas_in_range(message_range*0.5,src) //CIT CHANGE - ditto - for(var/_M in GLOB.player_list) var/mob/M = _M if(M.stat != DEAD) //not dead, not important diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 91401cbc88..3dbb454655 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -23,6 +23,7 @@ a_intent = INTENT_HARM //so we always get pushed instead of trying to swap sight = SEE_TURFS | SEE_MOBS | SEE_OBJS see_in_dark = 8 + hud_type = /datum/hud/ai med_hud = DATA_HUD_MEDICAL_BASIC sec_hud = DATA_HUD_SECURITY_BASIC d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED @@ -177,9 +178,11 @@ if(incapacitated()) return - var/icontype = input("Please, select a display!", "AI", null/*, null*/) in list("Clown", "Monochrome", "Blue", "Inverted", "Firewall", "Green", "Red", "Static", "Red October", "House", "Heartline", "Hades", "Helios", "President", "Syndicat Meow", "Alien", "Too Deep", "Triumvirate", "Triumvirate-M", "Text", "Matrix", "Dorf", "Bliss", "Not Malf", "Fuzzy", "Goon", "Database", "Glitchman", "Murica", "Nanotrasen", "Gentoo", "Angel", "TechDemon") //CIT CHANGE - adds 'TechDemon + var/icontype = input("Please, select a display!", "AI", null/*, null*/) in list("Clown", ":thinking:", "Monochrome", "Blue", "Inverted", "Firewall", "Green", "Red", "Static", "Red October", "House", "Heartline", "Hades", "Helios", "President", "Syndicat Meow", "Alien", "Too Deep", "Triumvirate", "Triumvirate-M", "Text", "Matrix", "Dorf", "Bliss", "Not Malf", "Fuzzy", "Goon", "Database", "Glitchman", "Murica", "Nanotrasen", "Gentoo", "Angel", "TechDemon") //CIT CHANGE - adds 'TechDemon if(icontype == "Clown") icon_state = "ai-clown2" + else if (icontype == ":thinking:") + icon_state = "ai-:thinking:" else if(icontype == "Monochrome") icon_state = "ai-mono" else if(icontype == "Blue") @@ -244,6 +247,7 @@ icon_state = "ai-angel" else if(icontype == "TechDemon") //CIT CHANGE - adds 'TechDemon icon_state = "ai-techdemon" + /mob/living/silicon/ai/Stat() ..() if(statpanel("Status")) @@ -328,7 +332,16 @@ /mob/living/silicon/ai/can_interact_with(atom/A) . = ..() - return . || (istype(loc, /obj/item/aicard))? (ISINRANGE(A.x, x - interaction_range, x + interaction_range) && ISINRANGE(A.y, y - interaction_range, y + interaction_range)): GLOB.cameranet.checkTurfVis(get_turf(A)) + if (.) + return + if (istype(loc, /obj/item/aicard)) + var/turf/T0 = get_turf(src) + var/turf/T1 = get_turf(A) + if (!T0 || ! T1) + return FALSE + return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.y + interaction_range) + else + return GLOB.cameranet.checkTurfVis(get_turf(A)) /mob/living/silicon/ai/cancel_camera() view_core() @@ -620,19 +633,20 @@ if(incapacitated()) return - var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Friend Computer", "Dorfy", "Blue Glow", "Red Glow") + var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Thinking", "Friend Computer", "Dorfy", "Blue Glow", "Red Glow") var/emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions - for (var/M in GLOB.ai_status_displays) //change status of displays - if(istype(M, /obj/machinery/ai_status_display)) - var/obj/machinery/ai_status_display/AISD = M - AISD.emotion = emote - //if Friend Computer, change ALL displays - else if(istype(M, /obj/machinery/status_display)) - var/obj/machinery/status_display/SD = M - if(emote=="Friend Computer") - SD.friendc = 1 - else - SD.friendc = 0 + for (var/each in GLOB.ai_status_displays) //change status of displays + var/obj/machinery/status_display/ai/M = each + M.emotion = emote + M.update() + if (emote == "Friend Computer") + var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS) + + if(!frequency) + return + + var/datum/signal/status_signal = new(list("command" = "friendcomputer")) + frequency.post_signal(src, status_signal) return //I am the icon meister. Bow fefore me. //>fefore @@ -1004,7 +1018,7 @@ target_ai = src //cheat! just give... ourselves as the spawned AI, because that's technically correct /mob/living/silicon/ai/proc/camera_visibility(mob/camera/aiEye/moved_eye) - GLOB.cameranet.visibility(moved_eye, client, all_eyes) + GLOB.cameranet.visibility(moved_eye, client, all_eyes, USE_STATIC_OPAQUE) /mob/living/silicon/ai/forceMove(atom/destination) . = ..() diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm index e28d9d84df..9b982d4bd5 100644 --- a/code/modules/mob/living/silicon/ai/death.dm +++ b/code/modules/mob/living/silicon/ai/death.dm @@ -25,11 +25,16 @@ spawn(10) explosion(src.loc, 3, 6, 12, 15) - for(var/obj/machinery/ai_status_display/O in GLOB.ai_status_displays) //change status - if(src.key) + if(src.key) + for(var/each in GLOB.ai_status_displays) //change status + var/obj/machinery/status_display/ai/O = each O.mode = 2 - if(istype(loc, /obj/item/aicard)) - loc.icon_state = "aicard-404" + O.update() + + if(istype(loc, /obj/item/aicard/aitater)) + loc.icon_state = "aitater-404" + else if(istype(loc, /obj/item/aicard)) + loc.icon_state = "aicard-404" /mob/living/silicon/ai/proc/ShutOffDoomsdayDevice() if(nuking) diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm index 36ae19b478..dfe76fe948 100644 --- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm +++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm @@ -18,15 +18,24 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) // The object used for the clickable stat() button. var/obj/effect/statclick/statclick - // The object used in vis_contents of obscured turfs - var/vis_contents + // The objects used in vis_contents of obscured turfs + var/list/vis_contents_objects + var/obj/effect/overlay/camera_static/vis_contents_opaque + var/obj/effect/overlay/camera_static/vis_contents_transparent // The image given to the effect in vis_contents on AI clients var/image/obscured + var/image/obscured_transparent /datum/cameranet/New() - vis_contents = new /obj/effect/overlay/camera_static() - obscured = new('icons/effects/cameravis.dmi', vis_contents, null, BYOND_LIGHTING_LAYER + 0.1) - obscured.plane = BYOND_LIGHTING_PLANE + 1 + vis_contents_opaque = new /obj/effect/overlay/camera_static() + vis_contents_transparent = new /obj/effect/overlay/camera_static/transparent() + vis_contents_objects = list(vis_contents_opaque, vis_contents_transparent) + + obscured = new('icons/effects/cameravis.dmi', vis_contents_opaque, null, CAMERA_STATIC_LAYER) + obscured.plane = CAMERA_STATIC_PLANE + + obscured_transparent = new('icons/effects/cameravis.dmi', vis_contents_transparent, null, CAMERA_STATIC_LAYER) + obscured_transparent.plane = CAMERA_STATIC_PLANE // Checks if a chunk has been Generated in x, y, z. /datum/cameranet/proc/chunkGenerated(x, y, z) @@ -46,7 +55,7 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) // Updates what the aiEye can see. It is recommended you use this when the aiEye moves or it's location is set. -/datum/cameranet/proc/visibility(list/moved_eyes, client/C, list/other_eyes) +/datum/cameranet/proc/visibility(list/moved_eyes, client/C, list/other_eyes, use_static = USE_STATIC_OPAQUE) if(!islist(moved_eyes)) moved_eyes = moved_eyes ? list(moved_eyes) : list() if(islist(other_eyes)) @@ -54,33 +63,48 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) else other_eyes = list() + if(C) + switch(use_static) + if(USE_STATIC_TRANSPARENT) + C.images += obscured_transparent + if(USE_STATIC_OPAQUE) + C.images += obscured + for(var/V in moved_eyes) var/mob/camera/aiEye/eye = V - var/static_range = eye.static_visibility_range - var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1) - var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1) - var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1) - var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1) - var/list/visibleChunks = list() + if(eye.loc) + // 0xf = 15 + var/static_range = eye.static_visibility_range + var/x1 = max(0, eye.x - static_range) & ~(CHUNK_SIZE - 1) + var/y1 = max(0, eye.y - static_range) & ~(CHUNK_SIZE - 1) + var/x2 = min(world.maxx, eye.x + static_range) & ~(CHUNK_SIZE - 1) + var/y2 = min(world.maxy, eye.y + static_range) & ~(CHUNK_SIZE - 1) - for(var/x = x1; x <= x2; x += CHUNK_SIZE) - for(var/y = y1; y <= y2; y += CHUNK_SIZE) - visibleChunks |= getCameraChunk(x, y, eye.z) + + for(var/x = x1; x <= x2; x += CHUNK_SIZE) + for(var/y = y1; y <= y2; y += CHUNK_SIZE) + visibleChunks |= getCameraChunk(x, y, eye.z) var/list/remove = eye.visibleCameraChunks - visibleChunks var/list/add = visibleChunks - eye.visibleCameraChunks for(var/chunk in remove) var/datum/camerachunk/c = chunk - c.remove(eye) + c.remove(eye, FALSE) for(var/chunk in add) var/datum/camerachunk/c = chunk c.add(eye) - if(C) - C.images += obscured + if(!eye.visibleCameraChunks.len) + var/client/client = eye.GetViewerClient() + if(client) + switch(eye.use_static) + if(USE_STATIC_TRANSPARENT) + client.images -= GLOB.cameranet.obscured_transparent + if(USE_STATIC_OPAQUE) + client.images -= GLOB.cameranet.obscured // Updates the chunks that the turf is located in. Use this when obstacles are destroyed or when doors open. @@ -173,5 +197,8 @@ GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new) mouse_opacity = MOUSE_OPACITY_ICON invisibility = INVISIBILITY_ABSTRACT - layer = BYOND_LIGHTING_LAYER + 0.1 - plane = BYOND_LIGHTING_PLANE + 1 + layer = CAMERA_STATIC_LAYER + plane = CAMERA_STATIC_PLANE + +/obj/effect/overlay/camera_static/transparent + mouse_opacity = MOUSE_OPACITY_TRANSPARENT diff --git a/code/modules/mob/living/silicon/ai/freelook/chunk.dm b/code/modules/mob/living/silicon/ai/freelook/chunk.dm index 9a84616fbf..c2794394ed 100644 --- a/code/modules/mob/living/silicon/ai/freelook/chunk.dm +++ b/code/modules/mob/living/silicon/ai/freelook/chunk.dm @@ -26,9 +26,17 @@ // Remove an AI eye from the chunk, then update if changed. -/datum/camerachunk/proc/remove(mob/camera/aiEye/eye) +/datum/camerachunk/proc/remove(mob/camera/aiEye/eye, remove_static_with_last_chunk = TRUE) eye.visibleCameraChunks -= src seenby -= eye + if(remove_static_with_last_chunk && !eye.visibleCameraChunks.len) + var/client/client = eye.GetViewerClient() + if(client) + switch(eye.use_static) + if(USE_STATIC_TRANSPARENT) + client.images -= GLOB.cameranet.obscured_transparent + if(USE_STATIC_OPAQUE) + client.images -= GLOB.cameranet.obscured // Called when a chunk has changed. I.E: A wall was deleted. @@ -81,12 +89,12 @@ for(var/turf in visAdded) var/turf/t = turf - t.vis_contents -= GLOB.cameranet.vis_contents + t.vis_contents -= GLOB.cameranet.vis_contents_objects for(var/turf in visRemoved) var/turf/t = turf if(obscuredTurfs[t] && !istype(t, /turf/open/ai_visible)) - t.vis_contents += GLOB.cameranet.vis_contents + t.vis_contents += GLOB.cameranet.vis_contents_objects changed = 0 @@ -128,7 +136,7 @@ for(var/turf in obscuredTurfs) var/turf/t = turf - t.vis_contents += GLOB.cameranet.vis_contents + t.vis_contents += GLOB.cameranet.vis_contents_objects #undef UPDATE_BUFFER #undef CHUNK_SIZE diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index e78c008362..2d617355ec 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -7,28 +7,81 @@ name = "Inactive AI Eye" invisibility = INVISIBILITY_MAXIMUM + hud_possible = list(ANTAG_HUD, AI_DETECT_HUD = HUD_LIST_LIST) var/list/visibleCameraChunks = list() var/mob/living/silicon/ai/ai = null var/relay_speech = FALSE - var/use_static = TRUE + var/use_static = USE_STATIC_OPAQUE var/static_visibility_range = 16 + var/ai_detector_visible = TRUE + var/ai_detector_color = COLOR_RED + +/mob/camera/aiEye/Initialize() + . = ..() + GLOB.aiEyes += src + update_ai_detect_hud() + setLoc(loc, TRUE) + +/mob/camera/aiEye/proc/update_ai_detect_hud() + var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT] + var/list/old_images = hud_list[AI_DETECT_HUD] + if(!ai_detector_visible) + hud.remove_from_hud(src) + QDEL_LIST(old_images) + return + + if(!hud.hudusers.len) + //no one is watching, do not bother updating anything + return + hud.remove_from_hud(src) + + var/static/list/vis_contents_objects = list() + var/obj/effect/overlay/ai_detect_hud/hud_obj = vis_contents_objects[ai_detector_color] + if(!hud_obj) + hud_obj = new /obj/effect/overlay/ai_detect_hud() + hud_obj.color = ai_detector_color + vis_contents_objects[ai_detector_color] = hud_obj + + var/list/new_images = list() + var/list/turfs = get_visible_turfs() + for(var/T in turfs) + var/image/I = (old_images.len > new_images.len) ? old_images[new_images.len + 1] : image(null, T) + I.loc = T + I.vis_contents += hud_obj + new_images += I + for(var/i in (new_images.len + 1) to old_images.len) + qdel(old_images[i]) + hud_list[AI_DETECT_HUD] = new_images + hud.add_to_hud(src) + +/mob/camera/aiEye/proc/get_visible_turfs() + if(!isturf(loc)) + return list() + var/client/C = GetViewerClient() + var/view = C ? getviewsize(C.view) : getviewsize(world.view) + var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z) + var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z) + return block(lowerleft, upperright) // Use this when setting the aiEye's location. // It will also stream the chunk that the new loc is in. -/mob/camera/aiEye/proc/setLoc(T) +/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE) if(ai) if(!isturf(ai.loc)) return T = get_turf(T) + if(!force_update && (T == get_turf(src)) ) + return //we are already here! if (T) forceMove(T) else - moveToNullspace() // ???? - if(use_static) + moveToNullspace() + if(use_static != USE_STATIC_NONE) ai.camera_visibility(src) if(ai.client && !ai.multicam_on) ai.client.eye = src + update_ai_detect_hud() update_parallax_contents() //Holopad if(istype(ai.current, /obj/machinery/holopad)) @@ -47,11 +100,6 @@ return ai.client return null -/mob/camera/aiEye/proc/RemoveImages() - var/client/C = GetViewerClient() - if(C && use_static) - C.images -= GLOB.cameranet.obscured - /mob/camera/aiEye/Destroy() if(ai) ai.all_eyes -= src @@ -59,6 +107,12 @@ for(var/V in visibleCameraChunks) var/datum/camerachunk/c = V c.remove(src) + GLOB.aiEyes -= src + if(ai_detector_visible) + var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT] + hud.remove_from_hud(src) + var/list/L = hud_list[AI_DETECT_HUD] + QDEL_LIST(L) return ..() /atom/proc/move_camera_by_click() @@ -132,3 +186,12 @@ . = ..() if(relay_speech && speaker && ai && !radio_freq && speaker != ai && near_camera(speaker)) ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode) + +/obj/effect/overlay/ai_detect_hud + name = "" + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + icon = 'icons/effects/alphacolors.dmi' + icon_state = "" + alpha = 100 + layer = ABOVE_ALL_MOB_LAYER + plane = GAME_PLANE diff --git a/code/modules/mob/living/silicon/ai/login.dm b/code/modules/mob/living/silicon/ai/login.dm index 2dc9a1fb1c..50e2a46bf4 100644 --- a/code/modules/mob/living/silicon/ai/login.dm +++ b/code/modules/mob/living/silicon/ai/login.dm @@ -1,9 +1,11 @@ /mob/living/silicon/ai/Login() ..() if(stat != DEAD) - for(var/obj/machinery/ai_status_display/O in GLOB.ai_status_displays) //change status + for(var/each in GLOB.ai_status_displays) //change status + var/obj/machinery/status_display/ai/O = each O.mode = 1 O.emotion = "Neutral" + O.update() if(multicam_on) end_multicam() view_core() diff --git a/code/modules/mob/living/silicon/ai/logout.dm b/code/modules/mob/living/silicon/ai/logout.dm index fe5ea98c49..58e2ec56f4 100644 --- a/code/modules/mob/living/silicon/ai/logout.dm +++ b/code/modules/mob/living/silicon/ai/logout.dm @@ -1,5 +1,7 @@ /mob/living/silicon/ai/Logout() ..() - for(var/obj/machinery/ai_status_display/O in GLOB.ai_status_displays) //change status + for(var/each in GLOB.ai_status_displays) //change status + var/obj/machinery/status_display/ai/O = each O.mode = 0 + O.update() view_core() diff --git a/code/modules/mob/living/silicon/ai/multicam.dm b/code/modules/mob/living/silicon/ai/multicam.dm index d77e7f8a40..20b5f96242 100644 --- a/code/modules/mob/living/silicon/ai/multicam.dm +++ b/code/modules/mob/living/silicon/ai/multicam.dm @@ -124,6 +124,7 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room) var/list/cameras_telegraphed = list() var/telegraph_cameras = TRUE var/telegraph_range = 7 + ai_detector_color = COLOR_ORANGE /mob/camera/aiEye/pic_in_pic/GetViewerClient() if(screen && screen.ai) @@ -139,6 +140,10 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room) else GLOB.cameranet.visibility(src) update_camera_telegraphing() + update_ai_detect_hud() + +/mob/camera/aiEye/pic_in_pic/get_visible_turfs() + return screen ? screen.get_visible_turfs() : list() /mob/camera/aiEye/pic_in_pic/proc/update_camera_telegraphing() if(!telegraph_cameras) diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index c5820eb8c1..928bd63dd1 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -1,4 +1,4 @@ -/mob/living/silicon/ai/say(message, language) +/mob/living/silicon/ai/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) if(parent && istype(parent) && parent.stat != DEAD) //If there is a defined "parent" AI, it is actually an AI, and it is alive, anything the AI tries to say is said by the parent instead. parent.say(message, language) return @@ -48,7 +48,7 @@ padloc = AREACOORD(padturf) else padloc = "(UNKNOWN)" - log_talk(src,"HOLOPAD in [padloc]: [key_name(src)] : [message]", LOGSAY) + src.log_talk(message, LOG_SAY, tag="HOLOPAD in [padloc]") send_speech(message, 7, T, "robot", get_spans(), language) to_chat(src, "Holopad transmitted, [real_name] \"[message]\"") else diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 7f4371b1bc..896d8674be 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -1,6 +1,6 @@ /mob/living/silicon/pai name = "pAI" - icon = 'modular_citadel/icons/mob/pai.dmi' // CITADEL EDIT + icon = 'icons/mob/pai.dmi' icon_state = "repairbot" mouse_opacity = MOUSE_OPACITY_OPAQUE density = FALSE @@ -58,7 +58,7 @@ var/canholo = TRUE var/obj/item/card/id/access_card = null var/chassis = "repairbot" - var/list/possible_chassis = list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE, "fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "borgi" = FALSE , "parrot" = FALSE, "bear" = FALSE , "mushroom" = FALSE, "crow" = FALSE , "fairy" = FALSE , "spiderbot" = FALSE ) //assoc value is whether it can be picked up.-- borgi and on = // CITADEL EDIT + var/list/possible_chassis = list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE, "fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE) //assoc value is whether it can be picked up. var/static/item_head_icon = 'icons/mob/pai_item_head.dmi' var/static/item_lh_icon = 'icons/mob/pai_item_lh.dmi' var/static/item_rh_icon = 'icons/mob/pai_item_rh.dmi' @@ -75,13 +75,7 @@ var/overload_maxhealth = 0 canmove = FALSE var/silent = FALSE - var/hit_slowdown = 0 var/brightness_power = 5 - var/slowdown = 0 - -/mob/living/silicon/pai/movement_delay() - . = ..() - . += slowdown /mob/living/silicon/pai/can_unbuckle() return FALSE @@ -266,9 +260,9 @@ /mob/living/silicon/pai/Process_Spacemove(movement_dir = 0) . = ..() if(!.) - slowdown = 2 + add_movespeed_modifier(MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD, TRUE, 100, multiplicative_slowdown = 2) return TRUE - slowdown = initial(slowdown) + remove_movespeed_modifier(MOVESPEED_ID_PAI_SPACEWALK_SPEEDMOD, TRUE) return TRUE /mob/living/silicon/pai/examine(mob/user) @@ -293,7 +287,5 @@ health = maxHealth - getBruteLoss() - getFireLoss() update_stat() - /mob/living/silicon/pai/process() emitterhealth = CLAMP((emitterhealth + emitterregen), -50, emittermaxhealth) - hit_slowdown = CLAMP((hit_slowdown - 1), 0, 100) diff --git a/code/modules/mob/living/silicon/pai/pai_defense.dm b/code/modules/mob/living/silicon/pai/pai_defense.dm index 417a24cd68..f20ccbc730 100644 --- a/code/modules/mob/living/silicon/pai/pai_defense.dm +++ b/code/modules/mob/living/silicon/pai/pai_defense.dm @@ -64,7 +64,6 @@ if(emitterhealth < 0) fold_in(force = TRUE) to_chat(src, "The impact degrades your holochassis!") - hit_slowdown += amount return amount /mob/living/silicon/pai/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE) diff --git a/code/modules/mob/living/silicon/pai/pai_shell.dm b/code/modules/mob/living/silicon/pai/pai_shell.dm index 85f7fe6afc..99484f395e 100644 --- a/code/modules/mob/living/silicon/pai/pai_shell.dm +++ b/code/modules/mob/living/silicon/pai/pai_shell.dm @@ -103,10 +103,6 @@ set_light(0) to_chat(src, "You disable your integrated light.") -/mob/living/silicon/pai/movement_delay() - . = ..() - . += 1 //A bit slower than humans, so they're easier to smash - /mob/living/silicon/pai/mob_pickup(mob/living/L) var/obj/item/clothing/head/mob_holder/holder = new(get_turf(src), src, chassis, item_head_icon, item_lh_icon, item_rh_icon) if(!L.put_in_hands(holder)) diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm index 6f7b8601d2..9f07900a25 100644 --- a/code/modules/mob/living/silicon/pai/say.dm +++ b/code/modules/mob/living/silicon/pai/say.dm @@ -1,8 +1,8 @@ -/mob/living/silicon/pai/say(msg) +/mob/living/silicon/pai/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) if(silent) to_chat(src, "Communication circuits remain unitialized.") else - ..(msg) + ..(message) /mob/living/silicon/pai/binarycheck() return 0 diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 7eb241e01f..75e8fd317f 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -2,7 +2,7 @@ /mob/living/silicon/robot/gib_animation() new /obj/effect/temp_visual/gib_animation(loc, "gibbed-r") -/mob/living/silicon/robot/dust() +/mob/living/silicon/robot/dust(just_ash, drop_items, force) if(mmi) qdel(mmi) ..() diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 473fcb24df..a389bf637c 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -8,6 +8,7 @@ bubble_icon = "robot" designation = "Default" //used for displaying the prefix & getting the current module of cyborg has_limbs = 1 + hud_type = /datum/hud/robot var/custom_name = "" var/braintype = "Cyborg" @@ -211,10 +212,6 @@ cell = null return ..() -/mob/living/silicon/robot/can_interact_with(atom/A) - . = ..() - return . || in_view_range(src, A) - /mob/living/silicon/robot/proc/pick_module() if(module.type != /obj/item/robot_module) return @@ -297,12 +294,12 @@ if(!ionpulse_on) return - if(cell.charge <= 50) + if(cell.charge <= 10) toggle_ionpulse() return - cell.charge -= 50 // 500 steps on a default cell. - return 1 + cell.charge -= 10 + return TRUE /mob/living/silicon/robot/proc/toggle_ionpulse() if(!ionpulse) @@ -381,7 +378,13 @@ return !cleared /mob/living/silicon/robot/can_interact_with(atom/A) - return !low_power_mode && ISINRANGE(A.x, x - interaction_range, x + interaction_range) && ISINRANGE(A.y, y - interaction_range, y + interaction_range) + if (low_power_mode) + return FALSE + var/turf/T0 = get_turf(src) + var/turf/T1 = get_turf(A) + if (!T0 || ! T1) + return FALSE + return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.y + interaction_range) /mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/weldingtool) && (user.a_intent != INTENT_HARM || user == src)) @@ -1118,10 +1121,10 @@ undeployment_action.Grant(src) /datum/action/innate/undeployment - name = "Disconnect from shell" - desc = "Stop controlling your shell and resume normal core operations." - icon_icon = 'icons/mob/actions/actions_AI.dmi' - button_icon_state = "ai_core" + name = "Disconnect from shell" + desc = "Stop controlling your shell and resume normal core operations." + icon_icon = 'icons/mob/actions/actions_AI.dmi' + button_icon_state = "ai_core" /datum/action/innate/undeployment/Trigger() if(!..()) diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index 4907e46751..c90c719e8a 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -22,11 +22,11 @@ uneq_active() visible_message("[M] disarmed [src]!", \ "[M] has disabled [src]'s active module!", null, COMBAT_MESSAGE_RANGE) - add_logs(M, src, "disarmed", "[I ? " removing \the [I]" : ""]") + log_combat(M, src, "disarmed", "[I ? " removing \the [I]" : ""]") else Stun(40) step(src,get_dir(M,src)) - add_logs(M, src, "pushed") + log_combat(M, src, "pushed") visible_message("[M] has forced back [src]!", \ "[M] has forced back [src]!", null, COMBAT_MESSAGE_RANGE) playsound(loc, 'sound/weapons/pierce.ogg', 50, 1, -1) diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index 5c24b15267..623174157b 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -3,13 +3,6 @@ return 1 return ..() -/mob/living/silicon/robot/movement_delay() - . = ..() - var/static/config_robot_delay - if(isnull(config_robot_delay)) - config_robot_delay = CONFIG_GET(number/robot_delay) - . += speed + config_robot_delay - /mob/living/silicon/robot/mob_negates_gravity() return magpulse diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 785d55e5f4..aca35762de 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -3,8 +3,7 @@ return ..() | SPAN_ROBOT /mob/living/proc/robot_talk(message) - log_talk(src,"[key_name(src)] : [message]",LOGSAY) - log_message(message, INDIVIDUAL_SAY_LOG) + log_talk(message, LOG_SAY) var/desig = "Default Cyborg" //ezmode for taters if(issilicon(src)) var/mob/living/silicon/S = src diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm index 647039e123..073a2eec2b 100644 --- a/code/modules/mob/living/silicon/silicon_defense.dm +++ b/code/modules/mob/living/silicon/silicon_defense.dm @@ -1,5 +1,5 @@ -/mob/living/silicon/grippedby(mob/living/user) +/mob/living/silicon/grippedby(mob/living/user, instant = FALSE) return //can't upgrade a simple pull into a more aggressive grab. /mob/living/silicon/get_ear_protection()//no ears @@ -9,13 +9,13 @@ if(..()) //if harm or disarm intent var/damage = 20 if (prob(90)) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1) visible_message("[M] has slashed at [src]!", \ "[M] has slashed at [src]!") if(prob(8)) flash_act(affect_silicon = 1) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") adjustBruteLoss(damage) updatehealth() else diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index 5f2a91519f..a92c1ff9e1 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -23,7 +23,7 @@ "[M] [response_harm] [src]!", null, COMBAT_MESSAGE_RANGE) playsound(loc, attacked_sound, 25, 1, -1) attack_threshold_check(harm_intent_damage) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") updatehealth() return TRUE @@ -57,14 +57,14 @@ playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) visible_message("[M] [response_disarm] [name]!", \ "[M] [response_disarm] [name]!", null, COMBAT_MESSAGE_RANGE) - add_logs(M, src, "disarmed") + log_combat(M, src, "disarmed") else var/damage = rand(15, 30) visible_message("[M] has slashed at [src]!", \ "[M] has slashed at [src]!", null, COMBAT_MESSAGE_RANGE) playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) attack_threshold_check(damage) - add_logs(M, src, "attacked") + log_combat(M, src, "attacked") return 1 /mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L) diff --git a/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm new file mode 100644 index 0000000000..0378b0b9ee --- /dev/null +++ b/code/modules/mob/living/simple_animal/bot/SuperBeepsky.dm @@ -0,0 +1,150 @@ +/mob/living/simple_animal/bot/secbot/grievous //This bot is powerful. If you managed to get 4 eswords somehow, you deserve this horror. Emag him for best results. + name = "General Beepsky" + desc = "Is that a secbot with four eswords in its arms...?" + icon = 'icons/mob/aibots.dmi' + icon_state = "grievous" + health = 150 + maxHealth = 150 + baton_type = /obj/item/melee/transforming/energy/sword + base_speed = 4 //he's a fast fucker + var/obj/item/weapon + var/block_chance = 50 + + +/mob/living/simple_animal/bot/secbot/grievous/toy //A toy version of general beepsky! + name = "Genewul Bweepskee" + desc = "An adorable looking secbot with four toy swords taped to its arms" + health = 50 + maxHealth = 50 + baton_type = /obj/item/toy/sword + +/mob/living/simple_animal/bot/secbot/grievous/bullet_act(obj/item/projectile/P) + visible_message("[src] deflects [P] with its energy swords!") + playsound(src, 'sound/weapons/blade1.ogg', 50, TRUE) + return FALSE + +/mob/living/simple_animal/bot/secbot/grievous/Crossed(atom/movable/AM) + ..() + if(ismob(AM) && AM == target) + visible_message("[src] flails his swords and cuts [AM]!") + playsound(src,'sound/effects/beepskyspinsabre.ogg',100,TRUE,-1) + stun_attack(AM) + +/mob/living/simple_animal/bot/secbot/grievous/Initialize() + . = ..() + weapon = new baton_type(src) + weapon.attack_self(src) + +/mob/living/simple_animal/bot/secbot/grievous/Destroy() + QDEL_NULL(weapon) + return ..() + +/mob/living/simple_animal/bot/secbot/grievous/special_retaliate_after_attack(mob/user) + if(mode != BOT_HUNT) + return + if(prob(block_chance)) + visible_message("[src] deflects [user]'s attack with his energy swords!") + playsound(src, 'sound/weapons/blade1.ogg', 50, TRUE, -1) + return TRUE + +/mob/living/simple_animal/bot/secbot/grievous/stun_attack(mob/living/carbon/C) //Criminals don't deserve to live + weapon.attack(C, src) + playsound(src, 'sound/weapons/blade1.ogg', 50, TRUE, -1) + if(C.stat == DEAD) + addtimer(CALLBACK(src, .proc/update_icon), 2) + back_to_idle() + + +/mob/living/simple_animal/bot/secbot/grievous/handle_automated_action() + if(!on) + return + switch(mode) + if(BOT_IDLE) // idle + update_icon() + walk_to(src,0) + look_for_perp() // see if any criminals are in range + if(!mode && auto_patrol) // still idle, and set to patrol + mode = BOT_START_PATROL // switch to patrol mode + if(BOT_HUNT) // hunting for perp + update_icon() + playsound(src,'sound/effects/beepskyspinsabre.ogg',100,TRUE,-1) + // general beepsky doesn't give up so easily, jedi scum + if(frustration >= 20) + walk_to(src,0) + back_to_idle() + return + if(target) // make sure target exists + if(Adjacent(target) && isturf(target.loc)) // if right next to perp + target_lastloc = target.loc //stun_attack() can clear the target if they're dead, so this needs to be set first + stun_attack(target) + anchored = TRUE + return + else // not next to perp + var/turf/olddist = get_dist(src, target) + walk_to(src, target,1,4) + if((get_dist(src, target)) >= (olddist)) + frustration++ + else + frustration = 0 + else + back_to_idle() + + if(BOT_START_PATROL) + look_for_perp() + start_patrol() + + if(BOT_PATROL) + look_for_perp() + bot_patrol() + +/mob/living/simple_animal/bot/secbot/grievous/look_for_perp() + anchored = FALSE + var/judgement_criteria = judgement_criteria() + for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal + if((C.stat) || (C.handcuffed)) + continue + + if((C.name == oldtarget_name) && (world.time < last_found + 100)) + continue + + threatlevel = C.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) + + if(!threatlevel) + continue + + else if(threatlevel >= 4) + target = C + oldtarget_name = C.name + speak("Level [threatlevel] infraction alert!") + playsound(src, pick('sound/voice/beepsky/criminal.ogg', 'sound/voice/beepsky/justice.ogg', 'sound/voice/beepsky/freeze.ogg'), 50, FALSE) + playsound(src,'sound/weapons/saberon.ogg',50,TRUE,-1) + visible_message("[src] ignites his energy swords!") + icon_state = "grievous-c" + visible_message("[src] points at [C.name]!") + mode = BOT_HUNT + INVOKE_ASYNC(src, .proc/handle_automated_action) + break + else + continue + + +/mob/living/simple_animal/bot/secbot/grievous/explode() + + walk_to(src,0) + visible_message("[src] lets out a huge cough as it blows apart!") + var/atom/Tsec = drop_location() + + var/obj/item/bot_assembly/secbot/Sa = new (Tsec) + Sa.build_step = 1 + Sa.add_overlay("hs_hole") + Sa.created_name = name + new /obj/item/assembly/prox_sensor(Tsec) + + if(prob(50)) + drop_part(robot_arm, Tsec) + + do_sparks(3, TRUE, src) + for(var/IS = 0 to 4) + drop_part(baton_type, Tsec) + new /obj/effect/decal/cleanable/oil(Tsec) + qdel(src) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index b38cf6395c..d2cfdb88e3 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -196,7 +196,7 @@ bot_reset() turn_on() //The bot automatically turns on when emagged, unless recently hit with EMP. to_chat(src, "(#$*#$^^( OVERRIDE DETECTED") - add_logs(user, src, "emagged") + log_combat(user, src, "emagged") return else //Bot is unlocked, but the maint panel has not been opened with a screwdriver yet. to_chat(user, "You need to open maintenance panel first!") @@ -762,7 +762,7 @@ Pass a positive integer as an argument to override a bot's default speed. else // no path, so calculate new one calc_summon_path() -/mob/living/simple_animal/bot/Collide(M as mob|obj) //Leave no door unopened! +/mob/living/simple_animal/bot/Bump(M as mob|obj) //Leave no door unopened! . = ..() if((istype(M, /obj/machinery/door/airlock) || istype(M, /obj/machinery/door/window)) && (!isnull(access_card))) var/obj/machinery/door/D = M @@ -900,7 +900,7 @@ Pass a positive integer as an argument to override a bot's default speed. name = paicard.pai.name faction = user.faction.Copy() language_holder = paicard.pai.language_holder.copy(src) - add_logs(user, paicard.pai, "uploaded to [bot_name],") + log_combat(user, paicard.pai, "uploaded to [bot_name],") return TRUE else to_chat(user, "[card] is inactive.") @@ -920,9 +920,9 @@ Pass a positive integer as an argument to override a bot's default speed. key = null paicard.forceMove(loc) if(user) - add_logs(user, paicard.pai, "ejected from [src.bot_name],") + log_combat(user, paicard.pai, "ejected from [src.bot_name],") else - add_logs(src, paicard.pai, "ejected") + log_combat(src, paicard.pai, "ejected") if(announce) to_chat(paicard.pai, "You feel your control fade as [paicard] ejects from [bot_name].") paicard = null diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm index c9ece767a5..56d37f667c 100644 --- a/code/modules/mob/living/simple_animal/bot/construction.dm +++ b/code/modules/mob/living/simple_animal/bot/construction.dm @@ -379,6 +379,8 @@ icon_state = "helmet_signaler" item_state = "helmet" created_name = "Securitron" //To preserve the name if it's a unique securitron I guess + var/swordamt = 0 //If you're converting it into a grievousbot, how many swords have you attached + var/toyswordamt = 0 //honk /obj/item/bot_assembly/secbot/attackby(obj/item/I, mob/user, params) ..() @@ -441,6 +443,29 @@ S.robot_arm = robot_arm qdel(I) qdel(src) + if(istype(I, /obj/item/wrench)) + to_chat(user, "You adjust [src]'s arm slots to mount extra weapons") + build_step ++ + return + if(istype(I, /obj/item/toy/sword)) + if(toyswordamt < 3 && swordamt <= 0) + if(!user.temporarilyRemoveItemFromInventory(I)) + return + created_name = "General Beepsky" + name = "helmet/signaler/prox sensor/robot arm/toy sword assembly" + icon_state = "grievous_assembly" + to_chat(user, "You superglue [I] onto one of [src]'s arm slots.") + qdel(I) + toyswordamt ++ + else + if(!can_finish_build(I, user)) + return + to_chat(user, "You complete the Securitron!...Something seems a bit wrong with it..?") + var/mob/living/simple_animal/bot/secbot/grievous/toy/S = new(Tsec) + S.name = created_name + S.robot_arm = robot_arm + qdel(I) + qdel(src) else if(istype(I, /obj/item/screwdriver)) //deconstruct cut_overlay("hs_arm") @@ -448,3 +473,35 @@ robot_arm = null to_chat(user, "You remove [dropped_arm] from [src].") build_step-- + if(toyswordamt > 0 || toyswordamt) + icon_state = initial(icon_state) + to_chat(user, "The superglue binding [src]'s toy swords to its chassis snaps!") + for(var/IS in 1 to toyswordamt) + new /obj/item/toy/sword(Tsec) + + if(ASSEMBLY_FIFTH_STEP) + if(istype(I, /obj/item/melee/transforming/energy/sword/saber)) + if(swordamt < 3) + if(!user.temporarilyRemoveItemFromInventory(I)) + return + created_name = "General Beepsky" + name = "helmet/signaler/prox sensor/robot arm/energy sword assembly" + icon_state = "grievous_assembly" + to_chat(user, "You bolt [I] onto one of [src]'s arm slots.") + qdel(I) + swordamt ++ + else + if(!can_finish_build(I, user)) + return + to_chat(user, "You complete the Securitron!...Something seems a bit wrong with it..?") + var/mob/living/simple_animal/bot/secbot/grievous/S = new(Tsec) + S.name = created_name + S.robot_arm = robot_arm + qdel(I) + qdel(src) + else if(istype(I, /obj/item/screwdriver)) //deconstruct + build_step-- + icon_state = initial(icon_state) + to_chat(user, "You unbolt [src]'s energy swords") + for(var/IS in 1 to swordamt) + new /obj/item/melee/transforming/energy/sword/saber(Tsec) diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index a204edbcaf..581711d271 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -26,7 +26,7 @@ var/lastfired = 0 var/shot_delay = 15 var/lasercolor = "" - var/disabled = 0//A holder for if it needs to be disabled, if true it will not seach for targets, shoot at targets, or move, currently only used for lasertag + var/disabled = FALSE //A holder for if it needs to be disabled, if true it will not seach for targets, shoot at targets, or move, currently only used for lasertag var/mob/living/carbon/target @@ -34,16 +34,18 @@ var/threatlevel = 0 var/target_lastloc //Loc of target when arrested. var/last_found //There's a delay - var/declare_arrests = 1 //When making an arrest, should it notify everyone wearing sechuds? - var/idcheck = 1 //If true, arrest people with no IDs - var/weaponscheck = 1 //If true, arrest people for weapons if they don't have access - var/check_records = 1 //Does it check security records? - var/arrest_type = 0 //If true, don't handcuff + var/declare_arrests = TRUE //When making an arrest, should it notify everyone wearing sechuds? + var/idcheck = TRUE //If true, arrest people with no IDs + var/weaponscheck = TRUE //If true, arrest people for weapons if they don't have access + var/check_records = TRUE //Does it check security records? + var/arrest_type = FALSE //If true, don't handcuff var/projectile = /obj/item/projectile/energy/electrode //Holder for projectile type var/shoot_sound = 'sound/weapons/taser.ogg' var/cell_type = /obj/item/stock_parts/cell var/vest_type = /obj/item/clothing/suit/armor/vest + do_footstep = TRUE + /mob/living/simple_animal/bot/ed209/Initialize(mapload,created_name,created_lasercolor) . = ..() @@ -199,14 +201,14 @@ Auto Patrol[]"}, to_chat(user, "You short out [src]'s target assessment circuits.") oldtarget_name = user.name audible_message("[src] buzzes oddly!") - declare_arrests = 0 + declare_arrests = FALSE icon_state = "[lasercolor]ed209[on]" set_weapon() /mob/living/simple_animal/bot/ed209/bullet_act(obj/item/projectile/Proj) if(istype(Proj , /obj/item/projectile/beam/laser)||istype(Proj, /obj/item/projectile/bullet)) if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE)) - if(!Proj.nodamage && Proj.damage < src.health) + if(!Proj.nodamage && Proj.damage < src.health && ishuman(Proj.firer)) retaliate(Proj.firer) ..() @@ -357,7 +359,7 @@ Auto Patrol[]"}, target = C oldtarget_name = C.name speak("Level [threatlevel] infraction alert!") - playsound(loc, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/edplaceholder.ogg'), 50, 0) + playsound(src, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/edplaceholder.ogg'), 50, FALSE) visible_message("[src] points at [C.name]!") mode = BOT_HUNT spawn(0) @@ -447,7 +449,7 @@ Auto Patrol[]"}, return var/obj/item/projectile/A = new projectile (loc) - playsound(loc, shoot_sound, 50, 1) + playsound(src, shoot_sound, 50, TRUE) A.preparePixelProjectile(target, src) A.fire() @@ -538,7 +540,7 @@ Auto Patrol[]"}, shootAt(A) /mob/living/simple_animal/bot/ed209/proc/stun_attack(mob/living/carbon/C) - playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) + playsound(src, 'sound/weapons/egloves.ogg', 50, TRUE, -1) icon_state = "[lasercolor]ed209-c" spawn(2) icon_state = "[lasercolor]ed209[on]" @@ -549,7 +551,7 @@ Auto Patrol[]"}, var/mob/living/carbon/human/H = C var/judgement_criteria = judgement_criteria() threat = H.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) - add_logs(src,C,"stunned") + log_combat(src,C,"stunned") if(declare_arrests) var/area/location = get_area(src) speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag [C] in [location].", radio_channel) @@ -558,12 +560,12 @@ Auto Patrol[]"}, /mob/living/simple_animal/bot/ed209/proc/cuff(mob/living/carbon/C) mode = BOT_ARREST - playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) + playsound(src, 'sound/weapons/cablecuff.ogg', 30, TRUE, -2) C.visible_message("[src] is trying to put zipties on [C]!",\ "[src] is trying to put zipties on you!") spawn(60) - if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. + if( !on || !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. return if(!C.handcuffed) C.handcuffed = new /obj/item/restraints/handcuffs/cable/zipties/used(C) diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index 65b78bf844..d586cc694b 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -2,7 +2,7 @@ name = "\improper honkbot" desc = "A little robot. It looks happy with its bike horn." icon = 'icons/mob/aibots.dmi' - icon_state = "honkbot1" + icon_state = "honkbot" density = FALSE anchored = FALSE health = 25 @@ -39,7 +39,7 @@ /mob/living/simple_animal/bot/honkbot/Initialize() . = ..() - icon_state = "honkbot[on]" + update_icon() auto_patrol = TRUE var/datum/job/clown/J = new/datum/job/clown access_card.access += J.get_access() @@ -48,22 +48,19 @@ /mob/living/simple_animal/bot/honkbot/proc/spam_flag_false() //used for addtimer spam_flag = FALSE -/mob/living/simple_animal/bot/honkbot/proc/blink_end() //used for addtimer - icon_state = "honkbot[on]" - /mob/living/simple_animal/bot/honkbot/proc/sensor_blink() icon_state = "honkbot-c" - addtimer(CALLBACK(src, .proc/blink_end), 5) + addtimer(CALLBACK(src, .proc/update_icon), 5, TIMER_OVERRIDE|TIMER_UNIQUE) //honkbots react with sounds. /mob/living/simple_animal/bot/honkbot/proc/react_ping() - playsound(src, 'sound/machines/ping.ogg', 50, 1, -1) //the first sound upon creation! + playsound(src, 'sound/machines/ping.ogg', 50, TRUE, -1) //the first sound upon creation! spam_flag = TRUE sensor_blink() addtimer(CALLBACK(src, .proc/spam_flag_false), 18) // calibrates before starting the honk /mob/living/simple_animal/bot/honkbot/proc/react_buzz() - playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1, -1) + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE, -1) sensor_blink() /mob/living/simple_animal/bot/honkbot/bot_reset() @@ -123,7 +120,7 @@ Maintenance panel panel is [open ? "opened" : "closed"]"}, /mob/living/simple_animal/bot/honkbot/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM). + if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM) return if(!istype(W, /obj/item/screwdriver) && (W.force) && (!target) && (W.damtype != STAMINA) ) // Check for welding tool to fix #2432. retaliate(user) @@ -138,10 +135,10 @@ Maintenance panel panel is [open ? "opened" : "closed"]"}, oldtarget_name = user.name audible_message("[src] gives out an evil laugh!") playsound(src, 'sound/machines/honkbot_evil_laugh.ogg', 75, 1, -1) // evil laughter - icon_state = "honkbot[on]" + update_icon() /mob/living/simple_animal/bot/honkbot/bullet_act(obj/item/projectile/Proj) - if((istype(Proj,/obj/item/projectile/beam)) || (istype(Proj,/obj/item/projectile/bullet) && (Proj.damage_type == BURN))||(Proj.damage_type == BRUTE) && (!Proj.nodamage && Proj.damage < health)) + if((istype(Proj,/obj/item/projectile/beam)) || (istype(Proj,/obj/item/projectile/bullet) && (Proj.damage_type == BURN))||(Proj.damage_type == BRUTE) && (!Proj.nodamage && Proj.damage < health && ishuman(Proj.firer))) retaliate(Proj.firer) ..() @@ -162,7 +159,7 @@ Maintenance panel panel is [open ? "opened" : "closed"]"}, /mob/living/simple_animal/bot/honkbot/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0) if(istype(AM, /obj/item)) - playsound(src, honksound, 50, 1, -1) + playsound(src, honksound, 50, TRUE, -1) var/obj/item/I = AM if(I.throwforce < health && I.thrownby && (istype(I.thrownby, /mob/living/carbon/human))) var/mob/living/carbon/human/H = I.thrownby @@ -172,7 +169,7 @@ Maintenance panel panel is [open ? "opened" : "closed"]"}, /mob/living/simple_animal/bot/honkbot/proc/bike_horn() //use bike_horn if (emagged <= 1) if (!spam_flag) - playsound(src, honksound, 50, 1, -1) + playsound(src, honksound, 50, TRUE, -1) spam_flag = TRUE //prevent spam sensor_blink() addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntimehorn) @@ -181,19 +178,19 @@ Maintenance panel panel is [open ? "opened" : "closed"]"}, playsound(src, "honkbot_e", 50, 0) spam_flag = TRUE // prevent spam icon_state = "honkbot-e" - addtimer(CALLBACK(src, .proc/blink_end), 30) + addtimer(CALLBACK(src, .proc/update_icon), 30, TIMER_OVERRIDE|TIMER_UNIQUE) addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntimehorn) /mob/living/simple_animal/bot/honkbot/proc/honk_attack(mob/living/carbon/C) // horn attack if(!spam_flag) - playsound(loc, honksound, 50, 1, -1) + playsound(loc, honksound, 50, TRUE, -1) spam_flag = TRUE // prevent spam sensor_blink() addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntimehorn) /mob/living/simple_animal/bot/honkbot/proc/stun_attack(mob/living/carbon/C) // airhorn stun if(!spam_flag) - playsound(loc, 'sound/items/AirHorn.ogg', 100, 1, -1) //HEEEEEEEEEEEENK!! + playsound(src, 'sound/items/AirHorn.ogg', 100, TRUE, -1) //HEEEEEEEEEEEENK!! sensor_blink() if(spam_flag == 0) if(ishuman(C)) @@ -213,7 +210,7 @@ Maintenance panel panel is [open ? "opened" : "closed"]"}, threatlevel = 6 // will never let you go addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntime) - add_logs(src,C,"honked") + log_combat(src,C,"honked") C.visible_message("[src] has honked [C]!",\ "[src] has honked you!") diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index 5e05a9cbd4..7167d87bde 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -261,7 +261,7 @@ if(assess_patient(H)) last_found = world.time if((last_newpatient_speak + 300) < world.time) //Don't spam these messages! - var/list/messagevoice = list("Hey, [H.name]! Hold on, I'm coming." = 'sound/voice/mcoming.ogg',"Wait [H.name]! I want to help!" = 'sound/voice/mhelp.ogg',"[H.name], you appear to be injured!" = 'sound/voice/minjured.ogg') + var/list/messagevoice = list("Hey, [H.name]! Hold on, I'm coming." = 'sound/voice/medbot/coming.ogg',"Wait [H.name]! I want to help!" = 'sound/voice/medbot/help.ogg',"[H.name], you appear to be injured!" = 'sound/voice/medbot/injured.ogg') var/message = pick(messagevoice) speak(message) playsound(loc, messagevoice[message], 50, 0) @@ -287,9 +287,9 @@ oldpatient = patient soft_reset() - if(!patient) + if(QDELETED(patient)) if(!shut_up && prob(1)) - var/list/messagevoice = list("Radar, put a mask on!" = 'sound/voice/mradar.ogg',"There's always a catch, and I'm the best there is." = 'sound/voice/mcatch.ogg',"I knew it, I should've been a plastic surgeon." = 'sound/voice/msurgeon.ogg',"What kind of medbay is this? Everyone's dropping like flies." = 'sound/voice/mflies.ogg',"Delicious!" = 'sound/voice/mdelicious.ogg') + var/list/messagevoice = list("Radar, put a mask on!" = 'sound/voice/medbot/radar.ogg',"There's always a catch, and I'm the best there is." = 'sound/voice/medbot/catch.ogg',"I knew it, I should've been a plastic surgeon." = 'sound/voice/medbot/surgeon.ogg',"What kind of medbay is this? Everyone's dropping like flies." = 'sound/voice/medbot/flies.ogg',"Delicious!" = 'sound/voice/medbot/delicious.ogg') var/message = pick(messagevoice) speak(message) playsound(loc, messagevoice[message], 50, 0) @@ -422,7 +422,7 @@ return if(C.stat == DEAD || (C.has_trait(TRAIT_FAKEDEATH))) - var/list/messagevoice = list("No! Stay with me!" = 'sound/voice/mno.ogg',"Live, damnit! LIVE!" = 'sound/voice/mlive.ogg',"I...I've never lost a patient before. Not today, I mean." = 'sound/voice/mlost.ogg') + var/list/messagevoice = list("No! Stay with me!" = 'sound/voice/medbot/no.ogg',"Live, damnit! LIVE!" = 'sound/voice/medbot/live.ogg',"I...I've never lost a patient before. Not today, I mean." = 'sound/voice/medbot/lost.ogg') var/message = pick(messagevoice) speak(message) playsound(loc, messagevoice[message], 50, 0) @@ -476,7 +476,7 @@ if(!reagent_id) //If they don't need any of that they're probably cured! if(C.maxHealth - C.health < heal_threshold) to_chat(src, "[C] is healthy! Your programming prevents you from injecting anyone without at least [heal_threshold] damage of any one type ([heal_threshold + 15] for oxygen damage.)") - var/list/messagevoice = list("All patched up!" = 'sound/voice/mpatchedup.ogg',"An apple a day keeps me away." = 'sound/voice/mapple.ogg',"Feel better soon!" = 'sound/voice/mfeelbetter.ogg') + var/list/messagevoice = list("All patched up!" = 'sound/voice/medbot/patchedup.ogg',"An apple a day keeps me away." = 'sound/voice/medbot/apple.ogg',"Feel better soon!" = 'sound/voice/medbot/feelbetter.ogg') var/message = pick(messagevoice) speak(message) playsound(loc, messagevoice[message], 50, 0) @@ -540,7 +540,7 @@ drop_part(robot_arm, Tsec) if(emagged && prob(25)) - playsound(loc, 'sound/voice/minsult.ogg', 50, 0) + playsound(loc, 'sound/voice/medbot/insult.ogg', 50, 0) do_sparks(3, TRUE, src) ..() diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 600f6e58e4..c45d435253 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -623,7 +623,7 @@ return // called when bot bumps into anything -/mob/living/simple_animal/bot/mulebot/Collide(atom/obs) +/mob/living/simple_animal/bot/mulebot/Bump(atom/obs) if(wires.is_cut(WIRE_AVOIDANCE)) // usually just bumps, but if avoidance disabled knock over mobs if(isliving(obs)) var/mob/living/L = obs @@ -631,7 +631,7 @@ visible_message("[src] bumps into [L]!") else if(!paicard) - add_logs(src, L, "knocked down") + log_combat(src, L, "knocked down") visible_message("[src] knocks over [L]!") L.Knockdown(160) return ..() @@ -639,7 +639,7 @@ // called from mob/living/carbon/human/Crossed() // when mulebot is in the same loc /mob/living/simple_animal/bot/mulebot/proc/RunOver(mob/living/carbon/human/H) - add_logs(src, H, "run over", null, "(DAMTYPE: [uppertext(BRUTE)])") + log_combat(src, H, "run over", null, "(DAMTYPE: [uppertext(BRUTE)])") H.visible_message("[src] drives over [H]!", \ "[src] drives over you!") playsound(loc, 'sound/effects/splat.ogg', 50, 1) diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index bcc85044b0..fca1f66546 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -2,7 +2,7 @@ name = "\improper Securitron" desc = "A little security robot. He looks less than thrilled." icon = 'icons/mob/aibots.dmi' - icon_state = "secbot0" + icon_state = "secbot" density = FALSE anchored = FALSE health = 25 @@ -24,21 +24,21 @@ var/baton_type = /obj/item/melee/baton var/mob/living/carbon/target var/oldtarget_name - var/threatlevel = 0 + var/threatlevel = FALSE var/target_lastloc //Loc of target when arrested. var/last_found //There's a delay - var/declare_arrests = 1 //When making an arrest, should it notify everyone on the security channel? - var/idcheck = 0 //If true, arrest people with no IDs - var/weaponscheck = 0 //If true, arrest people for weapons if they lack access - var/check_records = 1 //Does it check security records? - var/arrest_type = 0 //If true, don't handcuff + var/declare_arrests = TRUE //When making an arrest, should it notify everyone on the security channel? + var/idcheck = FALSE //If true, arrest people with no IDs + var/weaponscheck = FALSE //If true, arrest people for weapons if they lack access + var/check_records = TRUE //Does it check security records? + var/arrest_type = FALSE //If true, don't handcuff /mob/living/simple_animal/bot/secbot/beepsky name = "Officer Beep O'sky" desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey." - idcheck = 0 - weaponscheck = 0 - auto_patrol = 1 + idcheck = FALSE + weaponscheck = FALSE + auto_patrol = TRUE /mob/living/simple_animal/bot/secbot/beepsky/jr name = "Officer Pipsqueak" @@ -65,7 +65,7 @@ /mob/living/simple_animal/bot/secbot/Initialize() . = ..() - icon_state = "secbot[on]" + update_icon() var/datum/job/detective/J = new/datum/job/detective access_card.access += J.get_access() prev_access = access_card.access @@ -74,13 +74,15 @@ var/datum/atom_hud/secsensor = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] secsensor.add_hud_to(src) -/mob/living/simple_animal/bot/secbot/turn_on() +/mob/living/simple_animal/bot/secbot/update_icon() + if(mode == BOT_HUNT) + icon_state = "[initial(icon_state)]-c" + return ..() - icon_state = "secbot[on]" /mob/living/simple_animal/bot/secbot/turn_off() ..() - icon_state = "secbot[on]" + mode = BOT_IDLE /mob/living/simple_animal/bot/secbot/bot_reset() ..() @@ -167,9 +169,14 @@ Auto Patrol: []"}, final = final|JUDGE_EMAGGED return final +/mob/living/simple_animal/bot/secbot/proc/special_retaliate_after_attack(mob/user) //allows special actions to take place after being attacked. + return + /mob/living/simple_animal/bot/secbot/attack_hand(mob/living/carbon/human/H) if((H.a_intent == INTENT_HARM) || (H.a_intent == INTENT_DISARM)) retaliate(H) + if(special_retaliate_after_attack(H)) + return return ..() @@ -179,6 +186,8 @@ Auto Patrol: []"}, return if(!istype(W, /obj/item/screwdriver) && (W.force) && (!target) && (W.damtype != STAMINA) ) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. retaliate(user) + if(special_retaliate_after_attack(user)) + return /mob/living/simple_animal/bot/secbot/emag_act(mob/user) ..() @@ -187,13 +196,13 @@ Auto Patrol: []"}, to_chat(user, "You short out [src]'s target assessment circuits.") oldtarget_name = user.name audible_message("[src] buzzes oddly!") - declare_arrests = 0 - icon_state = "secbot[on]" + declare_arrests = FALSE + update_icon() /mob/living/simple_animal/bot/secbot/bullet_act(obj/item/projectile/Proj) if(istype(Proj , /obj/item/projectile/beam)||istype(Proj, /obj/item/projectile/bullet)) if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE)) - if(!Proj.nodamage && Proj.damage < src.health) + if(!Proj.nodamage && Proj.damage < src.health && ishuman(Proj.firer)) retaliate(Proj.firer) ..() @@ -222,13 +231,13 @@ Auto Patrol: []"}, /mob/living/simple_animal/bot/secbot/proc/cuff(mob/living/carbon/C) mode = BOT_ARREST - playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) + playsound(src, 'sound/weapons/cablecuff.ogg', 30, TRUE, -2) C.visible_message("[src] is trying to put zipties on [C]!",\ "[src] is trying to put zipties on you!") addtimer(CALLBACK(src, .proc/attempt_handcuff, C), 60) /mob/living/simple_animal/bot/secbot/proc/attempt_handcuff(mob/living/carbon/C) - if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. + if( !on || !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. return if(!C.handcuffed) C.handcuffed = new /obj/item/restraints/handcuffs/cable/zipties/used(C) @@ -236,14 +245,11 @@ Auto Patrol: []"}, playsound(src, "law", 50, 0) back_to_idle() -/mob/living/simple_animal/bot/secbot/proc/update_onsprite() - icon_state = "secbot[on]" - /mob/living/simple_animal/bot/secbot/proc/stun_attack(mob/living/carbon/C) var/judgement_criteria = judgement_criteria() - playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) + playsound(src, 'sound/weapons/egloves.ogg', 50, TRUE, -1) icon_state = "secbot-c" - addtimer(CALLBACK(src, .proc/update_onsprite), 2) + addtimer(CALLBACK(src, .proc/update_icon), 2) var/threat = 5 if(ishuman(C)) C.stuttering = 5 @@ -255,7 +261,7 @@ Auto Patrol: []"}, C.stuttering = 5 threat = C.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) - add_logs(src,C,"stunned") + log_combat(src,C,"stunned") if(declare_arrests) var/area/location = get_area(src) speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag [C] in [location].", radio_channel) @@ -384,7 +390,7 @@ Auto Patrol: []"}, target = C oldtarget_name = C.name speak("Level [threatlevel] infraction alert!") - playsound(loc, pick('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg'), 50, 0) + playsound(loc, pick('sound/voice/beepsky/criminal.ogg', 'sound/voice/beepsky/justice.ogg', 'sound/voice/beepsky/freeze.ogg'), 50, FALSE) visible_message("[src] points at [C.name]!") mode = BOT_HUNT INVOKE_ASYNC(src, .proc/handle_automated_action) diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index 0d1beb6384..ff91ca7dc3 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -32,6 +32,7 @@ del_on_death = TRUE initial_language_holder = /datum/language_holder/construct deathmessage = "collapses in a shattered heap." + hud_type = /datum/hud/constructs var/list/construct_spells = list() var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." var/master = null @@ -121,8 +122,8 @@ desc = "A massive, armored construct built to spearhead attacks and soak up enemy fire." icon_state = "behemoth" icon_living = "behemoth" - maxHealth = 200 - health = 200 + maxHealth = 150 + health = 150 response_harm = "harmlessly punches" harm_intent_damage = 0 obj_damage = 90 @@ -147,7 +148,7 @@ /mob/living/simple_animal/hostile/construct/armored/bullet_act(obj/item/projectile/P) if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) - var/reflectchance = 60 - round(P.damage/3) + var/reflectchance = 40 - round(P.damage/3) if(prob(reflectchance)) apply_damage(P.damage * 0.5, P.damage_type) visible_message("The [P.name] is reflected by [src]'s armored shell!", \ @@ -227,7 +228,7 @@ /mob/living/simple_animal/hostile/construct/builder name = "Artificer" real_name = "Artificer" - desc = "A bulbous construct dedicated to building and maintaining the Cult of Nar-Sie's armies." + desc = "A bulbous construct dedicated to building and maintaining the Cult of Nar'Sie's armies." icon_state = "artificer" icon_living = "artificer" maxHealth = 50 @@ -311,7 +312,7 @@ /mob/living/simple_animal/hostile/construct/harvester name = "Harvester" real_name = "Harvester" - desc = "A long, thin construct built to herald Nar-Sie's rise. It'll be all over soon." + desc = "A long, thin construct built to herald Nar'Sie's rise. It'll be all over soon." icon_state = "chosen" icon_living = "chosen" maxHealth = 40 @@ -328,7 +329,7 @@ can_repair_constructs = TRUE -/mob/living/simple_animal/hostile/construct/harvester/Collide(atom/AM) +/mob/living/simple_animal/hostile/construct/harvester/Bump(atom/AM) . = ..() if(istype(AM, /turf/closed/wall/mineral/cult) && AM != loc) //we can go through cult walls var/atom/movable/stored_pulling = pulling diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm index 68cd130803..ec20fbadcb 100644 --- a/code/modules/mob/living/simple_animal/corpse.dm +++ b/code/modules/mob/living/simple_animal/corpse.dm @@ -27,7 +27,6 @@ back = /obj/item/storage/backpack id = /obj/item/card/id/syndicate - /obj/effect/mob_spawn/human/corpse/syndicatecommando name = "Syndicate Commando" id_job = "Operative" @@ -76,7 +75,7 @@ /obj/effect/mob_spawn/human/corpse/pirate name = "Pirate" - skin_tone = "Caucasian1" //all pirates are white because it's easier that way + skin_tone = "caucasian1" //all pirates are white because it's easier that way outfit = /datum/outfit/piratecorpse hair_style = "Bald" facial_hair_style = "Shaved" @@ -155,7 +154,7 @@ outfit = /datum/outfit/wizardcorpse hair_style = "Bald" facial_hair_style = "Long Beard" - skin_tone = "Caucasian1" + skin_tone = "caucasian1" /datum/outfit/wizardcorpse name = "Space Wizard Corpse" @@ -203,4 +202,20 @@ ears = /obj/item/radio/headset back = /obj/item/storage/backpack/satchel/med id = /obj/item/card/id - glasses = /obj/item/clothing/glasses/hud/health \ No newline at end of file + glasses = /obj/item/clothing/glasses/hud/health + +/obj/effect/mob_spawn/human/corpse/bee_terrorist + name = "BLF Operative" + outfit = /datum/outfit/bee_terrorist + +/datum/outfit/bee_terrorist + name = "BLF Operative" + uniform = /obj/item/clothing/under/color/yellow + suit = /obj/item/clothing/suit/hooded/bee_costume + shoes = /obj/item/clothing/shoes/sneakers/yellow + gloves = /obj/item/clothing/gloves/color/yellow + ears = /obj/item/radio/headset + belt = /obj/item/storage/belt/fannypack/yellow/bee_terrorist + id = /obj/item/card/id + l_pocket = /obj/item/paper/fluff/bee_objectives + mask = /obj/item/clothing/mask/rat/bee diff --git a/code/modules/mob/living/simple_animal/damage_procs.dm b/code/modules/mob/living/simple_animal/damage_procs.dm index bb6794a36a..1031c5b7ee 100644 --- a/code/modules/mob/living/simple_animal/damage_procs.dm +++ b/code/modules/mob/living/simple_animal/damage_procs.dm @@ -2,7 +2,7 @@ /mob/living/simple_animal/proc/adjustHealth(amount, updating_health = TRUE, forced = FALSE) if(!forced && (status_flags & GODMODE)) return FALSE - bruteloss = CLAMP(bruteloss + amount, 0, maxHealth) + bruteloss = round(CLAMP(bruteloss + amount, 0, maxHealth),DAMAGE_PRECISION) if(updating_health) updatehealth() return amount diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 75e7b668ea..9a08276999 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -32,6 +32,8 @@ gold_core_spawnable = FRIENDLY_SPAWN collar_type = "cat" + do_footstep = TRUE + /mob/living/simple_animal/pet/cat/Initialize() . = ..() verbs += /mob/living/proc/lay_down diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 0787e536cb..3a5d02315b 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -14,6 +14,8 @@ speak_chance = 1 turns_per_move = 10 + do_footstep = TRUE + //Corgis and pugs are now under one dog subtype /mob/living/simple_animal/pet/dog/corgi @@ -46,6 +48,16 @@ gold_core_spawnable = FRIENDLY_SPAWN collar_type = "pug" +/mob/living/simple_animal/pet/dog/corgi/exoticcorgi + name = "Exotic Corgi" + desc = "As cute as it is colorful!" + icon = 'icons/mob/pets.dmi' + icon_state = "corgigrey" + icon_living = "corgigrey" + icon_dead = "corgigrey_dead" + animal_species = /mob/living/simple_animal/pet/dog/corgi/exoticcorgi + nofur = TRUE + /mob/living/simple_animal/pet/dog/Initialize() . = ..() var/dog_area = get_area(src) @@ -58,6 +70,10 @@ . = ..() regenerate_icons() +/mob/living/simple_animal/pet/dog/corgi/exoticcorgi/Initialize() + . = ..() + var/newcolor = rgb(rand(0, 255), rand(0, 255), rand(0, 255)) + add_atom_colour(newcolor, FIXED_COLOUR_PRIORITY) /mob/living/simple_animal/pet/dog/corgi/death(gibbed) ..(gibbed) @@ -540,7 +556,7 @@ /mob/living/simple_animal/pet/dog/corgi/puppy/void //Tribute to the corgis born in nullspace name = "\improper void puppy" real_name = "voidy" - desc = "A corgi puppy that has been infused with deep space energy. It's staring back.." + desc = "A corgi puppy that has been infused with deep space energy. It's staring back..." icon_state = "void_puppy" icon_living = "void_puppy" icon_dead = "void_puppy_dead" diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm index 0c297e2c70..cf3742fcc5 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm @@ -59,7 +59,7 @@ var/laws = \ "1. You may not involve yourself in the matters of another being, even if such matters conflict with Law Two or Law Three, unless the other being is another Drone.\n"+\ "2. You may not harm any being, regardless of intent or circumstance.\n"+\ - "3. Your goals are to build, maintain, repair, improve, and provide power to the best of your abilities, You must never actively work against these goals." + "3. Your goals are to actively build, maintain, repair, improve, and provide power to the best of your abilities within the facility that housed your activation." //for derelict drones so they don't go to station. var/heavy_emp_damage = 25 //Amount of damage sustained if hit by a heavy EMP pulse var/alarms = list("Atmosphere" = list(), "Fire" = list(), "Power" = list()) var/obj/item/internal_storage //Drones can store one item, of any size/type in their body @@ -69,7 +69,7 @@ var/visualAppearence = MAINTDRONE //What we appear as var/hacked = FALSE //If we have laws to destroy the station var/flavortext = \ - "\nUNLESS YOU ARE A FREE DRONE, DO NOT INTERFERE WITH THE ROUND AS A DRONE OR YOU WILL BE DRONE BANNED\n"+\ + "\nDO NOT INTERFERE WITH THE ROUND AS A DRONE OR YOU WILL BE DRONE BANNED\n"+\ "Drones are a ghost role that are allowed to fix the station and build things. Interfering with the round as a drone is against the rules.\n"+\ "Actions that constitute interference include, but are not limited to:\n"+\ " - Interacting with round critical objects (IDs, weapons, contraband, powersinks, bombs, etc.)\n"+\ diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index 9423cf9df6..564fae48ad 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -30,6 +30,8 @@ blood_volume = BLOOD_VOLUME_NORMAL var/obj/item/udder/udder = null + do_footstep = TRUE + /mob/living/simple_animal/hostile/retaliate/goat/Initialize() udder = new() . = ..() @@ -130,6 +132,8 @@ gold_core_spawnable = FRIENDLY_SPAWN blood_volume = BLOOD_VOLUME_NORMAL + do_footstep = TRUE + /mob/living/simple_animal/cow/Initialize() udder = new() . = ..() @@ -206,6 +210,8 @@ mob_size = MOB_SIZE_TINY gold_core_spawnable = FRIENDLY_SPAWN + do_footstep = TRUE + /mob/living/simple_animal/chick/Initialize() . = ..() pixel_x = rand(-6, 6) @@ -262,6 +268,8 @@ gold_core_spawnable = FRIENDLY_SPAWN var/static/chicken_count = 0 + do_footstep = TRUE + /mob/living/simple_animal/chicken/Initialize() . = ..() if(!body_color) diff --git a/code/modules/mob/living/simple_animal/friendly/fox.dm b/code/modules/mob/living/simple_animal/friendly/fox.dm index a0e9f08a82..28b66c26ee 100644 --- a/code/modules/mob/living/simple_animal/friendly/fox.dm +++ b/code/modules/mob/living/simple_animal/friendly/fox.dm @@ -19,6 +19,8 @@ response_harm = "kicks" gold_core_spawnable = FRIENDLY_SPAWN + do_footstep = TRUE + //Captain fox /mob/living/simple_animal/pet/fox/Renault name = "Renault" diff --git a/code/modules/mob/living/simple_animal/friendly/gondola.dm b/code/modules/mob/living/simple_animal/friendly/gondola.dm index 9b021d902c..78768219de 100644 --- a/code/modules/mob/living/simple_animal/friendly/gondola.dm +++ b/code/modules/mob/living/simple_animal/friendly/gondola.dm @@ -17,7 +17,7 @@ icon = 'icons/mob/gondolas.dmi' icon_state = "gondola" icon_living = "gondola" - loot = list(/obj/effect/decal/cleanable/blood/gibs, /obj/item/stack/sheet/animalhide/gondola = 1) + loot = list(/obj/effect/decal/cleanable/blood/gibs, /obj/item/stack/sheet/animalhide/gondola = 1, /obj/item/reagent_containers/food/snacks/meat/slab/gondola = 1) //Gondolas aren't affected by cold. atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 @@ -26,6 +26,8 @@ health = 200 del_on_death = TRUE + //Gondolas don't make footstep sounds + /mob/living/simple_animal/pet/gondola/Initialize() . = ..() CreateGondola() diff --git a/code/modules/mob/living/simple_animal/friendly/penguin.dm b/code/modules/mob/living/simple_animal/friendly/penguin.dm index 0bf9f4732d..9835840dbf 100644 --- a/code/modules/mob/living/simple_animal/friendly/penguin.dm +++ b/code/modules/mob/living/simple_animal/friendly/penguin.dm @@ -15,10 +15,12 @@ turns_per_move = 10 icon = 'icons/mob/penguins.dmi' + do_footstep = TRUE + /mob/living/simple_animal/pet/penguin/emperor name = "Emperor penguin" real_name = "penguin" - desc = "Emperor of all he surveys." + desc = "Emperor of all they survey." icon_state = "penguin" icon_living = "penguin" icon_dead = "penguin_dead" diff --git a/code/modules/mob/living/simple_animal/friendly/sloth.dm b/code/modules/mob/living/simple_animal/friendly/sloth.dm index f112b3f98e..324fa107fa 100644 --- a/code/modules/mob/living/simple_animal/friendly/sloth.dm +++ b/code/modules/mob/living/simple_animal/friendly/sloth.dm @@ -23,6 +23,8 @@ speed = 10 glide_size = 2 + do_footstep = TRUE + //Cargo Sloth /mob/living/simple_animal/sloth/paperwork diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm index 648db14941..701e244f89 100644 --- a/code/modules/mob/living/simple_animal/guardian/guardian.dm +++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm @@ -38,6 +38,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians melee_damage_upper = 15 butcher_results = list(/obj/item/ectoplasm = 1) AIStatus = AI_OFF + hud_type = /datum/hud/guardian dextrous_hud_type = /datum/hud/dextrous/guardian //if we're set to dextrous, account for it. var/list/guardian_overlays[GUARDIAN_TOTAL_LAYERS] var/reset = 0 //if the summoner has reset the guardian already @@ -370,7 +371,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians var/link = FOLLOW_LINK(M, src) to_chat(M, "[link] [my_message]") - log_talk(src,"GUARDIAN:[key_name(src)]: [input]",LOGSAY) + src.log_talk(input, LOG_SAY, tag="guardian") /mob/living/proc/guardian_comm() set name = "Communicate" @@ -392,7 +393,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians var/link = FOLLOW_LINK(M, src) to_chat(M, "[link] [my_message]") - log_talk(src,"GUARDIAN:[key_name(src)]: [input]",LOGSAY) + src.log_talk(input, LOG_SAY, tag="guardian") //FORCE RECALL/RESET diff --git a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm index fc25ec5ac6..8fb1de18df 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/explosive.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/explosive.dm @@ -81,7 +81,7 @@ else to_chat(user, "[src] glows with a strange light, and you don't touch it.") -/obj/guardian_bomb/Collide(atom/A) +/obj/guardian_bomb/Bump(atom/A) detonate(A) ..() diff --git a/code/modules/mob/living/simple_animal/guardian/types/fire.dm b/code/modules/mob/living/simple_animal/guardian/types/fire.dm index 8f456abc53..7a469dd12c 100644 --- a/code/modules/mob/living/simple_animal/guardian/types/fire.dm +++ b/code/modules/mob/living/simple_animal/guardian/types/fire.dm @@ -27,11 +27,11 @@ ..() collision_ignite(AM) -/mob/living/simple_animal/hostile/guardian/fire/CollidedWith(atom/movable/AM) +/mob/living/simple_animal/hostile/guardian/fire/Bumped(atom/movable/AM) ..() collision_ignite(AM) -/mob/living/simple_animal/hostile/guardian/fire/Collide(AM as mob|obj) +/mob/living/simple_animal/hostile/guardian/fire/Bump(AM as mob|obj) ..() collision_ignite(AM) diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index 762b662de7..3d92912f9c 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -36,6 +36,8 @@ death_sound = 'sound/voice/hiss6.ogg' deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..." + do_footstep = TRUE + /mob/living/simple_animal/hostile/alien/drone name = "alien drone" icon_state = "aliend" diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index 8573c3b7cb..acd8da9618 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -39,6 +39,8 @@ faction = list("russian") gold_core_spawnable = HOSTILE_SPAWN + do_footstep = TRUE + //SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! /mob/living/simple_animal/hostile/bear/Hudson name = "Hudson" @@ -81,6 +83,7 @@ icon_state = "bear_armor_upgrade" /obj/item/bear_armor/afterattack(atom/target, mob/user, proximity_flag) + . = ..() if(istype(target, /mob/living/simple_animal/hostile/bear) && proximity_flag) var/mob/living/simple_animal/hostile/bear/A = target if(A.armored) diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index d8edf7fe67..c7b46e6aed 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -209,14 +209,14 @@ var/datum/reagent/R = pick(typesof(/datum/reagent/toxin)) assign_reagent(GLOB.chemical_reagents_list[initial(R.id)]) - /mob/living/simple_animal/hostile/poison/bees/queen - name = "queen bee" - desc = "She's the queen of bees, BZZ BZZ!" - icon_base = "queen" - isqueen = TRUE +/mob/living/simple_animal/hostile/poison/bees/queen + name = "queen bee" + desc = "She's the queen of bees, BZZ BZZ!" + icon_base = "queen" + isqueen = TRUE - //the Queen doesn't leave the box on her own, and she CERTAINLY doesn't pollinate by herself +//the Queen doesn't leave the box on her own, and she CERTAINLY doesn't pollinate by herself /mob/living/simple_animal/hostile/poison/bees/queen/Found(atom/A) return FALSE @@ -295,3 +295,10 @@ forceMove(beehome.drop_location()) else ..() + +/mob/living/simple_animal/hostile/poison/bees/short + desc = "These bees seem unstable and won't survive for long." + +/mob/living/simple_animal/hostile/poison/bees/short/Initialize() + . = ..() + addtimer(CALLBACK(src, .proc/death), 50 SECONDS) diff --git a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm index 9ae70745cf..be88394692 100644 --- a/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm +++ b/code/modules/mob/living/simple_animal/hostile/bosses/boss.dm @@ -65,7 +65,7 @@ return 0 if(boss) if(say_when_triggered) - boss.say(say_when_triggered) + boss.say(say_when_triggered, forced = "boss action") if(!boss.atb.spend(boss_cost)) return 0 diff --git a/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm b/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm index 1b766a51d6..d31af79c19 100644 --- a/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm +++ b/code/modules/mob/living/simple_animal/hostile/bosses/paperwizard.dm @@ -23,6 +23,8 @@ attack_sound = 'sound/hallucinations/growl1.ogg' var/list/copies = list() + do_footstep = TRUE + //Summon Ability //Lets the wizard summon his art to fight for him diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm index d267da624f..4e7cb0ac70 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithless.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm @@ -34,6 +34,8 @@ faction = list("faithless") gold_core_spawnable = HOSTILE_SPAWN + do_footstep = TRUE + /mob/living/simple_animal/hostile/faithless/AttackingTarget() . = ..() if(. && prob(12) && iscarbon(target)) diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index 1df3053715..cfdf302d6b 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -52,6 +52,8 @@ var/datum/action/innate/spider/lay_web/lay_web var/directive = "" //Message passed down to children, to relay the creator's orders + do_footstep = TRUE + /mob/living/simple_animal/hostile/poison/giant_spider/Initialize() . = ..() lay_web = new @@ -80,7 +82,7 @@ humanize_spider(user) /mob/living/simple_animal/hostile/poison/giant_spider/proc/humanize_spider(mob/user) - if(key || !playable_spider)//Someone is in it or the fun police are shutting it down + if(key || !playable_spider || stat)//Someone is in it, it's dead, or the fun police are shutting it down return 0 var/spider_ask = alert("Become a spider?", "Are you australian?", "Yes", "No") if(spider_ask == "No" || !src || QDELETED(src)) @@ -520,7 +522,7 @@ for(var/M in GLOB.dead_mob_list) var/link = FOLLOW_LINK(M, user) to_chat(M, "[link] [my_message]") - log_talk(user, "SPIDERCOMMAND: [key_name(user)] : [message]",LOGSAY) + usr.log_talk(message, LOG_SAY, tag="spider command") /mob/living/simple_animal/hostile/poison/giant_spider/handle_temperature_damage() if(bodytemperature < minbodytemp) diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm index 3af442930e..100db06174 100644 --- a/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm +++ b/code/modules/mob/living/simple_animal/hostile/gorilla/emotes.dm @@ -7,5 +7,5 @@ key_third_person = "oogas" message = "oogas." message_param = "oogas at %t." - sound = "sound/creatures/gorilla.ogg" + sound = 'sound/creatures/gorilla.ogg' diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm index 214ced3613..5d1db8d35e 100644 --- a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm +++ b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla.dm @@ -38,6 +38,8 @@ var/list/gorilla_overlays[GORILLA_TOTAL_LAYERS] var/oogas = 0 + do_footstep = TRUE + // Gorillas like to dismember limbs from unconcious mobs. // Returns null when the target is not an unconcious carbon mob; a list of limbs (possibly empty) otherwise. /mob/living/simple_animal/hostile/gorilla/proc/target_bodyparts(atom/the_target) @@ -92,7 +94,7 @@ /mob/living/simple_animal/hostile/gorilla/handle_automated_speech(override) if(speak_chance && (override || prob(speak_chance))) - playsound(src, "sound/creatures/gorilla.ogg", 200) + playsound(src, 'sound/creatures/gorilla.ogg', 200) ..() /mob/living/simple_animal/hostile/gorilla/can_use_guns(obj/item/G) @@ -103,6 +105,6 @@ /mob/living/simple_animal/hostile/gorilla/proc/oogaooga() oogas++ if(oogas >= rand(2,6)) - playsound(src, "sound/creatures/gorilla.ogg", 200) + playsound(src, 'sound/creatures/gorilla.ogg', 200) oogas = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm index 3f0c468d26..93284fe666 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm @@ -29,6 +29,8 @@ del_on_death = 1 loot = list(/obj/effect/decal/cleanable/robot_debris) + do_footstep = TRUE + /mob/living/simple_animal/hostile/hivebot/Initialize() . = ..() deathmessage = "[src] blows apart!" diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index acb3550454..d8635060ee 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -4,9 +4,16 @@ obj_damage = 40 environment_smash = ENVIRONMENT_SMASH_STRUCTURES //Bitflags. Set to ENVIRONMENT_SMASH_STRUCTURES to break closets,tables,racks, etc; ENVIRONMENT_SMASH_WALLS for walls; ENVIRONMENT_SMASH_RWALLS for rwalls var/atom/target - var/ranged = 0 + var/ranged = FALSE var/rapid = 0 //How many shots per volley. var/rapid_fire_delay = 2 //Time between rapid fire shots + + var/dodging = FALSE + var/approaching_target = FALSE //We should dodge now + var/in_melee = FALSE //We should sidestep now + var/dodge_prob = 30 + var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee + var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile var/projectilesound var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING @@ -15,6 +22,9 @@ var/list/emote_taunt = list() var/taunt_chance = 0 + var/rapid_melee = 1 //Number of melee attacks between each npc pool tick. Spread evenly. + var/melee_queue_distance = 4 //If target is close enough start preparing to hit them if we have rapid_melee enabled + var/ranged_message = "fires" //Fluff text for ranged mobs var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is @@ -41,7 +51,6 @@ var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever - /mob/living/simple_animal/hostile/Initialize() . = ..() @@ -76,6 +85,34 @@ toggle_ai(AI_IDLE) // otherwise we go idle return 1 +/mob/living/simple_animal/hostile/handle_automated_movement() + . = ..() + if(dodging && target && in_melee && isturf(loc) && isturf(target.loc)) + var/datum/cb = CALLBACK(src,.proc/sidestep) + if(sidestep_per_cycle > 1) //For more than one just spread them equally - this could changed to some sensible distribution later + var/sidestep_delay = SSnpcpool.wait / sidestep_per_cycle + for(var/i in 1 to sidestep_per_cycle) + addtimer(cb, (i - 1)*sidestep_delay) + else //Otherwise randomize it to make the players guessing. + addtimer(cb,rand(1,SSnpcpool.wait)) + +/mob/living/simple_animal/hostile/proc/sidestep() + if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD) + return + var/target_dir = get_dir(src,target) + + var/static/list/cardinal_sidestep_directions = list(-90,-45,0,45,90) + var/static/list/diagonal_sidestep_directions = list(-45,0,45) + var/chosen_dir = 0 + if (target_dir & (target_dir - 1)) + chosen_dir = pick(diagonal_sidestep_directions) + else + chosen_dir = pick(cardinal_sidestep_directions) + if(chosen_dir) + chosen_dir = turn(target_dir,chosen_dir) + Move(get_step(src,chosen_dir)) + face_atom(target) //Looks better if they keep looking at you when dodging + /mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) FindTarget(list(user), 1) @@ -221,7 +258,23 @@ Aggro() return 1 -/mob/living/simple_animal/hostile/proc/MoveToTarget(var/list/possible_targets)//Step 5, handle movement between us and our target +//What we do after closing in +/mob/living/simple_animal/hostile/proc/MeleeAction(patience = TRUE) + if(rapid_melee > 1) + var/datum/callback/cb = CALLBACK(src, .proc/CheckAndAttack) + var/delay = SSnpcpool.wait / rapid_melee + for(var/i in 1 to rapid_melee) + addtimer(cb, (i - 1)*delay) + else + AttackingTarget() + if(patience) + GainPatience() + +/mob/living/simple_animal/hostile/proc/CheckAndAttack() + if(target && targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && !incapacitated()) + AttackingTarget() + +/mob/living/simple_animal/hostile/proc/MoveToTarget(list/possible_targets)//Step 5, handle movement between us and our target stop_automated_movement = 1 if(!target || !CanAttack(target)) LoseTarget() @@ -247,8 +300,11 @@ Goto(target,move_to_delay,minimum_distance) if(target) if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack - AttackingTarget() - GainPatience() + MeleeAction() + else + if(rapid_melee > 1 && target_distance <= melee_queue_distance) + MeleeAction(FALSE) + in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode return 1 return 0 if(environment_smash) @@ -266,6 +322,10 @@ return 0 /mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance) + if(target == src.target) + approaching_target = TRUE + else + approaching_target = FALSE walk_to(src, target, minimum_distance, delay) /mob/living/simple_animal/hostile/adjustHealth(amount, updating_health = TRUE, forced = FALSE) @@ -282,6 +342,7 @@ /mob/living/simple_animal/hostile/proc/AttackingTarget() + in_melee = TRUE return target.attack_animal(src) /mob/living/simple_animal/hostile/proc/Aggro() @@ -298,6 +359,8 @@ /mob/living/simple_animal/hostile/proc/LoseTarget() target = null + approaching_target = FALSE + in_melee = FALSE walk(src, 0) LoseAggro() @@ -331,6 +394,7 @@ return visible_message("[src] [ranged_message] at [A]!") + if(rapid > 1) var/datum/callback/cb = CALLBACK(src, .proc/Shoot, A) for(var/i in 1 to rapid) @@ -367,6 +431,22 @@ return iswallturf(T) || ismineralturf(T) +/mob/living/simple_animal/hostile/Move(atom/newloc, dir , step_x , step_y) + if(dodging && approaching_target && prob(dodge_prob) && moving_diagonally == 0 && isturf(loc) && isturf(newloc)) + return dodge(newloc,dir) + else + return ..() + +/mob/living/simple_animal/hostile/proc/dodge(moving_to,move_direction) + //Assuming we move towards the target we want to swerve toward them to get closer + var/cdir = turn(move_direction,45) + var/ccdir = turn(move_direction,-45) + dodging = FALSE + . = Move(get_step(loc,pick(cdir,ccdir))) + if(!.)//Can't dodge there so we just carry on + . = Move(moving_to,move_direction) + dodging = TRUE + /mob/living/simple_animal/hostile/proc/DestroyObjectsInDirection(direction) var/turf/T = get_step(targets_from, direction) if(T && T.Adjacent(targets_from)) diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm index dbafe9451c..d1e8f1f49e 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm @@ -26,6 +26,8 @@ var/hop_cooldown = 0 //Strictly for player controlled leapers var/projectile_ready = FALSE //Stopping AI leapers from firing whenever they want, and only doing it after a hop has finished instead + do_footstep = TRUE + /obj/item/projectile/leaper name = "leaper bubble" icon_state = "leaper" @@ -67,7 +69,6 @@ desc = "A small pool of sludge, containing trace amounts of leaper venom." icon = 'icons/effects/tomatodecal.dmi' icon_state = "tomato_floor1" - beauty = -200 /obj/structure/leaper_bubble name = "leaper bubble" @@ -204,7 +205,7 @@ if(AIStatus == AI_ON && ranged_cooldown <= world.time) projectile_ready = TRUE update_icons() - throw_at(new_turf, max(3,get_dist(src,new_turf)), 1, src, FALSE, callback = CALLBACK(src, .FinishHop)) + throw_at(new_turf, max(3,get_dist(src,new_turf)), 1, src, FALSE, callback = CALLBACK(src, .proc/FinishHop)) /mob/living/simple_animal/hostile/jungle/leaper/proc/FinishHop() density = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm b/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm index 616c9025b9..607db5d54f 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm @@ -24,6 +24,8 @@ projectilesound = 'sound/weapons/pierce.ogg' alpha = 50 + do_footstep = TRUE + /mob/living/simple_animal/hostile/jungle/mega_arachnid/Life() ..() if(target && ranged_cooldown > world.time && iscarbon(target)) diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm index 1fde4a9cd4..29f48c7295 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm @@ -31,6 +31,8 @@ var/attack_state = MOOK_ATTACK_NEUTRAL var/struck_target_leap = FALSE + do_footstep = TRUE + /mob/living/simple_animal/hostile/jungle/mook/CanPass(atom/movable/O) if(istype(O, /mob/living/simple_animal/hostile/jungle/mook)) var/mob/living/simple_animal/hostile/jungle/mook/M = O diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm index 5b634f8ae1..1a894734d8 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm @@ -47,7 +47,7 @@ hitsound_wall = 'sound/weapons/effects/searwall.ogg' nondirectional_sprite = TRUE -/obj/item/projectile/seedling/Collide(atom/A)//Stops seedlings from destroying other jungle mobs through FF +/obj/item/projectile/seedling/Bump(atom/A)//Stops seedlings from destroying other jungle mobs through FF if(isliving(A)) var/mob/living/L = A if("jungle" in L.faction) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 3895951b3c..97dede3d2f 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -53,6 +53,8 @@ Difficulty: Medium deathmessage = "falls to the ground, decaying into glowing particles." death_sound = "bodyfall" + do_footstep = TRUE + /mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance guidance = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 7d03ad4b9e..3f68c15dc2 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -52,6 +52,8 @@ Difficulty: Hard deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... " death_sound = 'sound/magic/enter_blood.ogg' + do_footstep = TRUE + /obj/item/gps/internal/bubblegum icon_state = null gpstag = "Bloody Signal" @@ -147,7 +149,7 @@ Difficulty: Hard Goto(target, move_to_delay, minimum_distance) -/mob/living/simple_animal/hostile/megafauna/bubblegum/Collide(atom/A) +/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A) if(charging) if(isturf(A) || isobj(A) && A.density) A.ex_act(EXPLODE_HEAVY) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 19e2fda91d..2c5d004054 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -235,8 +235,6 @@ Difficulty: Very Hard desc = "A completely indestructible chunk of crystal, rumoured to predate the start of this universe. It looks like you could store things inside it." icon = 'icons/obj/lavaland/artefacts.dmi' icon_state = "blackbox" - icon_on = "blackbox" - icon_off = "blackbox" light_range = 8 max_n_of_items = INFINITY resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF @@ -379,7 +377,7 @@ Difficulty: Very Hard to_chat(user, "It is activated by [activation_method].") /obj/machinery/anomalous_crystal/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode) - . = ..() + ..() if(isliving(speaker)) ActivationReaction(speaker, ACTIVATE_SPEECH) @@ -414,7 +412,7 @@ Difficulty: Very Hard playsound(user, activation_sound, 100, 1) return TRUE -/obj/machinery/anomalous_crystal/CollidedWith(atom/movable/AM) +/obj/machinery/anomalous_crystal/Bumped(atom/movable/AM) ..() if(ismob(AM)) ActivationReaction(AM, ACTIVATE_MOB_BUMP) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 9aee15d9b6..fac14eeb0e 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -63,6 +63,8 @@ Difficulty: Medium death_sound = 'sound/magic/demon_dies.ogg' var/datum/action/small_sprite/smallsprite = new/datum/action/small_sprite/drake() + do_footstep = TRUE + /mob/living/simple_animal/hostile/megafauna/dragon/Initialize() smallsprite.Grant(src) . = ..() @@ -243,7 +245,7 @@ Difficulty: Medium playsound(loc, 'sound/effects/meteorimpact.ogg', 200, 1) for(var/mob/living/L in orange(1, src)) if(L.stat) - visible_message("[src] slams down on [L], crushing them!") + visible_message("[src] slams down on [L], crushing [L.p_them()]!") L.gib() else L.adjustBruteLoss(75) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index bc873e68e7..63b18ad82d 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -641,7 +641,7 @@ Difficulty: Hard H.Goto(get_turf(caster), H.move_to_delay, 3) if(monster_damage_boost && (ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid))) L.adjustBruteLoss(damage) - add_logs(caster, L, "struck with a [name]") + log_combat(caster, L, "struck with a [name]") for(var/obj/mecha/M in T.contents - hit_things) //also damage mechs. hit_things += M if(M.occupant) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index a963e96624..2710d58a67 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -104,7 +104,7 @@ Difficulty: Medium adjustHealth(-maxHealth) //heal ourself to full in prep for splitting var/mob/living/simple_animal/hostile/megafauna/legion/L = new(loc) - L.maxHealth = maxHealth * 0.6 + L.maxHealth = round(maxHealth * 0.6,DAMAGE_PRECISION) maxHealth = L.maxHealth L.health = L.maxHealth diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index b3cba7e611..9dc51ac950 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -68,8 +68,8 @@ else ..() -/mob/living/simple_animal/hostile/megafauna/dust() - if(health > 0) +/mob/living/simple_animal/hostile/megafauna/dust(just_ash, drop_items, force) + if(!force && health > 0) return else ..() @@ -83,11 +83,8 @@ if(L.stat != DEAD) if(!client && ranged && ranged_cooldown <= world.time) OpenFire() - else if(L.stat >= SOFT_CRIT) - if(vore_active == TRUE && L.devourable == TRUE) - dragon_feeding(src,L) - else if(L.stat == DEAD) - devour(L) + else + devour(L) /mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L) if(!L) diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm index 0c660dd1cd..f74f9a436c 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goliath.dm @@ -33,6 +33,8 @@ var/pre_attack_icon = "Goliath_preattack" loot = list(/obj/item/stack/sheet/animalhide/goliath_hide) + do_footstep = TRUE + /mob/living/simple_animal/hostile/asteroid/goliath/Life() . = ..() handle_preattack() diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm index 32c7ba703b..73356658a3 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/hivelord.dm @@ -365,7 +365,7 @@ suit_store = /obj/item/tome r_pocket = /obj/item/restraints/legcuffs/bola/cult l_pocket = /obj/item/melee/cultblade/dagger - glasses = /obj/item/clothing/glasses/night/cultblind + glasses = /obj/item/clothing/glasses/hud/health/night/cultblind backpack_contents = list(/obj/item/reagent_containers/glass/beaker/unholywater = 1, /obj/item/cult_shift = 1, /obj/item/flashlight/flare/culttorch = 1, /obj/item/stack/sheet/runed_metal = 15) if("Lavaknight") //START OF CIT CHANGE uniform = /obj/item/clothing/under/assistantformal diff --git a/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm b/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm index b170ae0574..543ffe6131 100644 --- a/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm +++ b/code/modules/mob/living/simple_animal/hostile/nanotrasen.dm @@ -31,6 +31,8 @@ speak = list("Stop resisting!", "I AM THE LAW!", "Face the wrath of the golden bolt!", "Stop breaking the law, asshole!") search_objects = 1 + do_footstep = TRUE + /mob/living/simple_animal/hostile/nanotrasen/Aggro() ..() diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm index 0991c8922a..8210fd6490 100644 --- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm +++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm @@ -34,9 +34,9 @@ /mob/living/simple_animal/hostile/netherworld/migo/Initialize() . = ..() - migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/carhorn.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/megaphone.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/items/geiger/high1.ogg', 'sound/items/geiger/high2.ogg', 'sound/voice/bcreep.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/mpatchedup.ogg', 'sound/voice/mfeelbetter.ogg', 'sound/voice/human/manlaugh1.ogg', 'sound/voice/human/womanlaugh.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/clockcultalr.ogg', 'sound/ambience/antag/ling_aler.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/ambience/antag/monkey.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clockcult_gateway_disrupted.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/curse1.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosion_distant.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/effects/his_grace_awaken.ogg', 'sound/effects/pai_boot.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/ratvar_reveal.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/misc/airraid.ogg', 'sound/misc/bang.ogg','sound/misc/highlander.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/misc/splort.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/beam_sniper.ogg', 'sound/weapons/chainsawhit.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshot_silenced.ogg', 'sound/weapons/gunshot2.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/clockcult/steam_whoosh.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlockclose.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlockopen.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/cryo_warning.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/machines/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/triple_beep.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/poweroff.ogg', 'sound/ai/radiation.ogg', 'sound/ai/shuttlecalled.ogg', 'sound/ai/shuttledock.ogg', 'sound/ai/shuttlerecalled.ogg', 'sound/ai/aimalf.ogg') //hahahaha fuck you code divers + migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/carhorn.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/megaphone.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/items/geiger/high1.ogg', 'sound/items/geiger/high2.ogg', 'sound/voice/beepsky/creep.ogg', 'sound/voice/beepsky/iamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/medbot/patchedup.ogg', 'sound/voice/medbot/feelbetter.ogg', 'sound/voice/human/manlaugh1.ogg', 'sound/voice/human/womanlaugh.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/clockcultalr.ogg', 'sound/ambience/antag/ling_aler.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/ambience/antag/monkey.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clockcult_gateway_disrupted.ogg', 'sound/effects/clownstep2.ogg', 'sound/effects/curse1.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosion_distant.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/hyperspace_begin.ogg', 'sound/effects/hyperspace_end.ogg', 'sound/effects/his_grace_awaken.ogg', 'sound/effects/pai_boot.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/ratvar_reveal.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desceration-01.ogg', 'sound/misc/desceration-02.ogg', 'sound/misc/desceration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/misc/airraid.ogg', 'sound/misc/bang.ogg','sound/misc/highlander.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/misc/splort.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/beam_sniper.ogg', 'sound/weapons/chainsawhit.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gunshot_silenced.ogg', 'sound/weapons/gunshot2.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kenetic_accel.ogg', 'sound/machines/clockcult/steam_whoosh.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlockclose.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlockopen.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/cryo_warning.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/machines/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/triple_beep.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/outbreak5.ogg', 'sound/ai/outbreak7.ogg', 'sound/ai/poweroff.ogg', 'sound/ai/radiation.ogg', 'sound/ai/shuttlecalled.ogg', 'sound/ai/shuttledock.ogg', 'sound/ai/shuttlerecalled.ogg', 'sound/ai/aimalf.ogg') //hahahaha fuck you code divers -/mob/living/simple_animal/hostile/netherworld/migo/say(message) +/mob/living/simple_animal/hostile/netherworld/migo/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) ..() if(stat) return diff --git a/code/modules/mob/living/simple_animal/hostile/pirate.dm b/code/modules/mob/living/simple_animal/hostile/pirate.dm index f76357de1b..be40518d70 100644 --- a/code/modules/mob/living/simple_animal/hostile/pirate.dm +++ b/code/modules/mob/living/simple_animal/hostile/pirate.dm @@ -4,7 +4,7 @@ icon = 'icons/mob/simple_human.dmi' icon_state = "piratemelee" icon_living = "piratemelee" - icon_dead = "piratemelee_dead" + icon_dead = "pirate_dead" mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID) speak_chance = 0 turns_per_move = 5 @@ -14,15 +14,12 @@ speed = 0 maxHealth = 100 health = 100 - spacewalk = TRUE - harm_intent_damage = 5 - obj_damage = 60 - melee_damage_lower = 30 - melee_damage_upper = 30 - attacktext = "slashes" - attack_sound = 'sound/weapons/bladeslice.ogg' - + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + a_intent = INTENT_HARM atmos_requirements = list("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_atmos_damage = 15 speak_emote = list("yarrs") @@ -30,51 +27,65 @@ /obj/item/melee/transforming/energy/sword/pirate) del_on_death = 1 faction = list("pirate") + + +/mob/living/simple_animal/hostile/pirate/melee + name = "Pirate Swashbuckler" + icon_state = "piratemelee" + icon_living = "piratemelee" + icon_dead = "piratemelee_dead" + melee_damage_lower = 30 + melee_damage_upper = 30 + armour_penetration = 35 + attacktext = "slashes" + attack_sound = 'sound/weapons/blade1.ogg' var/obj/effect/light_emitter/red_energy_sword/sord -/mob/living/simple_animal/hostile/pirate/Initialize() + do_footstep = TRUE + +/mob/living/simple_animal/hostile/pirate/melee/space + name = "Space Pirate Swashbuckler" + icon_state = "piratespace" + icon_living = "piratespace" + icon_dead = "piratespace_dead" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speed = 1 + spacewalk = TRUE + +/mob/living/simple_animal/hostile/pirate/melee/Initialize() . = ..() sord = new(src) -/mob/living/simple_animal/hostile/pirate/Destroy() +/mob/living/simple_animal/hostile/pirate/melee/Destroy() QDEL_NULL(sord) return ..() +/mob/living/simple_animal/hostile/pirate/melee/Initialize() + . = ..() + set_light(2) + /mob/living/simple_animal/hostile/pirate/ranged name = "Pirate Gunner" icon_state = "pirateranged" icon_living = "pirateranged" - icon_dead = "piratemelee_dead" + icon_dead = "pirateranged_dead" projectilesound = 'sound/weapons/laser.ogg' ranged = 1 - rapid = 3 + rapid = 2 + rapid_fire_delay = 6 retreat_distance = 5 minimum_distance = 5 projectiletype = /obj/item/projectile/beam/laser loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged, /obj/item/gun/energy/laser) -/mob/living/simple_animal/hostile/pirate/space - name = "Space Pirate" - icon_state = "piratespace" - icon_living = "piratespace" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - speed = 1 - -/mob/living/simple_animal/hostile/pirate/space/Initialize() - . = ..() - set_light(3) - -/mob/living/simple_animal/hostile/pirate/space/ranged +/mob/living/simple_animal/hostile/pirate/ranged/space name = "Space Pirate Gunner" icon_state = "piratespaceranged" icon_living = "piratespaceranged" - projectilesound = 'sound/weapons/laser.ogg' - ranged = 1 - rapid = 3 - retreat_distance = 5 - minimum_distance = 5 - projectiletype = /obj/item/projectile/beam/laser - loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged, - /obj/item/gun/energy/laser) + icon_dead = "piratespaceranged_dead" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speed = 1 + spacewalk = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm index cd7acc6f5a..cd978b7066 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm @@ -33,6 +33,8 @@ maxbodytemp = 370 unsuitable_atmos_damage = 10 + do_footstep = TRUE + /mob/living/simple_animal/hostile/retaliate/clown/handle_temperature_damage() if(bodytemperature < minbodytemp) adjustBruteLoss(10) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm index a132503786..c50ace8871 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/spaceman.dm @@ -24,6 +24,8 @@ environment_smash = ENVIRONMENT_SMASH_NONE del_on_death = 0 + do_footstep = TRUE + /mob/living/simple_animal/hostile/retaliate/nanotrasenpeace //this should be in a different file name = "Nanotrasen Private Security Officer" desc = "An officer part of Nanotrasen's private security force." diff --git a/code/modules/mob/living/simple_animal/hostile/russian.dm b/code/modules/mob/living/simple_animal/hostile/russian.dm index c083b8fdd5..f66814f01f 100644 --- a/code/modules/mob/living/simple_animal/hostile/russian.dm +++ b/code/modules/mob/living/simple_animal/hostile/russian.dm @@ -29,6 +29,8 @@ status_flags = CANPUSH del_on_death = 1 + do_footstep = TRUE + /mob/living/simple_animal/hostile/russian/ranged icon_state = "russianranged" diff --git a/code/modules/mob/living/simple_animal/hostile/skeleton.dm b/code/modules/mob/living/simple_animal/hostile/skeleton.dm index fca6afd4a6..d0ae01f443 100644 --- a/code/modules/mob/living/simple_animal/hostile/skeleton.dm +++ b/code/modules/mob/living/simple_animal/hostile/skeleton.dm @@ -34,6 +34,8 @@ del_on_death = 1 loot = list(/obj/effect/decal/remains/human) + do_footstep = TRUE + /mob/living/simple_animal/hostile/skeleton/eskimo name = "undead eskimo" desc = "The reanimated remains of some poor traveler." diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm index 81d583ee65..804989e71e 100644 --- a/code/modules/mob/living/simple_animal/hostile/statue.dm +++ b/code/modules/mob/living/simple_animal/hostile/statue.dm @@ -133,7 +133,7 @@ // Cannot talk -/mob/living/simple_animal/hostile/statue/say() +/mob/living/simple_animal/hostile/statue/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) return 0 // Turn to dust when gibbed diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 8efa3e4d35..2c45743c55 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -1,9 +1,9 @@ /* CONTENTS LINE 10 - BASE MOB - LINE 43 - SWORD AND SHIELD - LINE 95 - GUNS - LINE 136 - MISC + LINE 52 - SWORD AND SHIELD + LINE 164 - GUNS + LINE 267 - MISC */ @@ -46,28 +46,87 @@ check_friendly_fire = 1 status_flags = CANPUSH del_on_death = 1 + dodging = TRUE + rapid_melee = 2 -///////////////Sword and shield//////////// + do_footstep = TRUE + +///////////////Melee//////////// + +/mob/living/simple_animal/hostile/syndicate/space + icon_state = "syndicate_space" + icon_living = "syndicate_space" + name = "Syndicate Commando" + maxHealth = 170 + health = 170 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speed = 1 + spacewalk = TRUE + +/mob/living/simple_animal/hostile/syndicate/space/Initialize() + . = ..() + set_light(4) + +/mob/living/simple_animal/hostile/syndicate/space/stormtrooper + icon_state = "syndicate_stormtrooper" + icon_living = "syndicate_stormtrooper" + name = "Syndicate Stormtrooper" + maxHealth = 250 + health = 250 /mob/living/simple_animal/hostile/syndicate/melee - melee_damage_lower = 25 - melee_damage_upper = 30 - icon_state = "syndicatemelee" - icon_living = "syndicatemelee" + melee_damage_lower = 15 + melee_damage_upper = 15 + icon_state = "syndicate_knife" + icon_living = "syndicate_knife" loot = list(/obj/effect/gibspawner/human) attacktext = "slashes" attack_sound = 'sound/weapons/bladeslice.ogg' - armour_penetration = 28 - light_color = LIGHT_COLOR_RED status_flags = 0 + +/mob/living/simple_animal/hostile/syndicate/melee/space + icon_state = "syndicate_space_knife" + icon_living = "syndicate_space_knife" + name = "Syndicate Commando" maxHealth = 170 health = 170 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speed = 1 spacewalk = TRUE -/mob/living/simple_animal/hostile/syndicate/melee/Initialize() +/mob/living/simple_animal/hostile/syndicate/melee/space/Initialize() + . = ..() + set_light(4) + +/mob/living/simple_animal/hostile/syndicate/melee/space/stormtrooper + icon_state = "syndicate_stormtrooper_knife" + icon_living = "syndicate_stormtrooper_knife" + name = "Syndicate Stormtrooper" + maxHealth = 250 + health = 250 + +/mob/living/simple_animal/hostile/syndicate/melee/sword + melee_damage_lower = 30 + melee_damage_upper = 30 + icon_state = "syndicate_sword" + icon_living = "syndicate_sword" + attacktext = "slashes" + attack_sound = 'sound/weapons/blade1.ogg' + armour_penetration = 35 + light_color = LIGHT_COLOR_RED + status_flags = 0 + var/obj/effect/light_emitter/red_energy_sword/sord + +/mob/living/simple_animal/hostile/syndicate/melee/sword/Initialize() . = ..() set_light(2) +/mob/living/simple_animal/hostile/syndicate/melee/sword/Destroy() + QDEL_NULL(sord) + return ..() + /mob/living/simple_animal/hostile/syndicate/melee/bullet_act(obj/item/projectile/Proj) if(!Proj) return @@ -77,74 +136,135 @@ visible_message("[src] blocks [Proj] with its shield!") return 0 - -/mob/living/simple_animal/hostile/syndicate/melee/space +/mob/living/simple_animal/hostile/syndicate/melee/sword/space + icon_state = "syndicate_space_sword" + icon_living = "syndicate_space_sword" + name = "Syndicate Commando" + maxHealth = 170 + health = 170 atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 - icon_state = "syndicatemeleespace" - icon_living = "syndicatemeleespace" - name = "Syndicate Commando" - loot = list(/obj/effect/gibspawner/human) speed = 1 - var/obj/effect/light_emitter/red_energy_sword/sord + spacewalk = TRUE -/mob/living/simple_animal/hostile/syndicate/melee/space/Initialize() +/mob/living/simple_animal/hostile/syndicate/melee/sword/space/Initialize() . = ..() sord = new(src) set_light(4) -/mob/living/simple_animal/hostile/syndicate/melee/space/Destroy() +/mob/living/simple_animal/hostile/syndicate/melee/sword/space/Destroy() QDEL_NULL(sord) return ..() -/mob/living/simple_animal/hostile/syndicate/melee/space/stormtrooper - icon_state = "syndicatemeleestormtrooper" - icon_living = "syndicatemeleestormtrooper" +/mob/living/simple_animal/hostile/syndicate/melee/sword/space/stormtrooper + icon_state = "syndicate_stormtrooper_sword" + icon_living = "syndicate_stormtrooper_sword" name = "Syndicate Stormtrooper" - maxHealth = 340 - health = 340 - loot = list(/obj/effect/gibspawner/human) + maxHealth = 250 + health = 250 ///////////////Guns//////////// /mob/living/simple_animal/hostile/syndicate/ranged ranged = 1 - rapid = 3 retreat_distance = 5 minimum_distance = 5 - icon_state = "syndicateranged" - icon_living = "syndicateranged" - casingtype = /obj/item/ammo_casing/c45/nostamina - projectilesound = 'sound/weapons/gunshot_smg.ogg' + icon_state = "syndicate_pistol" + icon_living = "syndicate_pistol" + casingtype = /obj/item/ammo_casing/c10mm + projectilesound = 'sound/weapons/gunshot.ogg' loot = list(/obj/effect/gibspawner/human) + dodging = FALSE + rapid_melee = 1 -/mob/living/simple_animal/hostile/syndicate/ranged/pilot - name = "Syndicate Salvage Pilot" +/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator //shuttle loan event + projectilesound = 'sound/weapons/gunshot_silenced.ogg' loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) /mob/living/simple_animal/hostile/syndicate/ranged/space - icon_state = "syndicaterangedspace" - icon_living = "syndicaterangedspace" + icon_state = "syndicate_space_pistol" + icon_living = "syndicate_space_pistol" name = "Syndicate Commando" + maxHealth = 170 + health = 170 atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 speed = 1 spacewalk = TRUE - loot = list(/obj/effect/gibspawner/human) /mob/living/simple_animal/hostile/syndicate/ranged/space/Initialize() . = ..() set_light(4) /mob/living/simple_animal/hostile/syndicate/ranged/space/stormtrooper - icon_state = "syndicaterangedstormtrooper" - icon_living = "syndicaterangedstormtrooper" + icon_state = "syndicate_stormtrooper_pistol" + icon_living = "syndicate_stormtrooper_pistol" name = "Syndicate Stormtrooper" - maxHealth = 200 - health = 200 - casingtype = /obj/item/ammo_casing/shotgun/buckshot - projectilesound = 'sound/weapons/gunshot.ogg' - loot = list(/obj/effect/gibspawner/human) + maxHealth = 250 + health = 250 + +/mob/living/simple_animal/hostile/syndicate/ranged/smg + rapid = 2 + icon_state = "syndicate_smg" + icon_living = "syndicate_smg" + casingtype = /obj/item/ammo_casing/c45/nostamina + projectilesound = 'sound/weapons/gunshot_smg.ogg' + +/mob/living/simple_animal/hostile/syndicate/ranged/smg/pilot //caravan ambush ruin + name = "Syndicate Salvage Pilot" + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) + +/mob/living/simple_animal/hostile/syndicate/ranged/smg/space + icon_state = "syndicate_space_smg" + icon_living = "syndicate_space_smg" + name = "Syndicate Commando" + maxHealth = 170 + health = 170 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speed = 1 + spacewalk = TRUE + +/mob/living/simple_animal/hostile/syndicate/ranged/smg/space/Initialize() + . = ..() + set_light(4) + +/mob/living/simple_animal/hostile/syndicate/ranged/smg/space/stormtrooper + icon_state = "syndicate_stormtrooper_smg" + icon_living = "syndicate_stormtrooper_smg" + name = "Syndicate Stormtrooper" + maxHealth = 250 + health = 250 + +/mob/living/simple_animal/hostile/syndicate/ranged/shotgun + rapid = 2 + rapid_fire_delay = 6 + minimum_distance = 3 + icon_state = "syndicate_shotgun" + icon_living = "syndicate_shotgun" + casingtype = /obj/item/ammo_casing/shotgun/buckshot //buckshot (up to 72.5 brute) fired in a two-round burst + +/mob/living/simple_animal/hostile/syndicate/ranged/shotgun/space + icon_state = "syndicate_space_shotgun" + icon_living = "syndicate_space_shotgun" + name = "Syndicate Commando" + maxHealth = 170 + health = 170 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speed = 1 + spacewalk = TRUE + +/mob/living/simple_animal/hostile/syndicate/ranged/shotgun/space/Initialize() + . = ..() + set_light(4) + +/mob/living/simple_animal/hostile/syndicate/ranged/shotgun/space/stormtrooper + icon_state = "syndicate_stormtrooper_shotgun" + icon_living = "syndicate_stormtrooper_shotgun" + name = "Syndicate Stormtrooper" + maxHealth = 250 + health = 250 ///////////////Misc//////////// diff --git a/code/modules/mob/living/simple_animal/hostile/wizard.dm b/code/modules/mob/living/simple_animal/hostile/wizard.dm index 921bd5959b..a946cbf45b 100644 --- a/code/modules/mob/living/simple_animal/hostile/wizard.dm +++ b/code/modules/mob/living/simple_animal/hostile/wizard.dm @@ -37,6 +37,8 @@ var/next_cast = 0 + do_footstep = TRUE + /mob/living/simple_animal/hostile/wizard/Initialize() . = ..() fireball = new /obj/effect/proc_holder/spell/aimed/fireball diff --git a/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm b/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm index d262725d7b..ec7451dc2e 100644 --- a/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm +++ b/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm @@ -127,6 +127,7 @@ var/list/banned_mobs /obj/item/fugu_gland/afterattack(atom/target, mob/user, proximity_flag) + . = ..() if(proximity_flag && isanimal(target)) var/mob/living/simple_animal/A = target if(A.buffed || (A.type in banned_mobs) || A.stat) diff --git a/code/modules/mob/living/simple_animal/hostile/zombie.dm b/code/modules/mob/living/simple_animal/hostile/zombie.dm new file mode 100644 index 0000000000..21c2d4804a --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/zombie.dm @@ -0,0 +1,58 @@ +/mob/living/simple_animal/hostile/zombie + name = "Shambling Corpse" + desc = "When there is no more room in hell, the dead will walk in outer space." + icon = 'icons/mob/simple_human.dmi' + icon_state = "zombie" + icon_living = "zombie" + mob_biotypes = list(MOB_ORGANIC, MOB_HUMANOID) + speak_chance = 0 + stat_attack = UNCONSCIOUS //braains + maxHealth = 100 + health = 100 + harm_intent_damage = 5 + melee_damage_lower = 21 + melee_damage_upper = 21 + attacktext = "bites" + attack_sound = 'sound/hallucinations/growl1.ogg' + a_intent = INTENT_HARM + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + spacewalk = FALSE + status_flags = CANPUSH + del_on_death = 1 + var/zombiejob = "Medical Doctor" + var/infection_chance = 0 + var/obj/effect/mob_spawn/human/corpse/delayed/corpse + +/mob/living/simple_animal/hostile/zombie/Initialize(mapload) + . = ..() + setup_visuals() + +/mob/living/simple_animal/hostile/zombie/proc/setup_visuals() + var/datum/preferences/dummy_prefs = new + dummy_prefs.pref_species = new /datum/species/zombie + dummy_prefs.be_random_body = TRUE + var/datum/job/J = SSjob.GetJob(zombiejob) + var/datum/outfit/O + if(J.outfit) + O = new J.outfit + //They have claws now. + O.r_hand = null + O.l_hand = null + + var/icon/P = get_flat_human_icon("zombie_[zombiejob]", J , dummy_prefs, "zombie", outfit_override = O) + icon = P + corpse = new(src) + corpse.outfit = O + corpse.mob_species = /datum/species/zombie + corpse.mob_name = name + +/mob/living/simple_animal/hostile/zombie/AttackingTarget() + . = ..() + if(. && ishuman(target) && prob(infection_chance)) + try_to_zombie_infect(target) + +/mob/living/simple_animal/hostile/zombie/drop_loot() + . = ..() + corpse.forceMove(drop_location()) + corpse.create() \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 7d9a4eefe6..4fdf431fa0 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -394,6 +394,9 @@ if(!isturf(src.loc) || !canmove || buckled) return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove) + if(client && stat == CONSCIOUS && parrot_state != icon_living) + icon_state = icon_living + //Because the most appropriate place to set icon_state is movement_delay(), clearly //-----SLEEPING if(parrot_state == PARROT_PERCH) @@ -604,12 +607,6 @@ * Procs */ -/mob/living/simple_animal/parrot/movement_delay() - if(client && stat == CONSCIOUS && parrot_state != icon_living) - icon_state = icon_living - //Because the most appropriate place to set icon_state is movement_delay(), clearly - return ..() - /mob/living/simple_animal/parrot/proc/isStuck() //Check to see if the parrot is stuck due to things like windows or doors or windowdoors if(parrot_lastmove) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 7f2864a43d..fcb201d0ad 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -91,6 +91,8 @@ var/my_z // I don't want to confuse this with client registered_z + var/do_footstep = FALSE + /mob/living/simple_animal/Initialize() . = ..() GLOB.simple_animals[AIStatus] += src @@ -101,13 +103,12 @@ real_name = name if(!loc) stack_trace("Simple animal being instantiated in nullspace") - if(vore_active) - init_belly() - if(!IsAdvancedToolUser()) - verbs |= /mob/living/simple_animal/proc/animal_nom + update_simplemob_varspeed() /mob/living/simple_animal/Destroy() GLOB.simple_animals[AIStatus] -= src + if (SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun)) + SSnpcpool.currentrun -= src if(nest) nest.spawned_mobs -= src @@ -119,6 +120,10 @@ return ..() +/mob/living/simple_animal/initialize_footstep() + if(do_footstep) + ..() + /mob/living/simple_animal/updatehealth() ..() health = CLAMP(health, 0, maxHealth) @@ -169,7 +174,7 @@ length += emote_see.len var/randomValue = rand(1,length) if(randomValue <= speak.len) - say(pick(speak)) + say(pick(speak), forced = "poly") else randomValue -= speak.len if(emote_see && randomValue <= emote_see.len) @@ -177,7 +182,7 @@ else emote("me [pick(emote_hear)]", 2) else - say(pick(speak)) + say(pick(speak), forced = "poly") else if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) emote("me", 1, pick(emote_see)) @@ -272,7 +277,7 @@ verb_say = pick(speak_emote) . = ..() -/mob/living/simple_animal/emote(act, m_type=1, message = null) +/mob/living/simple_animal/emote(act, m_type=1, message = null, intentional = FALSE) if(stat) return if(act == "scream") @@ -280,14 +285,14 @@ act = "me" ..(act, m_type, message) +/mob/living/simple_animal/proc/set_varspeed(var_value) + speed = var_value + update_simplemob_varspeed() - -/mob/living/simple_animal/movement_delay() - var/static/config_animal_delay - if(isnull(config_animal_delay)) - config_animal_delay = CONFIG_GET(number/animal_delay) - . += config_animal_delay - return ..() + speed + config_animal_delay +/mob/living/simple_animal/proc/update_simplemob_varspeed() + if(speed == 0) + remove_movespeed_modifier(MOVESPEED_ID_SIMPLEMOB_VARSPEED, TRUE) + add_movespeed_modifier(MOVESPEED_ID_SIMPLEMOB_VARSPEED, TRUE, 100, multiplicative_slowdown = speed, override = TRUE) /mob/living/simple_animal/Stat() ..() @@ -295,14 +300,17 @@ stat(null, "Health: [round((health / maxHealth) * 100)]%") return 1 +/mob/living/simple_animal/proc/drop_loot() + if(loot.len) + for(var/i in loot) + new i(loc) + /mob/living/simple_animal/death(gibbed) movement_type &= ~FLYING if(nest) nest.spawned_mobs -= src nest = null - if(loot.len) - for(var/i in loot) - new i(loc) + drop_loot() if(dextrous) drop_all_held_items() if(!gibbed) diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 34e7a2ddcd..034ccc4c39 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -134,35 +134,39 @@ icon_state = icon_dead ..() -/mob/living/simple_animal/slime/movement_delay() - if(bodytemperature >= 330.23) // 135 F or 57.08 C - return -1 // slimes become supercharged at high temperatures - +/mob/living/simple_animal/slime/on_reagent_change() . = ..() + remove_movespeed_modifier(MOVESPEED_ID_SLIME_REAGENTMOD, TRUE) + var/amount = 0 + if(reagents.has_reagent("morphine")) // morphine slows slimes down + amount = 2 + if(reagents.has_reagent("frostoil")) // Frostoil also makes them move VEEERRYYYYY slow + amount = 5 + if(amount) + add_movespeed_modifier(MOVESPEED_ID_SLIME_REAGENTMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = amount) +/mob/living/simple_animal/slime/updatehealth() + . = ..() + remove_movespeed_modifier(MOVESPEED_ID_SLIME_HEALTHMOD, FALSE) var/health_deficiency = (100 - health) + var/mod = 0 if(health_deficiency >= 45) - . += (health_deficiency / 25) + mod += (health_deficiency / 25) + if(health <= 0) + mod += 2 + add_movespeed_modifier(MOVESPEED_ID_SLIME_HEALTHMOD, TRUE, 100, multiplicative_slowdown = mod) - if(bodytemperature < 183.222) - . += (283.222 - bodytemperature) / 10 * 1.75 +/mob/living/simple_animal/slime/adjust_bodytemperature() + . = ..() + var/mod = 0 + if(bodytemperature >= 330.23) // 135 F or 57.08 C + mod = -1 // slimes become supercharged at high temperatures + else if(bodytemperature < 183.222) + mod = (283.222 - bodytemperature) / 10 * 1.75 + if(mod) + add_movespeed_modifier(MOVESPEED_ID_SLIME_TEMPMOD, TRUE, 100, override = TRUE, multiplicative_slowdown = mod) - if(reagents) - if(reagents.has_reagent("morphine")) // morphine slows slimes down - . *= 2 - - if(reagents.has_reagent("frostoil")) // Frostoil also makes them move VEEERRYYYYY slow - . *= 5 - - if(health <= 0) // if damaged, the slime moves twice as slow - . *= 2 - - var/static/config_slime_delay - if(isnull(config_slime_delay)) - config_slime_delay = CONFIG_GET(number/slime_delay) - . += config_slime_delay - -/mob/living/simple_animal/slime/ObjCollide(obj/O) +/mob/living/simple_animal/slime/ObjBump(obj/O) if(!client && powerlevel > 0) var/probab = 10 switch(powerlevel) @@ -306,9 +310,9 @@ discipline_slime(M) else if(stat == DEAD && surgeries.len) - if(M.a_intent == INTENT_HELP) + if(M.a_intent == INTENT_HELP || M.a_intent == INTENT_DISARM) for(var/datum/surgery/S in surgeries) - if(S.next_step(M)) + if(S.next_step(M,M.a_intent)) return 1 if(..()) //successful attack attacked += 10 @@ -321,9 +325,9 @@ /mob/living/simple_animal/slime/attackby(obj/item/W, mob/living/user, params) if(stat == DEAD && surgeries.len) - if(user.a_intent == INTENT_HELP) + if(user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM) for(var/datum/surgery/S in surgeries) - if(S.next_step(user)) + if(S.next_step(user,user.a_intent)) return 1 if(istype(W, /obj/item/stack/sheet/mineral/plasma) && !stat) //Let's you feed slimes plasma. if (user in Friends) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 47de9a0896..be98d1bfab 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -143,9 +143,14 @@ /mob/living/proc/add_trait(trait, source) if(!status_traits[trait]) status_traits[trait] = list(source) + on_add_trait(trait, source) else status_traits[trait] |= list(source) +/mob/living/proc/on_add_trait(trait, source) + if(trait == TRAIT_IGNORESLOWDOWN) + update_movespeed(FALSE) + /mob/living/proc/add_quirk(quirk, spawn_effects) //separate proc due to the way these ones are handled if(has_trait(quirk)) return @@ -156,7 +161,6 @@ return TRUE /mob/living/proc/remove_trait(trait, list/sources, force) - if(!status_traits[trait]) return @@ -165,6 +169,7 @@ if(!sources) // No defined source cures the trait entirely. status_traits -= trait + on_remove_trait(trait, sources, force) return if(!islist(sources)) @@ -179,6 +184,11 @@ if(!LAZYLEN(status_traits[trait])) status_traits -= trait + on_remove_trait(trait, sources, force) + +/mob/living/proc/on_remove_trait(trait, list/sources, force) + if(trait == TRAIT_IGNORESLOWDOWN) + update_movespeed(FALSE) /mob/living/proc/remove_quirk(quirk) var/datum/quirk/T = roundstart_quirks[quirk] @@ -260,13 +270,17 @@ /mob/living/proc/cure_fakedeath(list/sources) remove_trait(TRAIT_FAKEDEATH, sources) + remove_trait(TRAIT_DEATHCOMA, sources) if(stat != DEAD) tod = null update_stat() -/mob/living/proc/fakedeath(source) +/mob/living/proc/fakedeath(source, silent = FALSE) if(stat == DEAD) return + if(!silent) + emote("deathgasp") add_trait(TRAIT_FAKEDEATH, source) + add_trait(TRAIT_DEATHCOMA, source) tod = station_time_timestamp() update_stat() \ No newline at end of file diff --git a/code/modules/mob/living/ventcrawling.dm b/code/modules/mob/living/ventcrawling.dm index 600fa581cb..d795280686 100644 --- a/code/modules/mob/living/ventcrawling.dm +++ b/code/modules/mob/living/ventcrawling.dm @@ -120,3 +120,4 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, typecacheof(list( . = new_loc remove_ventcrawl() add_ventcrawl(.) + diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 9def2a759f..611139bbf9 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -49,4 +49,4 @@ for(var/datum/action/A in client.player_details.player_actions) A.Grant(src) - log_message("Client [key_name(src)] has taken ownership of mob [src]", INDIVIDUAL_OWNERSHIP_LOG) + log_message("Client [key_name(src)] has taken ownership of mob [src]([src.type])", LOG_OWNERSHIP) diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index fbd29e86a5..031cb1b58d 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -1,5 +1,5 @@ /mob/Logout() - log_message("[key_name(src)] is no longer owning mob [src]", INDIVIDUAL_OWNERSHIP_LOG) + log_message("[key_name(src)] is no longer owning mob [src]([src.type])", LOG_OWNERSHIP) SStgui.on_logout(src) unset_machine() GLOB.player_list -= src diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 6deffc97da..eb4cfb47b2 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -4,6 +4,7 @@ GLOB.alive_mob_list -= src GLOB.all_clockwork_mobs -= src GLOB.mob_directory -= tag + focus = null for (var/alert in alerts) clear_alert(alert, TRUE) if(observers && observers.len) @@ -32,9 +33,11 @@ continue var/datum/atom_hud/alternate_appearance/AA = v AA.onNewMob(src) - hook_vr("mob_new",list(src)) nutrition = rand(NUTRITION_LEVEL_START_MIN, NUTRITION_LEVEL_START_MAX) . = ..() + update_config_movespeed() + update_movespeed(TRUE) + hook_vr("mob_new",list(src)) /mob/GenerateTag() tag = "mob_[next_mob_id++]" @@ -69,6 +72,9 @@ to_chat(usr, t) +/mob/proc/get_photo_description(obj/item/camera/camera) + return "a ... thing?" + /mob/proc/show_message(msg, type, alt_msg, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2) if(!client) @@ -170,9 +176,6 @@ for(var/mob/M in get_hearers_in_view(range, src)) M.show_message( message, 2, deaf_message, 1) -/mob/proc/movement_delay() //update /living/movement_delay() if you change this - return 0 - /mob/proc/Life() set waitfor = FALSE @@ -314,17 +317,17 @@ set category = "Object" if(!src || !isturf(src.loc) || !(A in view(src.loc))) - return 0 + return FALSE if(istype(A, /obj/effect/temp_visual/point)) - return 0 + return FALSE var/tile = get_turf(A) if (!tile) - return 0 + return FALSE new /obj/effect/temp_visual/point(A,invisibility) - return 1 + return TRUE /mob/proc/can_resist() return FALSE //overridden in living.dm @@ -353,6 +356,11 @@ if(hud_used.pull_icon) hud_used.pull_icon.update_icon(src) +/mob/proc/update_rest_hud_icon() + if(hud_used) + if(hud_used.rest_icon) + hud_used.rest_icon.update_icon(src) + /mob/verb/mode() set name = "Activate Held Object" set category = "Object" @@ -368,12 +376,10 @@ if(I) I.attack_self(src) update_inv_hands() - if(!I)//CIT CHANGE - allows "using" empty hands use_that_empty_hand() //CIT CHANGE - ditto update_inv_hands() // CIT CHANGE - ditto. - /mob/verb/memory() set name = "Notes" set category = "IC" @@ -603,59 +609,61 @@ if(S.chemical_cost >=0 && S.can_be_used_by(src)) statpanel("[S.panel]",((S.chemical_cost > 0) ? "[S.chemical_cost]" : ""),S) +#define MOB_FACE_DIRECTION_DELAY 1 + // facing verbs /mob/proc/canface() if(!canmove) - return 0 - if(world.time < client.move_delay) - return 0 - if(stat==2) - return 0 + return FALSE + if(world.time < client.last_turn) + return FALSE + if(stat == DEAD || stat == UNCONSCIOUS) + return FALSE if(anchored) - return 0 + return FALSE if(notransform) - return 0 + return FALSE if(restrained()) - return 0 - return 1 + return FALSE + return TRUE /mob/proc/fall(forced) drop_all_held_items() /mob/verb/eastface() - set hidden = 1 + set hidden = TRUE if(!canface()) - return 0 + return FALSE setDir(EAST) - client.move_delay += movement_delay() - return 1 + client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY + return TRUE /mob/verb/westface() - set hidden = 1 + set hidden = TRUE if(!canface()) - return 0 + return FALSE setDir(WEST) - client.move_delay += movement_delay() - return 1 + client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY + return TRUE /mob/verb/northface() - set hidden = 1 + set hidden = TRUE if(!canface()) - return 0 + return FALSE setDir(NORTH) - client.move_delay += movement_delay() - return 1 + client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY + return TRUE /mob/verb/southface() - set hidden = 1 + set hidden = TRUE if(!canface()) - return 0 + return FALSE setDir(SOUTH) - client.move_delay += movement_delay() - return 1 + client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY + return TRUE /mob/proc/IsAdvancedToolUser()//This might need a rename but it should replace the can this mob use things check - return 0 + return FALSE /mob/proc/swap_hand() return @@ -799,7 +807,7 @@ //This will update a mob's name, real_name, mind.name, GLOB.data_core records, pda, id and traitor text //Calling this proc without an oldname will only update the mob and skip updating the pda, id and records ~Carn /mob/proc/fully_replace_character_name(oldname,newname) - log_message("[src] name changed from [oldname] to [newname]", INDIVIDUAL_OWNERSHIP_LOG) + log_message("[src] name changed from [oldname] to [newname]", LOG_OWNERSHIP) if(!newname) return 0 real_name = newname diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 3dff46ec84..298fee46cd 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -14,8 +14,7 @@ var/list/datum/action/chameleon_item_actions var/static/next_mob_id = 0 - var/stat = 0 //Whether a mob is alive or dead. TODO: Move this to living - Nodrak - + var/stat = CONSCIOUS //Whether a mob is alive or dead. TODO: Move this to living - Nodrak /*A bunch of this stuff really needs to go under their own defines instead of being globally attached to mob. A variable should only be globally attached to turfs/objects/whatever, when it is in fact needed as such. @@ -26,7 +25,7 @@ var/zone_selected = null var/computer_id = null - var/list/logging = list(INDIVIDUAL_ATTACK_LOG, INDIVIDUAL_SAY_LOG, INDIVIDUAL_EMOTE_LOG, INDIVIDUAL_OOC_LOG) + var/list/logging = list() var/obj/machinery/machine = null var/next_move = null @@ -40,6 +39,11 @@ var/lying_prev = 0 var/canmove = 1 + //MOVEMENT SPEED + var/list/movespeed_modification //Lazy list, see mob_movespeed.dm + var/cached_multiplicative_slowdown + ///////////////// + var/name_archive //For admin things like possession var/bodytemperature = BODYTEMP_NORMAL //310.15K / 98.6F @@ -104,3 +108,5 @@ var/list/mousemove_intercept_objects var/datum/click_intercept + + var/registered_z diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 0e37666f02..fa908db729 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -468,22 +468,37 @@ It's fairly easy to fix if dealing with single letters but not so much with comp var/mob/living/T = pick(nearby_mobs) ClickOn(T) -/mob/proc/log_message(message, message_type) - if(!LAZYLEN(message) || !message_type) +// Logs a message in a mob's individual log, and in the global logs as well if log_globally is true +/mob/log_message(message, message_type, color=null, log_globally = TRUE) + if(!LAZYLEN(message)) + stack_trace("Empty message") return + // Cannot use the list as a map if the key is a number, so we stringify it (thank you BYOND) + var/smessage_type = num2text(message_type) + if(client) - if(!islist(client.player_details.logging[message_type])) - client.player_details.logging[message_type] = list() + if(!islist(client.player_details.logging[smessage_type])) + client.player_details.logging[smessage_type] = list() - if(!islist(logging[message_type])) - logging[message_type] = list() + if(!islist(logging[smessage_type])) + logging[smessage_type] = list() - var/list/timestamped_message = list("[LAZYLEN(logging[message_type]) + 1]\[[time_stamp()]\] [key_name(src)]" = message) + var/colored_message = message + if(color) + if(color[1] == "#") + colored_message = "[message]" + else + colored_message = "[message]" + + var/list/timestamped_message = list("[LAZYLEN(logging[smessage_type]) + 1]\[[time_stamp()]\] [key_name(src)] [loc_name(src)]" = colored_message) + + logging[smessage_type] += timestamped_message - logging[message_type] += timestamped_message if(client) - client.player_details.logging[message_type] += timestamped_message + client.player_details.logging[smessage_type] += timestamped_message + + ..() /mob/proc/can_hear() . = TRUE diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 291ae1e425..cb532e4d5e 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -10,6 +10,10 @@ return TRUE return (!mover.density || !density || lying) +//DO NOT USE THIS UNLESS YOU ABSOLUTELY HAVE TO. THIS IS BEING PHASED OUT FOR THE MOVESPEED MODIFICATION SYSTEM. +//See mob_movespeed.dm +/mob/proc/movement_delay() //update /living/movement_delay() if you change this + return cached_multiplicative_slowdown /client/verb/drop_item() set hidden = 1 @@ -26,7 +30,6 @@ mob.control_object.setDir(direct) else mob.control_object.forceMove(get_step(mob.control_object,direct)) - return #define MOVEMENT_DELAY_BUFFER 0.75 #define MOVEMENT_DELAY_BUFFER_DELTA 1.25 @@ -38,7 +41,7 @@ next_move_dir_add = 0 next_move_dir_sub = 0 var/old_move_delay = move_delay - move_delay = world.time+world.tick_lag //this is here because Move() can now be called mutiple times per tick + move_delay = world.time + world.tick_lag //this is here because Move() can now be called mutiple times per tick if(!mob || !mob.loc) return FALSE if(!n || !direct) @@ -116,7 +119,7 @@ var/atom/movable/P = mob.pulling if(P && !ismob(P) && P.density) - mob.dir = turn(mob.dir, 180) + mob.setDir(turn(mob.dir, 180)) ///Process_Grab() ///Called by client/Move() @@ -357,9 +360,13 @@ set hidden = TRUE set instant = TRUE if(mob) - mob.toggle_move_intent() + mob.toggle_move_intent(usr) -/mob/proc/toggle_move_intent() +/mob/proc/toggle_move_intent(mob/user) + if(m_intent == MOVE_INTENT_RUN) + m_intent = MOVE_INTENT_WALK + else + m_intent = MOVE_INTENT_RUN if(hud_used && hud_used.static_inventory) for(var/obj/screen/mov_intent/selector in hud_used.static_inventory) - selector.toggle(src) + selector.update_icon(src) diff --git a/code/modules/mob/mob_movespeed.dm b/code/modules/mob/mob_movespeed.dm new file mode 100644 index 0000000000..5114972493 --- /dev/null +++ b/code/modules/mob/mob_movespeed.dm @@ -0,0 +1,106 @@ + +/*Current movespeed modification list format: list(id = list( + priority, + legacy slowdown/speedup amount, + )) +*/ + +//ANY ADD/REMOVE DONE IN UPDATE_MOVESPEED MUST HAVE THE UPDATE ARGUMENT SET AS FALSE! +/mob/proc/add_movespeed_modifier(id, update = TRUE, priority = 0, flags = NONE, override = FALSE, multiplicative_slowdown = 0) + var/list/temp = list(priority, flags, multiplicative_slowdown) //build the modification list + if(LAZYACCESS(movespeed_modification, id)) + if(movespeed_modifier_identical_check(movespeed_modification[id], temp)) + return FALSE + if(!override) + return FALSE + else + remove_movespeed_modifier(id, update) + LAZYSET(movespeed_modification, id, list(priority, flags, multiplicative_slowdown)) + if(update) + update_movespeed(TRUE) + return TRUE + +/mob/proc/remove_movespeed_modifier(id, update = TRUE) + if(!LAZYACCESS(movespeed_modification, id)) + return FALSE + LAZYREMOVE(movespeed_modification, id) + UNSETEMPTY(movespeed_modification) + if(update) + update_movespeed(FALSE) + return TRUE + +/mob/vv_edit_var(var_name, var_value) + var/slowdown_edit = (var_name == NAMEOF(src, cached_multiplicative_slowdown)) + var/diff + if(slowdown_edit && isnum(cached_multiplicative_slowdown) && isnum(var_value)) + remove_movespeed_modifier(MOVESPEED_ID_ADMIN_VAREDIT) + diff = var_value - cached_multiplicative_slowdown + . = ..() + if(. && slowdown_edit && isnum(diff)) + add_movespeed_modifier(MOVESPEED_ID_ADMIN_VAREDIT, TRUE, 100, override = TRUE, multiplicative_slowdown = diff) + +/mob/proc/has_movespeed_modifier(id) + return LAZYACCESS(movespeed_modification, id) + +/mob/proc/update_config_movespeed() + add_movespeed_modifier(MOVESPEED_ID_CONFIG_SPEEDMOD, FALSE, 100, override = TRUE, multiplicative_slowdown = get_config_multiplicative_speed()) + +/mob/proc/get_config_multiplicative_speed() + if(!islist(GLOB.mob_config_movespeed_type_lookup) || !GLOB.mob_config_movespeed_type_lookup[type]) + return 0 + else + return GLOB.mob_config_movespeed_type_lookup[type] + +/mob/proc/update_movespeed(resort = TRUE) + if(resort) + sort_movespeed_modlist() + . = 0 + for(var/id in get_movespeed_modifiers()) + var/list/data = movespeed_modification[id] + . += data[MOVESPEED_DATA_INDEX_MULTIPLICATIVE_SLOWDOWN] + cached_multiplicative_slowdown = . + +/mob/proc/get_movespeed_modifiers() + return movespeed_modification + +/mob/proc/movespeed_modifier_identical_check(list/mod1, list/mod2) + if(!islist(mod1) || !islist(mod2) || mod1.len < MOVESPEED_DATA_INDEX_MAX || mod2.len < MOVESPEED_DATA_INDEX_MAX) + return FALSE + for(var/i in 1 to MOVESPEED_DATA_INDEX_MAX) + if(mod1[i] != mod2[i]) + return FALSE + return TRUE + +/mob/proc/total_multiplicative_slowdown() + . = 0 + for(var/id in get_movespeed_modifiers()) + var/list/data = movespeed_modification[id] + . += data[MOVESPEED_DATA_INDEX_MULTIPLICATIVE_SLOWDOWN] + +/proc/movespeed_data_null_check(list/data) //Determines if a data list is not meaningful and should be discarded. + . = TRUE + if(data[MOVESPEED_DATA_INDEX_MULTIPLICATIVE_SLOWDOWN]) + . = FALSE + +/mob/proc/sort_movespeed_modlist() //Verifies it too. Sorts highest priority (first applied) to lowest priority (last applied) + if(!movespeed_modification) + return + var/list/assembled = list() + for(var/our_id in movespeed_modification) + var/list/our_data = movespeed_modification[our_id] + if(!islist(our_data) || (our_data.len < MOVESPEED_DATA_INDEX_PRIORITY) || movespeed_data_null_check(our_data)) + movespeed_modification -= our_id + continue + var/our_priority = our_data[MOVESPEED_DATA_INDEX_PRIORITY] + var/resolved = FALSE + for(var/their_id in assembled) + var/list/their_data = assembled[their_id] + if(their_data[MOVESPEED_DATA_INDEX_PRIORITY] < our_priority) + assembled.Insert(assembled.Find(their_id), our_id) + assembled[our_id] = our_data + resolved = TRUE + break + if(!resolved) + assembled[our_id] = our_data + movespeed_modification = assembled + UNSETEMPTY(movespeed_modification) \ No newline at end of file diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index c6f24aa8f8..2ab3b5f57e 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -20,7 +20,7 @@ /mob/proc/whisper(message, datum/language/language=null) say(message, language) //only living mobs actually whisper, everything else just talks -/mob/verb/me_verb(message as message) // CIT CHANGE - makes me command input box bigger +/mob/verb/me_verb(message as message) set name = "Me" set category = "IC" @@ -30,7 +30,7 @@ message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - usr.emote("me",1,message) + usr.emote("me",1,message,TRUE) /mob/proc/say_dead(var/message) var/name = real_name @@ -74,15 +74,14 @@ if(key) K = src.key - message = src.say_quote(message, get_spans()) - message = emoji_parse(message) - var/rendered = "DEAD: [name][alt_name] [message]" - log_message("DEAD: [message]", INDIVIDUAL_SAY_LOG) + var/spanned = src.say_quote(message, get_spans()) + var/rendered = "DEAD: [name][alt_name] [emoji_parse(spanned)]" + log_talk(message, LOG_SAY, tag="DEAD") deadchat_broadcast(rendered, follow_target = src, speaker_key = K) /mob/proc/check_emote(message) if(copytext(message, 1, 2) == "*") - emote(copytext(message, 2)) + emote(copytext(message, 2), intentional = TRUE) return 1 /mob/proc/hivecheck() diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm index a726c7f6d1..8b70fd246c 100644 --- a/code/modules/modular_computers/computers/machinery/console_presets.dm +++ b/code/modules/modular_computers/computers/machinery/console_presets.dm @@ -1,9 +1,9 @@ /obj/machinery/modular_computer/console/preset // Can be changed to give devices specific hardware - var/_has_id_slot = 0 - var/_has_printer = 0 - var/_has_battery = 0 - var/_has_ai = 0 + var/_has_id_slot = FALSE + var/_has_printer = FALSE + var/_has_battery = FALSE + var/_has_ai = FALSE /obj/machinery/modular_computer/console/preset/Initialize() . = ..() @@ -29,9 +29,9 @@ // ===== ENGINEERING CONSOLE ===== /obj/machinery/modular_computer/console/preset/engineering - console_department = "Engineering" - name = "engineering console" - desc = "A stationary computer. This one comes preloaded with engineering programs." + console_department = "Engineering" + name = "engineering console" + desc = "A stationary computer. This one comes preloaded with engineering programs." /obj/machinery/modular_computer/console/preset/engineering/install_programs() var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] @@ -41,10 +41,10 @@ // ===== RESEARCH CONSOLE ===== /obj/machinery/modular_computer/console/preset/research - console_department = "Research" - name = "research director's console" - desc = "A stationary computer. This one comes preloaded with research programs." - _has_ai = 1 + console_department = "Research" + name = "research director's console" + desc = "A stationary computer. This one comes preloaded with research programs." + _has_ai = TRUE /obj/machinery/modular_computer/console/preset/research/examine(mob/user) ..() @@ -60,11 +60,11 @@ // ===== COMMAND CONSOLE ===== /obj/machinery/modular_computer/console/preset/command - console_department = "Command" - name = "command console" - desc = "A stationary computer. This one comes preloaded with command programs." - _has_id_slot = 1 - _has_printer = 1 + console_department = "Command" + name = "command console" + desc = "A stationary computer. This one comes preloaded with command programs." + _has_id_slot = TRUE + _has_printer = TRUE /obj/machinery/modular_computer/console/preset/command/examine(mob/user) ..() @@ -77,9 +77,9 @@ // ===== CIVILIAN CONSOLE ===== /obj/machinery/modular_computer/console/preset/civilian - console_department = "Civilian" - name = "civilian console" - desc = "A stationary computer. This one comes preloaded with generic programs." + console_department = "Civilian" + name = "civilian console" + desc = "A stationary computer. This one comes preloaded with generic programs." /obj/machinery/modular_computer/console/preset/civilian/install_programs() var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] diff --git a/code/modules/modular_computers/documentation.md b/code/modules/modular_computers/documentation.md index 88d8602729..246da7c3d9 100644 --- a/code/modules/modular_computers/documentation.md +++ b/code/modules/modular_computers/documentation.md @@ -1,32 +1,62 @@ +# Modular computer programs - -#Modular computer programs Ok. so a quick rundown on how to make a program. This is kind of a shitty documentation, but oh well I was asked to. - ## Base setup -This is how the base program is setup. the rest is mostly tgui stuff. I'll use the ntnetmonitor as a base +This is how the base program is setup. the rest is mostly tgui stuff. I'll use the ntnetmonitor as a base ```DM /datum/computer_file/program/ntnetmonitor - filename = "ntmonitor" //This is obviously the name of the file itself. not much to be said - filedesc = "NTNet Diagnostics and Monitoring" // This is sort of the official name. it's what shows up on the main menu - program_icon_state = "comm_monitor" // This is what the screen will look like when the program is active - extended_desc = "This program is a dummy. // This is a sort of a description, visible when looking on the ntnet - size = 12 // size of the program. Big programs need more hard drive space. Don't make it too big though. - requires_ntnet = 1 // If this is set, the program will not run without an ntnet connection, and will close if the connection is lost. Mainly for primarily online programs. - required_access = access_network //This is access required to run the program itself. ONLY SET THIS FOR SUPER SECURE SHIT. This also acts as transfer_access as well. - transfer_access = access_change_ids // This is the access needed to download from ntnet or host on the ptp program. This is what you want to use most of the time. - available_on_ntnet = 1 //If it's available to download on ntnet. pretty self explanatory. - available_on_syndinet = 0 // ditto but on emagged syndie net. Use this for antag programs - usage_flags = PROGRAM_ALL // Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL - //^^- The comment above sorta explains it. Use this to limit what kind of machines can run the program. For example, comms program should be limited to consoles and laptops. - var/ui_header = "downloader_finished.gif" //This one is kinda cool. If you have the program minimized, this will show up in the header of the computer screen. - //you can even have the program change what the header is based on the situation! see alarm.dm for an example. + /// This is obviously the name of the file itself. not much to be said + filename = "ntmonitor" + + /// This is sort of the official name. it's what shows up on the main menu + filedesc = "NTNet Diagnostics and Monitoring" + + /// This is what the screen will look like when the program is active + program_icon_state = "comm_monitor" + + /// This is a sort of a description, visible when looking on the ntnet + extended_desc = "This program is a dummy." + + /// size of the program. Big programs need more hard drive space. Don't + /// make it too big though. + size = 12 + + /// If this is set, the program will not run without an ntnet connection, + /// and will close if the connection is lost. Mainly for primarily online + /// programs. + requires_ntnet = 1 + + /// This is access required to run the program itself. ONLY SET THIS FOR + /// SUPER SECURE SHIT. This also acts as transfer_access as well. + required_access = access_network + + /// This is the access needed to download from ntnet or host on the ptp + /// program. This is what you want to use most of the time. + transfer_access = access_change_ids + + /// If it's available to download on ntnet. pretty self explanatory. + available_on_ntnet = 1 + + /// ditto but on emagged syndie net. Use this for antag programs + available_on_syndinet = 0 + + /// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) + /// or PROGRAM_ALL. Use this to limit what kind of machines can run the + /// program. For example, comms program should be limited to consoles and laptops. + usage_flags = PROGRAM_ALL + + /// This one is kinda cool. If you have the program minimized, this will + /// show up in the header of the computer screen. You can even have the + /// program change what the header is based on the situation! See `alarm.dm` + /// for an example. + var/ui_header = "downloader_finished.gif" ``` -##Preinstalls +## Preinstalls + Now. for pre-installing stuff. Primarily done for consoles, there's an install_programs() proc in the console presets file in the machines folder. @@ -42,4 +72,3 @@ Basically, you want to do cpu.hard_drive.store_file(new/*program path here*()) Probably pretty self explanatory, but just in case. Will probably be expanded when new features come around or I get asked to mention something. - diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm index 813a3f47e7..29a1df9ebe 100644 --- a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm @@ -34,7 +34,7 @@ if(!message || !channel) return channel.add_message(message, username) - log_talk(user,"[key_name(user)] as [username] sent to [channel.title]: [message]",LOGCHAT) + user.log_talk(message, LOG_CHAT, tag="as [username] to channel [channel.title]") if("PRG_joinchannel") . = 1 diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm index f562c51700..97c41faf12 100644 --- a/code/modules/ninja/energy_katana.dm +++ b/code/modules/ninja/energy_katana.dm @@ -32,12 +32,8 @@ dash_toggled = !dash_toggled to_chat(user, "You [dash_toggled ? "enable" : "disable"] the dash function on [src].") -/obj/item/energy_katana/suicide_act(mob/living/carbon/user) - user.visible_message("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit cyber-seppuku!") - playsound(src, 'sound/weapons/blade1.ogg', 50, 1) - return(BRUTELOSS) - /obj/item/energy_katana/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() if(dash_toggled) jaunt.Teleport(user, target) if(proximity_flag && (isobj(target) || issilicon(target))) diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm index 1385f4e54c..ff1f620a3a 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm @@ -9,7 +9,7 @@ H.SetKnockdown(0) H.adjustStaminaLoss(-75) H.stuttering = 0 - H.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!")) + H.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!"), forced = "ninjaboost") a_boost-- to_chat(H, "There are [a_boost] adrenaline boosts remaining.") s_coold = 3 diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm index 81d731b110..8c8f92e522 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm @@ -19,7 +19,7 @@ return if(!ninjacost(200,N_STEALTH_CANCEL)) H.Beam(C,"n_beam",time=15) - H.say("Get over here!") + H.say("Get over here!", forced = "ninja net") var/obj/structure/energy_net/E = new /obj/structure/energy_net(C.drop_location()) E.affecting = C E.master = H diff --git a/code/modules/orbit/orbit.dm b/code/modules/orbit/orbit.dm index e62b53604a..79685b9104 100644 --- a/code/modules/orbit/orbit.dm +++ b/code/modules/orbit/orbit.dm @@ -55,6 +55,11 @@ if (!targetloc || (!lock && orbiter.loc != lastloc && orbiter.loc != targetloc)) orbiter.stop_orbit() return + var/turf/old_turf = get_turf(orbiter) + var/turf/new_turf = get_turf(targetloc) + if (old_turf?.z != new_turf?.z) + orbiter.onTransitZ(old_turf?.z, new_turf?.z) + // DO NOT PORT TO FORCEMOVE - MEMECODE WILL KILL MC orbiter.loc = targetloc orbiter.update_parallax_contents() orbiter.update_light() diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm index ac3e24030f..006151c4eb 100644 --- a/code/modules/paperwork/contract.dm +++ b/code/modules/paperwork/contract.dm @@ -100,7 +100,7 @@ /obj/item/paper/contract/infernal/suicide_act(mob/user) if(signed && (user == target.current) && istype(user, /mob/living/carbon/human/)) var/mob/living/carbon/human/H = user - H.forcesay("OH GREAT INFERNO! I DEMAND YOU COLLECT YOUR BOUNTY IMMEDIATELY!") + H.forcesay("OH GREAT INFERNO! I DEMAND YOU COLLECT YOUR BOUNTY IMMEDIATELY!", forced = "infernal contract suicide") H.visible_message("[H] holds up a contract claiming [user.p_their()] soul, then immediately catches fire. It looks like [user.p_theyre()] trying to commit suicide!") H.adjust_fire_stacks(20) H.IgniteMob() @@ -235,7 +235,7 @@ response = tgalert(target.current, "A devil is offering you another chance at life, at the price of your soul, do you accept?", "Infernal Resurrection", "Yes", "No", "Never for this round", 0, 200) if(response == "Yes") H.revive(1,0) - add_logs(user, H, "infernally revived via contract") + log_combat(user, H, "infernally revived via contract") user.visible_message("With a sudden blaze, [H] stands back up.") H.fakefire() fulfillContract(H, 1)//Revival contracts are always signed in blood diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index d63474e357..7c0edf3f0e 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -36,6 +36,7 @@ return OXYLOSS /obj/item/hand_labeler/afterattack(atom/A, mob/user,proximity) + . = ..() if(!proximity) return if(!mode) //if it's off, give up. @@ -89,7 +90,7 @@ name = "cyborg-hand labeler" /obj/item/hand_labeler/borg/afterattack(atom/A, mob/user, proximity) - ..(A, user, proximity) + . = ..(A, user, proximity) if(!iscyborg(user)) return diff --git a/code/modules/paperwork/paperplane.dm b/code/modules/paperwork/paperplane.dm index 7cc2580ee3..73eadfbc45 100644 --- a/code/modules/paperwork/paperplane.dm +++ b/code/modules/paperwork/paperplane.dm @@ -98,6 +98,10 @@ H.Knockdown(40) H.emote("scream") +/obj/item/paper/examine(mob/user) + ..() + to_chat(user, "Alt-click [src] to fold it into a paper plane.") + /obj/item/paper/AltClick(mob/living/carbon/user, obj/item/I) if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user))) return diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 9b7d3ae884..c99b094ab0 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -117,12 +117,13 @@ to_chat(M, "You feel a tiny prick!") . = 1 - add_logs(user, M, "stabbed", src) + log_combat(user, M, "stabbed", src) else . = ..() /obj/item/pen/afterattack(obj/O, mob/living/user, proximity) + . = ..() //Changing Name/Description of items. Only works if they have the 'unique_rename' flag set if(isobj(O) && proximity && (O.obj_flags & UNIQUE_RENAME)) var/penchoice = input(user, "What would you like to edit?", "Rename or change description?") as null|anything in list("Rename","Change description") diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index b8fac81df8..4e74ccc44b 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -175,7 +175,7 @@ to_chat(usr, "No images saved") return var/datum/picture/selection = tempAI.aicamera.selectpicture(usr) - var/obj/item/photo = new(loc, selection) + var/obj/item/photo/photo = new(loc, selection) photo.pixel_x = rand(-10, 10) photo.pixel_y = rand(-10, 10) toner -= 5 //AI prints color pictures only, thus they can do it more efficiently diff --git a/code/modules/photography/_pictures.dm b/code/modules/photography/_pictures.dm index 24892cf541..9f5babbbbc 100644 --- a/code/modules/photography/_pictures.dm +++ b/code/modules/photography/_pictures.dm @@ -4,21 +4,29 @@ var/caption var/icon/picture_image var/icon/picture_icon - var/psize_x = 0 - var/psize_y = 0 + var/psize_x = 96 + var/psize_y = 96 var/has_blueprints = FALSE var/logpath //If the picture has been logged this is the path. var/id //this var is NOT protected because the worst you can do with this that you couldn't do otherwise is overwrite photos, and photos aren't going to be used as attack logs/investigations anytime soon. -/datum/picture/New(name = "picture", desc = "This is a picture. A bugged one. Report it to coderbus!", image, icon, size_x = 96, size_y = 96, bp = FALSE, caption_, autogenerate_icon = FALSE) - picture_name = name - picture_desc = desc - picture_image = image - picture_icon = icon - psize_x = size_x - psize_y = size_y - has_blueprints = bp - caption = caption_ +/datum/picture/New(name, desc, image, icon, size_x, size_y, bp, caption_, autogenerate_icon) + if(!isnull(name)) + picture_name = name + if(!isnull(desc)) + picture_desc = desc + if(!isnull(image)) + picture_image = image + if(!isnull(icon)) + picture_icon = icon + if(!isnull(psize_x)) + psize_x = size_x + if(!isnull(psize_y)) + psize_y = size_y + if(!isnull(bp)) + has_blueprints = bp + if(!isnull(caption_)) + caption = caption_ if(autogenerate_icon && !picture_icon && picture_image) regenerate_small_icon() diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm index a3027c951f..b5e027efe2 100644 --- a/code/modules/photography/camera/camera.dm +++ b/code/modules/photography/camera/camera.dm @@ -6,12 +6,12 @@ icon = 'icons/obj/items_and_weapons.dmi' desc = "A polaroid camera." icon_state = "camera" - item_state = "electropack" + item_state = "camera" lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' w_class = WEIGHT_CLASS_SMALL flags_1 = CONDUCT_1 - slot_flags = ITEM_SLOT_BELT + slot_flags = ITEM_SLOT_NECK materials = list(MAT_METAL = 50, MAT_GLASS = 150) var/state_on = "camera" var/state_off = "camera_off" @@ -24,21 +24,12 @@ var/obj/item/disk/holodisk/disk var/sound/custom_sound var/silent = FALSE - var/picture_size_x = 1 - var/picture_size_y = 1 - var/picture_size_x_min = 0 - var/picture_size_y_min = 0 - var/picture_size_x_max = 3 - var/picture_size_y_max = 3 - -/obj/item/camera/CheckParts(list/parts_list) - ..() - var/obj/item/camera/C = locate(/obj/item/camera) in contents - if(C) - pictures_max = C.pictures_max - pictures_left = C.pictures_left - visible_message("[C] has been imbued with godlike power!") - qdel(C) + var/picture_size_x = 2 + var/picture_size_y = 2 + var/picture_size_x_min = 1 + var/picture_size_y_min = 1 + var/picture_size_x_max = 4 + var/picture_size_y_max = 4 /obj/item/camera/attack_self(mob/user) if(!disk) @@ -47,6 +38,10 @@ user.put_in_hands(disk) disk = null +/obj/item/camera/examine(mob/user) + . = ..() + to_chat(user, "Alt-click to change its focusing, allowing you to set how big of an area it will capture.") + /obj/item/camera/AltClick(mob/user) var/desired_x = input(user, "How high do you want the camera to shoot, between [picture_size_x_min] and [picture_size_x_max]?", "Zoom", picture_size_x) as num var/desired_y = input(user, "How wide do you want the camera to shoot, between [picture_size_y_min] and [picture_size_y_max]?", "Zoom", picture_size_y) as num @@ -86,9 +81,23 @@ ..() to_chat(user, "It has [pictures_left] photos left.") +//user can be atom or mob /obj/item/camera/proc/can_target(atom/target, mob/user, prox_flag) - if(!on || blending || !pictures_left || (!isturf(target) && !isturf(target.loc)) || !((isAI(user) && GLOB.cameranet.checkTurfVis(get_turf(target))) || ((user.client && (get_turf(target) in get_hear(user.client.view, user)) || (get_turf(target) in get_hear(world.view, user)))))) + if(!on || blending || !pictures_left) return FALSE + var/turf/T = get_turf(target) + if(!T) + return FALSE + if(istype(user)) + if(isAI(user) && !GLOB.cameranet.checkTurfVis(T)) + return FALSE + else if(user.client && !(get_turf(target) in get_hear(user.client.view, user))) + return FALSE + else if(!(get_turf(target) in get_hear(world.view, user))) + return FALSE + else //user is an atom + if(!(get_turf(target) in view(world.view, user))) + return FALSE return TRUE /obj/item/camera/afterattack(atom/target, mob/user, flag) @@ -109,10 +118,16 @@ return on = FALSE - addtimer(CALLBACK(src, .proc/cooldown), cooldown) + + var/realcooldown = cooldown + var/mob/living/carbon/human/H = user + if (H.has_trait(TRAIT_PHOTOGRAPHER)) + realcooldown *= 0.5 + addtimer(CALLBACK(src, .proc/cooldown), realcooldown) + icon_state = state_off - INVOKE_ASYNC(src, .proc/captureimage, target, user, flag, picture_size_x, picture_size_y) + INVOKE_ASYNC(src, .proc/captureimage, target, user, flag, picture_size_x - 1, picture_size_y - 1) /obj/item/camera/proc/cooldown() @@ -134,7 +149,7 @@ return FALSE size_x = CLAMP(size_x, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT) size_y = CLAMP(size_y, 0, CAMERA_PICTURE_SIZE_HARD_LIMIT) - var/list/desc = list() + var/list/desc = list("This is a photo of an area of [size_x+1] meters by [size_y+1] meters.") var/ai_user = isAI(user) var/list/seen var/list/viewlist = (user && user.client)? getviewsize(user.client.view) : getviewsize(world.view) @@ -146,15 +161,15 @@ var/blueprints = FALSE var/clone_area = SSmapping.RequestBlockReservation(size_x * 2 + 1, size_y * 2 + 1) for(var/turf/T in block(locate(target_turf.x - size_x, target_turf.y - size_y, target_turf.z), locate(target_turf.x + size_x, target_turf.y + size_y, target_turf.z))) - if((ai_user && GLOB.cameranet.checkTurfVis(T)) || T in seen) + if((ai_user && GLOB.cameranet.checkTurfVis(T)) || (T in seen)) turfs += T for(var/mob/M in T) mobs += M if(locate(/obj/item/areaeditor/blueprints) in T) blueprints = TRUE - CHECK_TICK for(var/i in mobs) - desc += camera_get_mobdesc(i) + var/mob/M = i + desc += M.get_photo_description(src) var/psize_x = (size_x * 2 + 1) * world.icon_size var/psize_y = (size_y * 2 + 1) * world.icon_size @@ -165,34 +180,10 @@ temp.Scale(psize_x, psize_y) temp.Blend(get_icon, ICON_OVERLAY) - var/datum/picture/P = new("picture", desc.Join(""), temp, null, psize_x, psize_y, blueprints) + var/datum/picture/P = new("picture", desc.Join(" "), temp, null, psize_x, psize_y, blueprints) after_picture(user, P, flag) blending = FALSE -/obj/item/camera/proc/camera_get_mobdesc(mob/M) - var/list/mob_details = list() - if(M.invisibility) - if(see_ghosts && isobserver(M)) - mob_details += "You can also see a g-g-g-g-ghooooost! " - else - return mob_details - var/list/holding = list() - if(isliving(M)) - var/mob/living/L = M - var/len = length(L.held_items) - if(len) - for(var/obj/item/I in L.held_items) - if(!holding.len) - holding += "They are holding \a [I]" - else if(L.held_items.Find(I) == len) - holding += ", and \a [I]." - else - holding += ", \a [I]" - holding += "." - holding = holding.Join("") - mob_details += "You can also see [L] on the photo[L.health < (L.maxHealth * 0.75) ? ", looking a bit hurt":""][holding ? ". [holding]":"."]." - return mob_details.Join("") - /obj/item/camera/proc/after_picture(mob/user, datum/picture/picture, proximity_flag) printpicture(user, picture) @@ -204,15 +195,15 @@ to_chat(user, "[pictures_left] photos left.") var/customize = alert(user, "Do you want to customize the photo?", "Customization", "Yes", "No") if(customize == "Yes") - var/name1 = stripped_input(user, "Set a name for this photo, or leave blank. 32 characters max.", "Name", max_length = 32) as text|null - var/desc1 = stripped_input(user, "Set a description to add to photo, or leave blank. 128 characters max.", "Caption", max_length = 128) as text|null - var/caption = stripped_input(user, "Set a caption for this photo, or leave blank. 256 characters max.", "Caption", max_length = 256) as text|null + var/name1 = stripped_input(user, "Set a name for this photo, or leave blank. 32 characters max.", "Name", max_length = 32) + var/desc1 = stripped_input(user, "Set a description to add to photo, or leave blank. 128 characters max.", "Caption", max_length = 128) + var/caption = stripped_input(user, "Set a caption for this photo, or leave blank. 256 characters max.", "Caption", max_length = 256) if(name1) picture.picture_name = name1 if(desc1) picture.picture_desc = "[desc1] - [picture.picture_desc]" if(caption) picture.caption = caption - p.set_picture(picture) + p.set_picture(picture, TRUE, TRUE) if(CONFIG_GET(flag/picture_logging_camera)) picture.log_to_file() diff --git a/code/modules/photography/camera/camera_image_capturing.dm b/code/modules/photography/camera/camera_image_capturing.dm index 7b9fe94f44..e7072026f5 100644 --- a/code/modules/photography/camera/camera_image_capturing.dm +++ b/code/modules/photography/camera/camera_image_capturing.dm @@ -1,6 +1,6 @@ /obj/effect/appearance_clone -/obj/effect/appearance_clone/New(loc, atom/A) //Intentionally not Initialize(). +/obj/effect/appearance_clone/New(loc, atom/A) //Intentionally not Initialize(), to make sure the clone assumes the intended appearance in time for the camera getFlatIcon. if(istype(A)) appearance = A.appearance dir = A.dir @@ -73,7 +73,8 @@ xo += AM.step_x yo += AM.step_y var/icon/img = getFlatIcon(A) - res.Blend(img, blendMode2iconMode(A.blend_mode), xo, yo) + if(img) + res.Blend(img, blendMode2iconMode(A.blend_mode), xo, yo) CHECK_TICK if(!silent) diff --git a/code/modules/photography/photos/album.dm b/code/modules/photography/photos/album.dm index b4481eb008..bd77d468d7 100644 --- a/code/modules/photography/photos/album.dm +++ b/code/modules/photography/photos/album.dm @@ -37,21 +37,39 @@ continue . |= P.picture.id +//Manual loading, DO NOT USE FOR HARDCODED/MAPPED IN ALBUMS. This is for if an album needs to be loaded mid-round from an ID. +/obj/item/storage/photo_album/proc/persistence_load() + var/list/data = SSpersistence.GetPhotoAlbums() + if(data[persistence_id]) + populate_from_id_list(data[persistence_id]) + /obj/item/storage/photo_album/proc/populate_from_id_list(list/ids) + var/list/current_ids = get_picture_id_list() for(var/i in ids) + if(i in current_ids) + continue var/obj/item/photo/P = load_photo_from_disk(i) if(istype(P)) if(!SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, P, null, TRUE, TRUE)) qdel(P) -#define ALBUM_DEFINE(id) /obj/item/storage/photo_album/##id/persistence_id = #id +/obj/item/storage/photo_album/HoS + persistence_id = "HoS" -ALBUM_DEFINE(HoS) -ALBUM_DEFINE(RD) -ALBUM_DEFINE(HoP) -ALBUM_DEFINE(Captain) -ALBUM_DEFINE(CMO) -ALBUM_DEFINE(QM) -ALBUM_DEFINE(CE) +/obj/item/storage/photo_album/RD + persistence_id = "RD" -#undef ALBUM_DEFINE +/obj/item/storage/photo_album/HoP + persistence_id = "HoP" + +/obj/item/storage/photo_album/Captain + persistence_id = "Captain" + +/obj/item/storage/photo_album/CMO + persistence_id = "CMO" + +/obj/item/storage/photo_album/QM + persistence_id = "QM" + +/obj/item/storage/photo_album/CE + persistence_id = "CE" diff --git a/code/modules/photography/photos/frame.dm b/code/modules/photography/photos/frame.dm index c3b9530e07..f379541b9c 100644 --- a/code/modules/photography/photos/frame.dm +++ b/code/modules/photography/photos/frame.dm @@ -90,6 +90,12 @@ if(istype(framed) && istype(framed.picture)) return framed.picture.id +//Manual loading, DO NOT USE FOR HARDCODED/MAPPED IN ALBUMS. This is for if an album needs to be loaded mid-round from an ID. +/obj/structure/sign/picture_frame/proc/persistence_load() + var/list/data = SSpersistence.GetPhotoFrames() + if(data[persistence_id]) + load_from_id(data[persistence_id]) + /obj/structure/sign/picture_frame/proc/load_from_id(id) var/obj/item/photo/P = load_photo_from_disk(id) if(istype(P)) diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm index b7a37febab..99e61cf3f0 100644 --- a/code/modules/photography/photos/photo.dm +++ b/code/modules/photography/photos/photo.dm @@ -12,13 +12,12 @@ grind_results = list("iodine" = 4) var/datum/picture/picture var/scribble //Scribble on the back. - var/sillynewscastervar //Photo objects with this set to 1 will not be ejected by a newscaster. Only gets set to 1 if a silicon puts one of their images into a newscaster /obj/item/photo/Initialize(mapload, datum/picture/P, datum_name = TRUE, datum_desc = TRUE) - set_picture(P, datum_name, datum_desc) + set_picture(P, datum_name, datum_desc, TRUE) return ..() -/obj/item/photo/proc/set_picture(datum/picture/P, setname, setdesc) +/obj/item/photo/proc/set_picture(datum/picture/P, setname, setdesc, name_override = FALSE) if(!istype(P)) return picture = P @@ -26,7 +25,10 @@ if(P.caption) scribble = P.caption if(setname && P.picture_name) - name = P.picture_name + if(name_override) + name = P.picture_name + else + name = "photo - [P.picture_name]" if(setdesc && P.picture_desc) desc = P.picture_desc diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index e9deb8233b..f48826df9d 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -101,10 +101,11 @@ var/update_state = -1 var/update_overlay = -1 var/icon_update_needed = FALSE + var/obj/machinery/computer/apc_control/remote_control = null /obj/machinery/power/apc/unlocked locked = FALSE - + /obj/machinery/power/apc/syndicate //general syndicate access req_access = list(ACCESS_SYNDICATE) @@ -117,6 +118,25 @@ /obj/machinery/power/apc/highcap/fifteen_k cell_type = /obj/item/stock_parts/cell/high/plus +/obj/machinery/power/apc/auto_name + auto_name = TRUE + +/obj/machinery/power/apc/auto_name/north //Pixel offsets get overwritten on New() + dir = NORTH + pixel_y = 23 + +/obj/machinery/power/apc/auto_name/south + dir = SOUTH + pixel_y = -23 + +/obj/machinery/power/apc/auto_name/east + dir = EAST + pixel_x = 24 + +/obj/machinery/power/apc/auto_name/west + dir = WEST + pixel_x = -25 + /obj/machinery/power/apc/get_cell() return cell @@ -245,6 +265,11 @@ if(integration_cog && is_servant_of_ratvar(user)) to_chat(user, "There is an integration cog installed!") + to_chat(user, "Alt-Click the APC to [ locked ? "unlock" : "lock"] the interface.") + + if(issilicon(user)) + to_chat(user, "Ctrl-Click the APC to switch the breaker [ operating ? "off" : "on"].") + // update the APC icon to show the three base states // also add overlays for indicator lights /obj/machinery/power/apc/update_icon() @@ -280,19 +305,17 @@ icon_state = "apc0" if(!(update_state & UPSTATE_ALLGOOD)) - cut_overlays() + SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) if(update & 2) - cut_overlays() + SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays) if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) - var/list/O = list( - "apcox-[locked]", - "apco3-[charging]") + SSvis_overlays.add_vis_overlay(src, icon, "apcox-[locked]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) + SSvis_overlays.add_vis_overlay(src, icon, "apco3-[charging]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) if(operating) - O += "apco0-[equipment]" - O += "apco1-[lighting]" - O += "apco2-[environ]" - add_overlay(O) + SSvis_overlays.add_vis_overlay(src, icon, "apco0-[equipment]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) + SSvis_overlays.add_vis_overlay(src, icon, "apco1-[lighting]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) + SSvis_overlays.add_vis_overlay(src, icon, "apco2-[environ]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir) // And now, separately for cleanness, the lighting changing if(update_state & UPSTATE_ALLGOOD) @@ -452,6 +475,8 @@ return /obj/machinery/power/apc/screwdriver_act(mob/living/user, obj/item/W) + if(..()) + return TRUE . = TRUE if(opened) if(cell) @@ -699,6 +724,13 @@ else to_chat(user, "Access denied.") +/obj/machinery/power/apc/proc/toggle_nightshift_lights(mob/living/user) + if(last_nightshift_switch > world.time - 100) //~10 seconds between each toggle to prevent spamming + to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") + return + last_nightshift_switch = world.time + set_nightshift(!nightshift_lights) + /obj/machinery/power/apc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) if(damage_flag == "melee" && damage_amount < 10 && (!(stat & BROKEN) || malfai)) return 0 @@ -863,6 +895,16 @@ return FALSE return TRUE +/obj/machinery/power/apc/can_interact(mob/user) + . = ..() + if (!. && !QDELETED(remote_control)) + . = remote_control.can_interact(user) + +/obj/machinery/power/apc/ui_status(mob/user) + . = ..() + if (!QDELETED(remote_control) && user == remote_control.operator) + . = UI_INTERACTIVE + /obj/machinery/power/apc/ui_act(action, params) if(..() || !can_use(usr, 1) || (locked && !usr.has_unlimited_silicon_privilege && !failure_timer && !(integration_cog && (is_servant_of_ratvar(usr))))) return @@ -882,11 +924,7 @@ toggle_breaker() . = TRUE if("toggle_nightshift") - if(last_nightshift_switch > world.time + 100) //don't spam.. - to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") - return - last_nightshift_switch = world.time - set_nightshift(!nightshift_lights) + toggle_nightshift_lights() . = TRUE if("charge") chargemode = !chargemode diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 04645b2267..f3f960c0ee 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -211,6 +211,8 @@ return TRUE /obj/machinery/power/generator/screwdriver_act(mob/user, obj/item/I) + if(..()) + return TRUE panel_open = !panel_open I.play_tool_sound(src) to_chat(user, "You [panel_open?"open":"close"] the panel on [src].") diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 0b6cf36921..a32d8dca45 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -345,6 +345,7 @@ else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE) && !turned_off()) use_power = IDLE_POWER_USE emergency_mode = TRUE + START_PROCESSING(SSmachines, src) else use_power = IDLE_POWER_USE set_light(0) @@ -361,7 +362,11 @@ /obj/machinery/light/process() - if(has_power() && cell) + if (!cell) + return PROCESS_KILL + if(has_power()) + if (cell.charge == cell.maxcharge) + return PROCESS_KILL cell.charge = min(cell.maxcharge, cell.charge + LIGHT_EMERGENCY_POWER_USE) //Recharge emergency power automatically while not using it if(emergency_mode && !use_emergency_power(LIGHT_EMERGENCY_POWER_USE)) update(FALSE) //Disables emergency mode and sets the color to normal diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 0bd230a166..f6110cb081 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -28,6 +28,9 @@ /obj/machinery/power/proc/add_avail(amount) if(powernet) powernet.newavail += amount + return TRUE + else + return FALSE /obj/machinery/power/proc/add_load(amount) if(powernet) @@ -52,13 +55,13 @@ // defaults to power_channel /obj/machinery/proc/powered(var/chan = -1) // defaults to power_channel if(!loc) - return 0 + return FALSE if(!use_power) - return 1 + return TRUE var/area/A = get_area(src) // make sure it's in an area if(!A) - return 0 // if not, then not powered + return FALSE // if not, then not powered if(chan == -1) chan = power_channel return A.powered(chan) // return power status of the area @@ -95,21 +98,21 @@ /obj/machinery/power/proc/connect_to_network() var/turf/T = src.loc if(!T || !istype(T)) - return 0 + return FALSE var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked if(!C || !C.powernet) - return 0 + return FALSE C.powernet.add_machine(src) - return 1 + return TRUE // remove and disconnect the machine from its current powernet /obj/machinery/power/proc/disconnect_from_network() if(!powernet) - return 0 + return FALSE powernet.remove_machine(src) - return 1 + return TRUE // attach a wire to a power machine - leads from the turf you are standing on //almost never called, overwritten by all power machines but terminal and generator @@ -330,7 +333,7 @@ power_source = cell shock_damage = cell_damage var/drained_hp = M.electrocute_act(shock_damage, source, siemens_coeff) //zzzzzzap! - add_logs(source, M, "electrocuted") + log_combat(source, M, "electrocuted") var/drained_energy = drained_hp*20 diff --git a/code/modules/power/rtg.dm b/code/modules/power/rtg.dm index 79e94be7f0..9dca8f3124 100644 --- a/code/modules/power/rtg.dm +++ b/code/modules/power/rtg.dm @@ -43,12 +43,6 @@ return return ..() -//ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/machinery/power/rtg/attack_hand(mob/user) - if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user, check_loc = 0)) - return - . = ..() - /obj/machinery/power/rtg/advanced desc = "An advanced RTG capable of moderating isotope decay, increasing power output but reducing lifetime. It uses plasma-fueled radiation collectors to increase output even further." power_gen = 1250 // 2500 on T1, 10000 on T4. diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 0e3545b1a7..4fe77def51 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -134,6 +134,8 @@ return TRUE /obj/machinery/power/rad_collector/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE if(loaded_tank) to_chat(user, "Remove the plasma tank first!") else diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index f6004b664a..3b0f7e7f6f 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -87,7 +87,7 @@ /obj/machinery/field var/hasShocked = FALSE //Used to add a delay between shocks. In some cases this used to crash servers by spawning hundreds of sparks every second. -/obj/machinery/field/CollidedWith(atom/movable/mover) +/obj/machinery/field/Bumped(atom/movable/mover) if(hasShocked) return if(isliving(mover)) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index bf03256219..eebdd5c9da 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -31,16 +31,19 @@ var/allow_switch_interact = TRUE var/projectile_type = /obj/item/projectile/beam/emitter - var/projectile_sound = 'sound/weapons/emitter.ogg' - var/datum/effect_system/spark_spread/sparks + var/obj/item/gun/energy/gun + var/list/gun_properties + var/mode = 0 + // The following 3 vars are mostly for the prototype var/manual = FALSE var/charge = 0 var/last_projectile_params + /obj/machinery/power/emitter/anchored anchored = TRUE @@ -269,10 +272,14 @@ return TRUE /obj/machinery/power/emitter/crowbar_act(mob/living/user, obj/item/I) + if(panel_open && gun) + return remove_gun(user) default_deconstruction_crowbar(I) return TRUE /obj/machinery/power/emitter/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE default_deconstruction_screwdriver(user, "emitter_open", "emitter", I) return TRUE @@ -295,9 +302,42 @@ else if(is_wire_tool(I) && panel_open) wires.interact(user) return - + else if(panel_open && !gun && istype(I,/obj/item/gun/energy)) + if(integrate(I,user)) + return return ..() +/obj/machinery/power/emitter/proc/integrate(obj/item/gun/energy/E,mob/user) + if(istype(E, /obj/item/gun/energy)) + if(!user.transferItemToLoc(E, src)) + return + gun = E + gun_properties = gun.get_turret_properties() + set_projectile() + return TRUE + +/obj/machinery/power/emitter/proc/remove_gun(mob/user) + if(!gun) + return + user.put_in_hands(gun) + gun = null + playsound(src, 'sound/items/deconstruct.ogg', 50, 1) + gun_properties = list() + set_projectile() + return TRUE + +/obj/machinery/power/emitter/proc/set_projectile() + if(LAZYLEN(gun_properties)) + if(mode || !gun_properties["lethal_projectile"]) + projectile_type = gun_properties["stun_projectile"] + projectile_sound = gun_properties["stun_projectile_sound"] + else + projectile_type = gun_properties["lethal_projectile"] + projectile_sound = gun_properties["lethal_projectile_sound"] + return + projectile_type = initial(projectile_type) + projectile_sound = initial(projectile_sound) + /obj/machinery/power/emitter/emag_act(mob/user) if(obj_flags & EMAGGED) return @@ -405,7 +445,7 @@ var/delay = 0 /obj/item/turret_control/afterattack(atom/targeted_atom, mob/user, proxflag, clickparams) - ..() + . = ..() var/obj/machinery/power/emitter/E = user.buckled E.setDir(get_dir(E,targeted_atom)) user.setDir(E.dir) diff --git a/code/modules/power/singularity/generator.dm b/code/modules/power/singularity/generator.dm index 4cc1143c02..a7df7198c0 100644 --- a/code/modules/power/singularity/generator.dm +++ b/code/modules/power/singularity/generator.dm @@ -17,12 +17,6 @@ var/energy = 0 var/creation_type = /obj/singularity -//ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/machinery/the_singularitygen/attack_hand(mob/user) - if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user, check_loc = 0)) - return - . = ..() - /obj/machinery/the_singularitygen/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/wrench)) default_unfasten_wrench(user, W, 0) diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 7f40f81903..a073997b9d 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -1,5 +1,5 @@ /obj/singularity/narsie //Moving narsie to a child object of the singularity so it can be made to function differently. --NEO - name = "Nar-sie's Avatar" + name = "Nar'Sie's Avatar" desc = "Your mind begins to bubble and ooze as it tries to comprehend what it sees." icon = 'icons/obj/magic_terror.dmi' pixel_x = -89 @@ -15,10 +15,10 @@ light_range = 15 light_color = rgb(255, 0, 0) gender = FEMALE - var/clashing = FALSE //If Nar-Sie is fighting Ratvar + var/clashing = FALSE //If Nar'Sie is fighting Ratvar /obj/singularity/narsie/large - name = "Nar-Sie" + name = "Nar'Sie" icon = 'icons/obj/narsie.dmi' // Pixel stuff centers Narsie. pixel_x = -236 @@ -29,13 +29,13 @@ /obj/singularity/narsie/large/Initialize() . = ..() - send_to_playing_players("NAR-SIE HAS RISEN") + send_to_playing_players("NAR'SIE HAS RISEN") sound_to_playing_players('sound/creatures/narsie_rises.ogg') var/area/A = get_area(src) if(A) var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/cult_effects.dmi', "ghostalertsie") - notify_ghosts("Nar-Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, action=NOTIFY_ATTACK) + notify_ghosts("Nar'Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, action=NOTIFY_ATTACK) INVOKE_ASYNC(src, .proc/narsie_spawn_animation) /obj/singularity/narsie/large/cult // For the new cult ending, guaranteed to end the round within 3 minutes @@ -118,10 +118,10 @@ return clashing -/obj/singularity/narsie/Collide(atom/A) +/obj/singularity/narsie/Bump(atom/A) var/turf/T = get_turf(A) if(T == loc) - T = get_step(A, A.dir) //please don't slam into a window like a bird, nar-sie + T = get_step(A, A.dir) //please don't slam into a window like a bird, Nar'Sie forceMove(T) @@ -185,12 +185,12 @@ /obj/singularity/narsie/proc/acquire(atom/food) if(food == target) return - to_chat(target, "NAR-SIE HAS LOST INTEREST IN YOU.") + to_chat(target, "NAR'SIE HAS LOST INTEREST IN YOU.") target = food if(ishuman(target)) - to_chat(target, "NAR-SIE HUNGERS FOR YOUR SOUL.") + to_chat(target, "NAR'SIE HUNGERS FOR YOUR SOUL.") else - to_chat(target, "NAR-SIE HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL.") + to_chat(target, "NAR'SIE HAS CHOSEN YOU TO LEAD HER TO HER NEXT MEAL.") //Wizard narsie /obj/singularity/narsie/wizard diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm index 2756d879d1..57f4a77fc2 100644 --- a/code/modules/power/singularity/particle_accelerator/particle.dm +++ b/code/modules/power/singularity/particle_accelerator/particle.dm @@ -28,7 +28,7 @@ addtimer(CALLBACK(src, .proc/move), 1) -/obj/effect/accelerated_particle/Collide(atom/A) +/obj/effect/accelerated_particle/Bump(atom/A) if(A) if(isliving(A)) toxmob(A) diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 25623661d6..bc05e784a4 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -81,6 +81,20 @@ /obj/singularity/blob_act(obj/structure/blob/B) return +/obj/singularity/attack_tk(mob/user) + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.visible_message("[C]'s head begins to collapse in on itself!", "Your head feels like it's collapsing in on itself! This was really not a good idea!", "You hear something crack and explode in gore.") + var/turf/T = get_turf(C) + for(var/i in 1 to 3) + C.apply_damage(30, BRUTE, BODY_ZONE_HEAD) + new /obj/effect/gibspawner/generic(T) + sleep(1) + C.ghostize() + var/obj/item/bodypart/head/rip_u = C.get_bodypart(BODY_ZONE_HEAD) + rip_u.dismember(BURN) //nice try jedi + qdel(rip_u) + /obj/singularity/ex_act(severity, target) switch(severity) if(1) @@ -101,12 +115,12 @@ return 0 //Will there be an impact? Who knows. Will we see it? No. -/obj/singularity/Collide(atom/A) +/obj/singularity/Bump(atom/A) consume(A) return -/obj/singularity/CollidedWith(atom/movable/AM) +/obj/singularity/Bumped(atom/movable/AM) consume(AM) diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 4a8ee61f0f..3637bf38f7 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -24,14 +24,14 @@ var/capacity = 5e6 // maximum charge var/charge = 0 // actual charge - var/input_attempt = 1 // 1 = attempting to charge, 0 = not attempting to charge - var/inputting = 1 // 1 = actually inputting, 0 = not inputting + var/input_attempt = TRUE // TRUE = attempting to charge, FALSE = not attempting to charge + var/inputting = TRUE // TRUE = actually inputting, FALSE = not inputting var/input_level = 50000 // amount of power the SMES attempts to charge by var/input_level_max = 200000 // cap on input_level var/input_available = 0 // amount of charge available from input last tick - var/output_attempt = 1 // 1 = attempting to output, 0 = not attempting to output - var/outputting = 1 // 1 = actually outputting, 0 = not outputting + var/output_attempt = TRUE // TRUE = attempting to output, FALSE = not attempting to output + var/outputting = TRUE // TRUE = actually outputting, FALSE = not outputting var/output_level = 50000 // amount of power the SMES attempts to output var/output_level_max = 200000 // cap on output_level var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power @@ -53,11 +53,11 @@ terminal = term break dir_loop - if(!terminal) - stat |= BROKEN - return - terminal.master = src - update_icon() + if(!terminal) + stat |= BROKEN + return + terminal.master = src + update_icon() /obj/machinery/power/smes/RefreshParts() var/IO = 0 @@ -141,11 +141,7 @@ //build the terminal and link it to the network make_terminal(T) terminal.connect_to_network() - return - - //disassembling the terminal - if(istype(I, /obj/item/wirecutters) && terminal && panel_open) - terminal.dismantle(user, I) + connect_to_network() return //crowbarring it ! @@ -160,6 +156,13 @@ return ..() +/obj/machinery/power/smes/wirecutter_act(mob/living/user, obj/item/I) + //disassembling the terminal + if(terminal && panel_open) + terminal.dismantle(user, I) + return TRUE + + /obj/machinery/power/smes/default_deconstruction_crowbar(obj/item/crowbar/C) if(istype(C) && terminal) to_chat(usr, "You must first remove the power terminal!") @@ -243,35 +246,36 @@ charge += load * SMESRATE // increase the charge - add_load(load) // add the load to the terminal side network + terminal.add_load(load) // add the load to the terminal side network else // if not enough capcity - inputting = 0 // stop inputting + inputting = FALSE // stop inputting else if(input_attempt && input_available > 0) - inputting = 1 + inputting = TRUE else - inputting = 0 + inputting = FALSE //outputting if(output_attempt) if(outputting) output_used = min( charge/SMESRATE, output_level) //limit output to that stored - charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) - - add_avail(output_used) // add output to powernet (smes side) + if (add_avail(output_used)) // add output to powernet if it exists (smes side) + charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) + else + outputting = FALSE if(output_used < 0.0001) // either from no charge or set to 0 - outputting = 0 + outputting = FALSE investigate_log("lost power and turned off", INVESTIGATE_SINGULO) else if(output_attempt && charge > output_level && output_level > 0) - outputting = 1 + outputting = TRUE else output_used = 0 else - outputting = 0 + outputting = FALSE // only update icon if state changed if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting) @@ -309,10 +313,6 @@ return -/obj/machinery/power/smes/add_load(amount) - if(terminal && terminal.powernet) - terminal.powernet.load += amount - /obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 53140d45e0..ad94d577ea 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -519,9 +519,11 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) /obj/machinery/power/supermatter_crystal/attack_tk(mob/user) if(iscarbon(user)) var/mob/living/carbon/C = user - to_chat(C, "That was a really dumb idea.") - var/obj/item/bodypart/head/rip_u = C.get_bodypart(BODY_ZONE_HEAD) - rip_u.dismember(BURN) //nice try jedi + to_chat(C, "That was a really dense idea.") + C.ghostize() + var/obj/item/organ/brain/rip_u = locate(/obj/item/organ/brain) in C.internal_organs + rip_u.Remove(C) + qdel(rip_u) /obj/machinery/power/supermatter_crystal/attack_paw(mob/user) dust_mob(user, cause = "monkey attack") @@ -593,7 +595,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) default_unfasten_wrench(user, tool, time = 20) return TRUE -/obj/machinery/power/supermatter_crystal/CollidedWith(atom/movable/AM) +/obj/machinery/power/supermatter_crystal/Bumped(atom/movable/AM) if(isliving(AM)) AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... [AM.p_their()] body starts to glow and catch flame before flashing into ash.",\ "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ @@ -615,13 +617,17 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) return message_admins("[src] has consumed [key_name_admin(user)] [ADMIN_JMP(src)].") investigate_log("has consumed [key_name(user)].", INVESTIGATE_SUPERMATTER) - user.dust() + user.dust(force = TRUE) matter_power += 200 else if(istype(AM, /obj/singularity)) return else if(isobj(AM)) if(!iseffect(AM)) - investigate_log("has consumed [AM].", INVESTIGATE_SUPERMATTER) + var/suspicion = "" + if(AM.fingerprintslast) + suspicion = "last touched by [AM.fingerprintslast]" + message_admins("[src] has consumed [AM], [suspicion] [ADMIN_JMP(src)].") + investigate_log("has consumed [AM] - [suspicion].", INVESTIGATE_SUPERMATTER) qdel(AM) if(!iseffect(AM)) matter_power += 200 diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index 5ee0b91b2b..33ebefbf4c 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -25,7 +25,7 @@ /obj/machinery/power/tesla_coil/Initialize() . = ..() - wires = new /datum/wires/tesla_coil(src) //CITADEL EDIT, Kevinz you cheaty fuccboi. + wires = new /datum/wires/tesla_coil(src) linked_techweb = SSresearch.science_tech /obj/machinery/power/tesla_coil/RefreshParts() @@ -68,11 +68,6 @@ return ..() -//ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/machinery/power/tesla_coil/attack_hand(mob/user) - if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user, check_loc = 0)) - return ..() - /obj/machinery/power/tesla_coil/tesla_act(power, tesla_flags, shocked_targets) if(anchored && !panel_open) obj_flags |= BEING_SHOCKED @@ -174,15 +169,9 @@ return ..() -//ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/machinery/power/grounding_rod/attack_hand(mob/user) - if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user, check_loc = 0)) - return - . = ..() - /obj/machinery/power/grounding_rod/tesla_act(var/power) if(anchored && !panel_open) flick("grounding_rodhit", src) tesla_buckle_check(power) else - ..() + ..() \ No newline at end of file diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 470b6e789c..0a54bcab33 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -24,8 +24,8 @@ var/energy_to_lower = -20 /obj/singularity/energy_ball/Initialize(mapload, starting_energy = 50, is_miniball = FALSE) - . = ..() miniball = is_miniball + . = ..() if(!is_miniball) set_light(10, 7, "#EEEEFF") @@ -123,12 +123,21 @@ EB.orbit(src, orbitsize, pick(FALSE, TRUE), rand(10, 25), pick(3, 4, 5, 6, 36)) -/obj/singularity/energy_ball/Collide(atom/A) +/obj/singularity/energy_ball/Bump(atom/A) dust_mobs(A) -/obj/singularity/energy_ball/CollidedWith(atom/movable/AM) +/obj/singularity/energy_ball/Bumped(atom/movable/AM) dust_mobs(AM) +/obj/singularity/energy_ball/attack_tk(mob/user) + if(iscarbon(user)) + var/mob/living/carbon/C = user + to_chat(C, "That was a shockingly dumb idea.") + var/obj/item/organ/brain/rip_u = locate(/obj/item/organ/brain) in C.internal_organs + C.ghostize(0) + qdel(rip_u) + C.death() + /obj/singularity/energy_ball/orbit(obj/singularity/energy_ball/target) if (istype(target)) target.orbiting_balls += src @@ -193,7 +202,8 @@ /obj/machinery/gateway, /obj/structure/lattice, /obj/structure/grille, - /obj/machinery/the_singularitygen/tesla)) + /obj/machinery/the_singularitygen/tesla, + /obj/structure/frame/machine)) for(var/A in typecache_filter_multi_list_exclusion(oview(source, zap_range+2), things_to_shock, blacklisted_tesla_types)) if(!(tesla_flags & TESLA_ALLOW_DUPLICATES) && LAZYACCESS(shocked_targets, A)) diff --git a/code/modules/procedural_mapping/mapGenerators/repair.dm b/code/modules/procedural_mapping/mapGenerators/repair.dm index 4b541e8a79..bc8e9f74f1 100644 --- a/code/modules/procedural_mapping/mapGenerators/repair.dm +++ b/code/modules/procedural_mapping/mapGenerators/repair.dm @@ -21,13 +21,13 @@ return var/datum/mapGenerator/repair/reload_station_map/mother1 = mother GLOB.reloading_map = TRUE - var/static/dmm_suite/reloader = new // This is kind of finicky on multi-Z maps but the reader would need to be // changed to allow Z cropping and that's a mess var/z_offset = SSmapping.station_start var/list/bounds for (var/path in SSmapping.config.GetFullMapPaths()) - bounds = reloader.load_map(file(path), measureOnly = FALSE, no_changeturf = FALSE,x_offset = 0, y_offset = 0, z_offset = z_offset, cropMap=TRUE, lower_crop_x = mother1.x_low, lower_crop_y = mother1.y_low, upper_crop_x = mother1.x_high, upper_crop_y = mother1.y_high) + var/datum/parsed_map/parsed = load_map(file(path), 1, 1, z_offset, measureOnly = FALSE, no_changeturf = FALSE, cropMap=TRUE, x_lower = mother1.x_low, y_lower = mother1.y_low, x_upper = mother1.x_high, y_upper = mother1.y_high) + bounds = parsed?.bounds z_offset += bounds[MAP_MAXZ] - bounds[MAP_MINZ] + 1 var/list/obj/machinery/atmospherics/atmos_machines = list() @@ -37,7 +37,7 @@ repopulate_sorted_areas() for(var/L in block(locate(bounds[MAP_MINX], bounds[MAP_MINY], SSmapping.station_start), - locate(bounds[MAP_MAXX], bounds[MAP_MAXY], z_offset - 1))) + locate(bounds[MAP_MAXX], bounds[MAP_MAXY], z_offset - 1))) set waitfor = FALSE var/turf/B = L atoms += B diff --git a/code/modules/projectiles/ammunition/_ammunition.dm b/code/modules/projectiles/ammunition/_ammunition.dm index c7d2069fc7..e16b9bb9b5 100644 --- a/code/modules/projectiles/ammunition/_ammunition.dm +++ b/code/modules/projectiles/ammunition/_ammunition.dm @@ -21,6 +21,9 @@ var/heavy_metal = TRUE var/harmful = TRUE //pacifism check for boolet, set to FALSE if bullet is non-lethal +/obj/item/ammo_casing/spent + name = "spent bullet casing" + BB = null /obj/item/ammo_casing/Initialize() . = ..() @@ -34,7 +37,7 @@ /obj/item/ammo_casing/update_icon() ..() icon_state = "[initial(icon_state)][BB ? "-live" : ""]" - desc = "[initial(desc)][BB ? "" : " This one is spent"]" + desc = "[initial(desc)][BB ? "" : " This one is spent."]" //proc to magically refill a casing with a new projectile /obj/item/ammo_casing/proc/newshot() //For energy weapons, syringe gun, shotgun shells and wands (!). diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm index 4c66939e53..39cf924169 100644 --- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm +++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm @@ -92,10 +92,12 @@ variance = 35 /obj/item/ammo_casing/shotgun/laserslug - name = "laser slug" - desc = "An advanced shotgun shell that uses a micro laser to replicate the effects of a laser weapon in a ballistic package." + name = "scatter laser shell" + desc = "An advanced shotgun shell that uses a micro laser to replicate the effects of a scatter laser weapon in a ballistic package." icon_state = "lshell" - projectile_type = /obj/item/projectile/beam/laser + projectile_type = /obj/item/projectile/beam/weak + pellets = 6 + variance = 35 /obj/item/ammo_casing/shotgun/techshell name = "unloaded technological shell" diff --git a/code/modules/projectiles/ammunition/caseless/misc.dm b/code/modules/projectiles/ammunition/caseless/misc.dm index fcb491f071..24f59ea658 100644 --- a/code/modules/projectiles/ammunition/caseless/misc.dm +++ b/code/modules/projectiles/ammunition/caseless/misc.dm @@ -8,15 +8,15 @@ throw_speed = 3 /obj/item/ammo_casing/caseless/laser - name = "laser casing" - desc = "You shouldn't be seeing this." - caliber = "laser" - icon_state = "s-casing-live" - projectile_type = /obj/item/projectile/beam - fire_sound = 'sound/weapons/laser.ogg' - firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy + name = "laser casing" + desc = "You shouldn't be seeing this." + caliber = "laser" + icon_state = "s-casing-live" + projectile_type = /obj/item/projectile/beam + fire_sound = 'sound/weapons/laser.ogg' + firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/energy /obj/item/ammo_casing/caseless/laser/gatling - projectile_type = /obj/item/projectile/beam/weak + projectile_type = /obj/item/projectile/beam/weak/penetrator variance = 0.8 click_cooldown_override = 1 diff --git a/code/modules/projectiles/ammunition/energy/gravity.dm b/code/modules/projectiles/ammunition/energy/gravity.dm index be3a4494cd..d8a6a1244d 100644 --- a/code/modules/projectiles/ammunition/energy/gravity.dm +++ b/code/modules/projectiles/ammunition/energy/gravity.dm @@ -1,36 +1,29 @@ -/obj/item/ammo_casing/energy/gravityrepulse +/obj/item/ammo_casing/energy/gravity + e_cost = 0 + fire_sound = 'sound/weapons/wave.ogg' + select_name = "gravity" + delay = 50 + var/obj/item/gun/energy/gravity_gun/gun + +/obj/item/ammo_casing/energy/gravity/Initialize(mapload) + if(istype(loc,/obj/item/gun/energy/gravity_gun)) + gun = loc + . = ..() + +/obj/item/ammo_casing/energy/gravity/Destroy() + gun = null + . = ..() + +/obj/item/ammo_casing/energy/gravity/repulse projectile_type = /obj/item/projectile/gravityrepulse - e_cost = 0 - fire_sound = 'sound/weapons/wave.ogg' select_name = "repulse" - delay = 50 - var/obj/item/gun/energy/gravity_gun/gun -/obj/item/ammo_casing/energy/gravityrepulse/Initialize(mapload, obj/item/gun/energy/gravity_gun/G) - gun = G - . = ..() - -/obj/item/ammo_casing/energy/gravityattract +/obj/item/ammo_casing/energy/gravity/attract projectile_type = /obj/item/projectile/gravityattract - e_cost = 0 - fire_sound = 'sound/weapons/wave.ogg' select_name = "attract" - delay = 50 - var/obj/item/gun/energy/gravity_gun/gun - -/obj/item/ammo_casing/energy/gravityattract/Initialize(mapload, obj/item/gun/energy/gravity_gun/G) - gun = G - . = ..() - -/obj/item/ammo_casing/energy/gravitychaos +/obj/item/ammo_casing/energy/gravity/chaos projectile_type = /obj/item/projectile/gravitychaos - e_cost = 0 - fire_sound = 'sound/weapons/wave.ogg' select_name = "chaos" - delay = 50 - var/obj/item/gun/energy/gravity_gun/gun -/obj/item/ammo_casing/energy/gravitychaos/Initialize(mapload, obj/item/gun/energy/gravity_gun/G) - gun = G - . = ..() + diff --git a/code/modules/projectiles/ammunition/special/magic.dm b/code/modules/projectiles/ammunition/special/magic.dm index 6e1d60c11f..51b39e0410 100644 --- a/code/modules/projectiles/ammunition/special/magic.dm +++ b/code/modules/projectiles/ammunition/special/magic.dm @@ -38,8 +38,8 @@ /obj/item/ammo_casing/magic/arcane_barrage projectile_type = /obj/item/projectile/magic/arcane_barrage -/obj/item/ammo_casing/magic/chaos/newshot() - ..() - /obj/item/ammo_casing/magic/honk projectile_type = /obj/item/projectile/bullet/honker + +/obj/item/ammo_casing/magic/locker + projectile_type = /obj/item/projectile/magic/locker diff --git a/code/modules/projectiles/boxes_magazines/external/rechargable.dm b/code/modules/projectiles/boxes_magazines/external/rechargable.dm index c4fb00aa22..fada5c8659 100644 --- a/code/modules/projectiles/boxes_magazines/external/rechargable.dm +++ b/code/modules/projectiles/boxes_magazines/external/rechargable.dm @@ -1,14 +1,14 @@ /obj/item/ammo_box/magazine/recharge - name = "power pack" - desc = "A rechargeable, detachable battery that serves as a magazine for laser rifles." - icon_state = "oldrifle-20" - ammo_type = /obj/item/ammo_casing/caseless/laser - caliber = "laser" - max_ammo = 20 + name = "power pack" + desc = "A rechargeable, detachable battery that serves as a magazine for laser rifles." + icon_state = "oldrifle-20" + ammo_type = /obj/item/ammo_casing/caseless/laser + caliber = "laser" + max_ammo = 20 /obj/item/ammo_box/magazine/recharge/update_icon() desc = "[initial(desc)] It has [stored_ammo.len] shot\s left." icon_state = "oldrifle-[round(ammo_count(),4)]" /obj/item/ammo_box/magazine/recharge/attack_self() //No popping out the "bullets" - return + return diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 569f886ec2..6a6921e949 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -49,8 +49,6 @@ var/datum/action/item_action/toggle_gunlight/alight var/mutable_appearance/flashlight_overlay - var/list/upgrades = list() - var/ammo_x_offset = 0 //used for positioning ammo count overlay on sprite var/ammo_y_offset = 0 var/flight_x_offset = 0 @@ -133,6 +131,9 @@ O.emp_act(severity) /obj/item/gun/afterattack(atom/target, mob/living/user, flag, params) + . = ..() + if(!target) + return if(firing_burst) return if(flag) //It's adjacent, is the user, or is on the user's person @@ -177,7 +178,6 @@ var/loop_counter = 0 bonus_spread += getinaccuracy(user) //CIT CHANGE - adds bonus spread while not aiming - if(ishuman(user) && user.a_intent == INTENT_HARM) var/mob/living/carbon/human/H = user for(var/obj/item/gun/G in H.held_items) @@ -337,22 +337,21 @@ if(loc == user) alight.Grant(user) else if(istype(I, /obj/item/kitchen/knife)) - if(!can_bayonet) - return ..() var/obj/item/kitchen/knife/K = I - if(!bayonet) - if(!user.transferItemToLoc(I, src)) - return - to_chat(user, "You attach \the [K] to the front of \the [src].") - bayonet = K - var/state = "bayonet" //Generic state. - if(bayonet.icon_state in icon_states('icons/obj/guns/bayonets.dmi')) //Snowflake state? - state = bayonet.icon_state - var/icon/bayonet_icons = 'icons/obj/guns/bayonets.dmi' - knife_overlay = mutable_appearance(bayonet_icons, state) - knife_overlay.pixel_x = knife_x_offset - knife_overlay.pixel_y = knife_y_offset - add_overlay(knife_overlay, TRUE) + if(!can_bayonet || !K.bayonet || bayonet) //ensure the gun has an attachment point available, and that the knife is compatible with it. + return ..() + if(!user.transferItemToLoc(I, src)) + return + to_chat(user, "You attach \the [K] to the front of \the [src].") + bayonet = K + var/state = "bayonet" //Generic state. + if(bayonet.icon_state in icon_states('icons/obj/guns/bayonets.dmi')) //Snowflake state? + state = bayonet.icon_state + var/icon/bayonet_icons = 'icons/obj/guns/bayonets.dmi' + knife_overlay = mutable_appearance(bayonet_icons, state) + knife_overlay.pixel_x = knife_x_offset + knife_overlay.pixel_y = knife_y_offset + add_overlay(knife_overlay, TRUE) else if(istype(I, /obj/item/screwdriver)) if(gun_light) var/obj/item/flashlight/seclite/S = gun_light diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index 61d2c81ea7..b9a509f33f 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -121,7 +121,7 @@ user.put_in_hands(magazine) magazine.update_icon() if(magazine.ammo_count()) - playsound(src, "sound/weapons/gun_magazine_remove_full.ogg", 70, 1) + playsound(src, 'sound/weapons/gun_magazine_remove_full.ogg', 70, 1) else playsound(src, "gun_remove_empty_magazine", 70, 1) magazine = null diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index 420e6c78ce..d1dcdf22d8 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -104,7 +104,7 @@ update_icon() /obj/item/gun/ballistic/automatic/c20r/afterattack() - ..() + . = ..() empty_alarm() return @@ -167,7 +167,7 @@ if(select == 2) underbarrel.afterattack(target, user, flag, params) else - ..() + . = ..() return /obj/item/gun/ballistic/automatic/m90/attackby(obj/item/A, mob/user, params) if(istype(A, /obj/item/ammo_casing)) @@ -264,7 +264,7 @@ icon_state = "bulldog[chambered ? "" : "-e"]" /obj/item/gun/ballistic/automatic/shotgun/bulldog/afterattack() - ..() + . = ..() empty_alarm() return @@ -286,6 +286,7 @@ can_suppress = FALSE burst_size = 3 fire_delay = 1 + spread = 7 pin = /obj/item/firing_pin/implant/pindicate /obj/item/gun/ballistic/automatic/l6_saw/unrestricted @@ -317,7 +318,7 @@ if(cover_open) to_chat(user, "[src]'s cover is open! Close it before firing!") else - ..() + . = ..() update_icon() //ATTACK HAND IGNORING PARENT RETURN VALUE diff --git a/code/modules/projectiles/guns/ballistic/laser_gatling.dm b/code/modules/projectiles/guns/ballistic/laser_gatling.dm index db5beb21ed..49ced8ff39 100644 --- a/code/modules/projectiles/guns/ballistic/laser_gatling.dm +++ b/code/modules/projectiles/guns/ballistic/laser_gatling.dm @@ -141,7 +141,7 @@ /obj/item/gun/ballistic/minigun/afterattack(atom/target, mob/living/user, flag, params) if(!ammo_pack || ammo_pack.loc != user) to_chat(user, "You need the backpack power source to fire the gun!") - ..() + . = ..() /obj/item/gun/ballistic/minigun/dropped(mob/living/user) ammo_pack.attach_gun(user) diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index cf6a856030..5f67194b29 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -9,6 +9,9 @@ fire_delay = 0 actions_types = list() +/obj/item/gun/ballistic/automatic/pistol/no_mag + spawnwithmagazine = FALSE + /obj/item/gun/ballistic/automatic/pistol/update_icon() ..() icon_state = "[initial(icon_state)][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" @@ -26,6 +29,9 @@ mag_type = /obj/item/ammo_box/magazine/m45 can_suppress = FALSE +/obj/item/gun/ballistic/automatic/pistol/m1911/no_mag + spawnwithmagazine = FALSE + /obj/item/gun/ballistic/automatic/pistol/m1911/kitchengun name = "\improper Kitchen Gun (TM)" desc = "Say goodbye to dirt with Kitchen Gun (TM)! Laser sight and night vision accessories sold separately." @@ -84,3 +90,4 @@ else to_chat(user, "..and falls into view. Whew, that was a close one.") user.dropItemToGround(src) + diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index cd11522342..2116f037a3 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -108,6 +108,8 @@ ..() /obj/item/gun/ballistic/revolver/detective/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE if(magazine.caliber == "38") to_chat(user, "You begin to reinforce the barrel of [src]...") if(magazine.ammo_count()) @@ -191,6 +193,8 @@ ..() /obj/item/gun/ballistic/revolver/russian/afterattack(atom/target, mob/living/user, flag, params) + . = ..(null, user, flag, params) + if(flag) if(!(target in user.contents) && ismob(target)) if(user.a_intent == INTENT_HARM) // Flogging action diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index c766d1c94e..6e8174b356 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -25,7 +25,7 @@ /obj/item/gun/energy/laser/retro/old name ="laser gun" icon_state = "retro" - desc = "First generation lasergun, developed by Nanotrasen. Suffers from ammo issues but its unique ability to recharge its ammo without the need of a magazine helps compensate. You really hope someone has developed a better lasergun while you were in cyro." + desc = "First generation lasergun, developed by Nanotrasen. Suffers from ammo issues but its unique ability to recharge its ammo without the need of a magazine helps compensate. You really hope someone has developed a better lasergun while you were in cryo." ammo_type = list(/obj/item/ammo_casing/energy/lasergun/old) ammo_x_offset = 3 diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 0e5ae56c81..4d18fed803 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -295,7 +295,7 @@ /obj/item/gun/energy/gravity_gun name = "one-point bluespace-gravitational manipulator" desc = "An experimental, multi-mode device that fires bolts of Zero-Point Energy, causing local distortions in gravity." - ammo_type = list(/obj/item/ammo_casing/energy/gravityrepulse, /obj/item/ammo_casing/energy/gravityattract, /obj/item/ammo_casing/energy/gravitychaos) + ammo_type = list(/obj/item/ammo_casing/energy/gravity/repulse, /obj/item/ammo_casing/energy/gravity/attract, /obj/item/ammo_casing/energy/gravity/chaos) item_state = "gravity_gun" icon_state = "gravity_gun" var/power = 4 diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm index ef705263c2..9dd25bf3e3 100644 --- a/code/modules/projectiles/guns/magic.dm +++ b/code/modules/projectiles/guns/magic.dm @@ -31,7 +31,7 @@ return else no_den_usage = 0 - ..() + . = ..() /obj/item/gun/magic/can_shoot() return charges diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index 4b3905721f..14b2ff0c1a 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -43,7 +43,7 @@ no_den_usage = 1 var/allowed_projectile_types = list(/obj/item/projectile/magic/change, /obj/item/projectile/magic/animate, /obj/item/projectile/magic/resurrection, /obj/item/projectile/magic/death, /obj/item/projectile/magic/teleport, /obj/item/projectile/magic/door, /obj/item/projectile/magic/aoe/fireball, - /obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage) + /obj/item/projectile/magic/spellblade, /obj/item/projectile/magic/arcane_barrage, /obj/item/projectile/magic/locker) /obj/item/gun/magic/staff/chaos/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) chambered.projectile_type = pick(allowed_projectile_types) @@ -94,3 +94,15 @@ if(attack_type == PROJECTILE_ATTACK) final_block_chance = 0 return ..() + +/obj/item/gun/magic/staff/locker + name = "staff of the locker" + desc = "An artefact that expells encapsulating bolts, for incapacitating thy enemy." + fire_sound = 'sound/magic/staff_change.ogg' + ammo_type = /obj/item/ammo_casing/magic/locker + icon_state = "locker" + item_state = "locker" + max_charges = 6 + recharge_rate = 4 + + diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index 9c9d253439..e3724fdf31 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -43,14 +43,14 @@ no_den_usage = 0 zap_self(user) else - ..() + . = ..() update_icon() /obj/item/gun/magic/wand/proc/zap_self(mob/living/user) user.visible_message("[user] zaps [user.p_them()]self with [src].") playsound(user, fire_sound, 50, 1) - user.log_message("zapped [user.p_them()]self with a [src]", INDIVIDUAL_ATTACK_LOG) + user.log_message("zapped [user.p_them()]self with a [src]", LOG_ATTACK) ///////////////////////////////////// diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm index 98469ebc31..0be09f94be 100644 --- a/code/modules/projectiles/guns/misc/beam_rifle.dm +++ b/code/modules/projectiles/guns/misc/beam_rifle.dm @@ -184,7 +184,7 @@ . = ..() fire_delay = delay current_tracers = list() - START_PROCESSING(SSprojectiles, src) + START_PROCESSING(SSfastprocess, src) zoom_lock_action = new(src) /obj/item/gun/energy/beam_rifle/Destroy() @@ -247,17 +247,7 @@ /obj/item/gun/energy/beam_rifle/proc/process_aim() if(istype(current_user) && current_user.client && current_user.client.mouseParams) var/angle = mouse_angle_from_client(current_user.client) - switch(angle) - if(316 to 360) - current_user.setDir(NORTH) - if(0 to 45) - current_user.setDir(NORTH) - if(46 to 135) - current_user.setDir(EAST) - if(136 to 225) - current_user.setDir(SOUTH) - if(226 to 315) - current_user.setDir(WEST) + current_user.setDir(angle2dir_cardinal(angle)) var/difference = abs(closer_angle_difference(lastangle, angle)) delay_penalty(difference * aiming_time_increase_angle_multiplier) lastangle = angle @@ -295,7 +285,7 @@ if(istype(user)) current_user = user LAZYOR(current_user.mousemove_intercept_objects, src) - mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED), CALLBACK(src, .proc/on_mob_move)) + mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_mob_move))) /obj/item/gun/energy/beam_rifle/onMouseDrag(src_object, over_object, src_location, over_location, params, mob) if(aiming) @@ -535,7 +525,7 @@ if(!QDELETED(target)) handle_impact(target) -/obj/item/projectile/beam/beam_rifle/Collide(atom/target) +/obj/item/projectile/beam/beam_rifle/Bump(atom/target) if(check_pierce(target)) permutated += target trajectory_ignore_forcemove = TRUE diff --git a/code/modules/projectiles/guns/misc/grenade_launcher.dm b/code/modules/projectiles/guns/misc/grenade_launcher.dm index e51c3573e3..eb36438a6c 100644 --- a/code/modules/projectiles/guns/misc/grenade_launcher.dm +++ b/code/modules/projectiles/guns/misc/grenade_launcher.dm @@ -28,16 +28,10 @@ else to_chat(usr, "The grenade launcher cannot hold more grenades.") -/obj/item/gun/grenadelauncher/afterattack(obj/target, mob/user , flag) - if(target == user) - return +/obj/item/gun/grenadelauncher/can_shoot() + return grenades.len - if(grenades.len) - fire_grenade(target,user) - else - to_chat(user, "The grenade launcher is empty.") - -/obj/item/gun/grenadelauncher/proc/fire_grenade(atom/target, mob/user) +/obj/item/gun/grenadelauncher/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0) user.visible_message("[user] fired a grenade!", \ "You fire the grenade launcher!") var/obj/item/grenade/F = grenades[1] //Now with less copypasta! diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm index f132d62b27..bd42f30626 100644 --- a/code/modules/projectiles/pins.dm +++ b/code/modules/projectiles/pins.dm @@ -19,6 +19,7 @@ gun = newloc /obj/item/firing_pin/afterattack(atom/target, mob/user, proximity_flag) + . = ..() if(proximity_flag) if(istype(target, /obj/item/gun)) var/obj/item/gun/G = target @@ -56,7 +57,8 @@ return TRUE /obj/item/firing_pin/proc/auth_fail(mob/living/user) - user.show_message(fail_message, 1) + if(user) + user.show_message(fail_message, 1) if(selfdestruct) user.show_message("SELF-DESTRUCTING...
", 1) to_chat(user, "[gun] explodes!") @@ -79,6 +81,8 @@ pin_removeable = TRUE /obj/item/firing_pin/test_range/pin_auth(mob/living/user) + if(!istype(user)) + return FALSE for(var/obj/machinery/magnetic_controller/M in range(user, 3)) return TRUE return FALSE @@ -156,7 +160,7 @@ var/unique_enzymes = null /obj/item/firing_pin/dna/afterattack(atom/target, mob/user, proximity_flag) - ..() + . = ..() if(proximity_flag && iscarbon(target)) var/mob/living/carbon/M = target if(M.dna && M.dna.unique_enzymes) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 027849c21d..a23bf1f4ed 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -16,7 +16,7 @@ resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF var/def_zone = "" //Aiming at - var/mob/firer = null//Who shot it + var/atom/movable/firer = null//Who shot it var/suppressed = FALSE //Attack message var/yo = null var/xo = null @@ -191,7 +191,11 @@ reagent_note += R.id + " (" reagent_note += num2text(R.volume) + ") " - add_logs(firer, L, "shot", src, reagent_note) + if(ismob(firer)) + log_combat(firer, L, "shot", src, reagent_note) + else + L.log_message("has been shot by [firer] with [src]", LOG_ATTACK, color="orange") + return L.apply_effects(stun, knockdown, unconscious, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter) /obj/item/projectile/proc/vol_by_damage() @@ -208,7 +212,7 @@ beam_index = pcache beam_segments[beam_index] = null -/obj/item/projectile/Collide(atom/A) +/obj/item/projectile/Bump(atom/A) var/datum/point/pcache = trajectory.copy_to() if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max) ricochets++ @@ -335,7 +339,7 @@ /obj/item/projectile/proc/fire(angle, atom/direct_target) //If no angle needs to resolve it from xo/yo! if(!log_override && firer && original) - add_logs(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]") + log_combat(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]") if(direct_target) if(prehit(direct_target)) direct_target.bullet_act(src, def_zone) @@ -430,7 +434,7 @@ continue if(safety-- <= 0) if(loc) - Collide(loc) + Bump(loc) if(!QDELETED(src)) qdel(src) return //Kill! @@ -471,7 +475,7 @@ step_towards(src, T) hitscan_last = loc if(can_hit_target(original, permutated)) - Collide(original) + Bump(original) if(!hitscanning && !forcemoved) pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move @@ -517,13 +521,6 @@ xo = targloc.x - curloc.x setAngle(Get_Angle(src, targloc) + spread) - //CIT CHANGES START HERE - makes it so laying down makes you unable to shoot through most objects - if(iscarbon(source)) - var/mob/living/carbon/checklad = source - if(istype(checklad) && checklad.resting) - pass_flags = 0 - //END OF CIT CHANGES - if(isliving(source) && params) var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params) p_x = calculated[2] @@ -572,7 +569,7 @@ /obj/item/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it. ..() if(isliving(AM) && (AM.density || AM == original) && !(src.pass_flags & PASSMOB)) - Collide(AM) + Bump(AM) /obj/item/projectile/Destroy() if(hitscan) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index aaa875b0f2..509d33faa0 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -38,6 +38,8 @@ /obj/item/projectile/beam/weak damage = 15 + +/obj/item/projectile/beam/weak/penetrator armour_penetration = 50 /obj/item/projectile/beam/practice @@ -54,7 +56,7 @@ name = "\improper X-ray beam" icon_state = "xray" damage = 15 - irradiate = 30 + irradiate = 300 range = 15 pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSCLOSEDTURF @@ -67,7 +69,7 @@ /obj/item/projectile/beam/disabler name = "disabler beam" icon_state = "omnilaser" - damage = 24 + damage = 36 damage_type = STAMINA flag = "energy" hitsound = 'sound/weapons/tap.ogg' diff --git a/code/modules/projectiles/projectile/bullets/lmg.dm b/code/modules/projectiles/projectile/bullets/lmg.dm index 03e64976d9..cf0e565cfc 100644 --- a/code/modules/projectiles/projectile/bullets/lmg.dm +++ b/code/modules/projectiles/projectile/bullets/lmg.dm @@ -40,5 +40,5 @@ /obj/item/projectile/bullet/incendiary/mm195x129 name = "1.95x129mm incendiary bullet" - damage = 15 + damage = 20 fire_stacks = 3 diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm index fc4ed0fa50..81cb34a0f7 100644 --- a/code/modules/projectiles/projectile/bullets/revolver.dm +++ b/code/modules/projectiles/projectile/bullets/revolver.dm @@ -14,9 +14,7 @@ /obj/item/projectile/bullet/c38 name = ".38 bullet" - damage = 15 - knockdown = 60 - stamina = 50 + damage = 25 // .357 (Syndie Revolver) diff --git a/code/modules/projectiles/projectile/energy/misc.dm b/code/modules/projectiles/projectile/energy/misc.dm index 21c3138add..86f4cc060c 100644 --- a/code/modules/projectiles/projectile/energy/misc.dm +++ b/code/modules/projectiles/projectile/energy/misc.dm @@ -3,7 +3,7 @@ icon_state = "declone" damage = 20 damage_type = CLONE - irradiate = 10 + irradiate = 100 impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser /obj/item/projectile/energy/dart //ninja throwing dart diff --git a/code/modules/projectiles/projectile/energy/stun.dm b/code/modules/projectiles/projectile/energy/stun.dm index b5b45a84ec..db1ad403a7 100644 --- a/code/modules/projectiles/projectile/energy/stun.dm +++ b/code/modules/projectiles/projectile/energy/stun.dm @@ -21,7 +21,7 @@ SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "tased", /datum/mood_event/tased) SEND_SIGNAL(C, COMSIG_LIVING_MINOR_SHOCK) if(C.dna && C.dna.check_mutation(HULK)) - C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk") else if((C.status_flags & CANKNOCKDOWN) && !C.has_trait(TRAIT_STUNIMMUNE)) addtimer(CALLBACK(C, /mob/living/carbon.proc/do_jitter_animation, jitter), 5) diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 94d4d49ded..8c9769f278 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -34,6 +34,7 @@ return if(target.anti_magic_check()) target.visible_message("[src] vanishes on contact with [target]!") + return if(iscarbon(target)) var/mob/living/carbon/C = target C.regenerate_limbs() @@ -236,7 +237,7 @@ for(var/obj/item/W in contents) new_mob.equip_to_appropriate_slot(W) - M.log_message("became [new_mob.real_name].", INDIVIDUAL_ATTACK_LOG) + M.log_message("became [new_mob.real_name]", LOG_ATTACK, color="orange") new_mob.a_intent = INTENT_HARM @@ -244,7 +245,7 @@ to_chat(new_mob, "Your form morphs into that of a [randomize].") - var/poly_msg = CONFIG_GET(keyed_string_list/policy)["polymorph"] + var/poly_msg = CONFIG_GET(keyed_list/policy)["polymorph"] if(poly_msg) to_chat(new_mob, poly_msg) @@ -335,6 +336,91 @@ return . = ..() + +/obj/item/projectile/magic/locker + name = "locker bolt" + icon_state = "locker" + nodamage = TRUE + flag = "magic" + var/weld = TRUE + var/created = FALSE //prevents creation of more then one locker if it has multiple hits + +/obj/item/projectile/magic/locker/prehit(atom/A) + if(ismob(A)) + var/mob/M = A + if(M.anti_magic_check()) + M.visible_message("[src] vanishes on contact with [A]!") + qdel(src) + return + if(M.anchored) + return ..() + M.forceMove(src) + return FALSE + return ..() + +/obj/item/projectile/magic/locker/on_hit(target) + if(created) + return ..() + var/obj/structure/closet/decay/C = new(get_turf(src)) + if(LAZYLEN(contents)) + for(var/atom/movable/AM in contents) + C.insert(AM) + C.welded = weld + C.update_icon() + created = TRUE + return ..() + +/obj/item/projectile/magic/locker/Destroy() + for(var/atom/movable/AM in contents) + AM.forceMove(get_turf(src)) + . = ..() + +/obj/structure/closet/decay + breakout_time = 600 + icon_welded = null + var/magic_icon = "cursed" + var/weakened_icon = "decursed" + var/auto_destroy = TRUE + +/obj/structure/closet/decay/Initialize() + . = ..() + if(auto_destroy) + addtimer(CALLBACK(src, .proc/bust_open), 5 MINUTES) + addtimer(CALLBACK(src, .proc/magicly_lock), 5) + +/obj/structure/closet/decay/proc/magicly_lock() + if(!welded) + return + icon_state = magic_icon + update_icon() + +/obj/structure/closet/decay/after_weld(weld_state) + if(weld_state) + unmagify() + +/obj/structure/closet/decay/proc/decay() + animate(src, alpha = 0, time = 30) + addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, src), 30) + +/obj/structure/closet/decay/open(mob/living/user) + . = ..() + if(.) + if(icon_state == magic_icon) //check if we used the magic icon at all before giving it the lesser magic icon + unmagify() + else + addtimer(CALLBACK(src, .proc/decay), 15 SECONDS) + +/obj/structure/closet/decay/contents_explosion(severity, target) + for(var/atom/A in contents) + A.ex_act(severity/2, target) //Difference is it does half the damage to contents from explosion, to make fireball not completely instakill + CHECK_TICK + +/obj/structure/closet/decay/proc/unmagify() + icon_state = weakened_icon + update_icon() + addtimer(CALLBACK(src, .proc/decay), 15 SECONDS) + icon_welded = "welded" + /obj/item/projectile/magic/aoe name = "Area Bolt" desc = "What the fuck does this do?!" @@ -345,9 +431,10 @@ if(proxdet) for(var/mob/living/L in range(1, get_turf(src))) if(L.stat != DEAD && L != firer && !L.anti_magic_check()) - return Collide(L) + return Bump(L) ..() + /obj/item/projectile/magic/aoe/lightning name = "lightning bolt" icon_state = "tesla_projectile" //Better sprites are REALLY needed and appreciated!~ @@ -423,3 +510,5 @@ var/turf/T = get_turf(target) for(var/i=0, i<50, i+=10) addtimer(CALLBACK(GLOBAL_PROC, .proc/explosion, T, -1, exp_heavy, exp_light, exp_flash, FALSE, FALSE, exp_fire), i) + + diff --git a/code/modules/projectiles/projectile/special/gravity.dm b/code/modules/projectiles/projectile/special/gravity.dm index 89f753d36d..fdc6b92ec9 100644 --- a/code/modules/projectiles/projectile/special/gravity.dm +++ b/code/modules/projectiles/projectile/special/gravity.dm @@ -13,7 +13,7 @@ /obj/item/projectile/gravityrepulse/Initialize() . = ..() - var/obj/item/ammo_casing/energy/gravityrepulse/C = loc + var/obj/item/ammo_casing/energy/gravity/repulse/C = loc if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items power = min(C.gun.power, 15) @@ -44,7 +44,7 @@ /obj/item/projectile/gravityattract/Initialize() . = ..() - var/obj/item/ammo_casing/energy/gravityattract/C = loc + var/obj/item/ammo_casing/energy/gravity/attract/C = loc if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items power = min(C.gun.power, 15) @@ -74,7 +74,7 @@ /obj/item/projectile/gravitychaos/Initialize() . = ..() - var/obj/item/ammo_casing/energy/gravitychaos/C = loc + var/obj/item/ammo_casing/energy/gravity/chaos/C = loc if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items power = min(C.gun.power, 15) diff --git a/code/modules/projectiles/projectile/special/hallucination.dm b/code/modules/projectiles/projectile/special/hallucination.dm index e158ed89f0..f65ebce51f 100644 --- a/code/modules/projectiles/projectile/special/hallucination.dm +++ b/code/modules/projectiles/projectile/special/hallucination.dm @@ -33,7 +33,7 @@ QDEL_NULL(fake_icon) return ..() -/obj/item/projectile/hallucination/Collide(atom/A) +/obj/item/projectile/hallucination/Bump(atom/A) if(!ismob(A)) if(hal_hitsound_wall) hal_target.playsound_local(loc, hal_hitsound_wall, 40, 1) @@ -169,7 +169,7 @@ hal_target.Knockdown(100) hal_target.stuttering += 20 if(hal_target.dna && hal_target.dna.check_mutation(HULK)) - hal_target.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + hal_target.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ), forced = "hulk") else if((hal_target.status_flags & CANKNOCKDOWN) && !hal_target.has_trait(TRAIT_STUNIMMUNE)) addtimer(CALLBACK(hal_target, /mob/living/carbon.proc/do_jitter_animation, 20), 5) diff --git a/code/modules/projectiles/projectile/special/meteor.dm b/code/modules/projectiles/projectile/special/meteor.dm index f4e60998e1..a92f141399 100644 --- a/code/modules/projectiles/projectile/special/meteor.dm +++ b/code/modules/projectiles/projectile/special/meteor.dm @@ -7,7 +7,7 @@ nodamage = 1 flag = "bullet" -/obj/item/projectile/meteor/Collide(atom/A) +/obj/item/projectile/meteor/Bump(atom/A) if(A == firer) forceMove(A.loc) return diff --git a/code/modules/projectiles/projectile/special/wormhole.dm b/code/modules/projectiles/projectile/special/wormhole.dm index 53f766a7d5..07b56a133f 100644 --- a/code/modules/projectiles/projectile/special/wormhole.dm +++ b/code/modules/projectiles/projectile/special/wormhole.dm @@ -4,13 +4,13 @@ hitsound = "sparks" damage = 0 nodamage = TRUE - hitscan = TRUE pass_flags = PASSGLASS | PASSTABLE | PASSGRILLE | PASSMOB var/obj/item/gun/energy/wormhole_projector/gun color = "#33CCFF" tracer_type = /obj/effect/projectile/tracer/wormhole impact_type = /obj/effect/projectile/impact/wormhole muzzle_type = /obj/effect/projectile/muzzle/wormhole + hitscan = TRUE /obj/item/projectile/beam/wormhole/orange name = "orange bluespace beam" @@ -21,7 +21,9 @@ if(casing) gun = casing.gun + /obj/item/projectile/beam/wormhole/on_hit(atom/target) if(!gun) qdel(src) + return gun.create_portal(src, get_turf(src)) diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 2def04cc66..8d3d704f79 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -550,8 +550,8 @@ if(!D) WARNING("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])") return FALSE - - update_total() + + update_total() var/cached_total = total_volume if(cached_total + amount > maximum_volume) amount = (maximum_volume - cached_total) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen. @@ -594,14 +594,14 @@ if(data) R.data = data R.on_new(data) - + + if(isliving(my_atom)) + R.on_mob_add(my_atom) //Must occur befor it could posibly run on_mob_delete update_total() if(my_atom) my_atom.on_reagent_change(ADD_REAGENT) if(!no_react) handle_reactions() - if(isliving(my_atom)) - R.on_mob_add(my_atom) return TRUE /datum/reagents/proc/add_reagent_list(list/list_reagents, list/data=null) // Like add_reagent but you can enter a list. Format it like this: list("toxin" = 10, "beer" = 15) diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 80e4453c7d..94cb2cd6e3 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -139,7 +139,7 @@ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) - ui = new(user, src, ui_key, "chem_dispenser", name, 550, 550, master_ui, state) + ui = new(user, src, ui_key, "chem_dispenser", name, 565, 550, master_ui, state) if(user.hallucinating()) ui.set_autoupdate(FALSE) //to not ruin the immersion by constantly changing the fake chemicals ui.open() @@ -278,7 +278,7 @@ var/chemid = reagent[1] visible_message("[src] buzzes.", "You hear a faint buzz.") to_chat(usr, "[src] cannot find Chemical ID: [chemid]!") - playsound(src, "sound/machines/buzz-two.ogg", 50, 1) + playsound(src, 'sound/machines/buzz-two.ogg', 50, 1) return if (resmismatch && alert("[src] is not yet capable of replicating this recipe with the precision it needs, do you want to save it anyway?",, "Yes","No") == "No") return @@ -384,6 +384,16 @@ final_list += list(avoid_assoc_duplicate_keys(fuck[1],key_list) = text2num(fuck[2])) return final_list +/obj/machinery/chem_dispenser/drinks/Initialize() + . = ..() + AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE) + +/obj/machinery/chem_dispenser/drinks/setDir() + var/old = dir + . = ..() + if(dir != old) + update_icon() // the beaker needs to be re-positioned if we rotate + /obj/machinery/chem_dispenser/drinks/display_beaker() var/mutable_appearance/b_o = beaker_overlay || mutable_appearance(icon, "disp_beaker") switch(dir) @@ -413,6 +423,7 @@ circuit = /obj/item/circuitboard/machine/chem_dispenser/drinks working_state = null nopower_state = null + pass_flags = PASSTABLE dispensable_reagents = list( "water", "ice", @@ -444,6 +455,24 @@ "tirizene" ) +/obj/machinery/chem_dispenser/drinks/fullupgrade //fully ugpraded stock parts, emagged + desc = "Contains a large reservoir of soft drinks. This model has had its safeties shorted out." + obj_flags = CAN_BE_HIT | EMAGGED + flags_1 = NODECONSTRUCT_1 + +/obj/machinery/chem_dispenser/drinks/fullupgrade/Initialize() + . = ..() + dispensable_reagents |= emagged_reagents //adds emagged reagents + component_parts = list() + component_parts += new /obj/item/circuitboard/machine/chem_dispenser/drinks(null) + component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null) + component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null) + component_parts += new /obj/item/stock_parts/capacitor/quadratic(null) + component_parts += new /obj/item/stock_parts/manipulator/femto(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stock_parts/cell/bluespace(null) + RefreshParts() + /obj/machinery/chem_dispenser/drinks/beer name = "booze dispenser" desc = "Contains a large reservoir of the good stuff." @@ -477,6 +506,23 @@ "fernet" ) +/obj/machinery/chem_dispenser/drinks/beer/fullupgrade //fully ugpraded stock parts, emagged + desc = "Contains a large reservoir of the good stuff. This model has had its safeties shorted out." + obj_flags = CAN_BE_HIT | EMAGGED + flags_1 = NODECONSTRUCT_1 + +/obj/machinery/chem_dispenser/drinks/beer/fullupgrade/Initialize() + . = ..() + dispensable_reagents |= emagged_reagents //adds emagged reagents + component_parts = list() + component_parts += new /obj/item/circuitboard/machine/chem_dispenser/drinks/beer(null) + component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null) + component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null) + component_parts += new /obj/item/stock_parts/capacitor/quadratic(null) + component_parts += new /obj/item/stock_parts/manipulator/femto(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stock_parts/cell/bluespace(null) + RefreshParts() /obj/machinery/chem_dispenser/mutagen name = "mutagen dispenser" @@ -488,6 +534,8 @@ /obj/machinery/chem_dispenser/mutagensaltpeter name = "botanical chemical dispenser" desc = "Creates and dispenses chemicals useful for botany." + flags_1 = NODECONSTRUCT_1 + dispensable_reagents = list( "mutagen", "saltpetre", @@ -502,10 +550,8 @@ "ammonia", "ash", "diethylamine") - -/obj/machinery/chem_dispenser/fullupgrade //fully upgraded stock parts - -/obj/machinery/chem_dispenser/fullupgrade/Initialize() + +/obj/machinery/chem_dispenser/mutagensaltpeter/Initialize() . = ..() component_parts = list() component_parts += new /obj/item/circuitboard/machine/chem_dispenser(null) @@ -516,3 +562,21 @@ component_parts += new /obj/item/stack/sheet/glass(null) component_parts += new /obj/item/stock_parts/cell/bluespace(null) RefreshParts() + +/obj/machinery/chem_dispenser/fullupgrade //fully ugpraded stock parts, emagged + desc = "Creates and dispenses chemicals. This model has had its safeties shorted out." + obj_flags = CAN_BE_HIT | EMAGGED + flags_1 = NODECONSTRUCT_1 + +/obj/machinery/chem_dispenser/fullupgrade/Initialize() + . = ..() + dispensable_reagents |= emagged_reagents //adds emagged reagents + component_parts = list() + component_parts += new /obj/item/circuitboard/machine/chem_dispenser(null) + component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null) + component_parts += new /obj/item/stock_parts/matter_bin/bluespace(null) + component_parts += new /obj/item/stock_parts/capacitor/quadratic(null) + component_parts += new /obj/item/stock_parts/manipulator/femto(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stock_parts/cell/bluespace(null) + RefreshParts() diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm index 25385ca2ff..0e482f8cec 100644 --- a/code/modules/reagents/chemistry/machinery/pandemic.dm +++ b/code/modules/reagents/chemistry/machinery/pandemic.dm @@ -190,7 +190,7 @@ if("create_culture_bottle") var/id = get_virus_id_by_index(text2num(params["index"])) var/datum/disease/advance/A = SSdisease.archive_diseases[id] - if(!A.mutable) + if(!istype(A) || !A.mutable) to_chat(usr, "ERROR: Cannot replicate virus strain.") return A = A.Copy() diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm index a8b0919906..4d60655488 100644 --- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm +++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm @@ -85,7 +85,7 @@ var/units = RC.reagents.trans_to(src, RC.amount_per_transfer_from_this) if(units) to_chat(user, "You transfer [units] units of the solution to [src].") - add_logs(usr, src, "has added [english_list(RC.reagents.reagent_list)] to [src]") + log_combat(usr, src, "has added [english_list(RC.reagents.reagent_list)] to [src]") return if(default_unfasten_wrench(user, I, 40)) on = FALSE @@ -144,7 +144,7 @@ if(on) message_admins("[ADMIN_LOOKUPFLW(usr)] activated a smoke machine that contains [english_list(reagents.reagent_list)] at [ADMIN_VERBOSEJMP(src)].") log_game("[key_name(usr)] activated a smoke machine that contains [english_list(reagents.reagent_list)] at [AREACOORD(src)].") - add_logs(usr, src, "has activated [src] which contains [english_list(reagents.reagent_list)] at [AREACOORD(src)].") + log_combat(usr, src, "has activated [src] which contains [english_list(reagents.reagent_list)] at [AREACOORD(src)].") if("goScreen") screen = params["screen"] . = TRUE diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index 6f65c33ea1..04f2a00302 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -88,29 +88,29 @@ /datum/reagent/proc/overdose_start(mob/living/M) to_chat(M, "You feel like you took too much of [name]!") - SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/overdose, name) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/overdose, name) return /datum/reagent/proc/addiction_act_stage1(mob/living/M) - SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/withdrawal_light, name) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/withdrawal_light, name) if(prob(30)) to_chat(M, "You feel like having some [name] right about now.") return /datum/reagent/proc/addiction_act_stage2(mob/living/M) - SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/withdrawal_medium, name) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/withdrawal_medium, name) if(prob(30)) to_chat(M, "You feel like you need [name]. You just can't get enough.") return /datum/reagent/proc/addiction_act_stage3(mob/living/M) - SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/withdrawal_severe, name) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/withdrawal_severe, name) if(prob(30)) to_chat(M, "You have an intense craving for [name].") return /datum/reagent/proc/addiction_act_stage4(mob/living/M) - SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/withdrawal_critical, name) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/withdrawal_critical, name) if(prob(30)) to_chat(M, "You're not feeling good at all! You really need some [name].") return diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm index 3e3282fb89..f9dbd1928b 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm @@ -153,6 +153,7 @@ All effects don't start immediately, but rather get worse over time; the rate is color = "#102000" // rgb: 16, 32, 0 nutriment_factor = 1 * REAGENTS_METABOLISM boozepwr = 80 + quality = DRINK_GOOD overdose_threshold = 60 addiction_threshold = 30 taste_description = "jitters and death" @@ -249,6 +250,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Made for a woman, strong enough for a man." color = "#666340" // rgb: 102, 99, 64 boozepwr = 10 + quality = DRINK_FANTASTIC taste_description = "dryness" glass_icon_state = "threemileislandglass" glass_name = "Three Mile Island Ice Tea" @@ -323,6 +325,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "An alcoholic beverage from Space China, made by infusing lizard tails in ethanol." color = "#7E4043" // rgb: 126, 64, 67 boozepwr = 45 + quality = DRINK_FANTASTIC taste_description = "scaley sweetness" /datum/reagent/consumable/ethanol/grappa @@ -394,6 +397,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "100 proof cinnamon schnapps, made for alcoholic teen girls on spring break." color = "#FFFF91" // rgb: 255, 255, 145 boozepwr = 25 + quality = DRINK_VERYGOOD taste_description = "burning cinnamon" glass_icon_state = "goldschlagerglass" glass_name = "glass of goldschlager" @@ -406,6 +410,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Tequila with silver in it, a favorite of alcoholic women in the club scene." color = "#585840" // rgb: 88, 88, 64 boozepwr = 60 + quality = DRINK_VERYGOOD taste_description = "metallic and expensive" glass_icon_state = "patronglass" glass_name = "glass of patron" @@ -418,6 +423,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "An all time classic, mild cocktail." color = "#664300" // rgb: 102, 67, 0 boozepwr = 25 + quality = DRINK_NICE taste_description = "mild and tart" glass_icon_state = "gintonicglass" glass_name = "Gin and Tonic" @@ -429,6 +435,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Rum, mixed with cola." taste_description = "cola" boozepwr = 40 + quality = DRINK_NICE color = "#3E1B00" glass_icon_state = "whiskeycolaglass" glass_name = "Rum and Coke" @@ -440,6 +447,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Viva la Revolucion! Viva Cuba Libre!" color = "#3E1B00" // rgb: 62, 27, 0 boozepwr = 50 + quality = DRINK_GOOD taste_description = "a refreshing marriage of citrus and rum" glass_icon_state = "cubalibreglass" glass_name = "Cuba Libre" @@ -460,17 +468,20 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Whiskey, mixed with cola. Surprisingly refreshing." color = "#3E1B00" // rgb: 62, 27, 0 boozepwr = 70 + quality = DRINK_NICE taste_description = "cola" glass_icon_state = "whiskeycolaglass" glass_name = "whiskey cola" glass_desc = "An innocent-looking mixture of cola and Whiskey. Delicious." + /datum/reagent/consumable/ethanol/martini name = "Classic Martini" id = "martini" description = "Vermouth with Gin. Not quite how 007 enjoyed it, but still delicious." color = "#664300" // rgb: 102, 67, 0 boozepwr = 60 + quality = DRINK_NICE taste_description = "dry class" glass_icon_state = "martiniglass" glass_name = "Classic Martini" @@ -482,6 +493,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Vodka with Gin. Not quite how 007 enjoyed it, but still delicious." color = "#664300" // rgb: 102, 67, 0 boozepwr = 65 + quality = DRINK_NICE taste_description = "shaken, not stirred" glass_icon_state = "martiniglass" glass_name = "Vodka martini" @@ -493,6 +505,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "That's just, like, your opinion, man..." color = "#A68340" // rgb: 166, 131, 64 boozepwr = 50 + quality = DRINK_GOOD taste_description = "bitter cream" glass_icon_state = "whiterussianglass" glass_name = "White Russian" @@ -504,6 +517,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Vodka, mixed with plain ol' orange juice. The result is surprisingly delicious." color = "#A68310" // rgb: 166, 131, 16 boozepwr = 55 + quality = DRINK_NICE taste_description = "oranges" glass_icon_state = "screwdriverglass" glass_name = "Screwdriver" @@ -531,6 +545,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A strange yet pleasurable mixture made of vodka, tomato and lime juice. Or at least you THINK the red stuff is tomato juice." color = "#664300" // rgb: 102, 67, 0 boozepwr = 55 + quality = DRINK_GOOD taste_description = "tomatoes with a hint of lime" glass_icon_state = "bloodymaryglass" glass_name = "Bloody Mary" @@ -547,6 +562,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "It's just as effective as Dutch-Courage!" color = "#664300" // rgb: 102, 67, 0 boozepwr = 80 + quality = DRINK_NICE taste_description = "alcoholic bravery" glass_icon_state = "bravebullglass" glass_name = "Brave Bull" @@ -570,6 +586,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Tequila, Grenadine, and Orange Juice." color = "#FFE48C" // rgb: 255, 228, 140 boozepwr = 45 + quality = DRINK_GOOD taste_description = "oranges with a hint of pomegranate" glass_icon_state = "tequilasunriseglass" glass_name = "tequila Sunrise" @@ -598,6 +615,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "This thing is ON FIRE! CALL THE DAMN SHUTTLE!" color = "#664300" // rgb: 102, 67, 0 boozepwr = 25 + quality = DRINK_VERYGOOD taste_description = "spicy toxins" glass_icon_state = "toxinsspecialglass" glass_name = "Toxins Special" @@ -614,6 +632,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Drink this and prepare for the LAW." color = "#664300" // rgb: 102, 67, 0 boozepwr = 90 //THE FIST OF THE LAW IS STRONG AND HARD + quality = DRINK_GOOD metabolization_rate = 0.8 taste_description = "JUSTICE" glass_icon_state = "beepskysmashglass" @@ -633,6 +652,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Whiskey-imbued cream, what else would you expect from the Irish?" color = "#664300" // rgb: 102, 67, 0 boozepwr = 70 + quality = DRINK_NICE taste_description = "creamy alcohol" glass_icon_state = "irishcreamglass" glass_name = "Irish Cream" @@ -644,6 +664,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Beer and Ale, brought together in a delicious mix. Intended for true men only." color = "#664300" // rgb: 102, 67, 0 boozepwr = 100 //For the manly only + quality = DRINK_NICE taste_description = "hair on your chest and your chin" glass_icon_state = "manlydorfglass" glass_name = "The Manly Dorf" @@ -670,6 +691,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "The liquor cabinet, brought together in a delicious mix. Intended for middle-aged alcoholic women only." color = "#664300" // rgb: 102, 67, 0 boozepwr = 35 + quality = DRINK_VERYGOOD taste_description = "a mixture of cola and alcohol" glass_icon_state = "longislandicedteaglass" glass_name = "Long Island Iced Tea" @@ -693,6 +715,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Coffee, Irish Cream, and cognac. You will get bombed." color = "#664300" // rgb: 102, 67, 0 boozepwr = 85 + quality = DRINK_GOOD taste_description = "angry and irish" glass_icon_state = "b52glass" glass_name = "B-52" @@ -708,6 +731,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Coffee, and alcohol. More fun than a Mimosa to drink in the morning." color = "#664300" // rgb: 102, 67, 0 boozepwr = 35 + quality = DRINK_NICE taste_description = "giving up on the day" glass_icon_state = "irishcoffeeglass" glass_name = "Irish Coffee" @@ -719,6 +743,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "On the rocks with salt on the rim. Arriba~!" color = "#8CFF8C" // rgb: 140, 255, 140 boozepwr = 35 + quality = DRINK_NICE taste_description = "dry and salty" glass_icon_state = "margaritaglass" glass_name = "Margarita" @@ -730,6 +755,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "For the lactose-intolerant. Still as classy as a White Russian." color = "#360000" // rgb: 54, 0, 0 boozepwr = 70 + quality = DRINK_NICE taste_description = "bitterness" glass_icon_state = "blackrussianglass" glass_name = "Black Russian" @@ -742,6 +768,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "The Detective's undercover drink of choice. He never could stomach gin..." color = "#664300" // rgb: 102, 67, 0 boozepwr = 30 + quality = DRINK_NICE taste_description = "mild dryness" glass_icon_state = "manhattanglass" glass_name = "Manhattan" @@ -754,6 +781,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A scientist's drink of choice, for pondering ways to blow up the station." color = "#664300" // rgb: 102, 67, 0 boozepwr = 45 + quality = DRINK_VERYGOOD taste_description = "death, the destroyer of worlds" glass_icon_state = "proj_manhattanglass" glass_name = "Manhattan Project" @@ -770,6 +798,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "For the more refined griffon." color = "#664300" // rgb: 102, 67, 0 boozepwr = 70 + quality = DRINK_NICE taste_description = "soda" glass_icon_state = "whiskeysodaglass2" glass_name = "whiskey soda" @@ -781,6 +810,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "The ultimate refreshment. Not what it sounds like." color = "#664300" // rgb: 102, 67, 0 boozepwr = 35 + quality = DRINK_NICE taste_description = "Jack Frost's piss" glass_icon_state = "antifreeze" glass_name = "Anti-freeze" @@ -796,6 +826,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Barefoot and pregnant." color = "#664300" // rgb: 102, 67, 0 boozepwr = 45 + quality = DRINK_VERYGOOD taste_description = "creamy berries" glass_icon_state = "b&p" glass_name = "Barefoot" @@ -815,6 +846,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A cold refreshment." color = "#FFFFFF" // rgb: 255, 255, 255 boozepwr = 35 + quality = DRINK_NICE taste_description = "refreshing cold" glass_icon_state = "snowwhite" glass_name = "Snow White" @@ -826,6 +858,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "AHHHH!!!!" color = "#820000" // rgb: 130, 0, 0 boozepwr = 75 + quality = DRINK_VERYGOOD taste_description = "sweet tasting iron" glass_icon_state = "demonsblood" glass_name = "Demons Blood" @@ -837,6 +870,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Creepy time!" color = "#A68310" // rgb: 166, 131, 16 boozepwr = 70 + quality = DRINK_VERYGOOD taste_description = "bitter iron" glass_icon_state = "devilskiss" glass_name = "Devils Kiss" @@ -848,6 +882,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "For when a gin and tonic isn't Russian enough." color = "#0064C8" // rgb: 0, 100, 200 boozepwr = 70 + quality = DRINK_NICE taste_description = "tart bitterness" glass_icon_state = "vodkatonicglass" glass_name = "vodka and tonic" @@ -860,6 +895,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Refreshingly lemony, deliciously dry." color = "#664300" // rgb: 102, 67, 0 boozepwr = 45 + quality = DRINK_GOOD taste_description = "dry, tart lemons" glass_icon_state = "ginfizzglass" glass_name = "gin fizz" @@ -872,6 +908,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Tropical cocktail." color = "#FF7F3B" // rgb: 255, 127, 59 boozepwr = 35 + quality = DRINK_GOOD taste_description = "lime and orange" glass_icon_state = "bahama_mama" glass_name = "Bahama Mama" @@ -883,6 +920,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A blue-space beverage!" color = "#2E6671" // rgb: 46, 102, 113 boozepwr = 35 + quality = DRINK_VERYGOOD taste_description = "concentrated matter" glass_icon_state = "singulo" glass_name = "Singulo" @@ -894,6 +932,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A spicy Vodka! Might be a little hot for the little guys!" color = "#664300" // rgb: 102, 67, 0 boozepwr = 70 + quality = DRINK_GOOD taste_description = "hot and spice" glass_icon_state = "sbitenglass" glass_name = "Sbiten" @@ -909,6 +948,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "The true Viking drink! Even though it has a strange red color." color = "#C73C00" // rgb: 199, 60, 0 boozepwr = 51 //Red drinks are stronger + quality = DRINK_GOOD taste_description = "sweet and salty alcohol" glass_icon_state = "red_meadglass" glass_name = "Red Mead" @@ -921,6 +961,7 @@ All effects don't start immediately, but rather get worse over time; the rate is color = "#664300" // rgb: 102, 67, 0 nutriment_factor = 1 * REAGENTS_METABOLISM boozepwr = 50 + quality = DRINK_NICE taste_description = "sweet, sweet alcohol" glass_icon_state = "meadglass" glass_name = "Mead" @@ -959,6 +1000,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "So very, very, very good." color = "#664300" // rgb: 102, 67, 0 boozepwr = 35 + quality = DRINK_VERYGOOD taste_description = "sweet 'n creamy" glass_icon_state = "aloe" glass_name = "Aloe" @@ -970,6 +1012,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A nice, strangely named drink." color = "#664300" // rgb: 102, 67, 0 boozepwr = 40 + quality = DRINK_GOOD taste_description = "lemons" glass_icon_state = "andalusia" glass_name = "Andalusia" @@ -981,6 +1024,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A drink made from your allies. Not as sweet as those made from your enemies." color = "#664300" // rgb: 102, 67, 0 boozepwr = 45 + quality = DRINK_NICE taste_description = "bitter yet free" glass_icon_state = "alliescocktail" glass_name = "Allies cocktail" @@ -992,6 +1036,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A drink for the daring, can be deadly if incorrectly prepared!" color = "#365000" // rgb: 54, 80, 0 boozepwr = 80 + quality = DRINK_VERYGOOD taste_description = "stomach acid" glass_icon_state = "acidspitglass" glass_name = "Acid Spit" @@ -1003,6 +1048,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Official drink of the Nanotrasen Gun-Club!" color = "#664300" // rgb: 102, 67, 0 boozepwr = 35 + quality = DRINK_GOOD taste_description = "dark and metallic" glass_icon_state = "amasecglass" glass_name = "Amasec" @@ -1014,6 +1060,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "You take a tiny sip and feel a burning sensation..." color = "#2E6671" // rgb: 46, 102, 113 boozepwr = 95 + quality = DRINK_GOOD taste_description = "your brain coming out your nose" glass_icon_state = "changelingsting" glass_name = "Changeling Sting" @@ -1033,6 +1080,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Mmm, tastes like chocolate cake..." color = "#2E6671" // rgb: 46, 102, 113 boozepwr = 25 + quality = DRINK_GOOD taste_description = "delicious anger" glass_icon_state = "irishcarbomb" glass_name = "Irish Car Bomb" @@ -1044,6 +1092,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Tastes like terrorism!" color = "#2E6671" // rgb: 46, 102, 113 boozepwr = 90 + quality = DRINK_GOOD taste_description = "purified antagonism" glass_icon_state = "syndicatebomb" glass_name = "Syndicate Bomb" @@ -1060,6 +1109,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "The surprise is, it's green!" color = "#2E6671" // rgb: 46, 102, 113 boozepwr = 35 + quality = DRINK_VERYGOOD taste_description = "tartness and bananas" glass_icon_state = "erikasurprise" glass_name = "Erika Surprise" @@ -1072,6 +1122,7 @@ All effects don't start immediately, but rather get worse over time; the rate is nutriment_factor = 1 * REAGENTS_METABOLISM color = "#2E6671" // rgb: 46, 102, 113 boozepwr = 65 + quality = DRINK_GOOD taste_description = "a beach" glass_icon_state = "driestmartiniglass" glass_name = "Driest Martini" @@ -1084,6 +1135,7 @@ All effects don't start immediately, but rather get worse over time; the rate is nutriment_factor = 1 * REAGENTS_METABOLISM color = "#FFFF91" // rgb: 255, 255, 140 boozepwr = 60 + quality = DRINK_GOOD taste_description = "a bad joke" glass_icon_state = "bananahonkglass" glass_name = "Banana Honk" @@ -1102,6 +1154,7 @@ All effects don't start immediately, but rather get worse over time; the rate is nutriment_factor = 1 * REAGENTS_METABOLISM color = "#664300" // rgb: 102, 67, 0 boozepwr = 59 //Proof that clowns are better than mimes right here + quality = DRINK_GOOD taste_description = "a pencil eraser" glass_icon_state = "silencerglass" glass_name = "Silencer" @@ -1119,6 +1172,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A weird mix of whiskey and blumpkin juice." color = "#1EA0FF" // rgb: 102, 67, 0 boozepwr = 50 + quality = DRINK_VERYGOOD taste_description = "molasses and a mouthful of pool water" glass_icon_state = "drunkenblumpkin" glass_name = "Drunken Blumpkin" @@ -1130,6 +1184,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Lemon juice/whiskey/sugar mixture. Moderate alcohol content." color = rgb(255, 201, 49) boozepwr = 35 + quality = DRINK_GOOD taste_description = "sour lemons" glass_icon_state = "whiskey_sour" glass_name = "whiskey sour" @@ -1155,6 +1210,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Whiskey sour/iron/uranium mixture resulting in a highly magnetic slurry. Mild alcohol content." //Requires no alcohol to make but has alcohol anyway because ~magic~ color = rgb(255, 91, 15) boozepwr = 10 + quality = DRINK_VERYGOOD metabolization_rate = 0.1 * REAGENTS_METABOLISM taste_description = "charged metal" // the same as teslium, honk honk. glass_icon_state = "fetching_fizz" @@ -1174,6 +1230,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Brave bull/syndicate bomb/absinthe mixture resulting in an energizing beverage. Mild alcohol content." color = rgb(140, 0, 0) boozepwr = 90 + quality = DRINK_VERYGOOD metabolization_rate = 0.4 * REAGENTS_METABOLISM taste_description = "bravado in the face of disaster" glass_icon_state = "hearty_punch" @@ -1209,6 +1266,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Nuclear proliferation never tasted so good." color = "#666300" // rgb: 102, 99, 0 boozepwr = 0 //custom drunk effect + quality = DRINK_FANTASTIC taste_description = "da bomb" glass_icon_state = "atomicbombglass" glass_name = "Atomic Bomb" @@ -1238,6 +1296,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Whoah, this stuff looks volatile!" color = "#664300" // rgb: 102, 67, 0 boozepwr = 0 //custom drunk effect + quality = DRINK_GOOD taste_description = "your brains smashed out by a lemon wrapped around a gold brick" glass_icon_state = "gargleblasterglass" glass_name = "Pan-Galactic Gargle Blaster" @@ -1266,6 +1325,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A strong neurotoxin that puts the subject into a death-like state." color = "#2E2E61" // rgb: 46, 46, 97 boozepwr = 0 //custom drunk effect + quality = DRINK_VERYGOOD taste_description = "a numbing sensation" glass_icon_state = "neurotoxinglass" glass_name = "Neurotoxin" @@ -1296,6 +1356,7 @@ All effects don't start immediately, but rather get worse over time; the rate is color = "#664300" // rgb: 102, 67, 0 nutriment_factor = 0 boozepwr = 0 //custom drunk effect + quality = DRINK_FANTASTIC metabolization_rate = 0.2 * REAGENTS_METABOLISM taste_description = "giving peace a chance" glass_icon_state = "hippiesdelightglass" @@ -1341,6 +1402,7 @@ All effects don't start immediately, but rather get worse over time; the rate is color = "#fcfdc6" // rgb: 252, 253, 198 nutriment_factor = 2 * REAGENTS_METABOLISM boozepwr = 1 + quality = DRINK_VERYGOOD taste_description = "custard and alcohol" glass_icon_state = "glass_yellow" glass_name = "eggnog" @@ -1353,6 +1415,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Side effects include self-mutilation and hoarding plasteel." color = RUNE_COLOR_DARKRED boozepwr = 10 + quality = DRINK_FANTASTIC taste_description = "bloody" glass_icon_state = "narsour" glass_name = "Nar'Sour" @@ -1402,6 +1465,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Kicks just as hard as licking the powercell on a baton, but tastier." color = "#cc0000" boozepwr = 35 + quality = DRINK_GOOD taste_description = "an invigorating bitter freshness which suffuses your being; no enemy of the station will go unrobusted this day" glass_icon_state = "quadruple_sec" glass_name = "Quadruple Sec" @@ -1420,6 +1484,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Law, Order, Alcohol, and Police Brutality distilled into one single elixir of JUSTICE." color = "#ff3300" boozepwr = 80 + quality = DRINK_FANTASTIC taste_description = "THE LAW" glass_icon_state = "quintuple_sec" glass_name = "Quintuple Sec" @@ -1441,6 +1506,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A fresh and sweet dessert shooter. Difficult to look manly while drinking this." color = "00ff00" boozepwr = 25 + quality = DRINK_GOOD taste_description = "chocolate and mint dancing around your mouth" glass_icon_state = "grasshopper" glass_name = "Grasshopper" @@ -1452,6 +1518,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A snappy way to end the day." color = "ccff99" boozepwr = 25 + quality = DRINK_NICE taste_description = "a slap on the face in the best possible way" glass_icon_state = "stinger" glass_name = "Stinger" @@ -1463,6 +1530,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Soothing hot herbal brew with restorative properties. Hints of citrus and berry flavors." color = "#00FFFF" boozepwr = 30 + quality = DRINK_FANTASTIC taste_description = "hot herbal brew with a hint of fruit" metabolization_rate = 2 * REAGENTS_METABOLISM //0.8u per tick glass_icon_state = "bastion_bourbon" @@ -1518,6 +1586,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Bubbly, classy, and undoubtedly strong - a Glitch City classic." color = "#FFEAC4" boozepwr = 90 //classy hooch, essentially, but lower pwr to make up for slightly easier access + quality = DRINK_GOOD taste_description = "ethylic alcohol with a hint of sugar" glass_icon_state = "fringe_weaver" glass_name = "Fringe Weaver" @@ -1529,6 +1598,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Sweet, light, and fruity - as girly as it gets." color = "#FF226C" boozepwr = 10 + quality = DRINK_GOOD taste_description = "your arteries clogging with sugar" nutriment_factor = 2 * REAGENTS_METABOLISM glass_icon_state = "sugar_rush" @@ -1546,6 +1616,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Sour, bitter, and smashingly sobering." color = "#5BD231" boozepwr = -10 //sobers you up - ideally, one would drink to get hit with brute damage now to avoid alcohol problems later + quality = DRINK_VERYGOOD taste_description = "a bitter SPIKE with a sour aftertaste" glass_icon_state = "crevice_spike" glass_name = "Crevice Spike" @@ -1572,6 +1643,7 @@ All effects don't start immediately, but rather get worse over time; the rate is color = "#45ca7a" taste_description = "mint and chocolate" boozepwr = 25 + quality = DRINK_GOOD glass_icon_state = "peppermint_patty" glass_name = "Peppermint Patty" glass_desc = "A boozy minty hot cocoa that warms your belly on a cold night." @@ -1586,6 +1658,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Named after a Greek hero, this mix is said to embolden a user's shield as if they were in a phalanx." color = "#F5E9D3" boozepwr = 80 + quality = DRINK_GOOD taste_description = "bitter, creamy cacao" glass_icon_state = "alexander" glass_name = "Alexander" @@ -1618,6 +1691,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "The one ride you'll gladly give up the wheel for." color = "#FFC55B" boozepwr = 80 + quality = DRINK_GOOD taste_description = "delicious freedom" glass_icon_state = "sidecar" glass_name = "Sidecar" @@ -1629,6 +1703,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A provocatively named classic. Funny enough, doctors recommend drinking it before taking a nap." color = "#F4C35A" boozepwr = 80 + quality = DRINK_GOOD taste_description = "seduction" glass_icon_state = "between_the_sheets" glass_name = "Between the Sheets" @@ -1653,6 +1728,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "Divinely windy." color = "#EEF191" boozepwr = 60 + quality = DRINK_GOOD taste_description = "divine windiness" glass_icon_state = "kamikaze" glass_name = "Kamikaze" @@ -1664,6 +1740,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A drink that looks as refreshing as it tastes." color = "#DFFAD9" boozepwr = 30 + quality = DRINK_GOOD taste_description = "refreshing mint" glass_icon_state = "mojito" glass_name = "Mojito" @@ -1692,12 +1769,13 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A very popular and bittersweet digestif, ideal after a heavy meal. Best served on a sawed-off cola bottle as per tradition." color = "#390600" // rgb: 57, 6, 0 boozepwr = 25 + quality = DRINK_NICE taste_description = "sweet relief" glass_icon_state = "godlyblend" glass_name = "glass of fernet cola" glass_desc = "A sawed-off cola bottle filled with Fernet Cola. Nothing better after eating like a lardass." -/datum/reagent/consumable/ethanol/fernetcola/on_mob_life(mob/living/carbon/M) +/datum/reagent/consumable/ethanol/fernet_cola/on_mob_life(mob/living/carbon/M) if(M.nutrition <= NUTRITION_LEVEL_STARVING) M.adjustToxLoss(0.5*REM, 0) M.nutrition = max(M.nutrition - 3, 0) @@ -1711,6 +1789,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "What if the Manhattan coctail ACTUALLY used a bitter herb liquour? Helps you sobers up." //also causes a bit of stamina damage to symbolize the afterdrink lazyness color = "#CA933F" // rgb: 202, 147, 63 boozepwr = -10 + quality = DRINK_NICE taste_description = "a sweet sobering mix" glass_icon_state = "fanciulli" glass_name = "glass of fanciulli" @@ -1734,6 +1813,7 @@ All effects don't start immediately, but rather get worse over time; the rate is description = "A refreshing mixture of bitter Fernet with mint creme liquour." color = "#4B5746" // rgb: 75, 87, 70 boozepwr = 35 + quality = DRINK_GOOD taste_description = "a bitter freshness" glass_icon_state= "minted_fernet" glass_name = "glass of branca menta" @@ -1749,3 +1829,108 @@ All effects don't start immediately, but rather get worse over time; the rate is M.adjustStaminaLoss(35) . = TRUE ..() + +/datum/reagent/consumable/ethanol/fruit_wine + name = "Fruit Wine" + id = "fruit_wine" + description = "A wine made from grown plants." + color = "#FFFFFF" + boozepwr = 35 + quality = DRINK_GOOD + taste_description = "bad coding" + can_synth = FALSE + var/list/names = list("null fruit" = 1) //Names of the fruits used. Associative list where name is key, value is the percentage of that fruit. + var/list/tastes = list("bad coding" = 1) //List of tastes. See above. + +/datum/reagent/consumable/ethanol/fruit_wine/on_new(list/data) + names = data["names"] + tastes = data["tastes"] + boozepwr = data["boozepwr"] + color = data["color"] + generate_data_info(data) + +/datum/reagent/consumable/ethanol/fruit_wine/on_merge(list/data, amount) + var/diff = (amount/volume) + if(diff < 1) + color = BlendRGB(color, data["color"], diff/2) //The percentage difference over two, so that they take average if equal. + else + color = BlendRGB(color, data["color"], (1/diff)/2) //Adjust so it's always blending properly. + var/oldvolume = volume-amount + + var/list/cachednames = data["names"] + for(var/name in names | cachednames) + names[name] = ((names[name] * oldvolume) + (cachednames[name] * amount)) / volume + + var/list/cachedtastes = data["tastes"] + for(var/taste in tastes | cachedtastes) + tastes[taste] = ((tastes[taste] * oldvolume) + (cachedtastes[taste] * amount)) / volume + + boozepwr *= oldvolume + var/newzepwr = data["boozepwr"] * amount + boozepwr += newzepwr + boozepwr /= volume //Blending boozepwr to volume. + generate_data_info(data) + +/datum/reagent/consumable/ethanol/fruit_wine/proc/generate_data_info(list/data) + var/minimum_percent = 0.15 //Percentages measured between 0 and 1. + var/list/primary_tastes = list() + var/list/secondary_tastes = list() + glass_name = "glass of [name]" + glass_desc = description + for(var/taste in tastes) + switch(tastes[taste]) + if(minimum_percent*2 to INFINITY) + primary_tastes += taste + if(minimum_percent to minimum_percent*2) + secondary_tastes += taste + + var/minimum_name_percent = 0.35 + name = "" + var/list/names_in_order = sortTim(names, /proc/cmp_numeric_dsc, TRUE) + var/named = FALSE + for(var/fruit_name in names) + if(names[fruit_name] >= minimum_name_percent) + name += "[fruit_name] " + named = TRUE + if(named) + name += "wine" + else + name = "mixed [names_in_order[1]] wine" + + var/alcohol_description + switch(boozepwr) + if(120 to INFINITY) + alcohol_description = "suicidally strong" + if(90 to 120) + alcohol_description = "rather strong" + if(70 to 90) + alcohol_description = "strong" + if(40 to 70) + alcohol_description = "rich" + if(20 to 40) + alcohol_description = "mild" + if(0 to 20) + alcohol_description = "sweet" + else + alcohol_description = "watery" //How the hell did you get negative boozepwr? + + var/list/fruits = list() + if(names_in_order.len <= 3) + fruits = names_in_order + else + for(var/i in 1 to 3) + fruits += names_in_order[i] + fruits += "other plants" + var/fruit_list = english_list(fruits) + description = "A [alcohol_description] wine brewed from [fruit_list]." + + var/flavor = "" + if(!primary_tastes.len) + primary_tastes = list("[alcohol_description] alcohol") + flavor += english_list(primary_tastes) + if(secondary_tastes.len) + flavor += ", with a hint of " + flavor += english_list(secondary_tastes) + taste_description = flavor + if(holder.my_atom) + holder.my_atom.on_reagent_change() diff --git a/code/modules/reagents/chemistry/reagents/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drink_reagents.dm index 4d2dba4659..0cbd1f2d85 100644 --- a/code/modules/reagents/chemistry/reagents/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drink_reagents.dm @@ -310,6 +310,7 @@ id = "arnold_palmer" description = "Encourages the patient to go golfing." color = "#FFB766" + quality = DRINK_NICE nutriment_factor = 2 taste_description = "bitter tea" glass_icon_state = "arnold_palmer" @@ -383,6 +384,7 @@ id = "nuka_cola" description = "Cola, cola never changes." color = "#100800" // rgb: 16, 8, 0 + quality = DRINK_VERYGOOD taste_description = "the future" glass_icon_state = "nuka_colaglass" glass_name = "glass of Nuka Cola" @@ -550,6 +552,7 @@ id = "soy_latte" description = "A nice and tasty beverage while you are reading your hippie books." color = "#664300" // rgb: 102, 67, 0 + quality = DRINK_NICE taste_description = "creamy coffee" glass_icon_state = "soy_latte" glass_name = "soy latte" @@ -571,6 +574,7 @@ id = "cafe_latte" description = "A nice, strong and tasty beverage while you are reading." color = "#664300" // rgb: 102, 67, 0 + quality = DRINK_NICE taste_description = "bitter cream" glass_icon_state = "cafe_latte" glass_name = "cafe latte" @@ -592,6 +596,7 @@ id = "doctorsdelight" description = "A gulp a day keeps the Medibot away! A mixture of juices that heals most damage types fairly quickly at the cost of hunger." color = "#FF8CFF" // rgb: 255, 140, 255 + quality = DRINK_VERYGOOD taste_description = "homely fruit" glass_icon_state = "doctorsdelightglass" glass_name = "Doctor's Delight" @@ -613,6 +618,7 @@ id = "chocolatepudding" description = "A great dessert for chocolate lovers." color = "#800000" + quality = DRINK_VERYGOOD nutriment_factor = 4 * REAGENTS_METABOLISM taste_description = "sweet chocolate" glass_icon_state = "chocolatepudding" @@ -624,6 +630,7 @@ id = "vanillapudding" description = "A great dessert for vanilla lovers." color = "#FAFAD2" + quality = DRINK_VERYGOOD nutriment_factor = 4 * REAGENTS_METABOLISM taste_description = "sweet vanilla" glass_icon_state = "vanillapudding" @@ -635,6 +642,7 @@ id = "cherryshake" description = "A cherry flavored milkshake." color = "#FFB6C1" + quality = DRINK_VERYGOOD nutriment_factor = 4 * REAGENTS_METABOLISM taste_description = "creamy cherry" glass_icon_state = "cherryshake" @@ -646,6 +654,7 @@ id = "bluecherryshake" description = "An exotic milkshake." color = "#00F1FF" + quality = DRINK_VERYGOOD nutriment_factor = 4 * REAGENTS_METABOLISM taste_description = "creamy blue cherry" glass_icon_state = "bluecherryshake" @@ -657,6 +666,7 @@ id = "pumpkin_latte" description = "A mix of pumpkin juice and coffee." color = "#F4A460" + quality = DRINK_VERYGOOD nutriment_factor = 3 * REAGENTS_METABOLISM taste_description = "creamy pumpkin" glass_icon_state = "pumpkin_latte" @@ -668,6 +678,7 @@ id = "gibbfloats" description = "Ice cream on top of a Dr. Gibb glass." color = "#B22222" + quality = DRINK_NICE nutriment_factor = 3 * REAGENTS_METABOLISM taste_description = "creamy cherry" glass_icon_state = "gibbfloats" @@ -693,6 +704,7 @@ id = "triple_citrus" description = "A solution." color = "#C8A5DC" + quality = DRINK_NICE taste_description = "extreme bitterness" glass_icon_state = "triplecitrus" //needs own sprite mine are trash glass_name = "glass of triple citrus" @@ -712,6 +724,7 @@ id = "chocolate_milk" description = "Milk for cool kids." color = "#7D4E29" + quality = DRINK_NICE taste_description = "chocolate milk" /datum/reagent/consumable/menthol diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index 33511c8905..989c5a9b82 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -28,7 +28,7 @@ /datum/reagent/drug/space_drugs/overdose_start(mob/living/M) to_chat(M, "You start tripping hard!") - SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/drugs/overdose, name) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "[id]_overdose", /datum/mood_event/overdose, name) /datum/reagent/drug/space_drugs/overdose_process(mob/living/M) if(M.hallucination < volume && prob(20)) @@ -49,7 +49,7 @@ if(prob(1)) var/smoke_message = pick("You feel relaxed.", "You feel calmed.","You feel alert.","You feel rugged.") to_chat(M, "[smoke_message]") - SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "smoked", /datum/mood_event/drugs/smoked, name) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "smoked", /datum/mood_event/smoked, name) M.AdjustStun(-20, 0) M.AdjustKnockdown(-20, 0) M.AdjustUnconscious(-20, 0) @@ -132,7 +132,7 @@ ..() . = 1 -/datum/reagent/krokodil/addiction_act_stage2(mob/living/M) +/datum/reagent/drug/krokodil/addiction_act_stage2(mob/living/M) if(prob(25)) to_chat(M, "Your skin feels loose...") ..() diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 459cbefaa2..685b37564a 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -13,12 +13,27 @@ taste_description = "generic food" taste_mult = 4 var/nutriment_factor = 1 * REAGENTS_METABOLISM + var/quality = 0 //affects mood, typically higher for mixed drinks with more complex recipes /datum/reagent/consumable/on_mob_life(mob/living/carbon/M) current_cycle++ M.nutrition += nutriment_factor holder.remove_reagent(src.id, metabolization_rate) +/datum/reagent/consumable/reaction_mob(mob/living/M, method=TOUCH, reac_volume) + if(method == INGEST) + if (quality && !M.has_trait(TRAIT_AGEUSIA)) + switch(quality) + if (DRINK_NICE) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "quality_drink", /datum/mood_event/quality_nice) + if (DRINK_GOOD) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "quality_drink", /datum/mood_event/quality_good) + if (DRINK_VERYGOOD) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "quality_drink", /datum/mood_event/quality_verygood) + if (DRINK_FANTASTIC) + SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "quality_drink", /datum/mood_event/quality_fantastic) + return ..() + /datum/reagent/consumable/nutriment name = "Nutriment" id = "nutriment" @@ -491,8 +506,10 @@ /datum/reagent/consumable/flour/reaction_turf(turf/T, reac_volume) if(!isspaceturf(T)) - var/obj/effect/decal/cleanable/reagentdecal = new/obj/effect/decal/cleanable/flour(T) - reagentdecal.reagents.add_reagent("flour", reac_volume) + var/obj/effect/decal/cleanable/flour/reagentdecal = new/obj/effect/decal/cleanable/flour(T) + reagentdecal = locate() in T //Might have merged with flour already there. + if(reagentdecal) + reagentdecal.reagents.add_reagent("flour", reac_volume) /datum/reagent/consumable/cherryjelly name = "Cherry Jelly" @@ -575,7 +592,7 @@ if(iscarbon(M) && (method in list(TOUCH, VAPOR, PATCH))) var/mob/living/carbon/C = M for(var/s in C.surgeries) - var/datum/surgery/S = s + var/datum/surgery/S = s S.success_multiplier = max(0.6, S.success_multiplier) // +60% success probability on each step, compared to bacchus' blessing's ~46% ..() diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 881e694780..aaa7e30de0 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -803,7 +803,7 @@ M.updatehealth() if(M.revive()) M.emote("gasp") - add_logs(M, M, "revived", src) + log_combat(M, M, "revived", src) ..() /datum/reagent/medicine/strange_reagent/on_mob_life(mob/living/carbon/M) @@ -1174,6 +1174,14 @@ color = "#F5F5F5" self_consuming = TRUE +/datum/reagent/medicine/corazone/on_mob_add(mob/living/M) + ..() + M.add_trait(TRAIT_STABLEHEART, id) + +/datum/reagent/medicine/corazone/on_mob_delete(mob/living/M) + M.remove_trait(TRAIT_STABLEHEART, id) + ..() + /datum/reagent/medicine/muscle_stimulant name = "Muscle Stimulant" id = "muscle_stimulant" diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index c3d36b6b6a..d45c1f65f0 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -218,9 +218,9 @@ M.stuttering = 1 M.stuttering = min(M.stuttering+4, 10) M.Dizzy(5) - if(iscultist(M) && prob(8)) - M.say(pick("Av'te Nar'sie","Pa'lid Mors","INO INO ORA ANA","SAT ANA!","Daim'niodeis Arc'iai Le'eones","R'ge Na'sie","Diabo us Vo'iscum","Eld' Mon Nobis")) - if(prob(20)) + if(iscultist(M) && prob(20)) + M.say(pick("Av'te Nar'Sie","Pa'lid Mors","INO INO ORA ANA","SAT ANA!","Daim'niodeis Arc'iai Le'eones","R'ge Na'sie","Diabo us Vo'iscum","Eld' Mon Nobis"), forced = "holy water") + if(prob(10)) M.visible_message("[M] starts having a seizure!", "You have a seizure!") M.Unconscious(120) to_chat(M, "[pick("Your blood is your bond - you are nothing without it", "Do not forget your place", \ @@ -231,7 +231,7 @@ clockwork_say(M, "...[text2ratvar(pick("Engine... your light grows dark...", "Where are you, master?", "He lies rusting in Error...", "Purge all untruths and... and... something..."))]") if("message") to_chat(M, "[pick("Ratvar's illumination of your mind has begun to flicker", "He lies rusting in Reebe, derelict and forgotten. And there he shall stay", \ - "You can't save him. Nothing can save him now", "It seems that Nar-Sie will triumph after all")].") + "You can't save him. Nothing can save him now", "It seems that Nar'Sie will triumph after all")].") if("emote") M.visible_message("[M] [pick("whimpers quietly", "shivers as though cold", "glances around in paranoia")].") if(data >= 60) // 30 units, 135 seconds @@ -414,7 +414,7 @@ else M.visible_message("[M] flexes [M.p_their()] arms.") if(prob(10)) - M.say(pick("Shit was SO cash.", "You are everything bad in the world.", "What sports do you play, other than 'jack off to naked drawn Japanese people?'", "Don’t be a stranger. Just hit me with your best shot.", "My name is John and I hate every single one of you.")) + M.say(pick("Shit was SO cash.", "You are everything bad in the world.", "What sports do you play, other than 'jack off to naked drawn Japanese people?'", "Don’t be a stranger. Just hit me with your best shot.", "My name is John and I hate every single one of you."), forced = "spraytan") ..() return @@ -457,6 +457,13 @@ race = /datum/species/jelly/slime mutationtext = "The pain subsides. Your whole body feels like slime." +/datum/reagent/mutationtoxin/felinid + name = "Felinid Mutation Toxin" + id = "felinidmutationtoxin" + color = "#5EFF3B" //RGB: 94, 255, 59 + race = /datum/species/human/felinid + mutationtext = "The pain subsides. You feel... like a degenerate." + /datum/reagent/mutationtoxin/lizard name = "Lizard Mutation Toxin" id = "lizardmutationtoxin" @@ -1230,17 +1237,16 @@ /datum/reagent/stimulum/on_mob_add(mob/living/L) ..() - L.add_trait(TRAIT_GOTTAGOFAST, id) + L.add_trait(TRAIT_STUNIMMUNE, id) + L.add_trait(TRAIT_SLEEPIMMUNE, id) /datum/reagent/stimulum/on_mob_delete(mob/living/L) - L.remove_trait(TRAIT_GOTTAGOFAST, id) + L.remove_trait(TRAIT_STUNIMMUNE, id) + L.remove_trait(TRAIT_SLEEPIMMUNE, id) ..() -/datum/reagent/stimulum/on_mob_life(mob/living/carbon/M) // Has a speedup, and the anti-stun effects of nicotine. - M.AdjustStun(-20, 0) - M.AdjustKnockdown(-20, 0) - M.AdjustUnconscious(-20, 0) - M.adjustStaminaLoss(-0.5*REM, 0) +/datum/reagent/stimulum/on_mob_life(mob/living/carbon/M) + M.adjustStaminaLoss(-2*REM, 0) current_cycle++ holder.remove_reagent(id, 0.99) //Gives time for the next tick of life(). . = TRUE //Update status effects. @@ -1643,7 +1649,7 @@ /datum/reagent/royal_bee_jelly/on_mob_life(mob/living/carbon/M) if(prob(2)) - M.say(pick("Bzzz...","BZZ BZZ","Bzzzzzzzzzzz...")) + M.say(pick("Bzzz...","BZZ BZZ","Bzzzzzzzzzzz..."), forced = "royal bee jelly") ..() //Misc reagents @@ -1665,7 +1671,7 @@ /datum/reagent/romerol/reaction_mob(mob/living/carbon/human/H, method=TOUCH, reac_volume) // Silently add the zombie infection organ to be activated upon death if(!H.getorganslot(ORGAN_SLOT_ZOMBIE)) - var/obj/item/organ/zombie_infection/ZI = new() + var/obj/item/organ/zombie_infection/nodamage/ZI = new() ZI.Insert(H) ..() @@ -1829,3 +1835,15 @@ if(prob(30)) to_chat(M, "You should sit down and take a rest...") ..() + +/datum/reagent/tranquility + name = "Tranquility" + id = "tranquility" + description = "A highly mutative liquid of unknown origin." + color = "#9A6750" //RGB: 154, 103, 80 + taste_description = "inner peace" + can_synth = FALSE + +/datum/reagent/tranquility/reaction_mob(mob/living/L, method=TOUCH, reac_volume, show_message = 1, touch_protection = 0) + if(method==PATCH || method==INGEST || method==INJECT || (method == VAPOR && prob(min(reac_volume,100)*(1 - touch_protection)))) + L.ForceContractDisease(new /datum/disease/transformation/gondola(), FALSE, TRUE) diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 0700aeb799..26edcb5b73 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -169,6 +169,28 @@ ..() . = 1 +/datum/reagent/toxin/ghoulpowder + name = "Ghoul Powder" + id = "ghoulpowder" + description = "A strong neurotoxin that slows metabolism to a death-like state, while keeping the patient fully active. Causes toxin buildup if used too long." + reagent_state = SOLID + color = "#664700" // rgb: 102, 71, 0 + toxpwr = 0.8 + taste_description = "death" + +/datum/reagent/toxin/ghoulpowder/on_mob_add(mob/living/L) + ..() + L.add_trait(TRAIT_FAKEDEATH, id) + +/datum/reagent/toxin/ghoulpowder/on_mob_delete(mob/living/L) + L.remove_trait(TRAIT_FAKEDEATH, id) + ..() + +/datum/reagent/toxin/ghoulpowder/on_mob_life(mob/living/carbon/M) + M.adjustOxyLoss(1*REM, 0) + ..() + . = 1 + /datum/reagent/toxin/mindbreaker name = "Mindbreaker Toxin" id = "mindbreaker" @@ -405,7 +427,7 @@ /datum/reagent/toxin/formaldehyde name = "Formaldehyde" id = "formaldehyde" - description = "Formaldehyde, on its own, is a fairly weak toxin. It contains trace amounts of Histamine, very rarely making it decay into Histamine.." + description = "Formaldehyde, on its own, is a fairly weak toxin. It contains trace amounts of Histamine, very rarely making it decay into Histamine." reagent_state = LIQUID color = "#B4004B" metabolization_rate = 0.5 * REAGENTS_METABOLISM @@ -605,7 +627,7 @@ /datum/reagent/toxin/amanitin/on_mob_delete(mob/living/M) var/toxdamage = current_cycle*3*REM - M.log_message("has taken [toxdamage] toxin damage from amanitin toxin", INDIVIDUAL_ATTACK_LOG) + M.log_message("has taken [toxdamage] toxin damage from amanitin toxin", LOG_ATTACK) M.adjustToxLoss(toxdamage) ..() diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index 4261dcc421..f2e9baeba4 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -584,7 +584,7 @@ /datum/chemical_reaction/plastic_polymers/on_reaction(datum/reagents/holder, created_volume) var/location = get_turf(holder.my_atom) - for(var/i in 1 to 10) + for(var/i in 1 to created_volume) new /obj/item/stack/sheet/plastic(location) /datum/chemical_reaction/pax diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm index 8b8c009a43..2bb8990eb8 100644 --- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm +++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm @@ -116,6 +116,31 @@ empulse(location, round(created_volume / 12), round(created_volume / 7), 1) holder.clear_reagents() + +/datum/chemical_reaction/beesplosion + name = "Bee Explosion" + id = "beesplosion" + required_reagents = list("honey" = 1, "strange_reagent" = 1, "radium" = 1) + +/datum/chemical_reaction/beesplosion/on_reaction(datum/reagents/holder, created_volume) + var/location = holder.my_atom.drop_location() + if(created_volume < 5) + playsound(location,'sound/effects/sparks1.ogg', 100, TRUE) + else + playsound(location,'sound/creatures/bee.ogg', 100, TRUE) + var/list/beeagents = list() + for(var/X in holder.reagent_list) + var/datum/reagent/R = X + if(required_reagents[R.id]) + continue + beeagents += R + var/bee_amount = round(created_volume * 0.2) + for(var/i in 1 to bee_amount) + var/mob/living/simple_animal/hostile/poison/bees/short/new_bee = new(location) + if(LAZYLEN(beeagents)) + new_bee.assign_reagent(pick(beeagents)) + + /datum/chemical_reaction/stabilizing_agent name = "stabilizing_agent" id = "stabilizing_agent" diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm index c68c0a16b0..db5f7b198c 100644 --- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm +++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm @@ -18,7 +18,7 @@ id = "m_spawn" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/grey - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimespawn/on_reaction(datum/reagents/holder) var/mob/living/simple_animal/slime/S = new(get_turf(holder.my_atom), "grey") @@ -30,7 +30,7 @@ id = "m_inaprov" results = list("epinephrine" = 3) required_reagents = list("water" = 5) - required_other = 1 + required_other = TRUE required_container = /obj/item/slime_extract/grey /datum/chemical_reaction/slime/slimemonkey @@ -38,7 +38,7 @@ id = "m_monkey" required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/grey - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimemonkey/on_reaction(datum/reagents/holder) for(var/i in 1 to 3) @@ -51,7 +51,7 @@ id = "slimetoxin" results = list("slime_toxin" = 1) required_reagents = list("plasma" = 1) - required_other = 1 + required_other = TRUE required_container = /obj/item/slime_extract/green /datum/chemical_reaction/slime/slimehuman @@ -59,7 +59,7 @@ id = "humanmuttoxin" results = list("stablemutationtoxin" = 1) required_reagents = list("blood" = 1) - required_other = 1 + required_other = TRUE required_container = /obj/item/slime_extract/green /datum/chemical_reaction/slime/slimelizard @@ -67,7 +67,7 @@ id = "lizardmuttoxin" results = list("lizardmutationtoxin" = 1) required_reagents = list("radium" = 1) - required_other = 1 + required_other = TRUE required_container = /obj/item/slime_extract/green //Metal @@ -76,7 +76,7 @@ id = "m_metal" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/metal - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimemetal/on_reaction(datum/reagents/holder) var/turf/location = get_turf(holder.my_atom) @@ -89,7 +89,7 @@ id = "m_glass" required_reagents = list("water" = 1) required_container = /obj/item/slime_extract/metal - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimeglass/on_reaction(datum/reagents/holder) var/turf/location = get_turf(holder.my_atom) @@ -103,7 +103,7 @@ id = "m_tele" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/gold - required_other = 1 + required_other = TRUE deletes_extract = FALSE //we do delete, but we don't do so instantly /datum/chemical_reaction/slime/slimemobspawn/on_reaction(datum/reagents/holder) @@ -142,7 +142,7 @@ id = "m_tele2" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/silver - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimebork/on_reaction(datum/reagents/holder) //BORK BORK BORK @@ -184,14 +184,14 @@ results = list("frostoil" = 10) required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/blue - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimestabilizer name = "Slime Stabilizer" id = "m_slimestabilizer" required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/blue - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimestabilizer/on_reaction(datum/reagents/holder) new /obj/item/slimepotion/slime/stabilizer(get_turf(holder.my_atom)) @@ -203,7 +203,7 @@ results = list("fluorosurfactant" = 20, "water" = 20) required_reagents = list("water" = 5) required_container = /obj/item/slime_extract/blue - required_other = 1 + required_other = TRUE //Dark Blue /datum/chemical_reaction/slime/slimefreeze @@ -211,7 +211,7 @@ id = "m_freeze" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/darkblue - required_other = 1 + required_other = TRUE deletes_extract = FALSE /datum/chemical_reaction/slime/slimefreeze/on_reaction(datum/reagents/holder) @@ -234,7 +234,7 @@ id = "m_fireproof" required_reagents = list("water" = 1) required_container = /obj/item/slime_extract/darkblue - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimefireproof/on_reaction(datum/reagents/holder) new /obj/item/slimepotion/fireproof(get_turf(holder.my_atom)) @@ -247,14 +247,14 @@ results = list("capsaicin" = 10) required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/orange - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimefire name = "Slime fire" id = "m_fire" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/orange - required_other = 1 + required_other = TRUE deletes_extract = FALSE /datum/chemical_reaction/slime/slimefire/on_reaction(datum/reagents/holder) @@ -279,7 +279,7 @@ results = list("phosphorus" = 10, "potassium" = 10, "sugar" = 10) required_reagents = list("water" = 5) required_container = /obj/item/slime_extract/orange - required_other = 1 + required_other = TRUE //Yellow /datum/chemical_reaction/slime/slimeoverload @@ -287,7 +287,7 @@ id = "m_emp" required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/yellow - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimeoverload/on_reaction(datum/reagents/holder, created_volume) empulse(get_turf(holder.my_atom), 3, 7) @@ -298,7 +298,7 @@ id = "m_cell" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/yellow - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimecell/on_reaction(datum/reagents/holder, created_volume) new /obj/item/stock_parts/cell/high/slime(get_turf(holder.my_atom)) @@ -309,7 +309,7 @@ id = "m_glow" required_reagents = list("water" = 1) required_container = /obj/item/slime_extract/yellow - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimeglow/on_reaction(datum/reagents/holder) var/turf/T = get_turf(holder.my_atom) @@ -323,7 +323,7 @@ id = "m_steroid" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/purple - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimepsteroid/on_reaction(datum/reagents/holder) new /obj/item/slimepotion/slime/steroid(get_turf(holder.my_atom)) @@ -335,7 +335,7 @@ results = list("regen_jelly" = 5) required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/purple - required_other = 1 + required_other = TRUE //Dark Purple /datum/chemical_reaction/slime/slimeplasma @@ -343,7 +343,7 @@ id = "m_plasma" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/darkpurple - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimeplasma/on_reaction(datum/reagents/holder) new /obj/item/stack/sheet/mineral/plasma(get_turf(holder.my_atom), 3) @@ -355,7 +355,7 @@ id = "m_slimemutator" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/red - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimemutator/on_reaction(datum/reagents/holder) new /obj/item/slimepotion/slime/mutator(get_turf(holder.my_atom)) @@ -366,7 +366,7 @@ id = "m_bloodlust" required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/red - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimebloodlust/on_reaction(datum/reagents/holder) for(var/mob/living/simple_animal/slime/slime in viewers(get_turf(holder.my_atom), null)) @@ -379,7 +379,7 @@ id = "m_speed" required_reagents = list("water" = 1) required_container = /obj/item/slime_extract/red - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimespeed/on_reaction(datum/reagents/holder) new /obj/item/slimepotion/speed(get_turf(holder.my_atom)) @@ -391,7 +391,7 @@ id = "m_potion" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/pink - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/docility/on_reaction(datum/reagents/holder) new /obj/item/slimepotion/slime/docility(get_turf(holder.my_atom)) @@ -402,7 +402,7 @@ id = "m_gender" required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/pink - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/gender/on_reaction(datum/reagents/holder) new /obj/item/slimepotion/genderchange(get_turf(holder.my_atom)) @@ -414,7 +414,7 @@ id = "mutationtoxin2" results = list("amutationtoxin" = 1) required_reagents = list("plasma" = 1) - required_other = 1 + required_other = TRUE required_container = /obj/item/slime_extract/black //Oil @@ -423,7 +423,7 @@ id = "m_explosion" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/oil - required_other = 1 + required_other = TRUE deletes_extract = FALSE /datum/chemical_reaction/slime/slimeexplosion/on_reaction(datum/reagents/holder) @@ -453,7 +453,7 @@ results = list("cornoil" = 10) required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/oil - required_other = 1 + required_other = TRUE //Light Pink /datum/chemical_reaction/slime/slimepotion2 @@ -461,19 +461,31 @@ id = "m_potion2" required_container = /obj/item/slime_extract/lightpink required_reagents = list("plasma" = 1) - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimepotion2/on_reaction(datum/reagents/holder) new /obj/item/slimepotion/slime/sentience(get_turf(holder.my_atom)) ..() +/datum/chemical_reaction/slime/renaming + name = "Renaming Potion" + id = "m_renaming_potion" + required_container = /obj/item/slime_extract/lightpink + required_reagents = list("water" = 1) + required_other = TRUE + +/datum/chemical_reaction/slime/renaming/on_reaction(datum/reagents/holder) + new /obj/item/slimepotion/slime/renaming(holder.my_atom.drop_location()) + ..() + + //Adamantine /datum/chemical_reaction/slime/adamantine name = "Adamantine" id = "adamantine" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/adamantine - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/adamantine/on_reaction(datum/reagents/holder) new /obj/item/stack/sheet/mineral/adamantine(get_turf(holder.my_atom)) @@ -485,7 +497,7 @@ id = "m_floor2" required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/bluespace - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimefloor2/on_reaction(datum/reagents/holder, created_volume) new /obj/item/stack/tile/bluespace(get_turf(holder.my_atom), 25) @@ -497,7 +509,7 @@ id = "m_crystal" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/bluespace - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimecrystal/on_reaction(datum/reagents/holder, created_volume) var/obj/item/stack/ore/bluespace_crystal/BC = new (get_turf(holder.my_atom)) @@ -509,7 +521,7 @@ id = "m_radio" required_reagents = list("water" = 1) required_container = /obj/item/slime_extract/bluespace - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimeradio/on_reaction(datum/reagents/holder, created_volume) new /obj/item/slimepotion/slime/slimeradio(get_turf(holder.my_atom)) @@ -521,7 +533,7 @@ id = "m_steroid2" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/cerulean - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimepsteroid2/on_reaction(datum/reagents/holder) new /obj/item/slimepotion/enhancer(get_turf(holder.my_atom)) @@ -532,7 +544,7 @@ id = "s_territory" required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/cerulean - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slime_territory/on_reaction(datum/reagents/holder) new /obj/item/areaeditor/blueprints/slime(get_turf(holder.my_atom)) @@ -544,7 +556,7 @@ id = "m_stop" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/sepia - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimestop/on_reaction(datum/reagents/holder) var/turf/T = get_turf(holder.my_atom) @@ -557,7 +569,7 @@ id = "m_camera" required_reagents = list("water" = 1) required_container = /obj/item/slime_extract/sepia - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimecamera/on_reaction(datum/reagents/holder) new /obj/item/camera(get_turf(holder.my_atom)) @@ -569,7 +581,7 @@ id = "m_floor" required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/sepia - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimefloor/on_reaction(datum/reagents/holder) new /obj/item/stack/tile/sepia(get_turf(holder.my_atom), 25) @@ -581,7 +593,7 @@ id = "s_paint" required_reagents = list("plasma" = 1) required_container = /obj/item/slime_extract/pyrite - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimepaint/on_reaction(datum/reagents/holder) var/chosen = pick(subtypesof(/obj/item/paint)) @@ -593,7 +605,7 @@ id = "s_crayon" required_reagents = list("blood" = 1) required_container = /obj/item/slime_extract/pyrite - required_other = 1 + required_other = TRUE /datum/chemical_reaction/slime/slimecrayon/on_reaction(datum/reagents/holder) var/chosen = pick(difflist(subtypesof(/obj/item/toy/crayon),typesof(/obj/item/toy/crayon/spraycan))) @@ -605,7 +617,7 @@ name = "Random Core" id = "slimerng" required_reagents = list("plasma" = 1) - required_other = 1 + required_other = TRUE required_container = /obj/item/slime_extract/rainbow /datum/chemical_reaction/slime/slimeRNG/on_reaction(datum/reagents/holder, created_volume) @@ -625,7 +637,7 @@ name = "Clusterblorble" id = "slimebomb" required_reagents = list("slimejelly" = 1) - required_other = 1 + required_other = TRUE required_container = /obj/item/slime_extract/rainbow /datum/chemical_reaction/slime/slimebomb/on_reaction(datum/reagents/holder, created_volume) @@ -641,7 +653,7 @@ name = "Transfer Potion" id = "slimetransfer" required_reagents = list("blood" = 1) - required_other = 1 + required_other = TRUE required_container = /obj/item/slime_extract/rainbow /datum/chemical_reaction/slime/slime_transfer/on_reaction(datum/reagents/holder) @@ -652,7 +664,7 @@ name = "Flight Potion" id = "flightpotion" required_reagents = list("holywater" = 5, "uranium" = 5) - required_other = 1 + required_other = TRUE required_container = /obj/item/slime_extract/rainbow /datum/chemical_reaction/slime/flight_potion/on_reaction(datum/reagents/holder) diff --git a/code/modules/reagents/chemistry/recipes/toxins.dm b/code/modules/reagents/chemistry/recipes/toxins.dm index ee31fb1a93..22e21b1db0 100644 --- a/code/modules/reagents/chemistry/recipes/toxins.dm +++ b/code/modules/reagents/chemistry/recipes/toxins.dm @@ -74,6 +74,12 @@ id = "zombiepowder" results = list("zombiepowder" = 2) required_reagents = list("carpotoxin" = 5, "morphine" = 5, "copper" = 5) + +/datum/chemical_reaction/ghoulpowder + name = "Ghoul Powder" + id = "ghoulpowder" + results = list("ghoulpowder" = 2) + required_reagents = list("zombiepowder" = 1, "epinephrine" = 1) /datum/chemical_reaction/mindbreaker name = "Mindbreaker Toxin" diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 4a40addc5d..85868f1895 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -45,9 +45,6 @@ if(user.a_intent == INTENT_HARM) return ..() -/obj/item/reagent_containers/afterattack(obj/target, mob/user , flag) - return - /obj/item/reagent_containers/proc/canconsume(mob/eater, mob/user) if(!iscarbon(eater)) return 0 @@ -101,7 +98,7 @@ R += num2text(A.volume) + ")," if(thrownby) - add_logs(thrownby, M, "splashed", R) + log_combat(thrownby, M, "splashed", R) reagents.reaction(target, TOUCH) else if(bartender_check(target) && thrown) @@ -110,7 +107,7 @@ else if(isturf(target) && reagents.reagent_list.len && thrownby) - add_logs(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]", "in [AREACOORD(target)]") + log_combat(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]", "in [AREACOORD(target)]") log_game("[key_name(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] in [AREACOORD(target)].") message_admins("[ADMIN_LOOKUPFLW(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] in [ADMIN_VERBOSEJMP(target)].") visible_message("[src] spills its contents all over [target].") diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index 911571a3bd..631e5ee281 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -111,7 +111,7 @@ Borg Hypospray var/list/injected = list() for(var/datum/reagent/RG in R.reagent_list) injected += RG.name - add_logs(user, M, "injected", src, "(CHEMICALS: [english_list(injected)])") + log_combat(user, M, "injected", src, "(CHEMICALS: [english_list(injected)])") /obj/item/reagent_containers/borghypo/attack_self(mob/user) var/chosen_reagent = modes[input(user, "What reagent do you want to dispense?") as null|anything in reagent_ids] @@ -197,6 +197,7 @@ Borg Shaker RG.add_reagent(reagent_ids[valueofi], 5) /obj/item/reagent_containers/borghypo/borgshaker/afterattack(obj/target, mob/user, proximity) + . = ..() if(!proximity) return @@ -251,6 +252,6 @@ Borg Shaker /obj/item/reagent_containers/borghypo/epi name = "epinephrine injector" - desc = "An advanced chemical synthesizer and injection system, designed to stabilize patients.." + desc = "An advanced chemical synthesizer and injection system, designed to stabilize patients." reagent_ids = list("epinephrine") accepts_reagent_upgrades = FALSE diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm index 4d737657f8..66befb1bb6 100644 --- a/code/modules/reagents/reagent_containers/bottle.dm +++ b/code/modules/reagents/reagent_containers/bottle.dm @@ -302,3 +302,105 @@ name = "BVAK bottle" desc = "A small bottle containing Bio Virus Antidote Kit." list_reagents = list("atropine" = 5, "epinephrine" = 5, "salbutamol" = 10, "spaceacillin" = 10) + +//Oldstation.dmm chemical storage bottles + +/obj/item/reagent_containers/glass/bottle/hydrogen + name = "hydrogen bottle" + list_reagents = list("hydrogen" = 30) + +/obj/item/reagent_containers/glass/bottle/lithium + name = "lithium bottle" + list_reagents = list("lithium" = 30) + +/obj/item/reagent_containers/glass/bottle/carbon + name = "carbon bottle" + list_reagents = list("carbon" = 30) + +/obj/item/reagent_containers/glass/bottle/nitrogen + name = "nitrogen bottle" + list_reagents = list("nitrogen" = 30) + +/obj/item/reagent_containers/glass/bottle/oxygen + name = "oxygen bottle" + list_reagents = list("oxygen" = 30) + +/obj/item/reagent_containers/glass/bottle/fluorine + name = "fluorine bottle" + list_reagents = list("fluorine" = 30) + +/obj/item/reagent_containers/glass/bottle/sodium + name = "sodium bottle" + list_reagents = list("sodium" = 30) + +/obj/item/reagent_containers/glass/bottle/aluminium + name = "aluminium bottle" + list_reagents = list("aluminium" = 30) + +/obj/item/reagent_containers/glass/bottle/silicon + name = "silicon bottle" + list_reagents = list("silicon" = 30) + +/obj/item/reagent_containers/glass/bottle/phosphorus + name = "phosphorus bottle" + list_reagents = list("phosphorus" = 30) + +/obj/item/reagent_containers/glass/bottle/sulfur + name = "sulfur bottle" + list_reagents = list("sulfur" = 30) + +/obj/item/reagent_containers/glass/bottle/chlorine + name = "chlorine bottle" + list_reagents = list("chlorine" = 30) + +/obj/item/reagent_containers/glass/bottle/potassium + name = "potassium bottle" + list_reagents = list("potassium" = 30) + +/obj/item/reagent_containers/glass/bottle/iron + name = "iron bottle" + list_reagents = list("iron" = 30) + +/obj/item/reagent_containers/glass/bottle/copper + name = "copper bottle" + list_reagents = list("copper" = 30) + +/obj/item/reagent_containers/glass/bottle/mercury + name = "mercury bottle" + list_reagents = list("mercury" = 30) + +/obj/item/reagent_containers/glass/bottle/radium + name = "radium bottle" + list_reagents = list("radium" = 30) + +/obj/item/reagent_containers/glass/bottle/water + name = "water bottle" + list_reagents = list("water" = 30) + +/obj/item/reagent_containers/glass/bottle/ethanol + name = "ethanol bottle" + list_reagents = list("ethanol" = 30) + +/obj/item/reagent_containers/glass/bottle/sugar + name = "sugar bottle" + list_reagents = list("sugar" = 30) + +/obj/item/reagent_containers/glass/bottle/sacid + name = "sulphuric acid bottle" + list_reagents = list("sacid" = 30) + +/obj/item/reagent_containers/glass/bottle/welding_fuel + name = "welding fuel bottle" + list_reagents = list("welding_fuel" = 30) + +/obj/item/reagent_containers/glass/bottle/silver + name = "silver bottle" + list_reagents = list("silver" = 30) + +/obj/item/reagent_containers/glass/bottle/iodine + name = "iodine bottle" + list_reagents = list("iodine" = 30) + +/obj/item/reagent_containers/glass/bottle/bromine + name = "bromine bottle" + list_reagents = list("bromine" = 30) diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm index 144db32a6c..5907c9e534 100644 --- a/code/modules/reagents/reagent_containers/dropper.dm +++ b/code/modules/reagents/reagent_containers/dropper.dm @@ -9,6 +9,7 @@ container_type = TRANSPARENT /obj/item/reagent_containers/dropper/afterattack(obj/target, mob/user , proximity) + . = ..() if(!proximity) return if(!target.reagents) @@ -68,7 +69,7 @@ for(var/datum/reagent/A in src.reagents.reagent_list) R += A.id + " (" R += num2text(A.volume) + ")," - add_logs(user, M, "squirted", R) + log_combat(user, M, "squirted", R) trans = src.reagents.trans_to(target, amount_per_transfer_from_this) to_chat(user, "You transfer [trans] unit\s of the solution.") diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 596973a2c4..1912e9fe9d 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -29,11 +29,10 @@ R += A.id + " (" R += num2text(A.volume) + ")," if(isturf(target) && reagents.reagent_list.len && thrownby) - add_logs(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]", "in [AREACOORD(target)]") - log_game("[key_name(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] in [AREACOORD(target)].") + log_combat(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]") message_admins("[ADMIN_LOOKUPFLW(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] at [ADMIN_VERBOSEJMP(target)].") reagents.reaction(M, TOUCH) - add_logs(user, M, "splashed", R) + log_combat(user, M, "splashed", R) reagents.clear_reagents() else if(M != user) @@ -44,7 +43,7 @@ if(!reagents || !reagents.total_volume) return // The drink might be empty after the delay, such as by spam-feeding M.visible_message("[user] feeds something to [M].", "[user] feeds something to you.") - add_logs(user, M, "fed", reagents.log_list()) + log_combat(user, M, "fed", reagents.log_list()) else to_chat(user, "You swallow a gulp of [src].") var/fraction = min(5/reagents.total_volume, 1) @@ -53,6 +52,7 @@ playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1) /obj/item/reagent_containers/glass/afterattack(obj/target, mob/user, proximity) + . = ..() if((!proximity) || !check_allowed_items(target,target_self=1)) return @@ -172,7 +172,7 @@ materials = list(MAT_GLASS=2500, MAT_PLASTIC=3000) volume = 120 amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(10,15,20,25,30,60,120) + possible_transfer_amounts = list(5,10,15,20,25,30,60,120) /obj/item/reagent_containers/glass/beaker/plastic/update_icon() icon_state = "beakerlarge" // hack to lets us reuse the large beaker reagent fill states @@ -186,7 +186,7 @@ materials = list(MAT_GLASS=2500, MAT_PLASTIC=3000, MAT_GOLD=1000, MAT_TITANIUM=1000) volume = 180 amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(10,15,20,25,30,60,120,180) + possible_transfer_amounts = list(5,10,15,20,25,30,60,120,180) /obj/item/reagent_containers/glass/beaker/noreact name = "cryostasis beaker" @@ -251,7 +251,7 @@ materials = list(MAT_METAL=200) w_class = WEIGHT_CLASS_NORMAL amount_per_transfer_from_this = 20 - possible_transfer_amounts = list(10,15,20,25,30,50,70) + possible_transfer_amounts = list(5,10,15,20,25,30,50,70) volume = 70 flags_inv = HIDEHAIR slot_flags = ITEM_SLOT_HEAD @@ -330,103 +330,3 @@ /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty list_reagents = list() - -/obj/item/reagent_containers/glass/beaker/large/hydrogen - name = "hydrogen beaker" - list_reagents = list("hydrogen" = 50) - -/obj/item/reagent_containers/glass/beaker/large/lithium - name = "lithium beaker" - list_reagents = list("lithium" = 50) - -/obj/item/reagent_containers/glass/beaker/large/carbon - name = "carbon beaker" - list_reagents = list("carbon" = 50) - -/obj/item/reagent_containers/glass/beaker/large/nitrogen - name = "nitrogen beaker" - list_reagents = list("nitrogen" = 50) - -/obj/item/reagent_containers/glass/beaker/large/oxygen - name = "oxygen beaker" - list_reagents = list("oxygen" = 50) - -/obj/item/reagent_containers/glass/beaker/large/fluorine - name = "fluorine beaker" - list_reagents = list("fluorine" = 50) - -/obj/item/reagent_containers/glass/beaker/large/sodium - name = "sodium beaker" - list_reagents = list("sodium" = 50) - -/obj/item/reagent_containers/glass/beaker/large/aluminium - name = "aluminium beaker" - list_reagents = list("aluminium" = 50) - -/obj/item/reagent_containers/glass/beaker/large/silicon - name = "silicon beaker" - list_reagents = list("silicon" = 50) - -/obj/item/reagent_containers/glass/beaker/large/phosphorus - name = "phosphorus beaker" - list_reagents = list("phosphorus" = 50) - -/obj/item/reagent_containers/glass/beaker/large/sulfur - name = "sulfur beaker" - list_reagents = list("sulfur" = 50) - -/obj/item/reagent_containers/glass/beaker/large/chlorine - name = "chlorine beaker" - list_reagents = list("chlorine" = 50) - -/obj/item/reagent_containers/glass/beaker/large/potassium - name = "potassium beaker" - list_reagents = list("potassium" = 50) - -/obj/item/reagent_containers/glass/beaker/large/iron - name = "iron beaker" - list_reagents = list("iron" = 50) - -/obj/item/reagent_containers/glass/beaker/large/copper - name = "copper beaker" - list_reagents = list("copper" = 50) - -/obj/item/reagent_containers/glass/beaker/large/mercury - name = "mercury beaker" - list_reagents = list("mercury" = 50) - -/obj/item/reagent_containers/glass/beaker/large/radium - name = "radium beaker" - list_reagents = list("radium" = 50) - -/obj/item/reagent_containers/glass/beaker/large/water - name = "water beaker" - list_reagents = list("water" = 50) - -/obj/item/reagent_containers/glass/beaker/large/ethanol - name = "ethanol beaker" - list_reagents = list("ethanol" = 50) - -/obj/item/reagent_containers/glass/beaker/large/sugar - name = "sugar beaker" - list_reagents = list("sugar" = 50) - -/obj/item/reagent_containers/glass/beaker/large/sacid - name = "sulphuric acid beaker" - list_reagents = list("sacid" = 50) - -/obj/item/reagent_containers/glass/beaker/large/welding_fuel - name = "welding fuel beaker" - list_reagents = list("welding_fuel" = 50) - -/obj/item/reagent_containers/glass/beaker/large/silver - name = "silver beaker" - list_reagents = list("silver" = 50) - -/obj/item/reagent_containers/glass/beaker/large/iodine - name = "iodine beaker" - list_reagents = list("iodine" = 50) - -/obj/item/reagent_containers/glass/beaker/large/bromine - name = "bromine beaker" - list_reagents = list("bromine" = 50) diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index b03dcedd1a..1fa8b408cd 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -30,7 +30,7 @@ for(var/datum/reagent/R in reagents.reagent_list) injected += R.name var/contained = english_list(injected) - add_logs(user, M, "attempted to inject", src, "([contained])") + log_combat(user, M, "attempted to inject", src, "([contained])") if(reagents.total_volume && (ignore_flags || M.can_inject(user, 1))) // Ignore flag should be checked first or there will be an error message. to_chat(M, "You feel a tiny prick!") @@ -48,7 +48,7 @@ to_chat(user, "[trans] unit\s injected. [reagents.total_volume] unit\s remaining in [src].") - add_logs(user, M, "injected", src, "([contained])") + log_combat(user, M, "injected", src, "([contained])") /obj/item/reagent_containers/hypospray/CMO list_reagents = list("omnizine" = 30) diff --git a/code/modules/reagents/reagent_containers/medspray.dm b/code/modules/reagents/reagent_containers/medspray.dm index fdb6a60894..8631c14ac0 100644 --- a/code/modules/reagents/reagent_containers/medspray.dm +++ b/code/modules/reagents/reagent_containers/medspray.dm @@ -46,7 +46,7 @@ to_chat(M, "You [apply_method] yourself with [src].") else - add_logs(user, M, "attempted to apply", src, reagents.log_list()) + log_combat(user, M, "attempted to apply", src, reagents.log_list()) M.visible_message("[user] attempts to [apply_method] [src] on [M].", \ "[user] attempts to [apply_method] [src] on [M].") if(!do_mob(user, M)) @@ -60,7 +60,7 @@ return else - add_logs(user, M, "applied", src, reagents.log_list()) + log_combat(user, M, "applied", src, reagents.log_list()) playsound(src, 'sound/effects/spray2.ogg', 50, 1, -6) var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1) reagents.reaction(M, apply_type, fraction) diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm index ac064c9c15..b11ef878b6 100644 --- a/code/modules/reagents/reagent_containers/patch.dm +++ b/code/modules/reagents/reagent_containers/patch.dm @@ -9,9 +9,7 @@ apply_type = PATCH apply_method = "apply" self_delay = 30 // three seconds - -/obj/item/reagent_containers/pill/patch/afterattack(obj/target, mob/user , proximity) - return // thanks inheritance again + dissolvable = FALSE /obj/item/reagent_containers/pill/patch/attack(mob/living/L, mob/user) if(ishuman(L)) diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index d2f840cda3..8941724274 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -13,6 +13,7 @@ var/apply_method = "swallow" var/roundstart = 0 var/self_delay = 0 //pills are instant, this is because patches inheret their aplication from pills + var/dissolvable = TRUE /obj/item/reagent_containers/pill/Initialize() . = ..() @@ -46,7 +47,7 @@ "[user] forces [M] to [apply_method] [src].") - add_logs(user, M, "fed", reagents.log_list()) + log_combat(user, M, "fed", reagents.log_list()) if(reagents.total_volume) reagents.reaction(M, apply_type) reagents.trans_to(M, reagents.total_volume) @@ -55,22 +56,24 @@ /obj/item/reagent_containers/pill/afterattack(obj/target, mob/user , proximity) + . = ..() if(!proximity) return - if(target.is_refillable()) - if(target.is_drainable() && !target.reagents.total_volume) - to_chat(user, "[target] is empty! There's nothing to dissolve [src] in.") - return + if(!dissolvable || !target.is_refillable()) + return + if(target.is_drainable() && !target.reagents.total_volume) + to_chat(user, "[target] is empty! There's nothing to dissolve [src] in.") + return - if(target.reagents.holder_full()) - to_chat(user, "[target] is full.") - return + if(target.reagents.holder_full()) + to_chat(user, "[target] is full.") + return - to_chat(user, "You dissolve [src] in [target].") - for(var/mob/O in viewers(2, user)) //viewers is necessary here because of the small radius - to_chat(O, "[user] slips something into [target]!") - reagents.trans_to(target, reagents.total_volume) - qdel(src) + to_chat(user, "You dissolve [src] in [target].") + for(var/mob/O in viewers(2, user)) //viewers is necessary here because of the small radius + to_chat(O, "[user] slips something into [target]!") + reagents.trans_to(target, reagents.total_volume) + qdel(src) /obj/item/reagent_containers/pill/tox name = "toxins pill" @@ -151,7 +154,7 @@ icon_state = "pill18" list_reagents = list("insulin" = 50) roundstart = 1 -///////////////////////////////////////// this pill is used only in a legion mob drop +///////////////////////////////////////// this pill is used only in a legion mob drop /obj/item/reagent_containers/pill/shadowtoxin name = "black pill" desc = "I wouldn't eat this if I were you." @@ -178,5 +181,18 @@ name = "speedy pill" list_reagents = list("aranesp" = 10) +/obj/item/reagent_containers/pill/floorpill + name = "floorpill" + desc = "A strange pill found in the depths of maintenance" + icon_state = "pill21" + var/static/list/names = list("maintenance pill","floorpill","mystery pill","suspicious pill","strange pill") + var/static/list/descs = list("Your feeling is telling you no, but...","Drugs are expensive, you can't afford not to eat any pills that you find."\ + , "Surely, there's no way this could go bad.") +/obj/item/reagent_containers/pill/floorpill/Initialize() + list_reagents = list(get_random_reagent_id() = rand(10,50)) + . = ..() + name = pick(names) + if(prob(20)) + desc = pick(descs) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 91a22ff1fb..e3576b3625 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -24,6 +24,7 @@ possible_transfer_amounts = list(5,10,15,20,25,30,50,100) /obj/item/reagent_containers/spray/afterattack(atom/A, mob/user) + . = ..() if(istype(A, /obj/structure/sink) || istype(A, /obj/structure/janitorialcart) || istype(A, /obj/machinery/hydroponics)) return @@ -195,7 +196,7 @@ /obj/item/reagent_containers/spray/pepper/afterattack(atom/A as mob|obj, mob/user) if (A.loc == user) return - ..() + . = ..() //water flower /obj/item/reagent_containers/spray/waterflower @@ -273,7 +274,7 @@ // Make it so the bioterror spray doesn't spray yourself when you click your inventory items if (A.loc == user) return - ..() + . = ..() /obj/item/reagent_containers/spray/chemsprayer/spray(atom/A) var/direction = get_dir(src, A) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 64ef740278..edec0daa17 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -48,6 +48,7 @@ return /obj/item/reagent_containers/syringe/afterattack(atom/target, mob/user , proximity) + . = ..() if(busy) return if(!proximity) @@ -110,7 +111,7 @@ if(SYRINGE_INJECT) // Always log attemped injections for admins var/contained = reagents.log_list() - add_logs(user, L, "attemped to inject", src, addition="which had [contained]") + log_combat(user, target, "attempted to inject", src, addition="which had [contained]") if(!reagents.total_volume) to_chat(user, "[src] is empty.") @@ -140,11 +141,9 @@ "[user] injects [L] with the syringe!") if(L != user) - add_logs(user, L, "injected", src, addition="which had [contained]") + log_combat(user, L, "injected", src, addition="which had [contained]") else - log_attack("[key_name(user)] injected [key_name(L)] with [src.name], which had [contained] (INTENT: [uppertext(user.a_intent)])") - L.log_message("Injected themselves ([contained]) with [src.name].", INDIVIDUAL_ATTACK_LOG) - + L.log_message("injected themselves ([contained]) with [src.name]", LOG_ATTACK, color="orange") var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1) reagents.reaction(L, INJECT, fraction) reagents.trans_to(target, amount_per_transfer_from_this) @@ -224,7 +223,7 @@ list_reagents = list("chloralhydrate" = 50) /obj/item/reagent_containers/syringe/lethal/execution - list_reagents = list("amatoxin" = 15, "formaldehyde" = 15, "cyanide" = 10, "facid" = 10) + list_reagents = list("plasma" = 15, "formaldehyde" = 15, "cyanide" = 10, "facid" = 10) /obj/item/reagent_containers/syringe/mulligan name = "Mulligan" @@ -260,10 +259,3 @@ desc = "A diamond-tipped syringe that pierces armor when launched at high velocity. It can hold up to 10 units." volume = 10 proj_piercing = 1 - -/obj/item/reagent_containers/syringe/alien - name = "Hive's Blessing" - desc = "A syringe filled with a strange viscous liquid. It might be best to leave it alone." - amount_per_transfer_from_this = 1 - volume = 1 - list_reagents = list("xenomicrobes" = 1) \ No newline at end of file diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 072c1b051f..45154a70f4 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -88,9 +88,7 @@ var/boom_message = "[ADMIN_LOOKUPFLW(P.firer)] triggered a fueltank explosion via projectile." GLOB.bombers += boom_message message_admins(boom_message) - var/log_message = "triggered a fueltank explosion via projectile." - P.firer.log_message(log_message, INDIVIDUAL_ATTACK_LOG) - log_attack("[key_name(P.firer)] [log_message]") + P.firer.log_message("triggered a fueltank explosion via projectile.", LOG_ATTACK) boom() /obj/structure/reagent_dispensers/fueltank/attackby(obj/item/I, mob/living/user, params) @@ -110,13 +108,12 @@ else var/turf/T = get_turf(src) user.visible_message("[user] catastrophically fails at refilling [user.p_their()] [W.name]!", "That was stupid of you.") + var/message_admins = "[ADMIN_LOOKUPFLW(user)] triggered a fueltank explosion via welding tool at [ADMIN_VERBOSEJMP(T)]." GLOB.bombers += message_admins message_admins(message_admins) - var/message_log = "triggered a fueltank explosion via welding tool at [AREACOORD(T)]." - user.log_message(message_log, INDIVIDUAL_ATTACK_LOG) - log_game("[key_name(user)] [message_log]") - log_attack("[key_name(user)] [message_log]") + + user.log_message("triggered a fueltank explosion via welding tool.", LOG_ATTACK) boom() return return ..() diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm index 8acecf25a1..5875e821a3 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/recycling/conveyor2.dm @@ -343,6 +343,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id) id = C.id /obj/item/conveyor_construct/afterattack(atom/A, mob/user, proximity) + . = ..() if(!proximity || user.stat || !isfloorturf(A) || istype(A, /area/shuttle)) return var/cdir = get_dir(A, user) @@ -366,6 +367,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id) id = "[rand()]" //this couldn't possibly go wrong /obj/item/conveyor_switch_construct/afterattack(atom/A, mob/user, proximity) + . = ..() if(!proximity || user.stat || !isfloorturf(A) || istype(A, /area/shuttle)) return var/found = 0 diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index 737f50859d..fd98c54b81 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -136,7 +136,7 @@ user.visible_message("[user] climbs into [src].", "You climb into [src].") else target.visible_message("[user] has placed [target] in [src].", "[user] has placed [target] in [src].") - add_logs(user, target, "stuffed", addition="into [src]") + log_combat(user, target, "stuffed", addition="into [src]") target.LAssailant = user update_icon() @@ -453,7 +453,7 @@ ..() flush() -/obj/machinery/disposal/deliveryChute/CollidedWith(atom/movable/AM) //Go straight into the chute +/obj/machinery/disposal/deliveryChute/Bumped(atom/movable/AM) //Go straight into the chute if(!AM.CanEnterDisposals()) return switch(dir) diff --git a/code/modules/recycling/disposal/holder.dm b/code/modules/recycling/disposal/holder.dm index 65a5dce1f5..2c0a6aa646 100644 --- a/code/modules/recycling/disposal/holder.dm +++ b/code/modules/recycling/disposal/holder.dm @@ -47,6 +47,7 @@ var/atom/movable/AM = A if(AM == src) continue + SEND_SIGNAL(AM, COMSIG_MOVABLE_DISPOSING, src, D) AM.forceMove(src) if(istype(AM, /obj/structure/bigDelivery) && !hasmob) var/obj/structure/bigDelivery/T = AM diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index bd2dae4fd9..fa2eaa22d4 100644 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -146,13 +146,7 @@ desc = "Used to set the destination of properly wrapped packages." icon = 'icons/obj/device.dmi' icon_state = "cargotagger" - var/currTag = 0 - //The whole system for the sorttype var is determined based on the order of this list, - //disposals must always be 1, since anything that's untagged will automatically go to disposals, or sorttype = 1 --Superxpdude - - //If you don't want to fuck up disposals, add to this list, and don't change the order. - //If you insist on changing the order, you'll have to change every sort junction to reflect the new order. --Pete - + var/currTag = 0 //Destinations are stored in code\globalvars\lists\flavor_misc.dm w_class = WEIGHT_CLASS_TINY item_state = "electronic" lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index 997d957a4e..491aa63e56 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -44,6 +44,8 @@ other types of metals and chemistry for reagents). var/dangerous_construction = FALSE //notify and log for admin investigations if this is printed. var/departmental_flags = ALL //bitflags for deplathes. var/list/datum/techweb_node/unlocked_by = list() + var/research_icon //Replaces the item icon in the research console + var/research_icon_state var/icon_cache /datum/design/Destroy() diff --git a/code/modules/research/designs/AI_module_designs.dm b/code/modules/research/designs/AI_module_designs.dm index eb92bb3737..6e6bd740bb 100644 --- a/code/modules/research/designs/AI_module_designs.dm +++ b/code/modules/research/designs/AI_module_designs.dm @@ -14,7 +14,7 @@ name = "Module Design (Safeguard)" desc = "Allows for the construction of a Safeguard AI Module." id = "safeguard_module" - materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) build_path = /obj/item/aiModule/supplied/safeguard category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -23,7 +23,7 @@ name = "Module Design (OneHuman)" desc = "Allows for the construction of a OneHuman AI Module." id = "onehuman_module" - materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) + materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 6000) build_path = /obj/item/aiModule/zeroth/oneHuman category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -32,7 +32,7 @@ name = "Module Design (ProtectStation)" desc = "Allows for the construction of a ProtectStation AI Module." id = "protectstation_module" - materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) build_path = /obj/item/aiModule/supplied/protectStation category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -41,7 +41,7 @@ name = "Module Design (Quarantine)" desc = "Allows for the construction of a Quarantine AI Module." id = "quarantine_module" - materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) build_path = /obj/item/aiModule/supplied/quarantine category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -50,7 +50,7 @@ name = "Module Design (OxygenIsToxicToHumans)" desc = "Allows for the construction of a Safeguard AI Module." id = "oxygen_module" - materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) build_path = /obj/item/aiModule/supplied/oxygen category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -59,7 +59,7 @@ name = "Module Design (Freeform)" desc = "Allows for the construction of a Freeform AI Module." id = "freeform_module" - materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) + materials = list(MAT_GLASS = 1000, MAT_GOLD = 10000)//Custom inputs should be more expensive to get build_path = /obj/item/aiModule/supplied/freeform category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -68,7 +68,7 @@ name = "Module Design (Reset)" desc = "Allows for the construction of a Reset AI Module." id = "reset_module" - materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) build_path = /obj/item/aiModule/reset category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -77,7 +77,7 @@ name = "Module Design (Purge)" desc = "Allows for the construction of a Purge AI Module." id = "purge_module" - materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) + materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000) build_path = /obj/item/aiModule/reset/purge category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -86,7 +86,7 @@ name = "Module Design (Law Removal)" desc = "Allows for the construction of a Law Removal AI Core Module." id = "remove_module" - materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) + materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000) build_path = /obj/item/aiModule/remove category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -95,7 +95,7 @@ name = "AI Core Module (Freeform)" desc = "Allows for the construction of a Freeform AI Core Module." id = "freeformcore_module" - materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) + materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 10000)//Ditto build_path = /obj/item/aiModule/core/freeformcore category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -104,7 +104,7 @@ name = "Core Module Design (Asimov)" desc = "Allows for the construction of an Asimov AI Core Module." id = "asimov_module" - materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) + materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000) build_path = /obj/item/aiModule/core/full/asimov category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -114,7 +114,7 @@ desc = "Allows for the construction of a P.A.L.A.D.I.N. AI Core Module." id = "paladin_module" build_type = IMPRINTER - materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) + materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000) build_path = /obj/item/aiModule/core/full/paladin category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -123,7 +123,7 @@ name = "Core Module Design (T.Y.R.A.N.T.)" desc = "Allows for the construction of a T.Y.R.A.N.T. AI Module." id = "tyrant_module" - materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) + materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000) build_path = /obj/item/aiModule/core/full/tyrant category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -132,7 +132,7 @@ name = "Core Module Design (Corporate)" desc = "Allows for the construction of a Corporate AI Core Module." id = "corporate_module" - materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) + materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000) build_path = /obj/item/aiModule/core/full/corp category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -141,7 +141,7 @@ name = "Core Module Design (Default)" desc = "Allows for the construction of a Default AI Core Module." id = "default_module" - materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) + materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 2000) build_path = /obj/item/aiModule/core/full/custom category = list("AI Modules") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm index 0a36394bc9..2050e96f5e 100644 --- a/code/modules/research/designs/autolathe_designs.dm +++ b/code/modules/research/designs/autolathe_designs.dm @@ -34,6 +34,14 @@ build_path = /obj/item/extinguisher category = list("initial","Tools") +/datum/design/pocketfireextinguisher + name = "Pocket Fire Extinguisher" + id = "pocketfireextinguisher" + build_type = AUTOLATHE + materials = list(MAT_METAL = 50, MAT_GLASS = 40) + build_path = /obj/item/extinguisher/mini + category = list("initial","Tools") + /datum/design/multitool name = "Multitool" id = "multitool" @@ -229,10 +237,10 @@ /datum/design/rglass name = "Reinforced Glass" id = "rglass" - build_type = AUTOLATHE | SMELTER + build_type = AUTOLATHE | SMELTER | PROTOLATHE materials = list(MAT_METAL = 1000, MAT_GLASS = MINERAL_MATERIAL_AMOUNT) build_path = /obj/item/stack/sheet/rglass - category = list("initial","Construction") + category = list("initial","Construction","Stock Parts") maxstack = 50 /datum/design/rods diff --git a/code/modules/research/designs/biogenerator_designs.dm b/code/modules/research/designs/biogenerator_designs.dm index e2d5652a35..3d48dd51ec 100644 --- a/code/modules/research/designs/biogenerator_designs.dm +++ b/code/modules/research/designs/biogenerator_designs.dm @@ -137,7 +137,7 @@ build_type = BIOGENERATOR materials = list(MAT_BIOMASS = 300) build_path = /obj/item/storage/belt/security - category = list("initial","Leather and Cloth") + category = list("initial","Organic Materials") /datum/design/medbelt name = "Medical Belt" @@ -145,7 +145,7 @@ build_type = BIOGENERATOR materials = list(MAT_BIOMASS = 300) build_path = /obj/item/storage/belt/medical - category = list("initial","Leather and Cloth") + category = list("initial","Organic Materials") /datum/design/janibelt name = "Janitorial Belt" @@ -153,7 +153,7 @@ build_type = BIOGENERATOR materials = list(MAT_BIOMASS = 300) build_path = /obj/item/storage/belt/janitor - category = list("initial","Leather and Cloth") + category = list("initial","Organic Materials") /datum/design/s_holster name = "Shoulder Holster" @@ -161,7 +161,7 @@ build_type = BIOGENERATOR materials = list(MAT_BIOMASS = 400) build_path = /obj/item/storage/belt/holster - category = list("initial","Leather and Cloth") + category = list("initial","Organic Materials") /datum/design/rice_hat name = "Rice Hat" @@ -169,4 +169,4 @@ build_type = BIOGENERATOR materials = list(MAT_BIOMASS = 300) build_path = /obj/item/clothing/head/rice_hat - category = list("initial","Leather and Cloth") + category = list("initial","Organic Materials") diff --git a/code/modules/research/designs/comp_board_designs.dm b/code/modules/research/designs/comp_board_designs.dm index bbea1ce267..68aee66d62 100644 --- a/code/modules/research/designs/comp_board_designs.dm +++ b/code/modules/research/designs/comp_board_designs.dm @@ -50,6 +50,7 @@ name = "Computer Design (AI Upload)" desc = "Allows for the construction of circuit boards used to build an AI Upload Console." id = "aiupload" + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) build_path = /obj/item/circuitboard/computer/aiupload category = list("Computer Boards") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE @@ -58,6 +59,7 @@ name = "Computer Design (Cyborg Upload)" desc = "Allows for the construction of circuit boards used to build a Cyborg Upload Console." id = "borgupload" + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) build_path = /obj/item/circuitboard/computer/borgupload category = list("Computer Boards") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE diff --git a/code/modules/research/designs/machine_designs.dm b/code/modules/research/designs/machine_designs.dm index 1ebadd2e34..93dfd3d2bb 100644 --- a/code/modules/research/designs/machine_designs.dm +++ b/code/modules/research/designs/machine_designs.dm @@ -306,6 +306,38 @@ category = list("Research Machinery") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE +/datum/design/board/nanite_chamber + name = "Machine Design (Nanite Chamber Board)" + desc = "The circuit board for a Nanite Chamber." + id = "nanite_chamber" + build_path = /obj/item/circuitboard/machine/nanite_chamber + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/public_nanite_chamber + name = "Machine Design (Public Nanite Chamber Board)" + desc = "The circuit board for a Public Nanite Chamber." + id = "public_nanite_chamber" + build_path = /obj/item/circuitboard/machine/public_nanite_chamber + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/nanite_programmer + name = "Machine Design (Nanite Programmer Board)" + desc = "The circuit board for a Nanite Programmer." + id = "nanite_programmer" + build_path = /obj/item/circuitboard/machine/nanite_programmer + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/board/nanite_program_hub + name = "Machine Design (Nanite Program Hub Board)" + desc = "The circuit board for a Nanite Program Hub." + id = "nanite_program_hub" + build_path = /obj/item/circuitboard/machine/nanite_program_hub + category = list("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_SCIENCE + /datum/design/board/microwave name = "Machine Design (Microwave Board)" desc = "The circuit board for a microwave." @@ -398,7 +430,7 @@ name = "Machine Design (Weapon Recharger Board)" desc = "The circuit board for a Weapon Recharger." id = "recharger" - materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) + materials = list(MAT_GLASS = 1000, MAT_GOLD = 2000) build_path = /obj/item/circuitboard/machine/recharger category = list("Misc. Machinery") departmental_flags = DEPARTMENTAL_FLAG_ALL @@ -475,6 +507,14 @@ category = list("Medical Machinery") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL +/datum/design/board/harvester + name = "Machine Design (Organ Harvester Board)" + desc = "The circuit board for an organ harvester." + id = "harvester" + build_path = /obj/item/circuitboard/machine/harvester + category = list("Medical Machinery") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + /datum/design/board/deepfryer name = "Machine Design (Deep Fryer)" desc = "The circuit board for a Deep Fryer." @@ -523,34 +563,10 @@ category = list ("Misc. Machinery") departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_ENGINEERING -/datum/design/board/nanite_chamber - name = "Machine Design (Nanite Chamber Board)" - desc = "The circuit board for a Nanite Chamber." - id = "nanite_chamber" - build_path = /obj/item/circuitboard/machine/nanite_chamber - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/public_nanite_chamber - name = "Machine Design (Public Nanite Chamber Board)" - desc = "The circuit board for a Public Nanite Chamber." - id = "public_nanite_chamber" - build_path = /obj/item/circuitboard/machine/public_nanite_chamber - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/nanite_programmer - name = "Machine Design (Nanite Programmer Board)" - desc = "The circuit board for a Nanite Programmer." - id = "nanite_programmer" - build_path = /obj/item/circuitboard/machine/nanite_programmer - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE - -/datum/design/board/nanite_program_hub - name = "Machine Design (Nanite Program Hub Board)" - desc = "The circuit board for a Nanite Program Hub." - id = "nanite_program_hub" - build_path = /obj/item/circuitboard/machine/nanite_program_hub - category = list("Research Machinery") - departmental_flags = DEPARTMENTAL_FLAG_SCIENCE +/datum/design/board/ore_silo + name = "Machine Design (Ore Silo)" + desc = "The circuit board for an ore silo." + id = "ore_silo" + build_path = /obj/item/circuitboard/machine/ore_silo + category = list ("Research Machinery") + departmental_flags = DEPARTMENTAL_FLAG_CARGO diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index e4ba942c84..97c769170d 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -704,6 +704,15 @@ materials = list(MAT_METAL=15000, MAT_GLASS=15000, MAT_SILVER=10000, MAT_GOLD=10000, MAT_TITANIUM=5000, MAT_DIAMOND=5000) construction_time = 120 category = list("Cyborg Upgrade Modules") + +/datum/design/borg_upgrade_surgicalprocessor + name = "Cyborg Upgrade (Surgical Processor)" + id = "borg_upgrade_surgicalprocessor" + build_type = MECHFAB + build_path = /obj/item/borg/upgrade/processor + materials = list(MAT_METAL=15000, MAT_GLASS=15000, MAT_SILVER=10000) + construction_time = 120 + category = list("Cyborg Upgrade Modules") /datum/design/borg_upgrade_trashofholding name = "Cyborg Upgrade (Trash Bag of Holding)" diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 5f1e5ea520..4b28ecd048 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -122,6 +122,16 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_ALL +/datum/design/crewpinpointer + name = "Crew Pinpointer" + desc = "Allows tracking of someone's location if their suit sensors are turned to tracking beacon." + id = "crewpinpointer" + build_type = PROTOLATHE + materials = list(MAT_METAL = 3000, MAT_GLASS = 1500, MAT_GOLD = 200) + build_path = /obj/item/pinpointer/crew + category = list("Medical Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + /datum/design/defibrillator name = "Defibrillator" id = "defibrillator" @@ -200,7 +210,7 @@ materials = list(MAT_METAL = 2000, MAT_SILVER = 1500, MAT_PLASMA = 500, MAT_TITANIUM = 1500) category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - + /datum/design/healthanalyzer_advanced name = "advanced health analyzer" desc = "A hand-held body scanner able to distinguish vital signs of the subject with high accuracy." @@ -210,7 +220,7 @@ materials = list(MAT_METAL = 5000, MAT_GLASS = 2500, MAT_SILVER = 2000, MAT_GOLD = 1500) category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL - + ///////////////////////////////////////// //////////Cybernetic Implants//////////// ///////////////////////////////////////// @@ -486,102 +496,93 @@ category = list("Medical Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL -/datum/design/surgery_lobotomy - name = "Lobotomy Surgery Disk" - desc = "A disk containing the instructions for a Lobotomy surgery." +///////////////////// +///Surgery Designs/// +///////////////////// +/datum/design/surgery + name = "Surgery Design" + desc = "what" + id = "surgery_parent" + research_icon = 'icons/obj/surgery.dmi' + research_icon_state = "surgery_any" + var/surgery + +/datum/design/surgery/lobotomy + name = "Lobotomy" + desc = "An invasive surgical procedure which guarantees removal of almost all brain traumas, but might cause another permanent trauma in return." id = "surgery_lobotomy" - build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100) - build_path = /obj/item/disk/surgery/lobotomy - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + surgery = /datum/surgery/advanced/lobotomy + research_icon_state = "surgery_head" -/datum/design/surgery_pacify - name = "Pacification Surgery Disk" - desc = "A disk containing the instructions for a Pacification surgery." +/datum/design/surgery/pacify + name = "Pacification" + desc = "A surgical procedure which permanently inhibits the aggression center of the brain, making the patient unwilling to cause direct harm." id = "surgery_pacify" - build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100) - build_path = /obj/item/disk/surgery/pacification - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + surgery = /datum/surgery/advanced/pacify + research_icon_state = "surgery_head" -/datum/design/surgery_viral_bonding - name = "Viral Bonding Surgery Disk" - desc = "A disk containing the instructions for a Viral Bonding surgery." +/datum/design/surgery/viral_bonding + name = "Viral Bonding" + desc = "A surgical procedure that forces a symbiotic relationship between a virus and its host. The patient must be dosed with spaceacillin, virus food, and formaldehyde." id = "surgery_viral_bond" - build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100) - build_path = /obj/item/disk/surgery/viral_bonding - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + surgery = /datum/surgery/advanced/viral_bonding + research_icon_state = "surgery_chest" -/datum/design/surgery_reconstruction - name = "Reconstruction Surgery Disk" - desc = "A disk containing the instructions for a Reconstruction surgery." +/datum/design/surgery/reconstruction + name = "Reconstruction" + desc = "A surgical procedure that gradually repairs damage done to a body without the assistance of chemicals. Unlike classic medicine, it is effective on corpses." id = "surgery_reconstruction" - build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100) - build_path = /obj/item/disk/surgery/reconstruction - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + surgery = /datum/surgery/advanced/reconstruction + research_icon_state = "surgery_chest" -/datum/design/surgery_revival - name = "Revival Surgery Disk" - desc = "A disk containing the instructions for a Revival surgery." +/datum/design/surgery/revival + name = "Revival" + desc = "An experimental surgical procedure which involves reconstruction and reactivation of the patient's brain even long after death. The body must still be able to sustain life." id = "surgery_revival" - build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100) - build_path = /obj/item/disk/surgery/revival - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + surgery = /datum/surgery/advanced/revival + research_icon_state = "surgery_head" -/datum/design/surgery_brainwashing - name = "Brainwashing Surgery Disk" - desc = "A disk containing the instructions for a Brainwashing surgery." +/datum/design/surgery/brainwashing + name = "Brainwashing" + desc = "A surgical procedure which directly implants a directive into the patient's brain, making it their absolute priority. It can be cleared using a mindshield implant." id = "surgery_brainwashing" - build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100) - build_path = /obj/item/disk/surgery/brainwashing - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + surgery = /datum/surgery/advanced/brainwashing + research_icon_state = "surgery_head" -/datum/design/surgery_nerve_splicing - name = "Nerve Splicing Surgery Disk" - desc = "A disk containing the instructions for a Nerve Splicing surgery." +/datum/design/surgery/nerve_splicing + name = "Nerve Splicing" + desc = "A surgical procedure which splices the patient's nerves, making them more resistant to stuns." id = "surgery_nerve_splice" - build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100) - build_path = /obj/item/disk/surgery/nerve_splicing - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + surgery = /datum/surgery/advanced/bioware/nerve_splicing + research_icon_state = "surgery_chest" -/datum/design/surgery_nerve_grounding - name = "Nerve Grounding Surgery Disk" - desc = "A disk containing the instructions for a Nerve Grounding surgery." +/datum/design/surgery/nerve_grounding + name = "Nerve Grounding" + desc = "A surgical procedure which makes the patient's nerves act as grounding rods, protecting them from electrical shocks." id = "surgery_nerve_ground" - build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100) - build_path = /obj/item/disk/surgery/nerve_grounding - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + surgery = /datum/surgery/advanced/bioware/nerve_grounding + research_icon_state = "surgery_chest" -/datum/design/surgery_vein_threading - name = "Vein Threading Surgery Disk" - desc = "A disk containing the instructions for a Vein Threading surgery." +/datum/design/surgery/vein_threading + name = "Vein Threading" + desc = "A surgical procedure which severely reduces the amount of blood lost in case of injury." id = "surgery_vein_thread" - build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100) - build_path = /obj/item/disk/surgery/vein_threading - category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + surgery = /datum/surgery/advanced/bioware/vein_threading + research_icon_state = "surgery_chest" -/datum/design/surgery_necrotic_revival - name = "Necrotic Revival Surgery Disk" - desc = "A disk containing the instructions for a Necrotic Revival surgery." +/datum/design/surgery/necrotic_revival + name = "Necrotic Revival" + desc = "An experimental surgical procedure that stimulates the growth of a Romerol tumor inside the patient's brain. Requires zombie powder or rezadone." id = "surgery_zombie" + surgery = /datum/surgery/advanced/necrotic_revival + research_icon_state = "surgery_head" + +/datum/design/holobarrier_med + name = "PENLITE holobarrier projector" + desc = "PENLITE holobarriers, a device that halts individuals with malicious diseases." build_type = PROTOLATHE - materials = list(MAT_METAL = 300, MAT_GLASS = 100) - build_path = /obj/item/disk/surgery/necrotic_revival + build_path = /obj/item/holosign_creator/medical + materials = list(MAT_METAL = 500, MAT_GLASS = 500, MAT_SILVER = 100) //a hint of silver since it can troll 2 antags (bad viros and sentient disease) + id = "holobarrier_med" category = list("Medical Designs") - departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL \ No newline at end of file diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index d8f25968db..17fc64867b 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -157,6 +157,16 @@ category = list("Equipment") departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING +/datum/design/forcefield_projector + name = "Forcefield Projector" + desc = "A device which can project temporary forcefields to seal off an area." + id = "forcefield_projector" + build_type = PROTOLATHE + materials = list(MAT_METAL = 2500, MAT_GLASS = 1000) + build_path = /obj/item/forcefield_projector + category = list("Equipment") + departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING + /datum/design/sci_goggles name = "Science Goggles" desc = "Goggles fitted with a portable analyzer capable of determining the research worth of an item or components of a machine." @@ -267,6 +277,16 @@ category = list("Equipment") departmental_flags = DEPARTMENTAL_FLAG_SERVICE +/datum/design/locator + name = "Bluespace locator" + desc = "Used to track portable teleportation beacons and targets with embedded tracking implants." + id = "locator" + build_type = PROTOLATHE + materials = list(MAT_METAL=1000, MAT_GLASS=500, MAT_SILVER = 500) + build_path = /obj/item/locator + category = list("Equipment") + departmental_flags = DEPARTMENTAL_FLAG_SECURITY + ///////////////////////////////////////// ////////////Janitor Designs////////////// ///////////////////////////////////////// diff --git a/code/modules/research/designs/nanite_designs.dm b/code/modules/research/designs/nanite_designs.dm index 02f543af1e..177d7073a1 100644 --- a/code/modules/research/designs/nanite_designs.dm +++ b/code/modules/research/designs/nanite_designs.dm @@ -5,8 +5,8 @@ build_type = NANITE_COMPILER construction_time = 50 category = list() - //research_icon = 'icons/obj/device.dmi' - //research_icon_state = "nanite_program" + research_icon = 'icons/obj/device.dmi' + research_icon_state = "nanite_program" var/program_type = /datum/nanite_program ////////////////////UTILITY NANITES////////////////////////////////////// diff --git a/code/modules/research/designs/smelting_designs.dm b/code/modules/research/designs/smelting_designs.dm index 79a9748b08..f2f21396d2 100644 --- a/code/modules/research/designs/smelting_designs.dm +++ b/code/modules/research/designs/smelting_designs.dm @@ -3,51 +3,63 @@ /datum/design/plasteel_alloy name = "Plasma + Iron alloy" id = "plasteel" - build_type = SMELTER + build_type = SMELTER | PROTOLATHE materials = list(MAT_METAL = MINERAL_MATERIAL_AMOUNT, MAT_PLASMA = MINERAL_MATERIAL_AMOUNT) build_path = /obj/item/stack/sheet/plasteel - category = list("initial") + category = list("initial", "Stock Parts") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + maxstack = 50 /datum/design/plastitanium_alloy name = "Plasma + Titanium alloy" id = "plastitanium" - build_type = SMELTER + build_type = SMELTER | PROTOLATHE materials = list(MAT_TITANIUM = MINERAL_MATERIAL_AMOUNT, MAT_PLASMA = MINERAL_MATERIAL_AMOUNT) build_path = /obj/item/stack/sheet/mineral/plastitanium - category = list("initial") + category = list("initial", "Stock Parts") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + maxstack = 50 /datum/design/plaglass_alloy name = "Plasma + Glass alloy" id = "plasmaglass" - build_type = SMELTER + build_type = SMELTER | PROTOLATHE materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT) build_path = /obj/item/stack/sheet/plasmaglass - category = list("initial") + category = list("initial", "Stock Parts") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + maxstack = 50 /datum/design/plasmarglass_alloy name = "Plasma + Metal + Glass alloy" id = "plasmareinforcedglass" - build_type = SMELTER + build_type = SMELTER | PROTOLATHE materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_METAL = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT) build_path = /obj/item/stack/sheet/plasmarglass - category = list("initial") + category = list("initial", "Stock Parts") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + maxstack = 50 /datum/design/titaniumglass_alloy name = "Titanium + Glass alloy" id = "titaniumglass" - build_type = SMELTER + build_type = SMELTER | PROTOLATHE materials = list(MAT_TITANIUM = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT) build_path = /obj/item/stack/sheet/titaniumglass - category = list("initial") + category = list("initial", "Stock Parts") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + maxstack = 50 /datum/design/plastitaniumglass_alloy name = "Plasma + Titanium + Glass alloy" id = "plastitaniumglass" - build_type = SMELTER + build_type = SMELTER | PROTOLATHE materials = list(MAT_PLASMA = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_TITANIUM = MINERAL_MATERIAL_AMOUNT * 0.5, MAT_GLASS = MINERAL_MATERIAL_AMOUNT) build_path = /obj/item/stack/sheet/plastitaniumglass - category = list("initial") + category = list("initial", "Stock Parts") + departmental_flags = DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING + maxstack = 50 /datum/design/alienalloy name = "Alien Alloy" diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm index 77c6cf85a0..0c7b7327fa 100644 --- a/code/modules/research/designs/weapon_designs.dm +++ b/code/modules/research/designs/weapon_designs.dm @@ -148,7 +148,7 @@ materials = list(MAT_METAL = 3000) build_path = /obj/item/grenade/chem_grenade/large category = list("Weapons") - departmental_flags = DEPARTMENTAL_FLAG_SECURITY | DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + departmental_flags = DEPARTMENTAL_FLAG_SECURITY | DEPARTMENTAL_FLAG_MEDICAL /datum/design/pyro_grenade name = "Pyro Grenade" diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index 37a83eb04f..5cc26381dd 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -36,10 +36,6 @@ Note: Must be placed within 3 tiles of the R&D Console . = 1 if(!is_insertion_ready(user)) return - if(istype (O, /obj/item/bodybag/bluespace)) // CITADEL ADD. STOP PUTTING FUCKING BORGS INTO THE ANALYZER - if(O.contents.len != 0) - to_chat(user, "\The [O] has dangerous levels of activity, you cannot put it in the [src.name]!") - return if(!user.transferItemToLoc(O, src)) to_chat(user, "\The [O] is stuck to your hand, you cannot put it in the [src.name]!") return @@ -63,11 +59,14 @@ Note: Must be placed within 3 tiles of the R&D Console /obj/machinery/rnd/destructive_analyzer/proc/reclaim_materials_from(obj/item/thing) . = 0 - if(linked_console && linked_console.linked_lathe) //Also sends salvaged materials to a linked protolathe, if any. + var/datum/component/material_container/storage = linked_console?.linked_lathe?.materials.mat_container + if(storage) //Also sends salvaged materials to a linked protolathe, if any. for(var/material in thing.materials) - var/can_insert = min((linked_console.linked_lathe.materials.max_amount - linked_console.linked_lathe.materials.total_amount), (max(thing.materials[material]*(decon_mod/10), thing.materials[material]))) - linked_console.linked_lathe.materials.insert_amount(can_insert, material) + var/can_insert = min((storage.max_amount - storage.total_amount), (max(thing.materials[material]*(decon_mod/10), thing.materials[material]))) + storage.insert_amount(can_insert, material) . += can_insert + if (.) + linked_console.linked_lathe.materials.silo_log(src, "reclaimed", 1, "[thing.name]", thing.materials) /obj/machinery/rnd/destructive_analyzer/proc/destroy_item(obj/item/thing, innermode = FALSE) if(QDELETED(thing) || QDELETED(src) || QDELETED(linked_console)) diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm index ccd9872b46..ebd386513e 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -583,7 +583,7 @@ /obj/item/relic/proc/corgicannon(mob/user) playsound(src, "sparks", rand(25,50), 1) var/mob/living/simple_animal/pet/dog/corgi/C = new/mob/living/simple_animal/pet/dog/corgi(get_turf(user)) - C.throw_at(pick(oview(10,user)), 10, rand(3,8), callback = CALLBACK(src, .throwSmoke, C)) + C.throw_at(pick(oview(10,user)), 10, rand(3,8), callback = CALLBACK(src, .proc/throwSmoke, C)) warn_admins(user, "Corgi Cannon", 0) /obj/item/relic/proc/clean(mob/user) diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm index 75ed53e936..d3cd399635 100644 --- a/code/modules/research/machinery/_production.dm +++ b/code/modules/research/machinery/_production.dm @@ -6,7 +6,7 @@ var/consoleless_interface = FALSE //Whether it can be used without a console. var/efficiency_coeff = 1 //Materials needed / coeff = actual. var/list/categories = list() - var/datum/component/material_container/materials //Store for hyper speed! + var/datum/component/remote_materials/materials var/allowed_department_flags = ALL var/production_animation //What's flick()'d on print. var/allowed_buildtypes = NONE @@ -19,19 +19,16 @@ var/screen = RESEARCH_FABRICATOR_SCREEN_MAIN var/selected_category -/obj/machinery/rnd/production/Initialize() +/obj/machinery/rnd/production/Initialize(mapload) . = ..() create_reagents(0) - materials = AddComponent(/datum/component/material_container, - list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE, MAT_PLASTIC), 0, - TRUE, list(/obj/item/stack), CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) - materials.precise_insertion = TRUE - RefreshParts() matching_designs = list() cached_designs = list() stored_research = new host_research = SSresearch.science_tech update_research() + materials = AddComponent(/datum/component/remote_materials, "lathe", mapload) + RefreshParts() /obj/machinery/rnd/production/proc/update_research() host_research.copy_research_to(stored_research, TRUE) @@ -47,9 +44,6 @@ /obj/machinery/rnd/production/RefreshParts() calculate_efficiency() -/obj/machinery/rnd/production/attack_hand(mob/user) - interact(user) //remove this snowflake shit when the refactor of storage components or some other pr that unsnowflakes attack_hand on machinery is in - /obj/machinery/rnd/production/ui_interact(mob/user) if(!consoleless_interface) return ..() @@ -70,9 +64,10 @@ reagents.maximum_volume += G.volume G.reagents.trans_to(src, G.reagents.total_volume) if(materials) - materials.max_amount = 0 + var/total_storage = 0 for(var/obj/item/stock_parts/matter_bin/M in component_parts) - materials.max_amount += M.rating * 75000 + total_storage += M.rating * 75000 + materials.set_local_size(total_storage) var/total_rating = 0 for(var/obj/item/stock_parts/manipulator/M in component_parts) total_rating += M.rating @@ -83,7 +78,6 @@ /obj/machinery/rnd/production/on_deconstruction() for(var/obj/item/reagent_containers/glass/G in component_parts) reagents.trans_to(G, G.reagents.maximum_volume) - materials.retrieve_all() return ..() /obj/machinery/rnd/production/proc/do_print(path, amount, list/matlist, notify_admins) @@ -92,18 +86,26 @@ message_admins("[ADMIN_LOOKUPFLW(usr)] has built [amount] of [path] at a [src]([type]).") for(var/i in 1 to amount) var/obj/item/I = new path(get_turf(src)) - if(!istype(I, /obj/item/stack/sheet) && !istype(I, /obj/item/stack/ore/bluespace_crystal)) + if(efficient_with(I.type)) I.materials = matlist.Copy() SSblackbox.record_feedback("nested tally", "item_printed", amount, list("[type]", "[path]")) /obj/machinery/rnd/production/proc/check_mat(datum/design/being_built, M) // now returns how many times the item can be built with the material + if (!materials.mat_container) // no connected silo + return 0 var/list/all_materials = being_built.reagents_list + being_built.materials - var/A = materials.amount(M) + var/A = materials.mat_container.amount(M) if(!A) A = reagents.get_reagent_amount(M) - return round(A / max(1, (all_materials[M]/efficiency_coeff))) + // these types don't have their .materials set in do_print, so don't allow + // them to be constructed efficiently + var/ef = efficient_with(being_built.build_path) ? efficiency_coeff : 1 + return round(A / max(1, all_materials[M] / ef)) + +/obj/machinery/rnd/production/proc/efficient_with(path) + return !ispath(path, /obj/item/stack/sheet) && !ispath(path, /obj/item/stack/ore/bluespace_crystal) /obj/machinery/rnd/production/proc/user_try_print_id(id, amount) if((!istype(linked_console) && requires_console) || !id) @@ -121,25 +123,33 @@ if(D.build_type && !(D.build_type & allowed_buildtypes)) say("This machine does not have the necessary manipulation systems for this design. Please contact Nanotrasen Support!") return FALSE + if(!materials.mat_container) + say("No connection to material storage, please contact the quartermaster.") + return FALSE + if(materials.on_hold()) + say("Mineral access is on hold, please contact the quartermaster.") + return FALSE var/power = 1000 amount = CLAMP(amount, 1, 50) for(var/M in D.materials) power += round(D.materials[M] * amount / 35) power = min(3000, power) use_power(power) + var/coeff = efficient_with(D.build_path) ? efficiency_coeff : 1 var/list/efficient_mats = list() for(var/MAT in D.materials) - efficient_mats[MAT] = D.materials[MAT]/efficiency_coeff - if(!materials.has_materials(efficient_mats, amount)) + efficient_mats[MAT] = D.materials[MAT]/coeff + if(!materials.mat_container.has_materials(efficient_mats, amount)) say("Not enough materials to complete prototype[amount > 1? "s" : ""].") return FALSE for(var/R in D.reagents_list) - if(!reagents.has_reagent(R, D.reagents_list[R]*amount/efficiency_coeff)) + if(!reagents.has_reagent(R, D.reagents_list[R]*amount/coeff)) say("Not enough reagents to complete prototype[amount > 1? "s" : ""].") return FALSE - materials.use_amount(efficient_mats, amount) + materials.mat_container.use_amount(efficient_mats, amount) + materials.silo_log(src, "built", -amount, "[D.name]", efficient_mats) for(var/R in D.reagents_list) - reagents.remove_reagent(R, D.reagents_list[R]*amount/efficiency_coeff) + reagents.remove_reagent(R, D.reagents_list[R]*amount/coeff) busy = TRUE if(production_animation) flick(production_animation, src) @@ -181,17 +191,23 @@ var/list/l = list() l += "

[host_research.organization] [department_tag] Department Lathe" l += "Security protocols: [(obj_flags & EMAGGED)? "Disabled" : "Enabled"]" - l += "Material Amount: [materials.total_amount] / [materials.max_amount]" + if (materials.mat_container) + l += "Material Amount: [materials.format_amount()]" + else + l += "No material storage connected, please contact the quartermaster." l += "Chemical volume: [reagents.total_volume] / [reagents.maximum_volume]" l += "Synchronize Research" l += "Main Screen
[RDSCREEN_NOBREAK]" return l /obj/machinery/rnd/production/proc/ui_screen_materials() + if (!materials.mat_container) + screen = RESEARCH_FABRICATOR_SCREEN_MAIN + return ui_screen_main() var/list/l = list() l += "

Material Storage:

" - for(var/mat_id in materials.materials) - var/datum/material/M = materials.materials[mat_id] + for(var/mat_id in materials.mat_container.materials) + var/datum/material/M = materials.mat_container.materials[mat_id] l += "* [M.amount] of [M.name]: " if(M.amount >= MINERAL_MATERIAL_AMOUNT) l += "Eject [RDSCREEN_NOBREAK]" if(M.amount >= MINERAL_MATERIAL_AMOUNT*5) l += "5x [RDSCREEN_NOBREAK]" @@ -230,6 +246,8 @@ return if(!coeff) coeff = efficiency_coeff + if(!efficient_with(D.build_path)) + coeff = 1 var/list/l = list() var/temp_material var/c = 50 @@ -281,9 +299,23 @@ if(ls["disposeall"]) //Causes the protolathe to dispose of all it's reagents. reagents.clear_reagents() if(ls["ejectsheet"]) //Causes the protolathe to eject a sheet of material - materials.retrieve_sheets(text2num(ls["eject_amt"]), ls["ejectsheet"]) + eject_sheets(ls["ejectsheet"], ls["eject_amt"]) updateUsrDialog() +/obj/machinery/rnd/production/proc/eject_sheets(eject_sheet, eject_amt) + var/datum/component/material_container/mat_container = materials.mat_container + if (!mat_container) + say("No access to material storage, please contact the quartermaster.") + return 0 + if (materials.on_hold()) + say("Mineral access is on hold, please contact the quartermaster.") + return 0 + var/count = mat_container.retrieve_sheets(text2num(eject_amt), eject_sheet, drop_location()) + var/list/matlist = list() + matlist[eject_sheet] = MINERAL_MATERIAL_AMOUNT + materials.silo_log(src, "ejected", -count, "sheets", matlist) + return count + /obj/machinery/rnd/production/proc/ui_screen_main() var/list/l = list() l += "
\ diff --git a/code/modules/research/nanites/nanite_chamber.dm b/code/modules/research/nanites/nanite_chamber.dm index 6f58cb2ad9..1fc2633989 100644 --- a/code/modules/research/nanites/nanite_chamber.dm +++ b/code/modules/research/nanites/nanite_chamber.dm @@ -4,12 +4,12 @@ circuit = /obj/item/circuitboard/machine/nanite_chamber icon = 'icons/obj/machines/nanite_chamber.dmi' icon_state = "nanite_chamber" + layer = ABOVE_WINDOW_LAYER use_power = IDLE_POWER_USE anchored = TRUE density = TRUE idle_power_usage = 50 active_power_usage = 300 - occupant_typecache = list(/mob/living) var/obj/machinery/computer/nanite_chamber_control/console var/locked = FALSE @@ -20,6 +20,10 @@ var/busy_message var/message_cooldown = 0 +/obj/machinery/nanite_chamber/Initialize() + . = ..() + occupant_typecache = GLOB.typecache_living + /obj/machinery/nanite_chamber/RefreshParts() scan_level = 0 for(var/obj/item/stock_parts/scanning_module/P in component_parts) @@ -224,4 +228,4 @@ /obj/machinery/nanite_chamber/MouseDrop_T(mob/target, mob/user) if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser()) return - close_machine(target) \ No newline at end of file + close_machine(target) diff --git a/code/modules/research/nanites/nanite_cloud_controller.dm b/code/modules/research/nanites/nanite_cloud_controller.dm index 8218f250bb..081b1a7a4c 100644 --- a/code/modules/research/nanites/nanite_cloud_controller.dm +++ b/code/modules/research/nanites/nanite_cloud_controller.dm @@ -167,7 +167,7 @@ if(backup) playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) var/datum/component/nanites/nanites = backup.nanites - nanites.add_program(disk.program.copy()) + nanites.add_program(null, disk.program.copy()) investigate_log("[key_name(usr)] uploaded program [disk.program.name] to cloud #[current_view]", INVESTIGATE_NANITES) . = TRUE if("remove_program") diff --git a/code/modules/research/nanites/nanite_hijacker.dm b/code/modules/research/nanites/nanite_hijacker.dm index 8c5ac0a84b..dba47a4ac2 100644 --- a/code/modules/research/nanites/nanite_hijacker.dm +++ b/code/modules/research/nanites/nanite_hijacker.dm @@ -15,6 +15,11 @@ if(disk) eject() +/obj/item/nanite_hijacker/examine(mob/user) + . = ..() + if(disk) + to_chat(user, "Alt-click [src] to eject the disk.") + /obj/item/nanite_hijacker/attackby(obj/item/I, mob/user) if(istype(I, /obj/item/disk/nanite_program)) var/obj/item/disk/nanite_program/N = I diff --git a/code/modules/research/nanites/nanite_programs.dm b/code/modules/research/nanites/nanite_programs.dm index 00581154d9..f691c828c9 100644 --- a/code/modules/research/nanites/nanite_programs.dm +++ b/code/modules/research/nanites/nanite_programs.dm @@ -224,7 +224,7 @@ if(5) //Program is scrambled and does something different var/rogue_type = pick(rogue_types) var/datum/nanite_program/rogue = new rogue_type - nanites.add_program(rogue, src) + nanites.add_program(null, rogue, src) qdel(src) /datum/nanite_program/proc/receive_signal(code, source) diff --git a/code/modules/research/nanites/nanite_programs/buffing.dm b/code/modules/research/nanites/nanite_programs/buffing.dm index e97f3b834d..a26cb7bd68 100644 --- a/code/modules/research/nanites/nanite_programs/buffing.dm +++ b/code/modules/research/nanites/nanite_programs/buffing.dm @@ -12,12 +12,6 @@ var/mob/living/carbon/human/H = host_mob H.physiology.stun_mod *= 0.5 -/datum/nanite_program/nervous/active_effect() - . = ..() - if(iscarbon(host_mob)) - var/mob/living/carbon/C = host_mob - C.adjustStaminaLoss(-2) - /datum/nanite_program/nervous/disable_passive_effect() . = ..() if(ishuman(host_mob)) @@ -133,4 +127,4 @@ /datum/nanite_program/mindshield/disable_passive_effect() . = ..() host_mob.remove_trait(TRAIT_MINDSHIELD, "nanites") - host_mob.sec_hud_set_implants() + host_mob.sec_hud_set_implants() \ No newline at end of file diff --git a/code/modules/research/nanites/nanite_programs/healing.dm b/code/modules/research/nanites/nanite_programs/healing.dm index 418039757c..df32a5d127 100644 --- a/code/modules/research/nanites/nanite_programs/healing.dm +++ b/code/modules/research/nanites/nanite_programs/healing.dm @@ -128,7 +128,7 @@ return var/update = FALSE for(var/obj/item/bodypart/L in parts) - if(L.heal_damage(1/parts.len, 1/parts.len)) + if(L.heal_damage(1/parts.len, 1/parts.len, only_robotic = TRUE, only_organic = FALSE)) update = TRUE if(update) host_mob.update_damage_overlays() @@ -208,10 +208,8 @@ if(!..()) return - playsound(host_mob, 'sound/machines/defib_zap.ogg', 75, 1, -1) - if(check_revivable()) - host_mob.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!", source = src) - addtimer(CALLBACK(src, .proc/zap), 30) + host_mob.notify_ghost_cloning("Your heart is being defibrillated by nanites. Re-enter your corpse if you want to be revived!") + addtimer(CALLBACK(src, .proc/zap), 50) /datum/nanite_program/triggered/defib/proc/check_revivable() if(!iscarbon(host_mob)) //nonstandard biology @@ -219,22 +217,25 @@ var/mob/living/carbon/C = host_mob if(C.suiciding || C.has_trait(TRAIT_NOCLONE) || C.hellbound) //can't revive return FALSE - if((world.time - C.timeofdeath) < 1800) //too late + if((world.time - C.timeofdeath) > 1800) //too late return FALSE - if((C.getBruteLoss() > 180) || (C.getFireLoss() > 180)) //too damaged + if((C.getBruteLoss() > 180) || (C.getFireLoss() > 180) || !C.can_be_revived()) //too damaged return FALSE if(!C.getorgan(/obj/item/organ/heart)) //what are we even shocking return FALSE var/obj/item/organ/brain/BR = C.getorgan(/obj/item/organ/brain) if(QDELETED(BR) || BR.damaged_brain) return FALSE - if(!C.ckey && !C.get_ghost()) + if(C.get_ghost()) return FALSE return TRUE /datum/nanite_program/triggered/defib/proc/zap() - if(check_revivable() && host_mob.ckey) - var/mob/living/carbon/C = host_mob + var/mob/living/carbon/C = host_mob + playsound(C, 'sound/machines/defib_charge.ogg', 50, 0) + sleep(30) + playsound(C, 'sound/machines/defib_zap.ogg', 50, 0) + if(check_revivable()) playsound(C, 'sound/machines/defib_success.ogg', 50, 0) C.set_heartattack(FALSE) C.revive() @@ -246,5 +247,5 @@ C.adjustBrainLoss( max(0, ((1800 - tplus) / 1800 * 150)), 150) log_game("[C] has been successfully defibrillated by nanites.") else - playsound(host_mob, 'sound/machines/defib_failed.ogg', 50, 0) + playsound(C, 'sound/machines/defib_failed.ogg', 50, 0) diff --git a/code/modules/research/nanites/nanite_programs/suppression.dm b/code/modules/research/nanites/nanite_programs/suppression.dm index 9d501e626a..3d89baba68 100644 --- a/code/modules/research/nanites/nanite_programs/suppression.dm +++ b/code/modules/research/nanites/nanite_programs/suppression.dm @@ -147,7 +147,7 @@ if(host_mob.stat == DEAD) return to_chat(host_mob, "You feel compelled to speak...") - host_mob.say(sentence) + host_mob.say(sentence, forced = "nanite speech") /datum/nanite_program/triggered/voice name = "Skull Echo" diff --git a/code/modules/research/nanites/public_chamber.dm b/code/modules/research/nanites/public_chamber.dm index 9630e6d1c8..c0d19e0375 100644 --- a/code/modules/research/nanites/public_chamber.dm +++ b/code/modules/research/nanites/public_chamber.dm @@ -4,12 +4,12 @@ circuit = /obj/item/circuitboard/machine/public_nanite_chamber icon = 'icons/obj/machines/nanite_chamber.dmi' icon_state = "nanite_chamber" + layer = ABOVE_WINDOW_LAYER use_power = IDLE_POWER_USE anchored = TRUE density = TRUE idle_power_usage = 50 active_power_usage = 300 - occupant_typecache = list(/mob/living) var/cloud_id = 1 var/locked = FALSE @@ -18,6 +18,10 @@ var/busy_icon_state var/message_cooldown = 0 +/obj/machinery/public_nanite_chamber/Initialize() + . = ..() + occupant_typecache = GLOB.typecache_living + /obj/machinery/public_nanite_chamber/RefreshParts() var/obj/item/circuitboard/machine/public_nanite_chamber/board = circuit if(board) @@ -171,4 +175,4 @@ /obj/machinery/public_nanite_chamber/MouseDrop_T(mob/target, mob/user) if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser()) return - close_machine(target) \ No newline at end of file + close_machine(target) diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index 0ba9312314..fb3c5908f5 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -197,6 +197,11 @@ Nothing else in the console has ID requirements. locked = FALSE return ..() +/obj/machinery/computer/rdconsole/multitool_act(mob/user, obj/item/multitool/I) + var/lathe = linked_lathe && linked_lathe.multitool_act(user, I) + var/print = linked_imprinter && linked_imprinter.multitool_act(user, I) + return lathe || print + /obj/machinery/computer/rdconsole/proc/list_categories(list/categories, menu_num as num) if(!categories) return @@ -268,7 +273,10 @@ Nothing else in the console has ID requirements. /obj/machinery/computer/rdconsole/proc/ui_protolathe_header() var/list/l = list() l += "" return l @@ -277,7 +285,6 @@ Nothing else in the console has ID requirements. var/list/l = list() l += ui_protolathe_header() l += "

Browsing [selected_category]:

" - var/coeff = linked_lathe.efficiency_coeff for(var/v in stored_research.researched_designs) var/datum/design/D = stored_research.researched_designs[v] if(!(selected_category in D.category)|| !(D.build_type & PROTOLATHE)) @@ -286,11 +293,13 @@ Nothing else in the console has ID requirements. continue var/temp_material var/c = 50 - var/t + var/coeff = linked_lathe.efficiency_coeff + if(!linked_lathe.efficient_with(D.build_path)) + coeff = 1 var/all_materials = D.materials + D.reagents_list for(var/M in all_materials) - t = linked_lathe.check_mat(D, M) + var/t = linked_lathe.check_mat(D, M) temp_material += " | " if (t < 1) temp_material += "[all_materials[M]/coeff] [CallMaterialName(M)]" @@ -332,16 +341,17 @@ Nothing else in the console has ID requirements. RDSCREEN_UI_LATHE_CHECK var/list/l = list() l += ui_protolathe_header() - var/coeff = linked_lathe.efficiency_coeff for(var/datum/design/D in matching_designs) if(!(isnull(linked_lathe.allowed_department_flags) || (D.departmental_flags & linked_lathe.allowed_department_flags))) continue var/temp_material var/c = 50 - var/t var/all_materials = D.materials + D.reagents_list + var/coeff = linked_lathe.efficiency_coeff + if(!linked_lathe.efficient_with(D.build_path)) + coeff = 1 for(var/M in all_materials) - t = linked_lathe.check_mat(D, M) + var/t = linked_lathe.check_mat(D, M) temp_material += " | " if (t < 1) temp_material += "[all_materials[M]/coeff] [CallMaterialName(M)]" @@ -364,11 +374,15 @@ Nothing else in the console has ID requirements. /obj/machinery/computer/rdconsole/proc/ui_protolathe_materials() //Legacy code RDSCREEN_UI_LATHE_CHECK + var/datum/component/material_container/mat_container = linked_lathe.materials.mat_container + if (!mat_container) + screen = RDSCREEN_PROTOLATHE + return ui_protolathe() var/list/l = list() l += ui_protolathe_header() l += "

Material Storage:

" - for(var/mat_id in linked_lathe.materials.materials) - var/datum/material/M = linked_lathe.materials.materials[mat_id] + for(var/mat_id in mat_container.materials) + var/datum/material/M = mat_container.materials[mat_id] l += "* [M.amount] of [M.name]: " if(M.amount >= MINERAL_MATERIAL_AMOUNT) l += "Eject [RDSCREEN_NOBREAK]" if(M.amount >= MINERAL_MATERIAL_AMOUNT*5) l += "5x [RDSCREEN_NOBREAK]" @@ -392,7 +406,10 @@ Nothing else in the console has ID requirements. /obj/machinery/computer/rdconsole/proc/ui_circuit_header() //Legacy Code var/list/l = list() l += "" return l @@ -419,7 +436,6 @@ Nothing else in the console has ID requirements. l += ui_circuit_header() l += "

Browsing [selected_category]:

" - var/coeff = linked_imprinter.efficiency_coeff for(var/v in stored_research.researched_designs) var/datum/design/D = stored_research.researched_designs[v] if(!(selected_category in D.category) || !(D.build_type & IMPRINTER)) @@ -430,6 +446,9 @@ Nothing else in the console has ID requirements. var/check_materials = TRUE var/all_materials = D.materials + D.reagents_list + var/coeff = linked_imprinter.efficiency_coeff + if(!linked_imprinter.efficient_with(D.build_path)) + coeff = 1 for(var/M in all_materials) temp_materials += " | " @@ -451,13 +470,15 @@ Nothing else in the console has ID requirements. l += ui_circuit_header() l += "

Search results:

" - var/coeff = linked_imprinter.efficiency_coeff for(var/datum/design/D in matching_designs) if(!(isnull(linked_imprinter.allowed_department_flags) || (D.departmental_flags & linked_imprinter.allowed_department_flags))) continue var/temp_materials var/check_materials = TRUE var/all_materials = D.materials + D.reagents_list + var/coeff = linked_imprinter.efficiency_coeff + if(!linked_imprinter.efficient_with(D.build_path)) + coeff = 1 for(var/M in all_materials) temp_materials += " | " if (!linked_imprinter.check_mat(D, M)) @@ -485,11 +506,15 @@ Nothing else in the console has ID requirements. /obj/machinery/computer/rdconsole/proc/ui_circuit_materials() //Legacy code! RDSCREEN_UI_IMPRINTER_CHECK + var/datum/component/material_container/mat_container = linked_imprinter.materials.mat_container + if (!mat_container) + screen = RDSCREEN_IMPRINTER + return ui_circuit() var/list/l = list() l += ui_circuit_header() l += "

Material Storage:

" - for(var/mat_id in linked_imprinter.materials.materials) - var/datum/material/M = linked_imprinter.materials.materials[mat_id] + for(var/mat_id in mat_container.materials) + var/datum/material/M = mat_container.materials[mat_id] l += "* [M.amount] of [M.name]: " if(M.amount >= MINERAL_MATERIAL_AMOUNT) l += "Eject [RDSCREEN_NOBREAK]" if(M.amount >= MINERAL_MATERIAL_AMOUNT*5) l += "5x [RDSCREEN_NOBREAK]" @@ -906,7 +931,10 @@ Nothing else in the console has ID requirements. if(QDELETED(linked_lathe)) say("No Protolathe Linked!") return - linked_lathe.materials.retrieve_sheets(text2num(ls["eject_amt"]), ls["ejectsheet"]) + if(!linked_lathe.materials.mat_container) + say("No material storage linked to protolathe!") + return + linked_lathe.eject_sheets(ls["ejectsheet"], ls["eject_amt"]) //Circuit Imprinter Materials if(ls["disposeI"]) //Causes the circuit imprinter to dispose of a single reagent (all of it) if(QDELETED(linked_imprinter)) @@ -922,7 +950,10 @@ Nothing else in the console has ID requirements. if(QDELETED(linked_imprinter)) say("No Circuit Imprinter Linked!") return - linked_imprinter.materials.retrieve_sheets(text2num(ls["eject_amt"]), ls["imprinter_ejectsheet"]) + if(!linked_imprinter.materials.mat_container) + say("No material storage linked to circuit imprinter!") + return + linked_imprinter.eject_sheets(ls["imprinter_ejectsheet"], ls["eject_amt"]) if(ls["disk_slot"]) disk_slot_selected = text2num(ls["disk_slot"]) if(ls["research_node"]) diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm index 7c23609c21..f11b0ad709 100644 --- a/code/modules/research/techweb/_techweb.dm +++ b/code/modules/research/techweb/_techweb.dm @@ -355,5 +355,4 @@ allowed_buildtypes = SMELTER /datum/techweb/specialized/autounlocking/exofab - node_autounlock_ids = list("robotics", "mmi", "cyborg", "mecha_odysseus", "mech_gygax", "mech_durand", "mecha_phazon", "mecha", "mech_tools", "clown") allowed_buildtypes = MECHFAB diff --git a/code/modules/research/techweb/_techweb_node.dm b/code/modules/research/techweb/_techweb_node.dm index f5fad6f3dd..875329a89c 100644 --- a/code/modules/research/techweb/_techweb_node.dm +++ b/code/modules/research/techweb/_techweb_node.dm @@ -15,7 +15,7 @@ var/list/boost_item_paths = list() //Associative list, path = list(point type = point_value). var/autounlock_by_boost = TRUE //boosting this will autounlock this node. var/export_price = 0 //Cargo export price. - var/list/research_costs = list() //Point cost to research. type = amount + var/list/research_costs = 0 //Point cost to research. type = amount var/category = "Misc" //Category /datum/techweb_node/proc/get_price(datum/techweb/host) diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index e5c29b366b..c704ede60c 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -1,16 +1,47 @@ //Current rate: 132500 research points in 90 minutes -//Base Node +//Base Nodes /datum/techweb_node/base id = "base" starting_node = TRUE display_name = "Basic Research Technology" description = "NT default research technologies." + // Default research tech, prevents bricking design_ids = list("basic_matter_bin", "basic_cell", "basic_scanning", "basic_capacitor", "basic_micro_laser", "micro_mani", "destructive_analyzer", "circuit_imprinter", "experimentor", "rdconsole", "design_disk", "tech_disk", "rdserver", "rdservercontrol", "mechfab", - "space_heater", "xlarge_beaker", "sec_rshot", "sec_bshot", "sec_slug", "sec_Islug", "sec_dart", "sec_38") //Default research tech, prevents bricking + "space_heater", "xlarge_beaker", "sec_rshot", "sec_bshot", "sec_slug", "sec_Islug", "sec_dart", "sec_38", + "rglass","plasteel","plastitanium","plasmaglass","plasmareinforcedglass","titaniumglass","plastitaniumglass") +/datum/techweb_node/mmi + id = "mmi" + starting_node = TRUE + display_name = "Man Machine Interface" + description = "A slightly Frankensteinian device that allows human brains to interface natively with software APIs." + design_ids = list("mmi") + +/datum/techweb_node/cyborg + id = "cyborg" + starting_node = TRUE + display_name = "Cyborg Construction" + description = "Sapient robots with preloaded tool modules and programmable laws." + design_ids = list("robocontrol", "sflash", "borg_suit", "borg_head", "borg_chest", "borg_r_arm", "borg_l_arm", "borg_r_leg", "borg_l_leg", "borgupload", + "cyborgrecharger", "borg_upgrade_restart", "borg_upgrade_rename") + +/datum/techweb_node/mech + id = "mecha" + starting_node = TRUE + display_name = "Mechanical Exosuits" + description = "Mechanized exosuits that are several magnitudes stronger and more powerful than the average human." + design_ids = list("mecha_tracking", "mechacontrol", "mechapower", "mech_recharger", "ripley_chassis", "firefighter_chassis", "ripley_torso", "ripley_left_arm", "ripley_right_arm", "ripley_left_leg", "ripley_right_leg", + "ripley_main", "ripley_peri", "mech_hydraulic_clamp") + +/datum/techweb_node/mech_tools + id = "mech_tools" + starting_node = TRUE + display_name = "Basic Exosuit Equipment" + description = "Various tools fit for basic mech units" + design_ids = list("mech_drill", "mech_mscanner", "mech_extinguisher", "mech_cable_layer") /////////////////////////Biotech///////////////////////// /datum/techweb_node/biotech @@ -27,7 +58,7 @@ display_name = "Advanced Biotechnology" description = "Advanced Biotechnology" prereq_ids = list("biotech") - design_ids = list("piercesyringe", "smoke_machine", "plasmarefiller", "limbgrower", "defibrillator", "meta_beaker", "healthanalyzer_advanced") + design_ids = list("piercesyringe", "crewpinpointer", "smoke_machine", "plasmarefiller", "limbgrower", "defibrillator", "meta_beaker", "healthanalyzer_advanced","harvester","holobarrier_med") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -103,7 +134,7 @@ display_name = "Advanced Engineering" description = "Pushing the boundaries of physics, one chainsaw-fist at a time." prereq_ids = list("engineering", "emp_basic") - design_ids = list("engine_goggles", "magboots", "weldingmask") + design_ids = list("engine_goggles", "magboots", "forcefield_projector", "weldingmask") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -159,7 +190,7 @@ display_name = "Applied Bluespace Research" description = "Using bluespace to make things faster and better." prereq_ids = list("bluespace_basic", "engineering") - design_ids = list("bs_rped","minerbag_holding", "bluespacebeaker", "bluespacesyringe", "bluespacebodybag", "phasic_scanning", "roastingstick") + design_ids = list("bs_rped","minerbag_holding", "bluespacebeaker", "bluespacesyringe", "bluespacebodybag", "phasic_scanning", "roastingstick", "ore_silo") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000) export_price = 5000 @@ -219,39 +250,20 @@ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 -/datum/techweb_node/mmi - id = "mmi" - display_name = "Man Machine Interface" - description = "A slightly Frankensteinian device that allows human brains to interface natively with software APIs." - prereq_ids = list("neural_programming") - design_ids = list("mmi") - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) - export_price = 5000 - /datum/techweb_node/posibrain id = "posibrain" display_name = "Positronic Brain" description = "Applied usage of neural technology allowing for autonomous AI units based on special metallic cubes with conductive and processing circuits." - prereq_ids = list("neural_programming", "mmi") + prereq_ids = list("neural_programming") design_ids = list("mmi_posi") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 -/datum/techweb_node/cyborg - id = "cyborg" - display_name = "Cyborg Construction" - description = "Sapient robots with preloaded tool modules and programmable laws." - prereq_ids = list("mmi", "robotics") - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000) - export_price = 5000 - design_ids = list("robocontrol", "sflash", "borg_suit", "borg_head", "borg_chest", "borg_r_arm", "borg_l_arm", "borg_r_leg", "borg_l_leg", "borgupload", - "cyborgrecharger", "borg_upgrade_restart", "borg_upgrade_rename") - /datum/techweb_node/cyborg_upg_util id = "cyborg_upg_util" display_name = "Cyborg Upgrades: Utility" - description = "Utility upgrades for cybogs." - prereq_ids = list("engineering", "cyborg") + description = "Utility upgrades for cyborgs." + prereq_ids = list("engineering") design_ids = list("borg_upgrade_holding", "borg_upgrade_lavaproof", "borg_upgrade_thrusters", "borg_upgrade_selfrepair", "borg_upgrade_expand", "borg_upgrade_rped") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000) export_price = 5000 @@ -260,8 +272,8 @@ id = "cyborg_upg_med" display_name = "Cyborg Upgrades: Medical" description = "Medical upgrades for cyborgs." - prereq_ids = list("adv_biotech", "cyborg") - design_ids = list("borg_upgrade_defibrillator", "borg_upgrade_piercinghypospray", "borg_upgrade_highstrengthsynthesiser", "borg_upgrade_expandedsynthesiser", "borg_upgrade_pinpointer") + prereq_ids = list("adv_biotech") + design_ids = list("borg_upgrade_defibrillator", "borg_upgrade_piercinghypospray", "borg_upgrade_highstrengthsynthesiser", "borg_upgrade_expandedsynthesiser", "borg_upgrade_pinpointer", "borg_upgrade_surgicalprocessor") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2000) export_price = 5000 @@ -415,7 +427,7 @@ display_name = "Subdermal Implants" description = "Electronic implants buried beneath the skin." prereq_ids = list("biotech") - design_ids = list("implanter", "implantcase", "implant_chem", "implant_tracking") + design_ids = list("implanter", "implantcase", "implant_chem", "implant_tracking", "locator") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -423,7 +435,7 @@ id = "cyber_organs" display_name = "Cybernetic Organs" description = "We have the technology to rebuild him." - prereq_ids = list("adv_biotech", "cyborg") + prereq_ids = list("adv_biotech") design_ids = list("cybernetic_heart", "cybernetic_liver", "cybernetic_liver_u", "cybernetic_lungs") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -432,7 +444,7 @@ id = "cyber_implants" display_name = "Cybernetic Implants" description = "Electronic implants that improve humans." - prereq_ids = list("adv_biotech", "cyborg", "adv_datatheory") + prereq_ids = list("adv_biotech", "adv_datatheory") design_ids = list("ci-nutriment", "ci-breather", "ci-gloweyes", "ci-welding", "ci-medhud", "ci-sechud") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -501,15 +513,6 @@ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 -/datum/techweb_node/engine_hardsuit - id = "engi_hardsuit" - display_name = "EVA Construction Equipment" - description = "Production of EVA construction equipment." - design_ids = list("chardsuit", "hardsuitjpack") - prereq_ids = list("adv_engi", "exp_tools") - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000) - export_price = 5000 - /////////////////////////weaponry tech///////////////////////// /datum/techweb_node/weaponry id = "weaponry" @@ -611,21 +614,11 @@ export_price = 5000 ////////////////////////mech technology//////////////////////// -/datum/techweb_node/mech - id = "mecha" - display_name = "Mechanical Exosuits" - description = "Mechanized exosuits that are several magnitudes stronger and more powerful than the average human." - prereq_ids = list("robotics", "adv_engi") - design_ids = list("mecha_tracking", "mechacontrol", "mechapower", "mech_recharger", "ripley_chassis", "firefighter_chassis", "ripley_torso", "ripley_left_arm", "ripley_right_arm", "ripley_left_leg", "ripley_right_leg", - "ripley_main", "ripley_peri", "mech_hydraulic_clamp") - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) - export_price = 5000 - /datum/techweb_node/adv_mecha id = "adv_mecha" display_name = "Advanced Exosuits" description = "For when you just aren't Gundam enough." - prereq_ids = list("adv_robotics", "mecha") + prereq_ids = list("adv_robotics") design_ids = list("mech_repair_droid") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -634,7 +627,7 @@ id = "mecha_odysseus" display_name = "EXOSUIT: Odysseus" description = "Odysseus exosuit designs" - prereq_ids = list("mecha") + prereq_ids = list("base") design_ids = list("odysseus_chassis", "odysseus_torso", "odysseus_head", "odysseus_left_arm", "odysseus_right_arm" ,"odysseus_left_leg", "odysseus_right_leg", "odysseus_main", "odysseus_peri") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) @@ -670,20 +663,11 @@ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 -/datum/techweb_node/mech_tools - id = "mech_tools" - display_name = "Basic Exosuit Equipment" - description = "Various tools fit for basic mech units" - prereq_ids = list("mecha") - design_ids = list("mech_drill", "mech_mscanner", "mech_extinguisher", "mech_cable_layer") - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) - export_price = 5000 - /datum/techweb_node/adv_mecha_tools id = "adv_mecha_tools" display_name = "Advanced Exosuit Equipment" description = "Tools for high level mech suits" - prereq_ids = list("adv_mecha", "mech_tools") + prereq_ids = list("adv_mecha") design_ids = list("mech_rcd") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -692,7 +676,7 @@ id = "med_mech_tools" display_name = "Medical Exosuit Equipment" description = "Tools for high level mech suits" - prereq_ids = list("mecha", "adv_biotech", "mech_tools") + prereq_ids = list("adv_biotech") design_ids = list("mech_sleeper", "mech_syringe_gun", "mech_medi_beam") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -710,7 +694,7 @@ id = "mecha_tools" display_name = "Exosuit Weapon (LBX AC 10 \"Scattershot\")" description = "An advanced piece of mech weaponry" - prereq_ids = list("mecha", "ballistic_weapons") + prereq_ids = list("ballistic_weapons") design_ids = list("mech_scattershot") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -719,7 +703,7 @@ id = "mech_carbine" display_name = "Exosuit Weapon (FNX-99 \"Hades\" Carbine)" description = "An advanced piece of mech weaponry" - prereq_ids = list("mecha", "ballistic_weapons") + prereq_ids = list("ballistic_weapons") design_ids = list("mech_carbine") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -728,7 +712,7 @@ id = "mmech_ion" display_name = "Exosuit Weapon (MKIV Ion Heavy Cannon)" description = "An advanced piece of mech weaponry" - prereq_ids = list("mecha", "electronic_weapons", "emp_adv") + prereq_ids = list("electronic_weapons", "emp_adv") design_ids = list("mech_ion") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -737,7 +721,7 @@ id = "mech_tesla" display_name = "Exosuit Weapon (MKI Tesla Cannon)" description = "An advanced piece of mech weaponry" - prereq_ids = list("mecha", "electronic_weapons", "adv_power") + prereq_ids = list("electronic_weapons", "adv_power") design_ids = list("mech_tesla") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -746,7 +730,7 @@ id = "mech_laser" display_name = "Exosuit Weapon (CH-PS \"Immolator\" Laser)" description = "A basic piece of mech weaponry" - prereq_ids = list("mecha", "beam_weapons") + prereq_ids = list("beam_weapons") design_ids = list("mech_laser") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -755,7 +739,7 @@ id = "mech_laser_heavy" display_name = "Exosuit Weapon (CH-LC \"Solaris\" Laser Cannon)" description = "An advanced piece of mech weaponry" - prereq_ids = list("mecha", "adv_beam_weapons") + prereq_ids = list("adv_beam_weapons") design_ids = list("mech_laser_heavy") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -764,7 +748,7 @@ id = "mech_grenade_launcher" display_name = "Exosuit Weapon (SGL-6 Grenade Launcher)" description = "An advanced piece of mech weaponry" - prereq_ids = list("mecha", "explosive_weapons") + prereq_ids = list("explosive_weapons") design_ids = list("mech_grenade_launcher") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -773,7 +757,7 @@ id = "mech_missile_rack" display_name = "Exosuit Weapon (SRM-8 Missile Rack)" description = "An advanced piece of mech weaponry" - prereq_ids = list("mecha", "explosive_weapons") + prereq_ids = list("explosive_weapons") design_ids = list("mech_missile_rack") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -782,7 +766,7 @@ id = "clusterbang_launcher" display_name = "Exosuit Module (SOB-3 Clusterbang Launcher)" description = "An advanced piece of mech weaponry" - prereq_ids = list("mecha", "explosive_weapons") + prereq_ids = list("explosive_weapons") design_ids = list("clusterbang_launcher") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -791,7 +775,7 @@ id = "mech_teleporter" display_name = "Exosuit Module (Teleporter Module)" description = "An advanced piece of mech Equipment" - prereq_ids = list("mech_tools", "adv_bluespace") + prereq_ids = list("adv_bluespace") design_ids = list("mech_teleporter") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -800,7 +784,7 @@ id = "mech_wormhole_gen" display_name = "Exosuit Module (Localized Wormhole Generator)" description = "An advanced piece of mech weaponry" - prereq_ids = list("mecha", "mech_tools", "adv_bluespace") + prereq_ids = list("adv_bluespace") design_ids = list("mech_wormhole_gen") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -809,7 +793,7 @@ id = "mech_taser" display_name = "Exosuit Weapon (PBT \"Pacifier\" Mounted Taser)" description = "A basic piece of mech weaponry" - prereq_ids = list("mecha", "electronic_weapons") + prereq_ids = list("electronic_weapons") design_ids = list("mech_taser") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -818,7 +802,7 @@ id = "mech_lmg" display_name = "Exosuit Weapon (\"Ultra AC 2\" LMG)" description = "An advanced piece of mech weaponry" - prereq_ids = list("mecha", "ballistic_weapons") + prereq_ids = list("ballistic_weapons") design_ids = list("mech_lmg") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 @@ -827,7 +811,7 @@ id = "mech_diamond_drill" display_name = "Exosuit Diamond Drill" description = "A diamond drill fit for a large exosuit" - prereq_ids = list("mecha", "adv_mining") + prereq_ids = list("adv_mining") design_ids = list("mech_diamond_drill") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) export_price = 5000 diff --git a/code/modules/research/xenobiology/crossbreeding/_corecross.dm b/code/modules/research/xenobiology/crossbreeding/_corecross.dm index 43f23d013d..d9d58083cb 100644 --- a/code/modules/research/xenobiology/crossbreeding/_corecross.dm +++ b/code/modules/research/xenobiology/crossbreeding/_corecross.dm @@ -38,7 +38,7 @@ To add a crossbreed: throw_range = 6 /obj/item/slimecross/Initialize() - ..() + . = ..() name = effect + " " + colour + " extract" var/itemcolor = "#FFFFFF" switch(colour) @@ -94,7 +94,7 @@ To add a crossbreed: var/list/list_reagents /obj/item/slimecrossbeaker/Initialize() - ..() + . = ..() create_reagents(50) if(list_reagents) for(var/reagent in list_reagents) diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm index ca334497a7..f56aa11bae 100644 --- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm +++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm @@ -431,8 +431,9 @@ datum/status_effect/stabilized/blue/on_remove() if(owner.fire_stacks <= 0) to_chat(owner, "[linked_extract] coats you in a watery goo, extinguishing the flames.") var/obj/O = owner.get_active_held_item() - O.extinguish() //All shamelessly copied from water's reaction_obj, since I didn't seem to be able to get it here for some reason. - O.acid_level = 0 + if(O) + O.extinguish() //All shamelessly copied from water's reaction_obj, since I didn't seem to be able to get it here for some reason. + O.acid_level = 0 // Monkey cube if(istype(O, /obj/item/reagent_containers/food/snacks/monkeycube)) to_chat(owner, "[linked_extract] kept your hands wet! It makes [O] expand!") diff --git a/code/modules/research/xenobiology/crossbreeding/burning.dm b/code/modules/research/xenobiology/crossbreeding/burning.dm index ba50229d9c..3b2131dd81 100644 --- a/code/modules/research/xenobiology/crossbreeding/burning.dm +++ b/code/modules/research/xenobiology/crossbreeding/burning.dm @@ -11,7 +11,7 @@ Burning extracts: icon_state = "burning" /obj/item/slimecross/burning/Initialize() - ..() + . = ..() create_reagents(10) /obj/item/slimecross/burning/attack_self(mob/user) @@ -300,7 +300,7 @@ Burning extracts: if(!on || !pictures_left || !isturf(target.loc)) return new /obj/effect/timestop(get_turf(target), 2, 50, list(user)) - ..() + . = ..() var/text = "The camera fades away" if(disk) text += ", leaving the disk behind!" diff --git a/code/modules/research/xenobiology/crossbreeding/charged.dm b/code/modules/research/xenobiology/crossbreeding/charged.dm index 8ae7bf8d54..b664380d9e 100644 --- a/code/modules/research/xenobiology/crossbreeding/charged.dm +++ b/code/modules/research/xenobiology/crossbreeding/charged.dm @@ -12,7 +12,7 @@ Charged extracts: icon_state = "charged" /obj/item/slimecross/charged/Initialize() - ..() + . = ..() create_reagents(10) /obj/item/slimecross/charged/attack_self(mob/user) @@ -196,7 +196,7 @@ Charged extracts: /obj/item/slimecross/charged/gold/Destroy() STOP_PROCESSING(SSobj, src) - ..() + return ..() /obj/item/slimecross/charged/oil colour = "oil" @@ -315,7 +315,7 @@ Charged extracts: var/uses = 2 /obj/item/slimepotion/spaceproof/afterattack(obj/item/clothing/C, mob/user, proximity) - ..() + . = ..() if(!uses) qdel(src) return @@ -352,7 +352,7 @@ Charged extracts: var/uses = 2 /obj/item/slimepotion/lavaproof/afterattack(obj/item/C, mob/user, proximity) - ..() + . = ..() if(!uses) qdel(src) return ..() diff --git a/code/modules/research/xenobiology/crossbreeding/consuming.dm b/code/modules/research/xenobiology/crossbreeding/consuming.dm index 2f6bf2a4f6..b51a7b2553 100644 --- a/code/modules/research/xenobiology/crossbreeding/consuming.dm +++ b/code/modules/research/xenobiology/crossbreeding/consuming.dm @@ -278,7 +278,7 @@ Consuming extracts: var/colour = "#FFFFFF" /obj/item/slime_cookie/pyrite/Initialize() - ..() + . = ..() var/tastemessage = "paint remover" switch(rand(1,7)) if(1) diff --git a/code/modules/research/xenobiology/crossbreeding/industrial.dm b/code/modules/research/xenobiology/crossbreeding/industrial.dm index 95f87ede0a..2ab39eb06f 100644 --- a/code/modules/research/xenobiology/crossbreeding/industrial.dm +++ b/code/modules/research/xenobiology/crossbreeding/industrial.dm @@ -21,7 +21,7 @@ Industrial extracts: return /obj/item/slimecross/industrial/Initialize() - ..() + . = ..() create_reagents(100) START_PROCESSING(SSobj,src) diff --git a/code/modules/research/xenobiology/crossbreeding/prismatic.dm b/code/modules/research/xenobiology/crossbreeding/prismatic.dm index 41063de3d1..65e66aa0e2 100644 --- a/code/modules/research/xenobiology/crossbreeding/prismatic.dm +++ b/code/modules/research/xenobiology/crossbreeding/prismatic.dm @@ -22,6 +22,7 @@ Prismatic extracts: desc = "It's constantly wet with a pungent-smelling, clear chemical." /obj/item/slimecross/prismatic/grey/afterattack(turf/target, mob/user, proximity) + . = ..() if(!proximity) return if(istype(target) && target.color != initial(target.color)) diff --git a/code/modules/research/xenobiology/crossbreeding/recurring.dm b/code/modules/research/xenobiology/crossbreeding/recurring.dm index 8a6e5c3cbb..b8ea51bae5 100644 --- a/code/modules/research/xenobiology/crossbreeding/recurring.dm +++ b/code/modules/research/xenobiology/crossbreeding/recurring.dm @@ -14,7 +14,7 @@ Recurring extracts: var/max_cooldown = 5 //In sets of 2 seconds. /obj/item/slimecross/recurring/Initialize() - ..() + . = ..() extract = new extract_type(src.loc) visible_message("[src] wraps a layer of goo around itself!") extract.name = name @@ -39,7 +39,7 @@ Recurring extracts: qdel(src) /obj/item/slimecross/recurring/Destroy() - ..() + . = ..() STOP_PROCESSING(SSobj,src) /obj/item/slimecross/recurring/grey diff --git a/code/modules/research/xenobiology/crossbreeding/regenerative.dm b/code/modules/research/xenobiology/crossbreeding/regenerative.dm index 7c8d8fa78f..6c7ea102bc 100644 --- a/code/modules/research/xenobiology/crossbreeding/regenerative.dm +++ b/code/modules/research/xenobiology/crossbreeding/regenerative.dm @@ -16,6 +16,7 @@ Regenerative extracts: /obj/item/slimecross/regenerative/afterattack(atom/target,mob/user,prox) + . = ..() if(!prox || !isliving(target)) return var/mob/living/H = target @@ -119,7 +120,7 @@ Regenerative extracts: C.name = "fireproofed [C.name]" C.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) C.add_atom_colour("#000080", FIXED_COLOUR_PRIORITY) - C.max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + C.max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT C.heat_protection = C.body_parts_covered C.resistance_flags |= FIRE_PROOF @@ -141,7 +142,7 @@ Regenerative extracts: do_sparks(5,FALSE,target) /obj/item/slimecross/regenerative/bluespace/Initialize() - ..() + . = ..() T = get_turf(src) /obj/item/slimecross/regenerative/sepia diff --git a/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm b/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm index 93e5a8e739..0b27b1e26f 100644 --- a/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm +++ b/code/modules/research/xenobiology/crossbreeding/selfsustaining.dm @@ -15,7 +15,7 @@ Self-sustaining extracts: //Just divides into the actual item. /obj/item/slimecross/selfsustaining/Initialize() - . = ..() + ..() visible_message("The [src] shudders, and splits into four smaller extracts.") for(var/i = 0, i < 4, i++) var/obj/item/autoslime/A = new /obj/item/autoslime(src.loc) @@ -24,11 +24,11 @@ Self-sustaining extracts: A.icon = icon A.icon_state = icon_state A.color = color - qdel(src) + return INITIALIZE_HINT_QDEL /obj/item/autoslime/Initialize() name = "self-sustaining " + extract.name - ..() + return ..() /obj/item/autoslime/attack_self(mob/user) var/reagentselect = input(user, "Choose the reagent the extract will produce.", "Self-sustaining Reaction") as null|anything in extract.activate_reagents diff --git a/code/modules/research/xenobiology/crossbreeding/stabilized.dm b/code/modules/research/xenobiology/crossbreeding/stabilized.dm index fe1b498667..0d589954d9 100644 --- a/code/modules/research/xenobiology/crossbreeding/stabilized.dm +++ b/code/modules/research/xenobiology/crossbreeding/stabilized.dm @@ -15,7 +15,7 @@ Stabilized extracts: var/mob/living/owner /obj/item/slimecross/stabilized/Initialize() - ..() + . = ..() START_PROCESSING(SSobj,src) /obj/item/slimecross/stabilized/Destroy() @@ -111,7 +111,7 @@ Stabilized extracts: mob_type = pick(mob_spawn_pets) /obj/item/slimecross/stabilized/gold/Initialize() - ..() + . = ..() generate_mobtype() /obj/item/slimecross/stabilized/gold/attack_self(mob/user) diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm index 26eefa0a68..5aebd1bafa 100644 --- a/code/modules/research/xenobiology/xenobio_camera.dm +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -50,7 +50,7 @@ scan_action = new potion_action = new stored_slimes = list() - listener = AddComponent(/datum/component/redirect, COMSIG_ATOM_CONTENTS_DEL, CALLBACK(src, .proc/on_contents_del)) + listener = AddComponent(/datum/component/redirect, list(COMSIG_ATOM_CONTENTS_DEL = CALLBACK(src, .proc/on_contents_del))) /obj/machinery/computer/camera_advanced/xenobio/Destroy() stored_slimes = null diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index b8cc61df97..e48e18c34c 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -391,7 +391,7 @@ /obj/item/slime_extract/lightpink/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) switch(activation_type) if(SLIME_ACTIVATE_MINOR) - var/obj/item/slimepotion/slime/docility/O = new(null, 1) + var/obj/item/slimepotion/slime/renaming/O = new(null, 1) if(!user.put_in_active_hand(O)) O.forceMove(user.drop_location()) playsound(user, 'sound/effects/splat.ogg', 50, 1) @@ -615,6 +615,7 @@ w_class = WEIGHT_CLASS_TINY /obj/item/slimepotion/afterattack(obj/item/reagent_containers/target, mob/user , proximity) + . = ..() if (istype(target)) to_chat(user, "You cannot transfer [src] to [target]! It appears the potion must be given directly to a slime to absorb." ) return @@ -671,7 +672,7 @@ to_chat(user, "You offer [src] to [SM]...") being_used = TRUE - var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]?", ROLE_ALIEN, null, ROLE_ALIEN, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm + var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm if(LAZYLEN(candidates)) var/mob/dead/observer/C = pick(candidates) SM.key = C.key @@ -725,11 +726,11 @@ if(SM.sentience_type != animal_type) to_chat(user, "You cannot transfer your consciousness to [SM]." ) return ..() - var/jb = jobban_isbanned(user, ROLE_ALIEN) + var/jb = jobban_isbanned(user, ROLE_MIND_TRANSFER) if(QDELETED(src) || QDELETED(M) || QDELETED(user)) return - if(jb) //ideally sentience and trasnference potions should be their own unique role. + if(jb) to_chat(user, "Your mind goes blank as you attempt to use the potion.") return @@ -833,7 +834,7 @@ icon_state = "potyellow" /obj/item/slimepotion/speed/afterattack(obj/C, mob/user) - ..() + . = ..() if(!istype(C)) to_chat(user, "The potion can only be used on items or vehicles!") return @@ -866,21 +867,21 @@ var/uses = 3 /obj/item/slimepotion/fireproof/afterattack(obj/item/clothing/C, mob/user) - ..() + . = ..() if(!uses) qdel(src) return if(!istype(C)) to_chat(user, "The potion can only be used on clothing!") return - if(C.max_heat_protection_temperature == FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT) + if(C.max_heat_protection_temperature >= FIRE_IMMUNITY_MAX_TEMP_PROTECT) to_chat(user, "The [C] is already fireproof!") return ..() to_chat(user, "You slather the blue gunk over the [C], fireproofing it.") C.name = "fireproofed [C.name]" C.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) C.add_atom_colour("#000080", FIXED_COLOUR_PRIORITY) - C.max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + C.max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT C.heat_protection = C.body_parts_covered C.resistance_flags |= FIRE_PROOF uses -- @@ -911,6 +912,39 @@ L.regenerate_icons() qdel(src) +/obj/item/slimepotion/slime/renaming + name = "renaming potion" + desc = "A potion that allows a self-aware being to change what name it subconciously presents to the world." + icon = 'icons/obj/chemical.dmi' + icon_state = "potgreen" + + var/being_used = FALSE + +/obj/item/slimepotion/slime/renaming/attack(mob/living/M, mob/user) + if(being_used || !ismob(M)) + return + if(!M.ckey) //only works on animals that aren't player controlled + to_chat(user, "[M] is not self aware, and cannot pick its own name.") + return + + being_used = TRUE + + to_chat(user, "You offer [src] to [user]...") + + var/new_name = stripped_input(M, "What would you like your name to be?", "Input a name", M.real_name, MAX_NAME_LEN) + + if(!new_name || QDELETED(src) || QDELETED(M) || new_name == M.real_name || !M.Adjacent(user)) + being_used = FALSE + return + + M.visible_message("[M] has a new name, [new_name].", "Your old name of [M.real_name] fades away, and your new name [new_name] anchors itself in your mind.") + message_admins("[ADMIN_LOOKUPFLW(user)] used [src] on [ADMIN_LOOKUPFLW(M)], letting them rename themselves into [new_name].") + + // pass null as first arg to not update records or ID/PDA + M.fully_replace_character_name(null, new_name) + + qdel(src) + /obj/item/slimepotion/slime/slimeradio name = "bluespace radio potion" desc = "A strange chemical that grants those who ingest it the ability to broadcast and receive subscape radio waves." diff --git a/code/modules/ruins/lavalandruin_code/pizzaparty.dm b/code/modules/ruins/lavalandruin_code/pizzaparty.dm new file mode 100644 index 0000000000..a7776f4e6a --- /dev/null +++ b/code/modules/ruins/lavalandruin_code/pizzaparty.dm @@ -0,0 +1,9 @@ +//lavaland_surface_pizzaparty.dmm + +/obj/effect/spawner/lootdrop/pizzaparty + name = "pizza bomb spawner" + loot = list(/obj/item/pizzabox/margherita = 3, + /obj/item/pizzabox/meat = 3, + /obj/item/pizzabox/mushroom = 3, + /obj/item/pizzabox/bomb = 1) + lootdoubles = FALSE diff --git a/code/modules/ruins/lavalandruin_code/puzzle.dm b/code/modules/ruins/lavalandruin_code/puzzle.dm index 9e798507da..70b0545ded 100644 --- a/code/modules/ruins/lavalandruin_code/puzzle.dm +++ b/code/modules/ruins/lavalandruin_code/puzzle.dm @@ -154,6 +154,8 @@ var/tile_count = width * height //Generate per tile icons + var/icon/base_icon = get_base_icon() + for(var/id in 1 to tile_count) var/y = width - round((id - 1) / width) var/x = ((id - 1) % width) + 1 @@ -163,7 +165,7 @@ var/y_start = 1 + ((y - 1) * world.icon_size) var/y_end = y_start + world.icon_size - 1 - var/icon/T = get_base_icon() + var/icon/T = new(base_icon) T.Crop(x_start,y_start,x_end,y_end) puzzle_pieces["[id]"] = T left_ids += id @@ -290,10 +292,10 @@ prisoner.notransform = FALSE prisoner = null -//Some armor so it's harder to kill someone by mistake. EDITED - Hugboxed +//Some armor so it's harder to kill someone by mistake. /obj/structure/puzzle_element/prison - resistance_flags = INDESTRUCTIBLE | ACID_PROOF | FIRE_PROOF | LAVA_PROOF - + armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 50, "rad" = 50, "fire" = 50, "acid" = 50) + /obj/structure/puzzle_element/prison/relaymove(mob/user) return @@ -348,4 +350,4 @@ //Move them into random block var/obj/structure/puzzle_element/E = pick(cube.elements) prisoner.forceMove(E) - return TRUE + return TRUE \ No newline at end of file diff --git a/code/modules/ruins/lavalandruin_code/syndicate_base.dm b/code/modules/ruins/lavalandruin_code/syndicate_base.dm new file mode 100644 index 0000000000..514ea35e8e --- /dev/null +++ b/code/modules/ruins/lavalandruin_code/syndicate_base.dm @@ -0,0 +1,22 @@ +//lavaland_surface_syndicate_base1.dmm + +/obj/machinery/vending/syndichem + name = "\improper SyndiChem" + desc = "A vending machine full of grenades and grenade accessories. Sponsored by DonkCo(tm)." + req_access = list(ACCESS_SYNDICATE) + products = list(/obj/item/stack/cable_coil/random = 5, + /obj/item/assembly/igniter = 20, + /obj/item/assembly/prox_sensor = 5, + /obj/item/assembly/signaler = 5, + /obj/item/assembly/timer = 5, + /obj/item/assembly/voice = 5, + /obj/item/assembly/health = 5, + /obj/item/assembly/infra = 5, + /obj/item/grenade/chem_grenade = 5, + /obj/item/grenade/chem_grenade/large = 5, + /obj/item/grenade/chem_grenade/pyro = 5, + /obj/item/grenade/chem_grenade/cryo = 5, + /obj/item/grenade/chem_grenade/adv_release = 5, + /obj/item/reagent_containers/food/drinks/bottle/holywater = 1) + product_slogans = "It's not pyromania if you're getting paid!;You smell that? Plasma, son. Nothing else in the world smells like that.;I love the smell of Plasma in the morning." + resistance_flags = FIRE_PROOF diff --git a/code/modules/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/ruins/objects_and_mobs/sin_ruins.dm index 7ad8a136f9..caa2ca7adf 100644 --- a/code/modules/ruins/objects_and_mobs/sin_ruins.dm +++ b/code/modules/ruins/objects_and_mobs/sin_ruins.dm @@ -129,7 +129,7 @@ hitsound = 'sound/weapons/bladeslice.ogg' /obj/item/kitchen/knife/envy/afterattack(atom/movable/AM, mob/living/carbon/human/user, proximity) - ..() + . = ..() if(!proximity) return if(!istype(user)) diff --git a/code/modules/ruins/spaceruin_code/miracle.dm b/code/modules/ruins/spaceruin_code/miracle.dm new file mode 100644 index 0000000000..075d43df25 --- /dev/null +++ b/code/modules/ruins/spaceruin_code/miracle.dm @@ -0,0 +1,4 @@ +/////////// miracle ruin + +/obj/item/storage/backpack/satchel/flat/secret/miracle_ruin + reward_all_of_these = list(/obj/item/gun/energy/pulse/prize) diff --git a/code/modules/ruins/spaceruin_code/oldstation.dm b/code/modules/ruins/spaceruin_code/oldstation.dm index b216145023..e72dbea044 100644 --- a/code/modules/ruins/spaceruin_code/oldstation.dm +++ b/code/modules/ruins/spaceruin_code/oldstation.dm @@ -47,3 +47,7 @@ SIGNIFICANT EVENTS OF NOTE
1: The primary radiation detectors were taken offline after 112 years due to power failure, secondary radiation detectors showed no residual \ radiation on station. Deduction, primarily detector was malfunctioning and was producing a radiation signal when there was none.

2: A data burst from a nearby Nanotrasen Space \ Station was received, this data burst contained research data that has been uploaded to our RnD labs.

3: Unknown invasion force has occupied Delta station." + +/obj/item/paper/fluff/ruins/oldstation/generator_manual + name = "S.U.P.E.R.P.A.C.M.A.N.-type portable generator manual" + info = "You can barely make out a faded sentence...

Wrench down the generator on top of a wire node connected to either a SMES input terminal or the power grid." diff --git a/code/modules/shuttle/arrivals.dm b/code/modules/shuttle/arrivals.dm index 60998215de..12e82e4212 100644 --- a/code/modules/shuttle/arrivals.dm +++ b/code/modules/shuttle/arrivals.dm @@ -11,6 +11,8 @@ callTime = INFINITY ignitionTime = 50 + movement_force = list("KNOCKDOWN" = 3, "THROW" = 0) + var/sound_played var/damaged //too damaged to undock? var/list/areas //areas in our shuttle @@ -18,6 +20,7 @@ var/obj/machinery/requests_console/console var/force_depart = FALSE var/perma_docked = FALSE //highlander with RESPAWN??? OH GOD!!! + var/obj/docking_port/stationary/target_dock // for badminry /obj/docking_port/mobile/arrivals/Initialize(mapload) . = ..() @@ -175,7 +178,10 @@ if(mode == SHUTTLE_IDLE) if(console) console.say(pickingup ? "Departing immediately for new employee pickup." : "Shuttle departing.") - request(SSshuttle.getDock("arrivals_stationary")) //we will intentionally never return SHUTTLE_ALREADY_DOCKED + var/obj/docking_port/stationary/target = target_dock + if(QDELETED(target)) + target = SSshuttle.getDock("arrivals_stationary") + request(target) //we will intentionally never return SHUTTLE_ALREADY_DOCKED /obj/docking_port/mobile/arrivals/proc/RequireUndocked(mob/user) if(mode == SHUTTLE_CALL || damaged) diff --git a/code/modules/shuttle/assault_pod.dm b/code/modules/shuttle/assault_pod.dm index 43b273914c..86132cf834 100644 --- a/code/modules/shuttle/assault_pod.dm +++ b/code/modules/shuttle/assault_pod.dm @@ -1,12 +1,11 @@ /obj/docking_port/mobile/assault_pod name = "assault pod" id = "steel_rain" - timid = FALSE dwidth = 3 width = 7 height = 7 -/obj/docking_port/mobile/assault_pod/request() +/obj/docking_port/mobile/assault_pod/request(obj/docking_port/stationary/S) if(!(z in SSmapping.levels_by_trait(ZTRAIT_STATION))) //No launching pods that have already launched return ..() diff --git a/code/modules/shuttle/computer.dm b/code/modules/shuttle/computer.dm index ee491f17be..0c957919a1 100644 --- a/code/modules/shuttle/computer.dm +++ b/code/modules/shuttle/computer.dm @@ -69,3 +69,6 @@ obj_flags |= EMAGGED to_chat(user, "You fried the consoles ID checking system.") +/obj/machinery/computer/shuttle/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE) + if(port && (shuttleId == initial(shuttleId) || override)) + shuttleId = port.id \ No newline at end of file diff --git a/code/modules/shuttle/docking.dm b/code/modules/shuttle/docking.dm index d881396092..7295ab2832 100644 --- a/code/modules/shuttle/docking.dm +++ b/code/modules/shuttle/docking.dm @@ -1,4 +1,4 @@ -//this is the main proc. It instantly moves our mobile port to stationary port new_dock +/// This is the main proc. It instantly moves our mobile port to stationary port `new_dock`. /obj/docking_port/mobile/proc/initiate_docking(obj/docking_port/stationary/new_dock, movement_direction, force=FALSE) // Crashing this ship with NO SURVIVORS @@ -8,8 +8,10 @@ if(!force) if(!check_dock(new_dock)) + remove_ripples() return DOCKING_BLOCKED if(!canMove()) + remove_ripples() return DOCKING_IMMOBILIZED var/obj/docking_port/stationary/old_dock = get_docked() @@ -32,12 +34,7 @@ // The underlying old area is the area assumed to be under the shuttle's starting location // If it no longer/has never existed it will be created - var/area/underlying_old_area - for(var/i in GLOB.sortedAreas) // Locate grabs subtypes and we want a particular type - var/area/place = i - if(place.type == underlying_area_type) - underlying_old_area = place - break + var/area/underlying_old_area = GLOB.areas_by_type[underlying_area_type] if(!underlying_old_area) underlying_old_area = new underlying_area_type(null) @@ -54,10 +51,9 @@ var/list/moved_atoms = list() //Everything not a turf that gets moved in the shuttle var/list/areas_to_move = list() //unique assoc list of areas on turfs being moved - remove_ripples() - . = preflight_check(old_turfs, new_turfs, areas_to_move, rotation) if(.) + remove_ripples() return /*******************************************Hiding turfs if necessary*******************************************/ @@ -75,10 +71,15 @@ if(!force) if(!check_dock(new_dock)) + remove_ripples() return DOCKING_BLOCKED if(!canMove()) + remove_ripples() return DOCKING_IMMOBILIZED + // Moving to the new location will trample the ripples there at the exact + // same time any mobs there are trampled, to avoid any discrepancy where + // the ripples go away before it is safe. takeoff(old_turfs, new_turfs, moved_atoms, rotation, movement_direction, old_dock, underlying_old_area) CHECK_TICK @@ -97,6 +98,8 @@ new_dock.last_dock_time = world.time setDir(new_dock.dir) + // remove any stragglers just in case, and clear the list + remove_ripples() return DOCKING_SUCCESS /obj/docking_port/mobile/proc/preflight_check(list/old_turfs, list/new_turfs, list/areas_to_move, rotation) diff --git a/code/modules/shuttle/elevator.dm b/code/modules/shuttle/elevator.dm index 959cc2dae7..de5d88ee17 100644 --- a/code/modules/shuttle/elevator.dm +++ b/code/modules/shuttle/elevator.dm @@ -1,11 +1,10 @@ /obj/docking_port/mobile/elevator name = "elevator" id = "elevator" - timid = FALSE dwidth = 3 width = 7 height = 7 movement_force = list("KNOCKDOWN" = 0, "THROW" = 0) /obj/docking_port/mobile/elevator/request(obj/docking_port/stationary/S) //No transit, no ignition, just a simple up/down platform - initiate_docking(S, TRUE) \ No newline at end of file + initiate_docking(S, force=TRUE) diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 20b8c26d83..cbc6810143 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -245,7 +245,7 @@ /obj/docking_port/mobile/emergency/proc/is_hijacked() var/has_people = FALSE - + var/hijacker_present = FALSE for(var/mob/living/player in GLOB.player_list) if(player.mind) if(player.stat != DEAD) @@ -258,10 +258,23 @@ if(shuttle_areas[get_area(player)]) has_people = TRUE var/location = get_turf(player.mind.current) - if(!(player.mind.has_antag_datum(/datum/antagonist)) && !istype(location, /turf/open/floor/plasteel/shuttle/red) && !istype(location, /turf/open/floor/mineral/plastitanium/brig)) + //Non-antag present. Can't hijack. + if(!(player.mind.has_antag_datum(/datum/antagonist)) && !istype(location, /turf/open/floor/plasteel/shuttle/red) && !istype(location, /turf/open/floor/mineral/plastitanium/red/brig)) + return FALSE + //Antag present, doesn't stop but let's see if we actually want to hijack + var/prevent = FALSE + for(var/datum/antagonist/A in player.mind.antag_datums) + if(A.can_hijack == HIJACK_HIJACKER) + hijacker_present = TRUE + prevent = FALSE + break //If we have both prevent and hijacker antags assume we want to hijack. + else if(A.can_hijack == HIJACK_PREVENT) + prevent = TRUE + if(prevent) return FALSE - return has_people + + return has_people && hijacker_present /obj/docking_port/mobile/emergency/proc/ShuttleDBStuff() set waitfor = FALSE @@ -409,17 +422,16 @@ /obj/docking_port/mobile/pod name = "escape pod" id = "pod" - timid = FALSE dwidth = 1 width = 3 height = 4 launch_status = UNLAUNCHED -/obj/docking_port/mobile/pod/request() - var/obj/machinery/computer/shuttle/S = getControlConsole() - if(!istype(S, /obj/machinery/computer/shuttle/pod)) +/obj/docking_port/mobile/pod/request(obj/docking_port/stationary/S) + var/obj/machinery/computer/shuttle/C = getControlConsole() + if(!istype(C, /obj/machinery/computer/shuttle/pod)) return ..() - if(GLOB.security_level >= SEC_LEVEL_RED || (S && (S.obj_flags & EMAGGED))) + if(GLOB.security_level >= SEC_LEVEL_RED || (C && (C.obj_flags & EMAGGED))) if(launch_status == UNLAUNCHED) launch_status = EARLY_LAUNCHED return ..() @@ -427,33 +439,12 @@ to_chat(usr, "Escape pods will only launch during \"Code Red\" security alert.") return TRUE -/obj/docking_port/mobile/pod/Initialize() - . = ..() - var/static/i = 1 - if(id == initial(id)) - id = "[initial(id)][i]" - if(name == initial(name)) - name = "[initial(name)] [i]" - - for(var/k in shuttle_areas) - var/area/place = k - for(var/obj/machinery/computer/shuttle/pod/pod_comp in place) - if(pod_comp.shuttleId == initial(pod_comp.shuttleId)) - pod_comp.shuttleId = id - if(pod_comp.possible_destinations == initial(pod_comp.possible_destinations)) - pod_comp.possible_destinations = "pod_lavaland[i]" - - i++ - - - /obj/docking_port/mobile/pod/cancel() return /obj/machinery/computer/shuttle/pod name = "pod control computer" admin_controlled = 1 - shuttleId = "pod" possible_destinations = "pod_asteroid" icon = 'icons/obj/terminals.dmi' icon_state = "dorm_available" @@ -470,6 +461,11 @@ obj_flags |= EMAGGED to_chat(user, "You fry the pod's alert level checking system.") +/obj/machinery/computer/shuttle/pod/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE) + . = ..() + if(possible_destinations == initial(possible_destinations) || override) + possible_destinations = "pod_lavaland[idnum]" + /obj/docking_port/stationary/random name = "escape pod" id = "pod" @@ -575,6 +571,10 @@ SSshuttle.emergency = current_emergency SSshuttle.backup_shuttle = src +/obj/docking_port/mobile/emergency/shuttle_build/register() + . = ..() + initiate_docking(SSshuttle.getDock("emergency_home")) + #undef TIME_LEFT #undef ENGINES_START_TIME #undef ENGINES_STARTED diff --git a/code/modules/shuttle/manipulator.dm b/code/modules/shuttle/manipulator.dm index ee12d7cf37..dcfb0793b5 100644 --- a/code/modules/shuttle/manipulator.dm +++ b/code/modules/shuttle/manipulator.dm @@ -40,6 +40,10 @@ add_overlay(hologram_projection) add_overlay(hologram_ship) +/obj/machinery/shuttle_manipulator/can_interact(mob/user) + // Only admins can use this, but they can use it from anywhere + return user.client && check_rights_for(user.client, R_ADMIN) + /obj/machinery/shuttle_manipulator/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state) ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) @@ -107,17 +111,23 @@ data["shuttles"] = list() for(var/i in SSshuttle.mobile) var/obj/docking_port/mobile/M = i + var/timeleft = M.timeLeft(1) var/list/L = list() L["name"] = M.name L["id"] = M.id L["timer"] = M.timer L["timeleft"] = M.getTimerStr() - var/can_fast_travel = FALSE - if(M.timer && M.timeLeft() >= 50) - can_fast_travel = TRUE - L["can_fast_travel"] = can_fast_travel - L["mode"] = capitalize(shuttlemode2str(M.mode)) - L["status"] = M.getStatusText() + if (timeleft > 1 HOURS) + L["timeleft"] = "Infinity" + L["can_fast_travel"] = M.timer && timeleft >= 50 + L["can_fly"] = TRUE + if(istype(M, /obj/docking_port/mobile/emergency)) + L["can_fly"] = FALSE + else if(!M.destination) + L["can_fast_travel"] = FALSE + if (M.mode != SHUTTLE_IDLE) + L["mode"] = capitalize(shuttlemode2str(M.mode)) + L["status"] = M.getDbgStatusText() if(M == existing_shuttle) data["existing_shuttle"] = L @@ -149,10 +159,19 @@ user.forceMove(get_turf(M)) . = TRUE break + + if("fly") + for(var/i in SSshuttle.mobile) + var/obj/docking_port/mobile/M = i + if(M.id == params["id"]) + . = TRUE + M.admin_fly_shuttle(user) + break + if("fast_travel") for(var/i in SSshuttle.mobile) var/obj/docking_port/mobile/M = i - if(M.id == params["id"] && M.timer && M.timeLeft() >= 50) + if(M.id == params["id"] && M.timer && M.timeLeft(1) >= 50) M.setTimer(50) . = TRUE message_admins("[key_name_admin(usr)] fast travelled [M]") @@ -194,7 +213,8 @@ preview_template = null if(!preview_shuttle) - load_template(loading_template) + if(load_template(loading_template)) + preview_shuttle.linkup(loading_template, destination_port) preview_template = loading_template // get the existing shuttle information, if any @@ -210,8 +230,7 @@ D = existing_shuttle.get_docked() if(!D) - var/m = "No dock found for preview shuttle ([preview_template.name]), aborting." - throw EXCEPTION(m) + CRASH("No dock found for preview shuttle ([preview_template.name]), aborting.") var/result = preview_shuttle.canDock(D) // truthy value means that it cannot dock for some reason @@ -228,7 +247,7 @@ preview_shuttle.movement_force = list("KNOCKDOWN" = 0, "THROW" = 0) preview_shuttle.initiate_docking(D) preview_shuttle.movement_force = force_memory - + . = preview_shuttle // Shuttle state involves a mode and a timer based on world.time, so @@ -245,11 +264,11 @@ existing_shuttle = null selected = null -/obj/machinery/shuttle_manipulator/proc/load_template( - datum/map_template/shuttle/S) +/obj/machinery/shuttle_manipulator/proc/load_template(datum/map_template/shuttle/S) + . = FALSE // load shuttle template, centred at shuttle import landmark, var/turf/landmark_turf = get_turf(locate(/obj/effect/landmark/shuttle_import) in GLOB.landmarks_list) - S.load(landmark_turf, centered = TRUE) + S.load(landmark_turf, centered = TRUE, register = FALSE) var/affected = S.get_affected_turfs(landmark_turf, centered=TRUE) @@ -262,18 +281,10 @@ for(var/T in affected) for(var/obj/docking_port/P in T) if(istype(P, /obj/docking_port/mobile)) - var/obj/docking_port/mobile/M = P found++ if(found > 1) qdel(P, force=TRUE) log_world("Map warning: Shuttle Template [S.mappath] has multiple mobile docking ports.") - else if(!M.timid) - // The shuttle template we loaded isn't "timid" which means - // it's already registered with the shuttles subsystem. - // This is a bad thing. - stack_trace("Template [S] is non-timid! Unloading.") - M.jumpToNullSpace() - return else preview_shuttle = P if(istype(P, /obj/docking_port/stationary)) @@ -289,8 +300,74 @@ return //Everything fine S.on_bought() + return TRUE /obj/machinery/shuttle_manipulator/proc/unload_preview() if(preview_shuttle) preview_shuttle.jumpToNullSpace() preview_shuttle = null + +/obj/docking_port/mobile/proc/admin_fly_shuttle(mob/user) + var/list/options = list() + + for(var/port in SSshuttle.stationary) + if (istype(port, /obj/docking_port/stationary/transit)) + continue // please don't do this + var/obj/docking_port/stationary/S = port + if (canDock(S) == SHUTTLE_CAN_DOCK) + options[S.name || S.id] = S + + options += "--------" + options += "Infinite Transit" + options += "Delete Shuttle" + options += "Into The Sunset (delete & greentext 'escape')" + + var/selection = input(user, "Select where to fly [name || id]:", "Fly Shuttle") as null|anything in options + if(!selection) + return + + switch(selection) + if("Infinite Transit") + destination = null + mode = SHUTTLE_IGNITING + setTimer(ignitionTime) + + if("Delete Shuttle") + if(alert(user, "Really delete [name || id]?", "Delete Shuttle", "Cancel", "Really!") != "Really!") + return + jumpToNullSpace() + + if("Into The Sunset (delete & greentext 'escape')") + if(alert(user, "Really delete [name || id] and greentext escape objectives?", "Delete Shuttle", "Cancel", "Really!") != "Really!") + return + intoTheSunset() + + else + if(options[selection]) + request(options[selection]) + +/obj/docking_port/mobile/emergency/admin_fly_shuttle(mob/user) + return // use the existing verbs for this + +/obj/docking_port/mobile/arrivals/admin_fly_shuttle(mob/user) + switch(alert(user, "Would you like to fly the arrivals shuttle once or change its destination?", "Fly Shuttle", "Fly", "Retarget", "Cancel")) + if("Cancel") + return + if("Fly") + return ..() + + var/list/options = list() + + for(var/port in SSshuttle.stationary) + if (istype(port, /obj/docking_port/stationary/transit)) + continue // please don't do this + var/obj/docking_port/stationary/S = port + if (canDock(S) == SHUTTLE_CAN_DOCK) + options[S.name || S.id] = S + + var/selection = input(user, "Select the new arrivals destination:", "Fly Shuttle") as null|anything in options + if(!selection) + return + target_dock = options[selection] + if(!QDELETED(target_dock)) + destination = target_dock diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/navigation_computer.dm index 19ef427f27..1b24823743 100644 --- a/code/modules/shuttle/navigation_computer.dm +++ b/code/modules/shuttle/navigation_computer.dm @@ -57,10 +57,9 @@ shuttle_port = null return - eyeobj = new /mob/camera/aiEye/remote/shuttle_docker() + eyeobj = new /mob/camera/aiEye/remote/shuttle_docker(null, src) var/mob/camera/aiEye/remote/shuttle_docker/the_eye = eyeobj - the_eye.origin = src - the_eye.dir = shuttle_port.dir + the_eye.setDir(shuttle_port.dir) var/turf/origin = locate(shuttle_port.x + x_offset, shuttle_port.y + y_offset, shuttle_port.z) for(var/V in shuttle_port.shuttle_areas) var/area/A = V @@ -73,7 +72,7 @@ I.loc = locate(origin.x + x_off, origin.y + y_off, origin.z) //we have to set this after creating the image because it might be null, and images created in nullspace are immutable. I.layer = ABOVE_NORMAL_TURF_LAYER I.plane = 0 - I.mouse_opacity = 0 + I.mouse_opacity = MOUSE_OPACITY_TRANSPARENT the_eye.placement_images[I] = list(x_off, y_off) /obj/machinery/computer/camera_advanced/shuttle_docker/give_eye_control(mob/user) @@ -137,7 +136,7 @@ my_port.dheight = shuttle_port.dheight my_port.dwidth = shuttle_port.dwidth my_port.hidden = shuttle_port.hidden - my_port.dir = the_eye.dir + my_port.setDir(the_eye.dir) my_port.forceMove(locate(eyeobj.x - x_offset, eyeobj.y - y_offset, eyeobj.z)) if(current_user.client) current_user.client.images -= the_eye.placed_images @@ -166,7 +165,7 @@ /obj/machinery/computer/camera_advanced/shuttle_docker/proc/rotateLandingSpot() var/mob/camera/aiEye/remote/shuttle_docker/the_eye = eyeobj var/list/image_cache = the_eye.placement_images - the_eye.dir = turn(the_eye.dir, -90) + the_eye.setDir(turn(the_eye.dir, -90)) for(var/i in 1 to image_cache.len) var/image/pic = image_cache[i] var/list/coords = image_cache[pic] @@ -247,12 +246,23 @@ current_user.client.images -= remove_images current_user.client.images += add_images +/obj/machinery/computer/camera_advanced/shuttle_docker/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override=FALSE) + if(port && (shuttleId == initial(shuttleId) || override)) + shuttleId = port.id + shuttlePortId = "[port.id]_custom" + if(dock) + jumpto_ports += dock.id + /mob/camera/aiEye/remote/shuttle_docker visible_icon = FALSE - use_static = FALSE + use_static = USE_STATIC_NONE var/list/placement_images = list() var/list/placed_images = list() +/mob/camera/aiEye/remote/shuttle_docker/Initialize(mapload, obj/machinery/computer/camera_advanced/origin) + src.origin = origin + return ..() + /mob/camera/aiEye/remote/shuttle_docker/setLoc(T) ..() var/obj/machinery/computer/camera_advanced/shuttle_docker/console = origin diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 9767334166..9942446868 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -183,10 +183,12 @@ All ShuttleMove procs go here /obj/machinery/door/airlock/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) . = ..() + var/current_area = get_area(src) for(var/obj/machinery/door/airlock/A in orange(1, src)) // does not include src - // Cycle linking is only disabled if we are actually adjacent to another airlock - shuttledocked = TRUE - A.shuttledocked = TRUE + if(get_area(A) != current_area) // does not include double-wide airlocks unless actually docked + // Cycle linking is only disabled if we are actually adjacent to another airlock + shuttledocked = TRUE + A.shuttledocked = TRUE /obj/machinery/camera/beforeShuttleMove(turf/newT, rotation, move_mode, obj/docking_port/mobile/moving_dock) . = ..() diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 32c186023b..307e714316 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -253,11 +253,6 @@ var/list/movement_force = list("KNOCKDOWN" = 3, "THROW" = 2) - // A timid shuttle will not register itself with the shuttle subsystem - // All shuttle templates MUST be timid, imports will fail if they're not - // Shuttle defined already on the map MUST NOT be timid, or they won't work - var/timid = TRUE - var/list/ripples = list() var/engine_coeff = 1 //current engine coeff var/current_engines = 0 //current engine power @@ -280,8 +275,6 @@ /obj/docking_port/mobile/Initialize(mapload) . = ..() - if(!timid) - register() if(!id) id = "[SSshuttle.mobile.len]" @@ -303,6 +296,25 @@ highlight("#0f0") #endif +// Called after the shuttle is loaded from template +/obj/docking_port/mobile/proc/linkup(datum/map_template/shuttle/template, obj/docking_port/stationary/dock) + var/list/static/shuttle_id = list() + var/idnum = ++shuttle_id[template] + if(idnum > 1) + if(id == initial(id)) + id = "[id][idnum]" + if(name == initial(name)) + name = "[name] [idnum]" + for(var/i in shuttle_areas) + var/area/place = i + for(var/obj/machinery/computer/shuttle/comp in place) + comp.connect_to_shuttle(src, dock, idnum) + for(var/obj/machinery/computer/camera_advanced/shuttle_docker/comp in place) + comp.connect_to_shuttle(src, dock, idnum) + for(var/obj/machinery/status_display/shuttle/sd in place) + sd.connect_to_shuttle(src, dock, idnum) + + //this is a hook for custom behaviour. Maybe at some point we could add checks to see if engines are intact /obj/docking_port/mobile/proc/canMove() return TRUE @@ -399,8 +411,9 @@ mode = SHUTTLE_IDLE return previous = null -// if(!destination) -// return + if(!destination) + // sent to transit with no destination -> unlimited timer + timer = INFINITY var/obj/docking_port/stationary/S0 = get_docked() var/obj/docking_port/stationary/S1 = assigned_transit if(S1) @@ -425,12 +438,7 @@ var/list/old_turfs = return_ordered_turfs(x, y, z, dir) - var/area/underlying_area - for(var/i in GLOB.sortedAreas) - var/area/place = i - if(place.type == underlying_area_type) - underlying_area = place - break + var/area/underlying_area = GLOB.areas_by_type[underlying_area_type] if(!underlying_area) underlying_area = new underlying_area_type(null) @@ -452,6 +460,22 @@ qdel(src, force=TRUE) +/obj/docking_port/mobile/proc/intoTheSunset() + // Loop over mobs + for(var/t in return_turfs()) + var/turf/T = t + for(var/mob/living/M in T.GetAllContents()) + // If they have a mind and they're not in the brig, they escaped + if(M.mind && !istype(t, /turf/open/floor/plasteel/shuttle/red) && !istype(t, /turf/open/floor/mineral/plastitanium/red/brig)) + M.mind.force_escaped = TRUE + // Ghostize them and put them in nullspace stasis (for stat & possession checks) + M.notransform = TRUE + M.ghostize(FALSE) + M.moveToNullspace() + + // Now that mobs are stowed, delete the shuttle + jumpToNullSpace() + /obj/docking_port/mobile/proc/create_ripples(obj/docking_port/stationary/S1, animate_time) var/list/turfs = ripple_area(S1) for(var/t in turfs) @@ -630,7 +654,9 @@ return "--:--" var/timeleft = timeLeft() - if(timeleft > 0) + if(timeleft > 1 HOURS) + return "--:--" + else if(timeleft > 0) return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" else return "00:00" @@ -638,6 +664,23 @@ /obj/docking_port/mobile/proc/getStatusText() var/obj/docking_port/stationary/dockedAt = get_docked() + + if(istype(dockedAt, /obj/docking_port/stationary/transit)) + if (timeLeft() > 1 HOURS) + return "hyperspace" + else + var/obj/docking_port/stationary/dst + if(mode == SHUTTLE_RECALL) + dst = previous + else + dst = destination + . = "transit towards [dst?.name || "unknown location"] ([getTimerStr()])" + else + return dockedAt?.name || "unknown" + + +/obj/docking_port/mobile/proc/getDbgStatusText() + var/obj/docking_port/stationary/dockedAt = get_docked() . = (dockedAt && dockedAt.name) ? dockedAt.name : "unknown" if(istype(dockedAt, /obj/docking_port/stationary/transit)) var/obj/docking_port/stationary/dst @@ -645,7 +688,14 @@ dst = previous else dst = destination - . += " towards [dst ? dst.name : "unknown location"] ([timeLeft(600)] minutes)" + if(dst) + . = "(transit to) [dst.name || dst.id]" + else + . = "(transit to) nowhere" + else if(dockedAt) + . = dockedAt.name || dockedAt.id + else + . = "unknown" // attempts to locate /obj/machinery/computer/shuttle with matching ID inside the shuttle diff --git a/code/modules/shuttle/shuttle_rotate.dm b/code/modules/shuttle/shuttle_rotate.dm index cc7eacec36..d0478db45c 100644 --- a/code/modules/shuttle/shuttle_rotate.dm +++ b/code/modules/shuttle/shuttle_rotate.dm @@ -106,11 +106,13 @@ If ever any of these procs are useful for non-shuttles, rename it to proc/rotate /obj/machinery/door/airlock/shuttleRotate(rotation, params) . = ..() - if(cyclelinkeddir) + if(cyclelinkeddir && (params & ROTATE_DIR)) cyclelinkeddir = angle2dir(rotation+dir2angle(cyclelinkeddir)) - cyclelinkairlock() + // If we update the linked airlock here, the partner airlock might + // not be present yet, so don't do that. Just assume we're still + // partnered with the same airlock as before. /obj/machinery/porta_turret/shuttleRotate(rotation, params) . = ..() if(wall_turret_direction && (params & ROTATE_DIR)) - wall_turret_direction = turn(wall_turret_direction,rotation) + wall_turret_direction = turn(wall_turret_direction,rotation) \ No newline at end of file diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm index 0f005f69f7..61e4a1bb46 100644 --- a/code/modules/shuttle/special.dm +++ b/code/modules/shuttle/special.dm @@ -223,7 +223,7 @@ #define LUXURY_MESSAGE_COOLDOWN 100 -/obj/effect/forcefield/luxury_shuttle/CollidedWith(atom/movable/AM) +/obj/effect/forcefield/luxury_shuttle/Bumped(atom/movable/AM) if(!isliving(AM)) return ..() diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm index 6f2997a078..b54fceb3e7 100644 --- a/code/modules/shuttle/supply.dm +++ b/code/modules/shuttle/supply.dm @@ -6,7 +6,8 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( /obj/item/disk/nuclear, /obj/machinery/nuclearbomb, /obj/item/beacon, - /obj/singularity, + /obj/singularity/narsie, + /obj/singularity/wizard, /obj/machinery/teleport/station, /obj/machinery/teleport/hub, /obj/machinery/quantumpad, @@ -22,7 +23,8 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( /obj/item/projectile/beam/wormhole, /obj/effect/portal, /obj/item/shared_storage, - /obj/structure/extraction_point + /obj/structure/extraction_point, + /obj/machinery/syndicatebomb ))) /obj/docking_port/mobile/supply @@ -37,9 +39,9 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( height = 7 movement_force = list("KNOCKDOWN" = 0, "THROW" = 0) - // When TRUE, these vars allow exporting emagged/contraband items, and add some special interactions to existing exports. - var/contraband = FALSE - var/emagged = FALSE + + //Export categories for this run, this is set by console sending the shuttle. + var/export_categories = EXPORT_CARGO /obj/docking_port/mobile/supply/register() . = ..() @@ -60,7 +62,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( return FALSE return TRUE -/obj/docking_port/mobile/supply/request() +/obj/docking_port/mobile/supply/request(obj/docking_port/stationary/S) if(mode != SHUTTLE_IDLE) return 2 return ..() @@ -116,7 +118,8 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( var/msg = "" var/matched_bounty = FALSE - var/sold_atoms = "" + + var/datum/export_report/ex = new for(var/place in shuttle_areas) var/area/shuttle/shuttle_area = place @@ -126,23 +129,21 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list( if(bounty_ship_item_and_contents(AM, dry_run = FALSE)) matched_bounty = TRUE if(!AM.anchored || istype(AM, /obj/mecha)) - sold_atoms += export_item_and_contents(AM, contraband, emagged, dry_run = FALSE) + export_item_and_contents(AM, export_categories , dry_run = FALSE, external_report = ex) - if(sold_atoms) - sold_atoms += "." + if(ex.exported_atoms) + ex.exported_atoms += "." //ugh if(matched_bounty) msg += "Bounty items received. An update has been sent to all bounty consoles. " - for(var/a in GLOB.exports_list) - var/datum/export/E = a - var/export_text = E.total_printout() + for(var/datum/export/E in ex.total_amount) + var/export_text = E.total_printout(ex) if(!export_text) continue msg += export_text + "\n" - SSshuttle.points += E.total_cost - E.export_end() + SSshuttle.points += ex.total_value[E] SSshuttle.centcom_message = msg - investigate_log("Shuttle contents sold for [SSshuttle.points - presale_points] credits. Contents: [sold_atoms || "none."] Message: [SSshuttle.centcom_message || "none."]", INVESTIGATE_CARGO) + investigate_log("Shuttle contents sold for [SSshuttle.points - presale_points] credits. Contents: [ex.exported_atoms || "none."] Message: [SSshuttle.centcom_message || "none."]", INVESTIGATE_CARGO) diff --git a/code/modules/shuttle/white_ship.dm b/code/modules/shuttle/white_ship.dm index 6264588a3a..bca16c35f1 100644 --- a/code/modules/shuttle/white_ship.dm +++ b/code/modules/shuttle/white_ship.dm @@ -5,11 +5,24 @@ shuttleId = "whiteship" possible_destinations = "whiteship_away;whiteship_home;whiteship_z4;whiteship_lavaland;whiteship_custom" +/obj/machinery/computer/shuttle/white_ship/pod + name = "Salvage Pod Console" + desc = "Used to control the Salvage Pod." + circuit = /obj/item/circuitboard/computer/white_ship/pod + shuttleId = "whiteship_pod" + possible_destinations = "whiteship_pod_home;whiteship_pod_custom" + +/obj/machinery/computer/shuttle/white_ship/pod/recall + name = "Salvage Pod Recall Console" + desc = "Used to recall the Salvage Pod." + circuit = /obj/item/circuitboard/computer/white_ship/pod/recall + possible_destinations = "whiteship_pod_home" + /obj/machinery/computer/camera_advanced/shuttle_docker/whiteship name = "White Ship Navigation Computer" desc = "Used to designate a precise transit location for the White Ship." shuttleId = "whiteship" - lock_override = CAMERA_LOCK_STATION + lock_override = NONE shuttlePortId = "whiteship_custom" shuttlePortName = "Custom Location" jumpto_ports = list("whiteship_away" = 1, "whiteship_home" = 1, "whiteship_z4" = 1) @@ -18,6 +31,18 @@ y_offset = -10 designate_time = 100 +/obj/machinery/computer/camera_advanced/shuttle_docker/whiteship/pod + name = "Salvage Pod Navigation Computer" + desc = "Used to designate a precise transit location for the Salvage Pod." + shuttleId = "whiteship_pod" + shuttlePortId = "whiteship_pod_custom" + shuttlePortName = "Custom Location" + jumpto_ports = list("whiteship_pod_home" = 1) + view_range = 7 + x_offset = -2 + y_offset = 0 + designate_time = 0 + /obj/machinery/computer/camera_advanced/shuttle_docker/whiteship/Initialize() . = ..() GLOB.jam_on_wardec += src @@ -25,3 +50,9 @@ /obj/machinery/computer/camera_advanced/shuttle_docker/whiteship/Destroy() GLOB.jam_on_wardec -= src return ..() + +/obj/effect/spawner/lootdrop/whiteship_cere_ripley + name = "25% mech 75% wreckage ripley spawner" + loot = list(/obj/mecha/working/ripley/mining = 1, + /obj/structure/mecha_wreckage/ripley = 5) + lootdoubles = FALSE diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 4e6ef7ed9b..98eb6f56bf 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -237,9 +237,9 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th switch(invocation_type) if("shout") if(prob(50))//Auto-mute? Fuck that noise - user.say(invocation) + user.say(invocation, forced = "spell") else - user.say(replacetext(invocation," ","`")) + user.say(replacetext(invocation," ","`"), forced = "spell") if("whisper") if(prob(50)) user.whisper(invocation) @@ -289,7 +289,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th before_cast(targets) invocation(user) if(user && user.ckey) - user.log_message("cast the spell [name].", INDIVIDUAL_ATTACK_LOG) + user.log_message("cast the spell [name].", LOG_ATTACK) if(recharge) recharging = TRUE if(sound) diff --git a/code/modules/spells/spell_types/area_teleport.dm b/code/modules/spells/spell_types/area_teleport.dm index 23fdc1ee54..ab399f4e2a 100644 --- a/code/modules/spells/spell_types/area_teleport.dm +++ b/code/modules/spells/spell_types/area_teleport.dm @@ -77,12 +77,12 @@ else switch(invocation_type) if("shout") - user.say("[invocation] [uppertext(chosenarea.name)]") + user.say("[invocation] [uppertext(chosenarea.name)]", forced = "spell") if(user.gender==MALE) playsound(user.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) else playsound(user.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) if("whisper") - user.whisper("[invocation] [uppertext(chosenarea.name)]") + user.whisper("[invocation] [uppertext(chosenarea.name)]", forced = "spell") return diff --git a/code/modules/spells/spell_types/construct_spells.dm b/code/modules/spells/spell_types/construct_spells.dm index 8e1957f524..39066ae4a9 100644 --- a/code/modules/spells/spell_types/construct_spells.dm +++ b/code/modules/spells/spell_types/construct_spells.dm @@ -79,7 +79,7 @@ /obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone name = "Summon Soulstone" - desc = "This spell reaches into Nar-Sie's realm, summoning one of the legendary fragments across time and space." + desc = "This spell reaches into Nar'Sie's realm, summoning one of the legendary fragments across time and space." school = "conjuration" charge_max = 2400 @@ -220,7 +220,7 @@ /obj/effect/proc_holder/spell/targeted/dominate name = "Dominate" - desc = "This spell dominates the mind of a lesser creature to the will of Nar'sie, allying it only to her direct followers." + desc = "This spell dominates the mind of a lesser creature to the will of Nar'Sie, allying it only to her direct followers." charge_max = 600 range = 7 diff --git a/code/modules/spells/spell_types/godhand.dm b/code/modules/spells/spell_types/godhand.dm index 27cfc9208b..919d87b4c7 100644 --- a/code/modules/spells/spell_types/godhand.dm +++ b/code/modules/spells/spell_types/godhand.dm @@ -24,7 +24,8 @@ ..() /obj/item/melee/touch_attack/afterattack(atom/target, mob/user, proximity) - user.say(catchphrase) + . = ..() + user.say(catchphrase, forced = "spell") playsound(get_turf(user), on_use_sound,50,1) charges-- if(charges <= 0) @@ -63,10 +64,9 @@ var/obj/item/bodypart/part = user.get_holding_bodypart_of_item(src) if(part) part.dismember() - ..() - return + return ..() M.gib() - ..() + return ..() /obj/item/melee/touch_attack/fleshtostone name = "\improper petrifying touch" @@ -93,4 +93,4 @@ return M.Stun(40) M.petrify() - ..() + return ..() diff --git a/code/modules/spells/spell_types/rightandwrong.dm b/code/modules/spells/spell_types/rightandwrong.dm index 414aa10933..bc1fc980bb 100644 --- a/code/modules/spells/spell_types/rightandwrong.dm +++ b/code/modules/spells/spell_types/rightandwrong.dm @@ -96,7 +96,7 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE) SSticker.mode.traitors += H.mind H.mind.add_antag_datum(/datum/antagonist/survivalist/guns) - H.log_message("Was made into a survivalist, and trusts no one!", INDIVIDUAL_ATTACK_LOG) + H.log_message("was made into a survivalist, and trusts no one!", LOG_ATTACK, color="red") var/gun_type = pick(GLOB.summoned_guns) var/obj/item/gun/G = new gun_type(get_turf(H)) @@ -116,7 +116,7 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE) if(prob(GLOB.summon_magic_triggered) && !(H.mind.has_antag_datum(/datum/antagonist))) H.mind.add_antag_datum(/datum/antagonist/survivalist/magic) - H.log_message("Was made into a survivalist, and trusts no one!", INDIVIDUAL_ATTACK_LOG) + H.log_message("was made into a survivalist, and trusts no one!", LOG_ATTACK, color="red") var/magic_type = pick(GLOB.summoned_magic) var/lucky = FALSE diff --git a/code/modules/spells/spell_types/spacetime_distortion.dm b/code/modules/spells/spell_types/spacetime_distortion.dm index c46e931aa7..7fd857dc51 100644 --- a/code/modules/spells/spell_types/spacetime_distortion.dm +++ b/code/modules/spells/spell_types/spacetime_distortion.dm @@ -81,8 +81,7 @@ /obj/effect/cross_action/spacetime_dist/Initialize(mapload) . = ..() - sound = "sound/guitar/[safepick(GLOB.guitar_notes)]" - dir = pick(GLOB.cardinals) + setDir(pick(GLOB.cardinals)) /obj/effect/cross_action/spacetime_dist/proc/walk_link(atom/movable/AM) if(ismob(AM)) diff --git a/code/modules/spells/spell_types/summonitem.dm b/code/modules/spells/spell_types/summonitem.dm index 0bb79eba21..2bc347b185 100644 --- a/code/modules/spells/spell_types/summonitem.dm +++ b/code/modules/spells/spell_types/summonitem.dm @@ -57,7 +57,7 @@ var/obj/item/organ/organ = item_to_retrieve if(organ.owner) // If this code ever runs I will be happy - add_logs(L, organ.owner, "magically removed [organ.name] from", addition="INTENT: [uppertext(L.a_intent)]") + log_combat(L, organ.owner, "magically removed [organ.name] from", addition="INTENT: [uppertext(L.a_intent)]") organ.Remove(organ.owner) else while(!isturf(item_to_retrieve.loc) && infinite_recursion < 10) //if it's in something you get the whole thing. diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm index 3d7894f98d..aec0e7806b 100644 --- a/code/modules/spells/spell_types/wizard.dm +++ b/code/modules/spells/spell_types/wizard.dm @@ -374,4 +374,4 @@ . = ..() if(ishuman(thrower)) var/mob/living/carbon/human/H = thrower - H.say("LIGHTNINGBOLT!!") + H.say("LIGHTNINGBOLT!!", forced = "spell") diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index be2c4f92bd..7b8caf28c8 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -168,12 +168,12 @@ top_layer = top_layer || mutable_appearance(icon, layer = ABOVE_MOB_LAYER) switch(cannon_direction) if(WEST) - dir = WEST + setDir(WEST) pixel_x = -192 top_layer.icon_state = "top_west" icon_state = "cannon_west" if(EAST) - dir = EAST + setDir(EAST) top_layer.icon_state = "top_east" icon_state = "cannon_east" add_overlay(top_layer) diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm index 0601ff28e1..e97627f16f 100644 --- a/code/modules/station_goals/dna_vault.dm +++ b/code/modules/station_goals/dna_vault.dm @@ -78,7 +78,7 @@ dna = list() /obj/item/dna_probe/afterattack(atom/target, mob/user, proximity) - ..() + . = ..() if(!proximity || !target) return //tray plants diff --git a/code/modules/surgery/advanced/bioware/nerve_grounding.dm b/code/modules/surgery/advanced/bioware/nerve_grounding.dm index 494cfaf59b..f4b23c89b1 100644 --- a/code/modules/surgery/advanced/bioware/nerve_grounding.dm +++ b/code/modules/surgery/advanced/bioware/nerve_grounding.dm @@ -1,10 +1,6 @@ -/obj/item/disk/surgery/nerve_grounding - name = "Nerve Grounding Surgery Disk" - desc = "The disk provides instructions on how to reroute the nervous system to ground electric shocks." - surgeries = list(/datum/surgery/advanced/bioware/nerve_grounding) - /datum/surgery/advanced/bioware/nerve_grounding - name = "nerve grounding" + name = "Nerve Grounding" + desc = "A surgical procedure which makes the patient's nerves act as grounding rods, protecting them from electrical shocks." steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/clamp_bleeders, diff --git a/code/modules/surgery/advanced/bioware/nerve_splicing.dm b/code/modules/surgery/advanced/bioware/nerve_splicing.dm index 944a05ed0e..6192786cc4 100644 --- a/code/modules/surgery/advanced/bioware/nerve_splicing.dm +++ b/code/modules/surgery/advanced/bioware/nerve_splicing.dm @@ -1,10 +1,6 @@ -/obj/item/disk/surgery/nerve_splicing - name = "Nerve Splicing Surgery Disk" - desc = "The disk provides instructions on how to splice the circulatory system to counter stuns and paralysis." - surgeries = list(/datum/surgery/advanced/bioware/nerve_splicing) - /datum/surgery/advanced/bioware/nerve_splicing - name = "nerve splicing" + name = "Nerve Splicing" + desc = "A surgical procedure which splices the patient's nerves, making them more resistant to stuns." steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/clamp_bleeders, diff --git a/code/modules/surgery/advanced/bioware/vein_threading.dm b/code/modules/surgery/advanced/bioware/vein_threading.dm index d618100a47..7a03833c51 100644 --- a/code/modules/surgery/advanced/bioware/vein_threading.dm +++ b/code/modules/surgery/advanced/bioware/vein_threading.dm @@ -1,10 +1,6 @@ -/obj/item/disk/surgery/vein_threading - name = "Vein Threading Surgery Disk" - desc = "The disk provides instructions on how to modify the circulatory system to greatly slow down bleeding." - surgeries = list(/datum/surgery/advanced/bioware/vein_threading) - /datum/surgery/advanced/bioware/vein_threading - name = "vein threading" + name = "Vein Threading" + desc = "A surgical procedure which severely reduces the amount of blood lost in case of injury." steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/clamp_bleeders, diff --git a/code/modules/surgery/advanced/brainwashing.dm b/code/modules/surgery/advanced/brainwashing.dm index eb40298a48..f4db9ddbee 100644 --- a/code/modules/surgery/advanced/brainwashing.dm +++ b/code/modules/surgery/advanced/brainwashing.dm @@ -4,7 +4,8 @@ surgeries = list(/datum/surgery/advanced/brainwashing) /datum/surgery/advanced/brainwashing - name = "brainwashing" + name = "Brainwashing" + desc = "A surgical procedure which directly implants a directive into the patient's brain, making it their absolute priority. It can be cleared using a mindshield implant." steps = list( /datum/surgery_step/incise, /datum/surgery_step/retract_skin, diff --git a/code/modules/surgery/advanced/lobotomy.dm b/code/modules/surgery/advanced/lobotomy.dm index 94eea5d8ff..f74346b193 100644 --- a/code/modules/surgery/advanced/lobotomy.dm +++ b/code/modules/surgery/advanced/lobotomy.dm @@ -1,10 +1,6 @@ -/obj/item/disk/surgery/lobotomy - name = "Lobotomy Surgery Disk" - desc = "The disk provides instructions on how to perform a lobotomy, to cure the most resilient brain ailments." - surgeries = list(/datum/surgery/advanced/lobotomy) - /datum/surgery/advanced/lobotomy - name = "lobotomy" + name = "Lobotomy" + desc = "An invasive surgical procedure which guarantees removal of almost all brain traumas, but might cause another permanent trauma in return." steps = list( /datum/surgery_step/incise, /datum/surgery_step/retract_skin, diff --git a/code/modules/surgery/advanced/necrotic_revival.dm b/code/modules/surgery/advanced/necrotic_revival.dm index 55f502a3d0..a88bb51b31 100644 --- a/code/modules/surgery/advanced/necrotic_revival.dm +++ b/code/modules/surgery/advanced/necrotic_revival.dm @@ -1,10 +1,6 @@ -/obj/item/disk/surgery/necrotic_revival - name = "Necrotic Revival Surgery Disk" - desc = "The disk provides instructions on how to make bodies keep working past death." - surgeries = list(/datum/surgery/advanced/necrotic_revival) - /datum/surgery/advanced/necrotic_revival - name = "necrotic revival" + name = "Necrotic Revival" + desc = "An experimental surgical procedure that stimulates the growth of a Romerol tumor inside the patient's brain. Requires zombie powder or rezadone." steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/saw, diff --git a/code/modules/surgery/advanced/pacification.dm b/code/modules/surgery/advanced/pacification.dm index ed3da3665f..15e34d003c 100644 --- a/code/modules/surgery/advanced/pacification.dm +++ b/code/modules/surgery/advanced/pacification.dm @@ -1,10 +1,6 @@ -/obj/item/disk/surgery/pacification - name = "Pacification Surgery Disk" - desc = "The disk provides instructions on how to suppress violence by manipulating the patient's brain." - surgeries = list(/datum/surgery/advanced/pacify) - /datum/surgery/advanced/pacify - name = "violence neutralization" + name = "Pacification" + desc = "A surgical procedure which permanently inhibits the aggression center of the brain, making the patient unwilling to cause direct harm." steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/saw, diff --git a/code/modules/surgery/advanced/reconstruction.dm b/code/modules/surgery/advanced/reconstruction.dm index 9fbf97070c..84d9f5b9f8 100644 --- a/code/modules/surgery/advanced/reconstruction.dm +++ b/code/modules/surgery/advanced/reconstruction.dm @@ -1,10 +1,6 @@ -/obj/item/disk/surgery/reconstruction - name = "Reconstruction Surgery Disk" - desc = "The disk provides instructions on how to repair a body without the use of chemicals." - surgeries = list(/datum/surgery/advanced/reconstruction) - /datum/surgery/advanced/reconstruction - name = "body reconstruction" + name = "Reconstruction" + desc = "A surgical procedure that gradually repairs damage done to a body without the assistance of chemicals. Unlike classic medicine, it is effective on corpses." steps = list(/datum/surgery_step/incise, /datum/surgery_step/incise, /datum/surgery_step/retract_skin, diff --git a/code/modules/surgery/advanced/revival.dm b/code/modules/surgery/advanced/revival.dm index cd602eabdf..085253e4c9 100644 --- a/code/modules/surgery/advanced/revival.dm +++ b/code/modules/surgery/advanced/revival.dm @@ -1,10 +1,6 @@ -/obj/item/disk/surgery/revival - name = "Revival Surgery Disk" - desc = "The disk provides instructions on how to bring a corpse back to life." - surgeries = list(/datum/surgery/advanced/revival) - /datum/surgery/advanced/revival - name = "revival" + name = "Revival" + desc = "An experimental surgical procedure which involves reconstruction and reactivation of the patient's brain even long after death. The body must still be able to sustain life." steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/saw, diff --git a/code/modules/surgery/advanced/viral_bonding.dm b/code/modules/surgery/advanced/viral_bonding.dm index 61f7185db6..115f8a2eed 100644 --- a/code/modules/surgery/advanced/viral_bonding.dm +++ b/code/modules/surgery/advanced/viral_bonding.dm @@ -1,10 +1,6 @@ -/obj/item/disk/surgery/viral_bonding - name = "Viral Bonding Surgery Disk" - desc = "The disk provides instructions on how to force symbiosis between a virus and its host." - surgeries = list(/datum/surgery/advanced/viral_bonding) - /datum/surgery/advanced/viral_bonding - name = "viral bonding" + name = "Viral Bonding" + desc = "A surgical procedure that forces a symbiotic relationship between a virus and its host. The patient must be dosed with spaceacillin, virus food, and formaldehyde." steps = list(/datum/surgery_step/incise, /datum/surgery_step/retract_skin, /datum/surgery_step/clamp_bleeders, diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index 8e0c6ee4cf..d8918b609f 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -10,20 +10,31 @@ var/mob/living/carbon/owner = null var/mob/living/carbon/original_owner = null var/status = BODYPART_ORGANIC + var/needs_processing = FALSE + var/body_zone //BODY_ZONE_CHEST, BODY_ZONE_L_ARM, etc , used for def_zone var/aux_zone // used for hands var/aux_layer var/body_part = null //bitflag used to check which clothes cover this bodypart var/use_digitigrade = NOT_DIGITIGRADE //Used for alternate legs, useless elsewhere + var/list/embedded_objects = list() + var/held_index = 0 //are we a hand? if so, which one! + var/is_pseudopart = FALSE //For limbs that don't really exist, eg chainsaws + + var/disabled = FALSE //If TRUE, limb is as good as missing + var/body_damage_coeff = 1 //Multiplier of the limb's damage that gets applied to the mob + var/stam_damage_coeff = 0.5 var/brutestate = 0 var/burnstate = 0 var/brute_dam = 0 var/burn_dam = 0 var/stamina_dam = 0 + var/max_stamina_damage = 0 var/max_damage = 0 - var/list/embedded_objects = list() - var/held_index = 0 //are we a hand? if so, which one! - var/is_pseudopart = FALSE //For limbs that don't really exist, eg chainsaws + var/stam_heal_tick = 3 //per Life(). + + var/brute_reduction = 0 //Subtracted to brute damage taken + var/burn_reduction = 0 //Subtracted to burn damage taken //Coloring and proper item icon update var/skin_tone = "" @@ -55,9 +66,9 @@ /obj/item/bodypart/examine(mob/user) ..() - if(brute_dam > 0) + if(brute_dam > DAMAGE_PRECISION) to_chat(user, "This limb has [brute_dam > 30 ? "severe" : "minor"] bruising.") - if(burn_dam > 0) + if(burn_dam > DAMAGE_PRECISION) to_chat(user, "This limb has [burn_dam > 30 ? "severe" : "minor"] burns.") /obj/item/bodypart/blob_act() @@ -114,6 +125,20 @@ for(var/obj/item/I in src) I.forceMove(T) +/obj/item/bodypart/proc/consider_processing() + if(stamina_dam > DAMAGE_PRECISION) + . = TRUE + //else if.. else if.. so on. + else + . = FALSE + needs_processing = . + +//Return TRUE to get whatever mob this is in to update health. +/obj/item/bodypart/proc/on_life() + if(stamina_dam > DAMAGE_PRECISION) //DO NOT update health here, it'll be done in the carbon's life. + if(heal_damage(brute = 0, burn = 0, stamina = stam_heal_tick, only_robotic = FALSE, only_organic = FALSE, updating_health = FALSE)) + . |= BODYPART_LIFE_UPDATE_HEALTH + //Applies brute and burn damage to the organ. Returns 1 if the damage-icon states changed at all. //Damage will not exceed max_damage using this proc //Cannot apply negative damage @@ -121,22 +146,21 @@ if(owner && (owner.status_flags & GODMODE)) return FALSE //godmode var/dmg_mlt = CONFIG_GET(number/damage_multiplier) - brute = max(brute * dmg_mlt, 0) - burn = max(burn * dmg_mlt, 0) - stamina = max(stamina * dmg_mlt, 0) - if(status == BODYPART_ROBOTIC) //This makes robolimbs not damageable by chems and makes it stronger - brute = max(0, brute - 5) - burn = max(0, burn - 4) - //No stamina scaling.. for now.. + brute = round(max(brute * dmg_mlt, 0),DAMAGE_PRECISION) + burn = round(max(burn * dmg_mlt, 0),DAMAGE_PRECISION) + stamina = round(max(stamina * dmg_mlt, 0),DAMAGE_PRECISION) + brute = max(0, brute - brute_reduction) + burn = max(0, burn - burn_reduction) + //No stamina scaling.. for now.. if(!brute && !burn && !stamina) return FALSE switch(animal_origin) - if(ALIEN_BODYPART,LARVA_BODYPART) //aliens take double burn + if(ALIEN_BODYPART,LARVA_BODYPART) //aliens take double burn //nothing can burn with so much snowflake code around burn *= 2 - var/can_inflict = max_damage - (brute_dam + burn_dam) + var/can_inflict = max_damage - get_damage() if(can_inflict <= 0) return FALSE @@ -144,21 +168,23 @@ if(total_damage > can_inflict) var/excess = total_damage - can_inflict - brute = brute * (excess / total_damage) - burn = burn * (excess / total_damage) + brute = round(brute * (excess / total_damage),DAMAGE_PRECISION) + burn = round(burn * (excess / total_damage),DAMAGE_PRECISION) brute_dam += brute burn_dam += burn //We've dealt the physical damages, if there's room lets apply the stamina damage. - var/current_damage = brute_dam + burn_dam + stamina_dam //This time around, count stamina loss too. + var/current_damage = get_damage(TRUE) //This time around, count stamina loss too. var/available_damage = max_damage - current_damage - stamina_dam += CLAMP(stamina, 0, available_damage) + stamina_dam += round(CLAMP(stamina, 0, min(max_stamina_damage - stamina_dam, available_damage)), DAMAGE_PRECISION) if(owner && updating_health) owner.updatehealth() - if(stamina) - owner.update_stamina() + if(stamina > DAMAGE_PRECISION) + owner.update_stamina() + consider_processing() + check_disabled() return update_bodypart_damage_state() //Heals brute and burn damage for the organ. Returns 1 if the damage-icon states changed at all. @@ -172,18 +198,38 @@ if(only_organic && status != BODYPART_ORGANIC) //This makes robolimbs not healable by chems. return - brute_dam = max(brute_dam - brute, 0) - burn_dam = max(burn_dam - burn, 0) - stamina_dam = max(stamina_dam - stamina, 0) + brute_dam = round(max(brute_dam - brute, 0), DAMAGE_PRECISION) + burn_dam = round(max(burn_dam - burn, 0), DAMAGE_PRECISION) + stamina_dam = round(max(stamina_dam - stamina, 0), DAMAGE_PRECISION) if(owner && updating_health) owner.updatehealth() + consider_processing() + check_disabled() return update_bodypart_damage_state() +//Returns total damage. +/obj/item/bodypart/proc/get_damage(include_stamina = FALSE) + var/total = brute_dam + burn_dam + if(include_stamina) + total += stamina_dam + return total -//Returns total damage...kinda pointless really -/obj/item/bodypart/proc/get_damage() - return brute_dam + burn_dam +//Checks disabled status thresholds +/obj/item/bodypart/proc/check_disabled() + if(!can_dismember() || owner.has_trait(TRAIT_NODISMEMBER)) + return + if(!disabled && (get_damage(TRUE) >= max_damage)) + set_disabled(TRUE) + else if(disabled && (get_damage(TRUE) <= (max_damage * 0.5))) + set_disabled(FALSE) +/obj/item/bodypart/proc/set_disabled(new_disabled = TRUE) + if(disabled == new_disabled) + return + disabled = new_disabled + owner.update_health_hud() //update the healthdoll + owner.update_body() + owner.update_canmove() //Updates an organ's brute/burn states for use by update_damage_overlays() //Returns 1 if we need to update overlays. 0 otherwise. @@ -196,8 +242,6 @@ return TRUE return FALSE - - //Change organ status /obj/item/bodypart/proc/change_bodypart_status(new_limb_status, heal_limb, change_icon_to_default) status = new_limb_status @@ -350,6 +394,7 @@ limb.icon_state = "[species_id]_[body_zone]_[icon_gender]" else limb.icon_state = "[species_id]_[body_zone]" + // Citadel Start if(should_draw_citadel) limb.icon = 'modular_citadel/icons/mob/mutant_bodyparts.dmi' @@ -358,6 +403,7 @@ else limb.icon_state = "[species_id]_[body_zone]" // Citadel End + if(aux_zone) aux = image(limb.icon, "[species_id]_[aux_zone]", -aux_layer, image_dir) . += aux @@ -394,6 +440,8 @@ body_part = CHEST px_x = 0 px_y = 0 + stam_damage_coeff = 1 + max_stamina_damage = 100 var/obj/item/cavity_item /obj/item/bodypart/chest/Destroy() @@ -440,13 +488,28 @@ icon_state = "default_human_l_arm" attack_verb = list("slapped", "punched") max_damage = 50 - body_zone =BODY_ZONE_L_ARM + max_stamina_damage = 50 + body_zone = BODY_ZONE_L_ARM body_part = ARM_LEFT aux_zone = BODY_ZONE_PRECISE_L_HAND aux_layer = HANDS_PART_LAYER + body_damage_coeff = 0.75 held_index = 1 px_x = -6 px_y = 0 + stam_heal_tick = 2 + +/obj/item/bodypart/l_arm/set_disabled(new_disabled = TRUE) + ..() + if(disabled) + to_chat(owner, "Your [name] is too damaged to function!") + owner.emote("scream") + if(held_index) + owner.dropItemToGround(owner.get_item_for_held_index(held_index)) + if(owner.hud_used) + var/obj/screen/inventory/hand/L = owner.hud_used.hand_slots["[held_index]"] + if(L) + L.update_icon() /obj/item/bodypart/l_arm/monkey icon = 'icons/mob/animal_parts.dmi' @@ -480,9 +543,24 @@ body_part = ARM_RIGHT aux_zone = BODY_ZONE_PRECISE_R_HAND aux_layer = HANDS_PART_LAYER + body_damage_coeff = 0.75 held_index = 2 px_x = 6 px_y = 0 + stam_heal_tick = 2 + max_stamina_damage = 50 + +/obj/item/bodypart/r_arm/set_disabled(new_disabled = TRUE) + ..() + if(disabled) + to_chat(owner, "Your [name] is too damaged to function!") + owner.emote("scream") + if(held_index) + owner.dropItemToGround(owner.get_item_for_held_index(held_index)) + if(owner.hud_used) + var/obj/screen/inventory/hand/R = owner.hud_used.hand_slots["[held_index]"] + if(R) + R.update_icon() /obj/item/bodypart/r_arm/monkey icon = 'icons/mob/animal_parts.dmi' @@ -514,8 +592,17 @@ max_damage = 50 body_zone = BODY_ZONE_L_LEG body_part = LEG_LEFT + body_damage_coeff = 0.75 px_x = -2 px_y = 12 + stam_heal_tick = 2 + max_stamina_damage = 50 + +/obj/item/bodypart/l_leg/set_disabled(new_disabled = TRUE) + ..() + if(disabled) + to_chat(owner, "Your [name] is too damaged to function!") + owner.emote("scream") /obj/item/bodypart/l_leg/digitigrade name = "left digitigrade leg" @@ -552,8 +639,17 @@ max_damage = 50 body_zone = BODY_ZONE_R_LEG body_part = LEG_RIGHT + body_damage_coeff = 0.75 px_x = 2 px_y = 12 + max_stamina_damage = 50 + stam_heal_tick = 2 + +/obj/item/bodypart/r_leg/set_disabled(new_disabled = TRUE) + ..() + if(disabled) + to_chat(owner, "Your [name] is too damaged to function!") + owner.emote("scream") /obj/item/bodypart/r_leg/digitigrade name = "right digitigrade leg" diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 87e13955fe..73d0ab305e 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -1,7 +1,7 @@ /obj/item/bodypart/proc/can_dismember(obj/item/I) if(dismemberable) - . = (get_damage() >= (max_damage - I.armour_penetration/2)) + return TRUE //Dismember a limb /obj/item/bodypart/proc/dismember(dam_type = BRUTE) @@ -16,7 +16,7 @@ return FALSE var/obj/item/bodypart/affecting = C.get_bodypart(BODY_ZONE_CHEST) - affecting.receive_damage(CLAMP(brute_dam/2, 15, 50), CLAMP(burn_dam/2, 0, 50)) //Damage the chest based on limb's existing damage + affecting.receive_damage(CLAMP(brute_dam/2 * affecting.body_damage_coeff, 15, 50), CLAMP(burn_dam/2 * affecting.body_damage_coeff, 0, 50)) //Damage the chest based on limb's existing damage C.visible_message("[C]'s [src.name] has been violently dismembered!") C.emote("scream") SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "dismembered", /datum/mood_event/dismembered) @@ -51,7 +51,7 @@ return FALSE if(C.has_trait(TRAIT_NODISMEMBER)) return FALSE - + . = list() var/organ_spilled = 0 var/turf/T = get_turf(C) C.add_splatter_floor(T) @@ -64,14 +64,15 @@ O.Remove(C) O.forceMove(T) organ_spilled = 1 + . += X if(cavity_item) cavity_item.forceMove(T) + . += cavity_item cavity_item = null organ_spilled = 1 if(organ_spilled) C.visible_message("[C]'s internal organs spill out onto the floor!") - return 1 @@ -158,7 +159,8 @@ ..() /obj/item/bodypart/chest/drop_limb(special) - return + if(special) + ..() /obj/item/bodypart/r_arm/drop_limb(special) var/mob/living/carbon/C = owner @@ -242,16 +244,11 @@ name = "[owner.real_name]'s head" ..() - - - - - //Attach a limb to a human and drop any existing limb of that type. /obj/item/bodypart/proc/replace_limb(mob/living/carbon/C, special) if(!istype(C)) return - var/obj/item/bodypart/O = locate(src.type) in C.bodyparts + var/obj/item/bodypart/O = C.get_bodypart(body_zone) if(O) O.drop_limb(1) attach_limb(C, special) @@ -259,7 +256,7 @@ /obj/item/bodypart/head/replace_limb(mob/living/carbon/C, special) if(!istype(C)) return - var/obj/item/bodypart/head/O = locate(src.type) in C.bodyparts + var/obj/item/bodypart/head/O = C.get_bodypart(body_zone) if(O) if(!special) return diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 57b1c09cab..c08e6e5673 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -11,6 +11,8 @@ throw_range = 2 //No head bowling px_x = 0 px_y = -8 + stam_damage_coeff = 1 + max_stamina_damage = 100 var/mob/living/brain/brainmob = null //The current occupant. var/obj/item/organ/brain/brain = null //The brain organ @@ -31,6 +33,11 @@ var/lip_style = null var/lip_color = "white" +/obj/item/bodypart/head/can_dismember(obj/item/I) + if(!((owner.stat == DEAD) || owner.InFullCritical())) + return FALSE + return ..() + /obj/item/bodypart/head/drop_organs(mob/user) var/turf/T = get_turf(src) if(status != BODYPART_ROBOTIC) diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm index 91336331c4..1921f68165 100644 --- a/code/modules/surgery/bodyparts/helpers.dm +++ b/code/modules/surgery/bodyparts/helpers.dm @@ -13,33 +13,35 @@ /mob/living/carbon/has_hand_for_held_index(i) if(i) var/obj/item/bodypart/L = hand_bodyparts[i] - if(L) + if(L && !L.disabled) return L return FALSE -/mob/proc/has_left_hand() +/mob/proc/has_left_hand(check_disabled = TRUE) return TRUE -/mob/living/carbon/has_left_hand() +/mob/living/carbon/has_left_hand(check_disabled = TRUE) for(var/obj/item/bodypart/L in hand_bodyparts) if(L.held_index % 2) - return TRUE + if(!check_disabled || !L.disabled) + return TRUE return FALSE /mob/living/carbon/alien/larva/has_left_hand() return 1 -/mob/proc/has_right_hand() +/mob/proc/has_right_hand(check_disabled = TRUE) return TRUE -/mob/living/carbon/has_right_hand() +/mob/living/carbon/has_right_hand(check_disabled = TRUE) for(var/obj/item/bodypart/L in hand_bodyparts) if(!(L.held_index % 2)) - return TRUE + if(!check_disabled || !L.disabled) + return TRUE return FALSE /mob/living/carbon/alien/larva/has_right_hand() @@ -48,17 +50,19 @@ //Limb numbers -/mob/proc/get_num_arms() +/mob/proc/get_num_arms(check_disabled = TRUE) return 2 -/mob/living/carbon/get_num_arms() +/mob/living/carbon/get_num_arms(check_disabled = TRUE) . = 0 for(var/X in bodyparts) var/obj/item/bodypart/affecting = X if(affecting.body_part == ARM_RIGHT) - .++ + if(!check_disabled || !affecting.disabled) + .++ if(affecting.body_part == ARM_LEFT) - .++ + if(!check_disabled || !affecting.disabled) + .++ //sometimes we want to ignore that we don't have the required amount of arms. @@ -69,17 +73,19 @@ return 1 //so we can still handcuff larvas. -/mob/proc/get_num_legs() +/mob/proc/get_num_legs(check_disabled = TRUE) return 2 -/mob/living/carbon/get_num_legs() +/mob/living/carbon/get_num_legs(check_disabled = TRUE) . = 0 for(var/X in bodyparts) var/obj/item/bodypart/affecting = X if(affecting.body_part == LEG_RIGHT) - .++ + if(!check_disabled || !affecting.disabled) + .++ if(affecting.body_part == LEG_LEFT) - .++ + if(!check_disabled || !affecting.disabled) + .++ //sometimes we want to ignore that we don't have the required amount of legs. /mob/proc/get_leg_ignore() @@ -110,6 +116,27 @@ full -= zone return full +/mob/living/proc/get_disabled_limbs() + return list() + +/mob/living/carbon/get_disabled_limbs() + var/list/full = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) + var/list/disabled = list() + for(var/zone in full) + var/obj/item/bodypart/affecting = get_bodypart(zone) + if(affecting && affecting.disabled) + disabled += zone + return disabled + +/mob/living/carbon/alien/larva/get_disabled_limbs() + var/list/full = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST) + var/list/disabled = list() + for(var/zone in full) + var/obj/item/bodypart/affecting = get_bodypart(zone) + if(affecting && affecting.disabled) + disabled += zone + return disabled + //Remove all embedded objects from all limbs on the carbon mob /mob/living/carbon/proc/remove_all_embedded_objects() var/turf/T = get_turf(src) diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm index f8e939dbcd..c535ef4a20 100644 --- a/code/modules/surgery/bodyparts/robot_bodyparts.dm +++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm @@ -19,6 +19,9 @@ flags_1 = CONDUCT_1 icon_state = "borg_l_arm" status = BODYPART_ROBOTIC + + brute_reduction = 5 + burn_reduction = 4 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -37,6 +40,9 @@ flags_1 = CONDUCT_1 icon_state = "borg_r_arm" status = BODYPART_ROBOTIC + + brute_reduction = 5 + burn_reduction = 4 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -55,6 +61,9 @@ flags_1 = CONDUCT_1 icon_state = "borg_l_leg" status = BODYPART_ROBOTIC + + brute_reduction = 5 + burn_reduction = 4 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -73,6 +82,9 @@ flags_1 = CONDUCT_1 icon_state = "borg_r_leg" status = BODYPART_ROBOTIC + + brute_reduction = 5 + burn_reduction = 4 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -90,6 +102,9 @@ flags_1 = CONDUCT_1 icon_state = "borg_chest" status = BODYPART_ROBOTIC + + brute_reduction = 5 + burn_reduction = 4 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -149,6 +164,9 @@ flags_1 = CONDUCT_1 icon_state = "borg_head" status = BODYPART_ROBOTIC + + brute_reduction = 5 + burn_reduction = 4 light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG @@ -222,24 +240,32 @@ name = "surplus prosthetic left arm" desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing." icon = 'icons/mob/augmentation/surplus_augments.dmi' + brute_reduction = 0 + burn_reduction = 0 max_damage = 20 /obj/item/bodypart/r_arm/robot/surplus name = "surplus prosthetic right arm" desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing." icon = 'icons/mob/augmentation/surplus_augments.dmi' + brute_reduction = 0 + burn_reduction = 0 max_damage = 20 /obj/item/bodypart/l_leg/robot/surplus name = "surplus prosthetic left leg" desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing." icon = 'icons/mob/augmentation/surplus_augments.dmi' + brute_reduction = 0 + burn_reduction = 0 max_damage = 20 /obj/item/bodypart/r_leg/robot/surplus name = "surplus prosthetic right leg" desc = "A skeletal, robotic limb. Outdated and fragile, but it's still better than nothing." icon = 'icons/mob/augmentation/surplus_augments.dmi' + brute_reduction = 0 + burn_reduction = 0 max_damage = 20 diff --git a/code/modules/surgery/dental_implant.dm b/code/modules/surgery/dental_implant.dm index 4e1d8eb743..87e6d096b3 100644 --- a/code/modules/surgery/dental_implant.dm +++ b/code/modules/surgery/dental_implant.dm @@ -32,7 +32,7 @@ if(!..()) return 0 to_chat(owner, "You grit your teeth and burst the implanted [target.name]!") - add_logs(owner, null, "swallowed an implanted pill", target) + log_combat(owner, null, "swallowed an implanted pill", target) if(target.reagents.total_volume) target.reagents.reaction(owner, INGEST) target.reagents.trans_to(owner, target.reagents.total_volume) diff --git a/code/modules/surgery/helpers.dm b/code/modules/surgery/helpers.dm index d74d7b6691..59440cc3ee 100644 --- a/code/modules/surgery/helpers.dm +++ b/code/modules/surgery/helpers.dm @@ -68,7 +68,7 @@ user.visible_message("[user] drapes [I] over [M]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name].", \ "You drape [I] over [M]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name].") - add_logs(user, M, "operated on", null, "(OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])") + log_combat(user, M, "operated on", null, "(OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])") else to_chat(user, "You need to expose [M]'s [parse_zone(selected_zone)] first!") diff --git a/code/modules/surgery/limb_augmentation.dm b/code/modules/surgery/limb_augmentation.dm index 10ef58ff18..46fe262189 100644 --- a/code/modules/surgery/limb_augmentation.dm +++ b/code/modules/surgery/limb_augmentation.dm @@ -14,14 +14,14 @@ user.visible_message("[user] begins to sever the muscles on [target]'s [parse_zone(user.zone_selected)].", "You begin to sever the muscles on [target]'s [parse_zone(user.zone_selected)]...") -/datum/surgery_step/add_limb +/datum/surgery_step/replace_limb name = "replace limb" implements = list(/obj/item/bodypart = 100, /obj/item/organ_storage = 100) time = 32 var/obj/item/bodypart/L = null // L because "limb" -/datum/surgery_step/add_limb/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) +/datum/surgery_step/replace_limb/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) if(istype(tool, /obj/item/organ_storage) && istype(tool.contents[1], /obj/item/bodypart)) tool = tool.contents[1] var/obj/item/bodypart/aug = tool @@ -42,29 +42,24 @@ /datum/surgery/augmentation name = "augmentation" - steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, /datum/surgery_step/replace, /datum/surgery_step/saw, /datum/surgery_step/add_limb) + steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, /datum/surgery_step/replace, /datum/surgery_step/saw, /datum/surgery_step/replace_limb) species = list(/mob/living/carbon/human) possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD) requires_real_bodypart = TRUE //SURGERY STEP SUCCESSES -/datum/surgery_step/add_limb/success(mob/user, mob/living/carbon/target, target_zone, obj/item/bodypart/tool, datum/surgery/surgery) +/datum/surgery_step/replace_limb/success(mob/user, mob/living/carbon/target, target_zone, obj/item/bodypart/tool, datum/surgery/surgery) if(L) - user.visible_message("[user] successfully augments [target]'s [parse_zone(target_zone)]!", "You successfully augment [target]'s [parse_zone(target_zone)].") if(istype(tool, /obj/item/organ_storage)) tool.icon_state = initial(tool.icon_state) tool.desc = initial(tool.desc) tool.cut_overlays() tool = tool.contents[1] - L.change_bodypart_status(BODYPART_ROBOTIC, TRUE) - L.icon = tool.icon - L.max_damage = tool.max_damage - qdel(tool) - target.update_body_parts() - target.updatehealth() - target.update_hair() - add_logs(user, target, "augmented", addition="by giving him new [parse_zone(target_zone)] INTENT: [uppertext(user.a_intent)]") + if(istype(tool) && user.temporarilyRemoveItemFromInventory(tool)) + tool.replace_limb(target, TRUE) + user.visible_message("[user] successfully augments [target]'s [parse_zone(target_zone)]!", "You successfully augment [target]'s [parse_zone(target_zone)].") + log_combat(user, target, "augmented", addition="by giving him new [parse_zone(target_zone)] INTENT: [uppertext(user.a_intent)]") else to_chat(user, "[target] has no organic [parse_zone(target_zone)] there!") - return 1 + return TRUE diff --git a/code/modules/surgery/organ_manipulation.dm b/code/modules/surgery/organ_manipulation.dm index 3b260017d3..553de45c60 100644 --- a/code/modules/surgery/organ_manipulation.dm +++ b/code/modules/surgery/organ_manipulation.dm @@ -141,7 +141,7 @@ if(I && I.owner == target) user.visible_message("[user] successfully extracts [I] from [target]'s [parse_zone(target_zone)]!", "You successfully extract [I] from [target]'s [parse_zone(target_zone)].") - add_logs(user, target, "surgically removed [I.name] from", addition="INTENT: [uppertext(user.a_intent)]") + log_combat(user, target, "surgically removed [I.name] from", addition="INTENT: [uppertext(user.a_intent)]") I.Remove(target) I.forceMove(get_turf(target)) else diff --git a/code/modules/surgery/organic_steps.dm b/code/modules/surgery/organic_steps.dm index beed1cce7f..01eb751f39 100644 --- a/code/modules/surgery/organic_steps.dm +++ b/code/modules/surgery/organic_steps.dm @@ -89,7 +89,7 @@ //drill bone /datum/surgery_step/drill name = "drill bone" - implements = list(/obj/item/surgicaldrill = 100, /obj/item/pickaxe/drill = 60, /obj/item/mecha_parts/mecha_equipment/drill = 60, TOOL_SCREWDRIVER = 20) + implements = list(/obj/item/surgicaldrill = 100, /obj/item/screwdriver/power = 80, /obj/item/pickaxe/drill = 60, /obj/item/mecha_parts/mecha_equipment/drill = 60, TOOL_SCREWDRIVER = 20) time = 30 /datum/surgery_step/drill/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index ea2385ce5f..ad07ce7238 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -42,6 +42,9 @@ to_chat(user, "[src] is assembled in the [zone == BODY_ZONE_R_ARM ? "right" : "left"] arm configuration. You can use a screwdriver to reassemble it.") /obj/item/organ/cyberimp/arm/screwdriver_act(mob/living/user, obj/item/I) + . = ..() + if(.) + return TRUE I.play_tool_sound(src) if(zone == BODY_ZONE_R_ARM) zone = BODY_ZONE_L_ARM diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index 1a3f5a6ae9..38967711fe 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -106,7 +106,7 @@ ..() if(crit_fail) return - owner.adjustStaminaLoss(-3.5) + if(owner.AmountStun() > STUN_SET_AMOUNT) owner.SetStun(STUN_SET_AMOUNT) if(owner.AmountKnockdown() > STUN_SET_AMOUNT) diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm index 787a599428..cf22a633cf 100644 --- a/code/modules/surgery/organs/autosurgeon.dm +++ b/code/modules/surgery/organs/autosurgeon.dm @@ -58,6 +58,8 @@ return ..() /obj/item/autosurgeon/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE if(!storedorgan) to_chat(user, "There's no implant in [src] for you to remove.") else diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index d38e76ae6e..bfe1b2dc3b 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -242,7 +242,7 @@ if (mobhook && mobhook.parent != M) QDEL_NULL(mobhook) if (!mobhook) - mobhook = M.AddComponent(/datum/component/redirect, list(COMSIG_ATOM_DIR_CHANGE), CALLBACK(src, .proc/update_visuals)) + mobhook = M.AddComponent(/datum/component/redirect, list(COMSIG_ATOM_DIR_CHANGE = CALLBACK(src, .proc/update_visuals))) /obj/item/organ/eyes/robotic/glow/Remove(mob/living/carbon/M) . = ..() @@ -266,7 +266,7 @@ active = FALSE remove_mob_overlay() -/obj/item/organ/eyes/robotic/glow/proc/update_visuals(olddir, newdir) +/obj/item/organ/eyes/robotic/glow/proc/update_visuals(datum/source, olddir, newdir) if((LAZYLEN(eye_lighting) < light_beam_distance) || !on_mob) regenerate_light_effects() var/turf/scanfrom = get_turf(owner) diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm index 5424486649..52904b8af1 100644 --- a/code/modules/surgery/organs/heart.dm +++ b/code/modules/surgery/organs/heart.dm @@ -54,11 +54,11 @@ var/sound/fastbeat = sound('sound/health/fastbeat.ogg', repeat = TRUE) var/mob/living/carbon/H = owner - if(H.health <= HEALTH_THRESHOLD_CRIT && beat != BEAT_SLOW) + if(H.health <= H.crit_threshold && beat != BEAT_SLOW) beat = BEAT_SLOW H.playsound_local(get_turf(H), slowbeat,40,0, channel = CHANNEL_HEARTBEAT) to_chat(owner, "You feel your heart slow down...") - if(beat == BEAT_SLOW && H.health > HEALTH_THRESHOLD_CRIT) + if(beat == BEAT_SLOW && H.health > H.crit_threshold) H.stop_sound_channel(CHANNEL_HEARTBEAT) beat = BEAT_NONE diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm index cd9e60d3d1..ee767566e6 100755 --- a/code/modules/surgery/organs/liver.dm +++ b/code/modules/surgery/organs/liver.dm @@ -40,7 +40,7 @@ C.reagents.metabolize(C, can_overdose=TRUE) if(damage > 10 && prob(damage/3))//the higher the damage the higher the probability - to_chat(C, "You feel [pick("nauseated", "a dull pain in your lower body", "confused")].") + to_chat(C, "You feel a dull pain in your abdomen.") if(damage > maxHealth)//cap liver damage damage = maxHealth diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index 9e53a902a2..9f71f58ff8 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -64,7 +64,7 @@ if(!breath || (breath.total_moles() == 0)) if(H.reagents.has_reagent(crit_stabilizing_reagent)) return - if(H.health >= HEALTH_THRESHOLD_CRIT) + if(H.health >= H.crit_threshold) H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) else if(!H.has_trait(TRAIT_NOCRITDAMAGE)) H.adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS) @@ -111,7 +111,7 @@ H.throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) else H.failed_last_breath = FALSE - if(H.health >= HEALTH_THRESHOLD_CRIT) + if(H.health >= H.crit_threshold) H.adjustOxyLoss(-5) gas_breathed = breath_gases[/datum/gas/oxygen][MOLES] H.clear_alert("not_enough_oxy") @@ -139,7 +139,7 @@ H.throw_alert("nitro", /obj/screen/alert/not_enough_nitro) else H.failed_last_breath = FALSE - if(H.health >= HEALTH_THRESHOLD_CRIT) + if(H.health >= H.crit_threshold) H.adjustOxyLoss(-5) gas_breathed = breath_gases[/datum/gas/nitrogen][MOLES] H.clear_alert("nitro") @@ -176,7 +176,7 @@ H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2) else H.failed_last_breath = FALSE - if(H.health >= HEALTH_THRESHOLD_CRIT) + if(H.health >= H.crit_threshold) H.adjustOxyLoss(-5) gas_breathed = breath_gases[/datum/gas/carbon_dioxide][MOLES] H.clear_alert("not_enough_co2") @@ -206,7 +206,7 @@ H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) else H.failed_last_breath = FALSE - if(H.health >= HEALTH_THRESHOLD_CRIT) + if(H.health >= H.crit_threshold) H.adjustOxyLoss(-5) gas_breathed = breath_gases[/datum/gas/plasma][MOLES] H.clear_alert("not_enough_tox") @@ -270,12 +270,61 @@ H.reagents.add_reagent("no2",1) breath_gases[/datum/gas/nitryl][MOLES]-=gas_breathed + // Stimulum gas_breathed = breath_gases[/datum/gas/stimulum][MOLES] if (gas_breathed > gas_stimulation_min) var/existing = H.reagents.get_reagent_amount("stimulum") H.reagents.add_reagent("stimulum",max(0, 1 - existing)) breath_gases[/datum/gas/stimulum][MOLES]-=gas_breathed + + // Miasma + if (breath_gases[/datum/gas/miasma]) + var/miasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/miasma][MOLES]) + + //Miasma sickness + if(prob(0.5 * miasma_pp)) + var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(2,3) + miasma_disease.name = "Unknown" + miasma_disease.try_infect(owner) + + // Miasma side effects + switch(miasma_pp) + if(1 to 5) + // At lower pp, give out a little warning + SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "smell") + if(prob(5)) + to_chat(owner, "There is an unpleasant smell in the air.") + if(5 to 15) + //At somewhat higher pp, warning becomes more obvious + if(prob(15)) + to_chat(owner, "You smell something horribly decayed inside this room.") + SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/bad_smell) + if(15 to 30) + //Small chance to vomit. By now, people have internals on anyway + if(prob(5)) + to_chat(owner, "The stench of rotting carcasses is unbearable!") + SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench) + owner.vomit() + if(30 to INFINITY) + //Higher chance to vomit. Let the horror start + if(prob(15)) + to_chat(owner, "The stench of rotting carcasses is unbearable!") + SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench) + owner.vomit() + else + SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "smell") + + // In a full miasma atmosphere with 101.34 pKa, about 10 disgust per breath, is pretty low compared to threshholds + // Then again, this is a purely hypothetical scenario and hardly reachable + owner.adjust_disgust(0.1 * miasma_pp) + + breath_gases[/datum/gas/miasma][MOLES]-=gas_breathed + + // Clear out moods when no miasma at all + else + SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "smell") + handle_breath_temperature(breath, H) breath.garbage_collect() return TRUE diff --git a/code/modules/surgery/organs/stomach.dm b/code/modules/surgery/organs/stomach.dm index cb7833a373..24132bcf04 100755 --- a/code/modules/surgery/organs/stomach.dm +++ b/code/modules/surgery/organs/stomach.dm @@ -42,13 +42,13 @@ SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "disgust") if(DISGUST_LEVEL_GROSS to DISGUST_LEVEL_VERYGROSS) H.throw_alert("disgust", /obj/screen/alert/gross) - SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/disgust/gross) + SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/gross) if(DISGUST_LEVEL_VERYGROSS to DISGUST_LEVEL_DISGUSTED) H.throw_alert("disgust", /obj/screen/alert/verygross) - SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/disgust/verygross) + SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/verygross) if(DISGUST_LEVEL_DISGUSTED to INFINITY) H.throw_alert("disgust", /obj/screen/alert/disgusted) - SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/disgust/disgusted) + SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "disgust", /datum/mood_event/disgusted) /obj/item/organ/stomach/Remove(mob/living/carbon/M, special = 0) var/mob/living/carbon/human/H = owner diff --git a/code/modules/surgery/organs/tails.dm b/code/modules/surgery/organs/tails.dm index 8b05cbef08..b6701643a9 100644 --- a/code/modules/surgery/organs/tails.dm +++ b/code/modules/surgery/organs/tails.dm @@ -10,9 +10,8 @@ /obj/item/organ/tail/Remove(mob/living/carbon/human/H, special = 0) ..() - if(istype(H)) - H.endTailWag() - + if(H && H.dna && H.dna.species) + H.dna.species.stop_wagging_tail(H) /obj/item/organ/tail/cat name = "cat tail" diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm index 59db1fb804..d5b2d16e67 100644 --- a/code/modules/surgery/organs/tongue.dm +++ b/code/modules/surgery/organs/tongue.dm @@ -83,7 +83,7 @@ //Hacks var/mob/living/carbon/human/user = usr var/rendered = "[user.name]: [message]" - log_talk(user,"ABDUCTOR:[key_name(user)] : [rendered]",LOGSAY) + user.log_talk(message, LOG_SAY, tag="abductor") for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) var/obj/item/organ/tongue/T = H.getorganslot(ORGAN_SLOT_TONGUE) if(!T || T.type != type) diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index 4dda6d7591..c9a3871511 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -1,5 +1,6 @@ /datum/surgery var/name = "surgery" + var/desc = "surgery description" var/status = 1 var/list/steps = list() //Steps in a surgery var/step_in_progress = 0 //Actively performing a Surgery @@ -92,7 +93,15 @@ var/datum/species/abductor/S = H.dna.species if(S.scientist) return TRUE - + + if(iscyborg(user)) + var/mob/living/silicon/robot/R = user + var/obj/item/surgical_processor/SP = locate() in R.module.modules + if(!SP) + return FALSE + if(type in SP.advanced_surgeries) + return TRUE + var/turf/T = get_turf(target) var/obj/structure/table/optable/table = locate(/obj/structure/table/optable, T) if(!table || !table.computer) @@ -129,7 +138,6 @@ //TODO //specific steps for some surgeries (fluff text) -//R&D researching new surgeries (especially for non-humans) //more interesting failure options //randomised complications //more surgeries! diff --git a/code/modules/surgery/surgery_step.dm b/code/modules/surgery/surgery_step.dm index bc6c04bca7..f670b80167 100644 --- a/code/modules/surgery/surgery_step.dm +++ b/code/modules/surgery/surgery_step.dm @@ -14,6 +14,8 @@ if(accept_hand) if(!tool) success = TRUE + if(iscyborg(user)) + success = TRUE if(accept_any_item) if(tool && tool_check(user, tool)) diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm index 7afee36543..aaf859ee52 100644 --- a/code/modules/surgery/tools.dm +++ b/code/modules/surgery/tools.dm @@ -198,6 +198,7 @@ icon_state = "evidenceobj" /obj/item/organ_storage/afterattack(obj/item/I, mob/user, proximity) + . = ..() if(!proximity) return if(contents.len) @@ -234,3 +235,29 @@ else to_chat(user, "[src] is empty.") return + +/obj/item/surgical_processor //allows medical cyborgs to scan and initiate advanced surgeries + name = "\improper Surgical Processor" + desc = "A device for scanning and initiating surgeries from a disk or operating computer." + icon = 'icons/obj/device.dmi' + icon_state = "spectrometer" + item_flags = NOBLUDGEON + var/list/advanced_surgeries = list() + +/obj/item/surgical_processor/afterattack(obj/item/O, mob/user, proximity) + . = ..() + if(!proximity) + return + if(istype(O, /obj/item/disk/surgery)) + to_chat(user, "You load the surgery protocol from [O] into [src].") + var/obj/item/disk/surgery/D = O + if(do_after(user, 10, target = O)) + advanced_surgeries |= D.surgeries + return TRUE + if(istype(O, /obj/machinery/computer/operating)) + to_chat(user, "You copy surgery protocols from [O] into [src].") + var/obj/machinery/computer/operating/OC = O + if(do_after(user, 10, target = O)) + advanced_surgeries |= OC.advanced_surgeries + return TRUE + return diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 59cf27bbf3..8da5d7c113 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -1,9 +1,11 @@ //include unit test files in this module in this ifdef -// CITADEL EDIT add vore_tests.dm +//Keep this sorted alphabetically + #ifdef UNIT_TESTS -#include "unit_test.dm" -#include "reagent_recipe_collisions.dm" #include "reagent_id_typos.dm" -//#include "vore_tests.dm" +#include "reagent_recipe_collisions.dm" +#include "spawn_humans.dm" #include "subsystem_init.dm" +#include "timer_sanity.dm" +#include "unit_test.dm" #endif diff --git a/code/modules/unit_tests/spawn_humans.dm b/code/modules/unit_tests/spawn_humans.dm new file mode 100644 index 0000000000..7189e87277 --- /dev/null +++ b/code/modules/unit_tests/spawn_humans.dm @@ -0,0 +1,7 @@ +/datum/unit_test/spawn_humans/Run() + var/locs = block(run_loc_bottom_left, run_loc_top_right) + + for(var/I in 1 to 5) + new /mob/living/carbon/human(pick(locs)) + + sleep(50) diff --git a/code/modules/unit_tests/timer_sanity.dm b/code/modules/unit_tests/timer_sanity.dm new file mode 100644 index 0000000000..1e5b022b19 --- /dev/null +++ b/code/modules/unit_tests/timer_sanity.dm @@ -0,0 +1,3 @@ +/datum/unit_test/timer_sanity/Run() + if(SStimer.bucket_count < 0) + Fail("SStimer is going into negative bucket count from something") \ No newline at end of file diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index a58d8a6269..a38b27af6c 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -202,6 +202,15 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) surplus = 40 include_modes = list(/datum/game_mode/nuclear) +/datum/uplink_item/dangerous/carbine + name = "M-90gl Carbine" + desc = "A fully-loaded, specialized three-round burst carbine that fires 5.56mm ammunition from a 30 round magazine \ + with a togglable 40mm under-barrel grenade launcher." + item = /obj/item/gun/ballistic/automatic/m90 + cost = 18 + surplus = 50 + include_modes = list(/datum/game_mode/nuclear) + /datum/uplink_item/dangerous/machinegun name = "L6 Squad Automatic Weapon" desc = "A fully-loaded Aussec Armoury belt-fed machine gun. \ @@ -395,6 +404,15 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) player_minimum = 25 restricted = TRUE +/datum/uplink_item/dangerous/buzzkill + name = "Buzzkill Grenade Box" + desc = "A box with three grenades that release a swarm of angry bees upon activation. These bees indiscriminately attack friend or foe \ + with random toxins. Courtesy of the BLF and Tiger Cooperative." + item = /obj/item/storage/box/syndie_kit/bee_grenades + cost = 15 + surplus = 35 + include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + // Ammunition /datum/uplink_item/ammo category = "Ammunition" @@ -509,6 +527,22 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 20 include_modes = list(/datum/game_mode/nuclear) +/datum/uplink_item/ammo/carbine + name = "5.56mm Toploader Magazine" + desc = "An additional 30-round 5.56mm magazine; suitable for use with the M-90gl carbine. \ + These bullets pack less punch than 1.95mm rounds, but they still offer more power than .45 ammo." + item = /obj/item/ammo_box/magazine/m556 + cost = 4 + include_modes = list(/datum/game_mode/nuclear) + +/datum/uplink_item/ammo/a40mm + name = "40mm Grenade" + desc = "A 40mm HE grenade for use with the M-90gl's under-barrel grenade launcher. \ + Your teammates will ask you to not shoot these down small hallways." + item = /obj/item/ammo_casing/a40mm + cost = 2 + include_modes = list(/datum/game_mode/nuclear) + /datum/uplink_item/ammo/machinegun cost = 6 surplus = 0 @@ -531,6 +565,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A 50-round magazine of 1.95x129mm ammunition for use in the L6 SAW; equipped with special properties \ to puncture even the most durable armor." item = /obj/item/ammo_box/magazine/mm195x129/ap + cost = 9 /datum/uplink_item/ammo/machinegun/incen name = "1.95x129mm (Incendiary) Box Magazine" @@ -1002,8 +1037,10 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/device_tools/ai_detector name = "Artificial Intelligence Detector" - desc = "A functional multitool that turns red when it detects an artificial intelligence watching it or its \ - holder. Knowing when an artificial intelligence is watching you is useful for knowing when to maintain cover." + desc = "A functional multitool that turns red when it detects an artificial intelligence watching it, and can be \ + activated to display their exact viewing location and nearby security camera blind spots. Knowing when \ + an artificial intelligence is watching you is useful for knowing when to maintain cover, and finding nearby \ + blind spots can help you identify escape routes." item = /obj/item/multitool/ai_detect cost = 1 @@ -1016,16 +1053,12 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/device_tools/briefcase_launchpad name = "Briefcase Launchpad" - desc = "A briefcase containing a launchpad, a device able to teleport items and people to and from targets up to three tiles away from the briefcase. \ - Also includes a remote control. Touch the briefcase with the remote to link it." + desc = "A briefcase containing a launchpad, a device able to teleport items and people to and from targets up to eight tiles away from the briefcase. \ + Also includes a remote control, disguised as an ordinary folder. Touch the briefcase with the remote to link it." surplus = 0 - item = /obj/item/briefcase_launchpad + item = /obj/item/storage/briefcase/launchpad cost = 6 -/datum/uplink_item/device_tools/briefcase_launchpad/purchase(mob/user, datum/component/uplink/U) - spawn_item(/obj/item/launchpad_remote, user) //free remote - ..() - /datum/uplink_item/device_tools/magboots name = "Blood-Red Magboots" desc = "A pair of magnetic boots with a Syndicate paintjob that assist with freer movement in space or on-station \ @@ -1256,6 +1289,12 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 4 restricted = TRUE +/datum/uplink_item/implants/stealthimplant + name = "Stealth Implant" + desc = "This one-of-a-kind implant will make you almost invisible if you play your cards right." + item = /obj/item/implanter/stealth + cost = 8 + // Cybernetics /datum/uplink_item/cyber_implants category = "Cybernetic Implants" @@ -1293,12 +1332,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 40 cant_discount = TRUE -/datum/uplink_item/cyber_implants/stealthimplant - name = "Stealth Implant" - desc = "This one-of-a-kind implant will make you almost invisible if you play your cards right." - item = /obj/item/implanter/stealth - cost = 8 - // Role-specific items /datum/uplink_item/role_restricted category = "Role-Restricted" @@ -1455,6 +1488,17 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 15 restricted_roles = list("Clown") +/datum/uplink_item/role_restricted/clowncar + name = "Clown Car" + desc = "The Clown Car is the ultimate transportation method for any worthy clown! \ + Simply insert your bikehorn and get in, and get ready to have the funniest ride of your life! \ + You can ram any spacemen you come across and stuff them into your car, kidnapping them and locking them inside until \ + someone saves them or they manage to crawl out. Be sure not to ram into any walls or vending machines, as the springloaded seats \ + are very sensetive. Now with our included lube defense mechanism which will protect you against any angry shitcurity!" + item = /obj/vehicle/sealed/car/clowncar + cost = 15 + restricted_roles = list("Clown") + // Pointless /datum/uplink_item/badass category = "(Pointless) Badassery" @@ -1572,7 +1616,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/badass/random name = "Random Item" desc = "Picking this will purchase a random item. Useful if you have some TC to spare or if you haven't decided on a strategy yet." - item = /obj/item/paper + item = /obj/effect/gibspawner/generic // non-tangible item because techwebs use this path to determine illegal tech cost = 0 cant_discount = TRUE diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm index ef529ab849..be59a6df65 100644 --- a/code/modules/vehicles/_vehicle.dm +++ b/code/modules/vehicles/_vehicle.dm @@ -1,6 +1,3 @@ -#define VEHICLE_CONTROL_PERMISSION 1 -#define VEHICLE_CONTROL_DRIVE 2 - /obj/vehicle name = "generic vehicle" desc = "Yell at coderbus." @@ -72,8 +69,8 @@ return FALSE occupants[M] = NONE add_control_flags(M, control_flags) - grant_passenger_actions(M) after_add_occupant(M) + grant_passenger_actions(M) return TRUE /obj/vehicle/proc/after_add_occupant(mob/M) @@ -119,8 +116,12 @@ step(trailer, dir_to_move) return did_move else + after_move(direction) return step(src, direction) +/obj/vehicle/proc/after_move(direction) + return + /obj/vehicle/proc/add_control_flags(mob/controller, flags) if(!istype(controller) || !flags) return FALSE @@ -139,12 +140,12 @@ remove_controller_actions_by_flag(controller, i) return TRUE -/obj/vehicle/Collide(atom/movable/M) +/obj/vehicle/Bump(atom/movable/M) . = ..() if(emulate_door_bumps) - if(istype(M, /obj/machinery/door) && has_buckled_mobs()) + if(istype(M, /obj/machinery/door)) for(var/m in occupants) - M.CollidedWith(m) + M.Bumped(m) /obj/vehicle/Move(newloc, dir) . = ..() diff --git a/code/modules/vehicles/cars/car.dm b/code/modules/vehicles/cars/car.dm new file mode 100644 index 0000000000..9e9b41bd80 --- /dev/null +++ b/code/modules/vehicles/cars/car.dm @@ -0,0 +1,78 @@ +/obj/vehicle/sealed/car + layer = ABOVE_MOB_LAYER + anchored = TRUE + var/car_traits = NONE //Bitflag for special behavior such as kidnapping + var/engine_sound = 'sound/vehicles/carrev.ogg' + var/last_enginesound_time + var/engine_sound_length = 20 //Set this to the length of the engine sound + var/escape_time = 200 //Time it takes to break out of the car + +/obj/vehicle/sealed/car/generate_actions() + . = ..() + initialize_controller_action_type(/datum/action/vehicle/sealed/remove_key, VEHICLE_CONTROL_DRIVE) + if(car_traits & CAN_KIDNAP) + initialize_controller_action_type(/datum/action/vehicle/sealed/DumpKidnappedMobs, VEHICLE_CONTROL_DRIVE) + +/obj/vehicle/sealed/car/MouseDrop_T(atom/dropping, mob/M) + if(!M.canmove || M.stat || M.restrained()) + return FALSE + if(isliving(dropping) && M != dropping) + var/mob/living/L = dropping + L.visible_message("[M] starts forcing [L] into [src]!") + mob_try_forced_enter(M, L) + return ..() + +/obj/vehicle/sealed/car/mob_try_exit(mob/M, mob/user, silent = FALSE) + if(M == user && (occupants[M] & VEHICLE_CONTROL_KIDNAPPED)) + to_chat(user, "You push against the back of [src] trunk to try and get out.") + if(!do_after(user, escape_time, target = src)) + return FALSE + to_chat(user,"[user] gets out of [src]") + mob_exit(M, silent) + return TRUE + mob_exit(M, silent) + return TRUE + +/obj/vehicle/sealed/car/after_move(direction) + if(world.time < last_enginesound_time + engine_sound_length) + return + last_enginesound_time = world.time + playsound(src, engine_sound, 100, TRUE) + +/obj/vehicle/sealed/car/attacked_by(obj/item/I, mob/living/user) + if(!I.force) + return + if(occupants[user]) + to_chat(user, "Your attack bounces off of the car's padded interior.") + return + return ..() + +/obj/vehicle/sealed/car/attack_hand(mob/living/user) + . = ..() + if(!(car_traits & CAN_KIDNAP)) + return + if(occupants[user]) + return + to_chat(user, "You start opening [src]'s trunk.") + if(do_after(user, 30)) + if(return_amount_of_controllers_with_flag(VEHICLE_CONTROL_KIDNAPPED)) + to_chat(user, "The people stuck in [src]'s trunk all come tumbling out.") + DumpSpecificMobs(VEHICLE_CONTROL_KIDNAPPED) + else + to_chat(user, "It seems [src]'s trunk was empty.") + +/obj/vehicle/sealed/car/proc/mob_try_forced_enter(mob/forcer, mob/M, silent = FALSE) + if(!istype(M)) + return FALSE + if(occupant_amount() >= max_occupants) + return FALSE + if(do_mob(forcer, get_enter_delay(M), target = src)) + mob_forced_enter(M, silent) + return TRUE + return FALSE + +/obj/vehicle/sealed/car/proc/mob_forced_enter(mob/M, silent = FALSE) + if(!silent) + M.visible_message("[M] is forced into \the [src]!") + M.forceMove(src) + add_occupant(M, VEHICLE_CONTROL_KIDNAPPED) diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm new file mode 100644 index 0000000000..3485c0f378 --- /dev/null +++ b/code/modules/vehicles/cars/clowncar.dm @@ -0,0 +1,130 @@ +/obj/vehicle/sealed/car/clowncar + name = "clown car" + desc = "How someone could even fit in there is beyond me." + icon_state = "clowncar" + max_integrity = 150 + armor = list("melee" = 70, "bullet" = 40, "laser" = 40, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) + enter_delay = 20 + max_occupants = 50 + movedelay = 0.6 + car_traits = CAN_KIDNAP + key_type = /obj/item/bikehorn + key_type_exact = FALSE + var/droppingoil = FALSE + var/RTDcooldown = 150 + var/lastRTDtime = 0 + +/obj/vehicle/sealed/car/clowncar/generate_actions() + . = ..() + initialize_controller_action_type(/datum/action/vehicle/sealed/horn/clowncar, VEHICLE_CONTROL_DRIVE) + +/obj/vehicle/sealed/car/clowncar/auto_assign_occupant_flags(mob/M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.mind && H.mind.assigned_role == "Clown") //Ensures only clowns can drive the car. (Including more at once) + add_control_flags(M, VEHICLE_CONTROL_DRIVE|VEHICLE_CONTROL_PERMISSION) + return + add_control_flags(M, VEHICLE_CONTROL_KIDNAPPED) + +/obj/vehicle/sealed/car/clowncar/mob_forced_enter(mob/M, silent = FALSE) + . = ..() + playsound(src, pick('sound/vehicles/clowncar_load1.ogg', 'sound/vehicles/clowncar_load2.ogg'), 75) + +/obj/vehicle/sealed/car/clowncar/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(prob(33)) + visible_message("[src] spews out a ton of space lube!") + new /obj/effect/particle_effect/foam(loc) //YEET + +/obj/vehicle/sealed/car/clowncar/attacked_by(obj/item/I, mob/living/user) + . = ..() + if(istype(I, /obj/item/reagent_containers/food/snacks/grown/banana)) + var/obj/item/reagent_containers/food/snacks/grown/banana/banana = I + obj_integrity += min(banana.seed.potency, max_integrity-obj_integrity) + to_chat(user, "You use the [banana] to repair the [src]!") + qdel(banana) + +/obj/vehicle/sealed/car/clowncar/Bump(atom/movable/M) + . = ..() + if(isliving(M)) + if(ismegafauna(M)) + return + var/mob/living/L = M + if(iscarbon(L)) + var/mob/living/carbon/C = L + C.Knockdown(40) //I play to make sprites go horizontal + L.visible_message("[src] rams into [L] and sucks him up!") //fuck off shezza this isn't ERP. + mob_forced_enter(L) + playsound(src, pick('sound/vehicles/clowncar_ram1.ogg', 'sound/vehicles/clowncar_ram2.ogg', 'sound/vehicles/clowncar_ram3.ogg'), 75) + else if(istype(M, /turf/closed)) + visible_message("[src] rams into [M] and crashes!") + playsound(src, pick('sound/vehicles/clowncar_crash1.ogg', 'sound/vehicles/clowncar_crash2.ogg'), 75) + playsound(src, 'sound/vehicles/clowncar_crashpins.ogg', 75) + DumpMobs(TRUE) + +/obj/vehicle/sealed/car/clowncar/emag_act(mob/user) + if(obj_flags & EMAGGED) + return + obj_flags |= EMAGGED + to_chat(user, "You scramble the clowncar child safety lock and a panel with 6 colorful buttons appears!") + initialize_controller_action_type(/datum/action/vehicle/sealed/RollTheDice, VEHICLE_CONTROL_DRIVE) + +/obj/vehicle/sealed/car/clowncar/Destroy() + playsound(src, 'sound/vehicles/clowncar_fart.ogg', 100) + return ..() + +/obj/vehicle/sealed/car/clowncar/after_move(direction) + . = ..() + if(droppingoil) + new /obj/effect/decal/cleanable/oil/slippery(loc) + +/obj/vehicle/sealed/car/clowncar/proc/RollTheDice(mob/user) + if(world.time - lastRTDtime < RTDcooldown) + to_chat(user, "The button panel is currently recharging.") + return + lastRTDtime = world.time + var/randomnum = rand(1,6) + switch(randomnum) + if(1) + visible_message("[user] has pressed one of the colorful buttons on [src] and a special banana peel drops out of it.") + new /obj/item/grown/bananapeel/specialpeel(loc) + if(2) + visible_message("[user] has pressed one of the colorful buttons on [src] and unknown chemicals flood out of it.") + var/datum/reagents/R = new/datum/reagents(300) + R.my_atom = src + R.add_reagent(get_random_reagent_id(), 100) + var/datum/effect_system/foam_spread/foam = new + foam.set_up(200, loc, R) + foam.start() + if(3) + visible_message("[user] has pressed one of the colorful buttons on [src] and the clown car turns on its singularity disguise system.") + icon = 'icons/obj/singularity.dmi' + icon_state = "singularity_s1" + addtimer(CALLBACK(src, .proc/ResetIcon), 100) + if(4) + visible_message("[user] has pressed one of the colorful buttons on [src] and the clown car spews out a cloud of laughing gas.") + var/datum/reagents/R = new/datum/reagents(300) + R.my_atom = src + R.add_reagent("superlaughter", 50) + var/datum/effect_system/smoke_spread/chem/smoke = new() + smoke.set_up(R, 4) + smoke.attach(src) + smoke.start() + if(5) + visible_message("[user] has pressed one of the colorful buttons on [src] and the clown car starts dropping an oil trail.") + droppingoil = TRUE + addtimer(CALLBACK(src, .proc/StopDroppingOil), 30) + if(6) + visible_message("[user] has pressed one of the colorful buttons on [src] and the clown car lets out a comedic toot.") + playsound(src, 'sound/vehicles/clowncar_fart.ogg', 100) + for(var/mob/living/L in orange(loc, 6)) + L.emote("laughs") + for(var/mob/living/L in occupants) + L.emote("laughs") + +/obj/vehicle/sealed/car/clowncar/proc/ResetIcon() + icon = initial(icon) + icon_state = initial(icon_state) + +/obj/vehicle/sealed/car/clowncar/proc/StopDroppingOil() + droppingoil = FALSE diff --git a/code/modules/vehicles/entered.dm b/code/modules/vehicles/entered.dm index d0fb93f7f0..30ae49ecd2 100644 --- a/code/modules/vehicles/entered.dm +++ b/code/modules/vehicles/entered.dm @@ -35,22 +35,74 @@ if(!istype(M)) return FALSE if(!silent) - M.visible_message("[M] climbs into \the [src]!") + M.visible_message("[M] climbs into \the [src]!") M.forceMove(src) add_occupant(M) return TRUE -/obj/vehicle/sealed/proc/mob_try_exit(mob/M, mob/user, silent = FALSE) - mob_exit(M, silent) +/obj/vehicle/sealed/proc/mob_try_exit(mob/M, mob/user, silent = FALSE, randomstep = FALSE) + mob_exit(M, silent, randomstep) -/obj/vehicle/sealed/proc/mob_exit(mob/M, silent = FALSE) +/obj/vehicle/sealed/proc/mob_exit(mob/M, silent = FALSE, randomstep = FALSE) if(!istype(M)) return FALSE remove_occupant(M) M.forceMove(exit_location(M)) + if(randomstep) + var/turf/target_turf = get_step(exit_location(M), pick(GLOB.cardinals)) + M.throw_at(target_turf, 5, 10) + if(!silent) - M.visible_message("[M] drops out of \the [src]!") + M.visible_message("[M] drops out of \the [src]!") return TRUE /obj/vehicle/sealed/proc/exit_location(M) return drop_location() + +/obj/vehicle/sealed/attackby(obj/item/I, mob/user, params) + if(key_type && !is_key(inserted_key) && is_key(I)) + if(user.transferItemToLoc(I, src)) + to_chat(user, "You insert [I] into [src].") + if(inserted_key) //just in case there's an invalid key + inserted_key.forceMove(drop_location()) + inserted_key = I + else + to_chat(user, "[I] seems to be stuck to your hand!") + return + return ..() + +/obj/vehicle/sealed/proc/remove_key(mob/user) + if(!inserted_key) + to_chat(user, "There is no key in [src]!") + return + if(!is_occupant(user) || !(occupants[user] & VEHICLE_CONTROL_DRIVE)) + to_chat(user, "You must be driving [src] to remove [src]'s key!") + return + to_chat(user, "You remove [inserted_key] from [src].") + inserted_key.forceMove(drop_location()) + user.put_in_hands(inserted_key) + inserted_key = null + +/obj/vehicle/sealed/Destroy() + DumpMobs() + explosion(loc, 0, 1, 2, 3, 0) + return ..() + +/obj/vehicle/sealed/proc/DumpMobs(randomstep = TRUE) + for(var/i in occupants) + mob_exit(i, null, randomstep) + if(iscarbon(i)) + var/mob/living/carbon/Carbon = i + Carbon.Knockdown(40) + +/obj/vehicle/sealed/proc/DumpSpecificMobs(flag, randomstep = TRUE) + for(var/i in occupants) + if((occupants[i] & flag)) + mob_exit(i, null, randomstep) + if(iscarbon(i)) + var/mob/living/carbon/C = i + C.Knockdown(40) + + +/obj/vehicle/sealed/AllowDrop() + return FALSE diff --git a/code/modules/vehicles/ridden.dm b/code/modules/vehicles/ridden.dm index b15b570550..25a337bc22 100644 --- a/code/modules/vehicles/ridden.dm +++ b/code/modules/vehicles/ridden.dm @@ -14,7 +14,10 @@ /obj/vehicle/ridden/examine(mob/user) . = ..() if(key_type) - to_chat(user, "Put a key inside it by clicking it with the key. If there's a key inside, you can remove it via Alt-Click!") + if(!inserted_key) + to_chat(user, "Put a key inside it by clicking it with the key.") + else + to_chat(user, "Alt-click [src] to remove the key.") /obj/vehicle/ridden/generate_action_type(actiontype) var/datum/action/vehicle/ridden/A = ..() diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm index 2daf3897c5..9af5b13a7d 100644 --- a/code/modules/vehicles/scooter.dm +++ b/code/modules/vehicles/scooter.dm @@ -26,7 +26,7 @@ . = ..() for(var/m in buckled_mobs) var/mob/living/buckled_mob = m - if(buckled_mob.get_num_legs() > 0) + if(buckled_mob.get_num_legs(FALSE) > 0) buckled_mob.pixel_y = 5 else buckled_mob.pixel_y = -4 @@ -41,14 +41,15 @@ /obj/vehicle/ridden/scooter/skateboard name = "skateboard" - desc = "An unfinished scooter which can only barely be called a skateboard. It's still rideable, but probably unsafe. Looks like you'll need to add a few rods to make handlebars." + desc = "An unfinished scooter which can only barely be called a skateboard. It's still rideable, but probably unsafe. Looks like you'll need to add a few rods to make handlebars. Alt-click to adjust speed." icon_state = "skateboard" density = FALSE + var/adjusted_speed = FALSE /obj/vehicle/ridden/scooter/skateboard/Initialize() . = ..() var/datum/component/riding/D = LoadComponent(/datum/component/riding) - D.vehicle_move_delay = 0 + D.vehicle_move_delay = 1 D.set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER) D.set_vehicle_dir_layer(NORTH, OBJ_LAYER) D.set_vehicle_dir_layer(EAST, OBJ_LAYER) @@ -63,7 +64,7 @@ density = FALSE return ..() -/obj/vehicle/ridden/scooter/skateboard/Collide(atom/A) +/obj/vehicle/ridden/scooter/skateboard/Bump(atom/A) . = ..() if(A.density && has_buckled_mobs()) var/mob/living/H = buckled_mobs[1] @@ -92,6 +93,17 @@ M.put_in_hands(board) qdel(src) +/obj/vehicle/ridden/scooter/skateboard/AltClick(mob/user) + var/datum/component/riding/R = src.GetComponent(/datum/component/riding) + if (!adjusted_speed) + R.vehicle_move_delay = 0 + to_chat(user, "You adjust the wheels on [src] to make it go faster.") + adjusted_speed = TRUE + else + R.vehicle_move_delay = 1 + to_chat(user, "You adjust the wheels on [src] to make it go slower.") + adjusted_speed = FALSE + //CONSTRUCTION /obj/item/scooter_frame name = "scooter frame" @@ -136,6 +148,8 @@ return ..() /obj/vehicle/ridden/scooter/skateboard/screwdriver_act(mob/living/user, obj/item/I) + if(..()) + return TRUE to_chat(user, "You begin to deconstruct and remove the wheels on [src]...") if(I.use_tool(src, user, 20, volume=50)) to_chat(user, "You deconstruct the wheels on [src].") @@ -176,7 +190,7 @@ to_chat(M, "You pop out the Wheely-Heel's wheels.") return ..() -/obj/vehicle/ridden/scooter/wheelys/Collide(atom/A) +/obj/vehicle/ridden/scooter/wheelys/Bump(atom/A) . = ..() if(A.density && has_buckled_mobs()) var/mob/living/H = buckled_mobs[1] diff --git a/code/modules/vehicles/speedbike.dm b/code/modules/vehicles/speedbike.dm index e05504b0dc..6526e6d89a 100644 --- a/code/modules/vehicles/speedbike.dm +++ b/code/modules/vehicles/speedbike.dm @@ -47,7 +47,7 @@ var/static/mutable_appearance/overlay = mutable_appearance(icon, "speedwagon_cover", ABOVE_MOB_LAYER) max_buckled_mobs = 4 var/crash_all = FALSE //CHAOS - pixel_y = -48 //to fix the offset when Initialized() + pixel_y = -48 pixel_x = -48 /obj/vehicle/ridden/space/speedwagon/Initialize() @@ -59,10 +59,14 @@ D.set_riding_offsets(2, list(TEXT_NORTH = list(19, -5, 4), TEXT_SOUTH = list(-13, 3, 4), TEXT_EAST = list(-4, -3, 4.1), TEXT_WEST = list(4, 28, 3.9))) D.set_riding_offsets(3, list(TEXT_NORTH = list(-10, -18, 4.2), TEXT_SOUTH = list(16, 25, 3.9), TEXT_EAST = list(-22, 30), TEXT_WEST = list(22, -3, 4.1))) D.set_riding_offsets(4, list(TEXT_NORTH = list(19, -18, 4.2), TEXT_SOUTH = list(-13, 25, 3.9), TEXT_EAST = list(-22, 3, 3.9), TEXT_WEST = list(22, 28))) + D.set_vehicle_dir_offsets(NORTH, -48, -48) + D.set_vehicle_dir_offsets(SOUTH, -48, -48) + D.set_vehicle_dir_offsets(EAST, -48, -48) + D.set_vehicle_dir_offsets(WEST, -48, -48) for(var/i in GLOB.cardinals) D.set_vehicle_dir_layer(i, BELOW_MOB_LAYER) -/obj/vehicle/ridden/space/speedwagon/Collide(atom/movable/A) +/obj/vehicle/ridden/space/speedwagon/Bump(atom/movable/A) . = ..() if(A.density && has_buckled_mobs()) var/atom/throw_target = get_edge_target_turf(A, dir) @@ -85,4 +89,4 @@ if(has_buckled_mobs()) for(var/atom/A in range(2, src)) if(!(A in buckled_mobs)) - Collide(A) + Bump(A) diff --git a/code/modules/vehicles/vehicle_actions.dm b/code/modules/vehicles/vehicle_actions.dm index 905be5b3ba..4ff6770e85 100644 --- a/code/modules/vehicles/vehicle_actions.dm +++ b/code/modules/vehicles/vehicle_actions.dm @@ -103,6 +103,7 @@ /datum/action/vehicle/sealed/climb_out name = "Climb Out" desc = "Climb out of your vehicle!" + button_icon_state = "car_eject" /datum/action/vehicle/sealed/climb_out/Trigger() if(..() && istype(vehicle_entered_target)) @@ -110,3 +111,56 @@ /datum/action/vehicle/ridden var/obj/vehicle/ridden/vehicle_ridden_target + +/datum/action/vehicle/sealed/remove_key + name = "Remove key" + desc = "Take your key out of the vehicle's ignition" + button_icon_state = "car_removekey" + +/datum/action/vehicle/sealed/remove_key/Trigger() + vehicle_entered_target.remove_key(owner) + +//CLOWN CAR ACTION DATUMS +/datum/action/vehicle/sealed/horn + name = "Honk Horn" + desc = "Honk your classy horn." + button_icon_state = "car_horn" + var/hornsound = 'sound/items/carhorn.ogg' + var/last_honk_time + +/datum/action/vehicle/sealed/horn/Trigger() + if(world.time - last_honk_time > 20) + vehicle_entered_target.visible_message("[vehicle_entered_target] loudly honks") + to_chat(owner, "You press the vehicle's horn.") + playsound(vehicle_entered_target, hornsound, 75) + last_honk_time = world.time + +/datum/action/vehicle/sealed/horn/clowncar/Trigger() + if(world.time - last_honk_time > 20) + vehicle_entered_target.visible_message("[vehicle_entered_target] loudly honks") + to_chat(owner, "You press the vehicle's horn.") + last_honk_time = world.time + if(vehicle_target.inserted_key) + vehicle_target.inserted_key.attack_self(owner) //The key plays a sound + else + playsound(vehicle_entered_target, hornsound, 75) + +/datum/action/vehicle/sealed/DumpKidnappedMobs + name = "Dump kidnapped mobs" + desc = "Dump all objects and people in your car on the floor." + button_icon_state = "car_dump" + +/datum/action/vehicle/sealed/DumpKidnappedMobs/Trigger() + vehicle_entered_target.visible_message("[vehicle_entered_target] starts dumping the people inside of it.") + vehicle_entered_target.DumpSpecificMobs(VEHICLE_CONTROL_KIDNAPPED) + + +/datum/action/vehicle/sealed/RollTheDice + name = "Press a colorful button" + desc = "Press one of those colorful buttons on your display panel!" + button_icon_state = "car_rtd" + +/datum/action/vehicle/sealed/RollTheDice/Trigger() + if(istype(vehicle_entered_target, /obj/vehicle/sealed/car/clowncar)) + var/obj/vehicle/sealed/car/clowncar/C = vehicle_entered_target + C.RollTheDice(owner) diff --git a/code/modules/vending/boozeomat.dm b/code/modules/vending/boozeomat.dm index 0d193c451d..735967440f 100644 --- a/code/modules/vending/boozeomat.dm +++ b/code/modules/vending/boozeomat.dm @@ -63,6 +63,9 @@ /obj/item/reagent_containers/food/drinks/drinkingglass/shotglass = 4); req_access = list(ACCESS_CAPTAIN) +/obj/machinery/vending/boozeomat/syndicate_access + req_access = list(ACCESS_SYNDICATE) + /obj/item/vending_refill/boozeomat machine_name = "Booze-O-Mat" icon_state = "refill_booze" diff --git a/code/modules/vending/cigarette.dm b/code/modules/vending/cigarette.dm index 6b9334362d..36434f6b03 100644 --- a/code/modules/vending/cigarette.dm +++ b/code/modules/vending/cigarette.dm @@ -20,6 +20,16 @@ /obj/item/storage/fancy/cigarettes/cigars/cohiba = 1) refill_canister = /obj/item/vending_refill/cigarette +/obj/machinery/vending/cigarette/syndicate + products = list(/obj/item/storage/fancy/cigarettes/cigpack_syndicate = 7, + /obj/item/storage/fancy/cigarettes/cigpack_uplift = 3, + /obj/item/storage/fancy/cigarettes/cigpack_robust = 2, + /obj/item/storage/fancy/cigarettes/cigpack_carp = 3, + /obj/item/storage/fancy/cigarettes/cigpack_midori = 1, + /obj/item/storage/box/matches = 10, + /obj/item/lighter/greyscale = 4, + /obj/item/storage/fancy/rollingpapers = 5) + /obj/machinery/vending/cigarette/beach //Used in the lavaland_biodome_beach.dmm ruin name = "\improper ShadyCigs Ultra" desc = "Now with extra premium products!" diff --git a/code/modules/vending/medical.dm b/code/modules/vending/medical.dm index bea449845c..5ff07cc842 100644 --- a/code/modules/vending/medical.dm +++ b/code/modules/vending/medical.dm @@ -38,3 +38,7 @@ /obj/item/vending_refill/medical machine_name = "NanoMed Plus" icon_state = "refill_medical" + +/obj/machinery/vending/medical/syndicate_access + name = "\improper SyndiMed Plus" + req_access = list(ACCESS_SYNDICATE) \ No newline at end of file diff --git a/code/modules/vending/medical_wall.dm b/code/modules/vending/medical_wall.dm index ef19ce3b9b..84c4891589 100644 --- a/code/modules/vending/medical_wall.dm +++ b/code/modules/vending/medical_wall.dm @@ -20,3 +20,9 @@ /obj/item/vending_refill/wallmed machine_name = "NanoMed" icon_state = "refill_medical" + +/obj/machinery/vending/wallmed/pubby + products = list(/obj/item/reagent_containers/syringe = 3, + /obj/item/reagent_containers/pill/patch/styptic = 1, + /obj/item/reagent_containers/pill/patch/silver_sulf = 1, + /obj/item/reagent_containers/medspray/sterilizine = 1) diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm index 03d2ac3921..8b56ccf00d 100644 --- a/code/modules/zombie/items.dm +++ b/code/modules/zombie/items.dm @@ -31,11 +31,11 @@ return else if(isliving(target)) if(ishuman(target)) - check_infection(target, user) + try_to_zombie_infect(target) else check_feast(target, user) -/obj/item/zombie_hand/proc/check_infection(mob/living/carbon/human/target, mob/user) +/proc/try_to_zombie_infect(mob/living/carbon/human/target) CHECK_DNA_AND_SPECIES(target) if(NOZOMBIE in target.dna.species.species_traits) @@ -50,6 +50,7 @@ infection.Insert(target) + /obj/item/zombie_hand/suicide_act(mob/user) user.visible_message("[user] is ripping [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!") if(isliving(user)) diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm index 12aa2e41f6..329cc127ab 100644 --- a/code/modules/zombie/organs.dm +++ b/code/modules/zombie/organs.dm @@ -4,6 +4,7 @@ zone = BODY_ZONE_HEAD slot = ORGAN_SLOT_ZOMBIE icon_state = "blacktumor" + var/causes_damage = TRUE var/datum/species/old_species = /datum/species/human var/living_transformation_time = 30 var/converts_living = FALSE @@ -44,7 +45,10 @@ return if(!(src in owner.internal_organs)) Remove(owner) - + if (causes_damage && !iszombie(owner) && owner.stat != DEAD) + owner.adjustToxLoss(1) + if (prob(10)) + to_chat(owner, "You feel sick...") if(timer_id) return if(owner.suiciding) @@ -64,16 +68,21 @@ /obj/item/organ/zombie_infection/proc/zombify() timer_id = null + if(!converts_living && owner.stat != DEAD) + return + if(!iszombie(owner)) old_species = owner.dna.species.type owner.set_species(/datum/species/zombie/infectious) - if(!converts_living && owner.stat != DEAD) - return - var/stand_up = (owner.stat == DEAD) || (owner.stat == UNCONSCIOUS) - if(!owner.revive(full_heal = TRUE)) + //Fully heal the zombie's damage the first time they rise + owner.setToxLoss(0, 0) + owner.setOxyLoss(0, 0) + owner.heal_overall_damage(INFINITY, INFINITY, INFINITY, FALSE, FALSE, TRUE) + + if(!owner.revive()) return owner.grab_ghost() @@ -82,3 +91,6 @@ owner.do_jitter_animation(living_transformation_time) owner.Stun(living_transformation_time) to_chat(owner, "You are now a zombie!") + +/obj/item/organ/zombie_infection/nodamage + causes_damage = FALSE diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm b/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm index ebf114c2de..4eba44598c 100644 --- a/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm +++ b/modular_citadel/code/modules/mob/living/carbon/human/species_types/furrypeople.dm @@ -13,9 +13,34 @@ liked_food = MEAT | FRIED disliked_food = TOXIC +//Curiosity killed the cat's wagging tail. /datum/species/mammal/spec_death(gibbed, mob/living/carbon/human/H) if(H) - H.endTailWag() + stop_wagging_tail(H) + +/datum/species/mammal/spec_stun(mob/living/carbon/human/H,amount) + if(H) + stop_wagging_tail(H) + . = ..() + +/datum/species/mammal/can_wag_tail(mob/living/carbon/human/H) + return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/mammal/is_wagging_tail(mob/living/carbon/human/H) + return ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/mammal/start_wagging_tail(mob/living/carbon/human/H) + if("tail_human" in mutant_bodyparts) + mutant_bodyparts -= "mam_tail" + mutant_bodyparts |= "mam_waggingtail" + H.update_body() + +/datum/species/mammal/stop_wagging_tail(mob/living/carbon/human/H) + if("mam_waggingtail" in mutant_bodyparts) + mutant_bodyparts -= "mam_waggingtail" + mutant_bodyparts |= "mam_tail" + H.update_body() + /datum/species/mammal/qualifies_for_rank(rank, list/features) return TRUE @@ -45,7 +70,30 @@ /datum/species/avian/spec_death(gibbed, mob/living/carbon/human/H) if(H) - H.endTailWag() + stop_wagging_tail(H) + +/datum/species/avian/spec_stun(mob/living/carbon/human/H,amount) + if(H) + stop_wagging_tail(H) + . = ..() + +/datum/species/avian/can_wag_tail(mob/living/carbon/human/H) + return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/avian/is_wagging_tail(mob/living/carbon/human/H) + return ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/avian/start_wagging_tail(mob/living/carbon/human/H) + if("tail_human" in mutant_bodyparts) + mutant_bodyparts -= "mam_tail" + mutant_bodyparts |= "mam_waggingtail" + H.update_body() + +/datum/species/avian/stop_wagging_tail(mob/living/carbon/human/H) + if("mam_waggingtail" in mutant_bodyparts) + mutant_bodyparts -= "mam_waggingtail" + mutant_bodyparts |= "mam_tail" + H.update_body() /datum/species/avian/qualifies_for_rank(rank, list/features) return TRUE @@ -76,7 +124,30 @@ /datum/species/aquatic/spec_death(gibbed, mob/living/carbon/human/H) if(H) - H.endTailWag() + stop_wagging_tail(H) + +/datum/species/aquatic/spec_stun(mob/living/carbon/human/H,amount) + if(H) + stop_wagging_tail(H) + . = ..() + +/datum/species/aquatic/can_wag_tail(mob/living/carbon/human/H) + return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/aquatic/is_wagging_tail(mob/living/carbon/human/H) + return ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/aquatic/start_wagging_tail(mob/living/carbon/human/H) + if("tail_human" in mutant_bodyparts) + mutant_bodyparts -= "mam_tail" + mutant_bodyparts |= "mam_waggingtail" + H.update_body() + +/datum/species/aquatic/stop_wagging_tail(mob/living/carbon/human/H) + if("mam_waggingtail" in mutant_bodyparts) + mutant_bodyparts -= "mam_waggingtail" + mutant_bodyparts |= "mam_tail" + H.update_body() /datum/species/aquatic/qualifies_for_rank(rank, list/features) return TRUE @@ -106,7 +177,30 @@ /datum/species/insect/spec_death(gibbed, mob/living/carbon/human/H) if(H) - H.endTailWag() + stop_wagging_tail(H) + +/datum/species/insect/spec_stun(mob/living/carbon/human/H,amount) + if(H) + stop_wagging_tail(H) + . = ..() + +/datum/species/insect/can_wag_tail(mob/living/carbon/human/H) + return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/insect/is_wagging_tail(mob/living/carbon/human/H) + return ("mam_waggingtail" in mutant_bodyparts) + +/datum/species/insect/start_wagging_tail(mob/living/carbon/human/H) + if("tail_human" in mutant_bodyparts) + mutant_bodyparts -= "mam_tail" + mutant_bodyparts |= "mam_waggingtail" + H.update_body() + +/datum/species/insect/stop_wagging_tail(mob/living/carbon/human/H) + if("mam_waggingtail" in mutant_bodyparts) + mutant_bodyparts -= "mam_waggingtail" + mutant_bodyparts |= "mam_tail" + H.update_body() /datum/species/insect/qualifies_for_rank(rank, list/features) return TRUE diff --git a/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index c91be1c7ac..59c4bc5ef1 100644 --- a/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/modular_citadel/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -94,7 +94,7 @@ organ.forceMove(get_turf(H)) qdel(organ) H.update_body() - + else if (select_alteration == "Ears") var/list/snowflake_ears_list = list("Normal" = null) for(var/path in GLOB.mam_ears_list) @@ -108,7 +108,7 @@ if(new_ears) H.dna.features["mam_ears"] = new_ears H.update_body() - + else if (select_alteration == "Tail") var/list/snowflake_tails_list = list("Normal" = null) for(var/path in GLOB.mam_tails_list) @@ -124,7 +124,7 @@ if(new_tail != "None") H.dna.features["taur"] = "None" H.update_body() - + else if (select_alteration == "Taur body") var/list/snowflake_taur_list = list("Normal" = null) for(var/path in GLOB.taur_list)