diff --git a/.github/workflows/size_labeling.yml b/.github/workflows/size_labeling.yml index d0dfe06c8377..47d3ee779c4a 100644 --- a/.github/workflows/size_labeling.yml +++ b/.github/workflows/size_labeling.yml @@ -5,7 +5,7 @@ jobs: runs-on: ubuntu-latest steps: - name: size-label - uses: pascalgn/size-label-action@v0.4.3 + uses: pascalgn/size-label-action@v0.5.4 env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" IGNORED: "**/*.bundle.*\n**/*.chunk.*" # **/*.dmm\n diff --git a/citadel.dme b/citadel.dme index 594f84f16d01..280e6670c8e0 100644 --- a/citadel.dme +++ b/citadel.dme @@ -46,7 +46,6 @@ #include "code\__DEFINES\damage_organs.dm" #include "code\__DEFINES\directional.dm" #include "code\__DEFINES\dna.dm" -#include "code\__DEFINES\event_args.dm" #include "code\__DEFINES\fonts.dm" #include "code\__DEFINES\frames.dm" #include "code\__DEFINES\gamemode.dm" @@ -162,6 +161,7 @@ #include "code\__DEFINES\controllers\timer.dm" #include "code\__DEFINES\datums\beam.dm" #include "code\__DEFINES\datums\design.dm" +#include "code\__DEFINES\datums\event_args.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\components\riding.dm" @@ -177,32 +177,36 @@ #include "code\__DEFINES\dcs\signals\elements\signals_element_conflict_checking.dm" #include "code\__DEFINES\dcs\signals\items\signals_inducer.dm" #include "code\__DEFINES\dcs\signals\modules\signals_module_fishing.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-buckling.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-clickchain.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-context_system.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-defense.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-reachability.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-throwing.dm" +#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom-tool_system.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_appearance.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_attack.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_buckling.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_context_system.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_defense.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_main.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_mouse.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movable.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_radiation.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_throwing.dm" -#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_tool_system.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_x_act.dm" +#include "code\__DEFINES\dcs\signals\signals_item\signals_item-clickchain.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item-interaction.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_economy.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_inventory.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_mouse.dm" #include "code\__DEFINES\dcs\signals\signals_item\signals_item_storage.dm" +#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob-inventory.dm" +#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob-perspective.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_appearance.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_combat.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_defense.dm" -#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_inventory.dm" +#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_legacy.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_main.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_mobility.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_organs.dm" -#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_perspectiive.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_simple.dm" #include "code\__DEFINES\economy\category.dm" #include "code\__DEFINES\economy\currency.dm" @@ -259,6 +263,7 @@ #include "code\__DEFINES\mobs\biology.dm" #include "code\__DEFINES\mobs\characteristics.dm" #include "code\__DEFINES\mobs\grab.dm" +#include "code\__DEFINES\mobs\iff.dm" #include "code\__DEFINES\mobs\intent.dm" #include "code\__DEFINES\mobs\life.dm" #include "code\__DEFINES\mobs\mobility.dm" @@ -292,6 +297,7 @@ #include "code\__DEFINES\projectiles\ammo_casing.dm" #include "code\__DEFINES\projectiles\ammo_magazine.dm" #include "code\__DEFINES\projectiles\guns.dm" +#include "code\__DEFINES\projectiles\projectile.dm" #include "code\__DEFINES\projectiles\system.dm" #include "code\__DEFINES\radiation\flags.dm" #include "code\__DEFINES\radiation\ignore.dm" @@ -335,6 +341,7 @@ #include "code\__DEFINES\traits\sources.dm" #include "code\__DEFINES\traits\unsorted.dm" #include "code\__DEFINES\turfs\change_turf.dm" +#include "code\__DEFINES\turfs\turfs.dm" #include "code\__DEFINES\turfs\type_generation.dm" #include "code\__HELPERS\_auxtools_api.dm" #include "code\__HELPERS\_global_objects.dm" @@ -379,7 +386,6 @@ #include "code\__HELPERS\vector.dm" #include "code\__HELPERS\verbs.dm" #include "code\__HELPERS\animations\attack.dm" -#include "code\__HELPERS\datastructs\armor.dm" #include "code\__HELPERS\datastructs\bodytypes.dm" #include "code\__HELPERS\datastructs\filters.dm" #include "code\__HELPERS\datastructs\ingredients.dm" @@ -390,6 +396,7 @@ #include "code\__HELPERS\files\paths.dm" #include "code\__HELPERS\files\walk.dm" #include "code\__HELPERS\game\depth.dm" +#include "code\__HELPERS\game\combat\arc.dm" #include "code\__HELPERS\game\turfs\line.dm" #include "code\__HELPERS\game\turfs\offsets.dm" #include "code\__HELPERS\graphs\astar.dm" @@ -638,10 +645,11 @@ #include "code\controllers\subsystem\persistence\modules\bulk_entity_serializers\trash.dm" #include "code\controllers\subsystem\processing\chemistry.dm" #include "code\controllers\subsystem\processing\circuits.dm" -#include "code\controllers\subsystem\processing\fastprocess.dm" #include "code\controllers\subsystem\processing\instruments.dm" #include "code\controllers\subsystem\processing\moving_cameras.dm" #include "code\controllers\subsystem\processing\obj.dm" +#include "code\controllers\subsystem\processing\process_20fps.dm" +#include "code\controllers\subsystem\processing\process_5fps.dm" #include "code\controllers\subsystem\processing\processing.dm" #include "code\controllers\subsystem\processing\projectiles.dm" #include "code\controllers\subsystem\processing\turfs.dm" @@ -697,6 +705,7 @@ #include "code\datums\announce\location\global.dm" #include "code\datums\announce\location\main_station.dm" #include "code\datums\announce\location\overmap_sector.dm" +#include "code\datums\armor\armor.dm" #include "code\datums\armor\core.dm" #include "code\datums\armor\mecha.dm" #include "code\datums\armor\military.dm" @@ -765,7 +774,10 @@ #include "code\datums\components\atoms\radioactive.dm" #include "code\datums\components\crafting\crafting.dm" #include "code\datums\components\crafting\guncrafting.dm" +#include "code\datums\components\items\passive_parry.dm" #include "code\datums\components\items\wielding.dm" +#include "code\datums\components\mobs\block_frame.dm" +#include "code\datums\components\mobs\parry_frame.dm" #include "code\datums\components\movable\aquarium.dm" #include "code\datums\components\movable\spatial_grid.dm" #include "code\datums\components\objects\slippery.dm" @@ -895,6 +907,7 @@ #include "code\datums\repositories\unique.dm" #include "code\datums\soundbytes\_soundbyte.dm" #include "code\datums\soundbytes\announcer.dm" +#include "code\datums\soundbytes\effects\combat.dm" #include "code\datums\soundbytes\effects\explosion.dm" #include "code\datums\soundbytes\effects\sparks.dm" #include "code\datums\soundbytes\effects\spray.dm" @@ -1011,18 +1024,20 @@ #include "code\game\area\station\security_areas.dm" #include "code\game\atoms\action_feedback.dm" #include "code\game\atoms\appearance.dm" +#include "code\game\atoms\atom-construction.dm" +#include "code\game\atoms\atom-damage.dm" +#include "code\game\atoms\atom-defense.dm" #include "code\game\atoms\atom.dm" #include "code\game\atoms\buckling.dm" -#include "code\game\atoms\defense.dm" #include "code\game\atoms\defense_old.dm" #include "code\game\atoms\materials.dm" #include "code\game\atoms\movement.dm" #include "code\game\atoms\say.dm" #include "code\game\atoms\vv.dm" +#include "code\game\atoms\movable\movable-movement.dm" +#include "code\game\atoms\movable\movable-throw.dm" #include "code\game\atoms\movable\movable.dm" -#include "code\game\atoms\movable\movement.dm" #include "code\game\atoms\movable\pulling.dm" -#include "code\game\atoms\movable\throwing.dm" #include "code\game\atoms\movable\vv.dm" #include "code\game\atoms\movable\special\overlay.dm" #include "code\game\atoms\movable\special\render.dm" @@ -1036,6 +1051,7 @@ #include "code\game\click\cyborg.dm" #include "code\game\click\drag_drop.dm" #include "code\game\click\item_attack.dm" +#include "code\game\click\items-item_attack_chain.dm" #include "code\game\click\items.dm" #include "code\game\click\mobs.dm" #include "code\game\click\observer.dm" @@ -1452,16 +1468,19 @@ #include "code\game\magic\Uristrunes.dm" #include "code\game\objects\attacks.dm" #include "code\game\objects\banners.dm" -#include "code\game\objects\defense.dm" #include "code\game\objects\empulse.dm" #include "code\game\objects\explosion.dm" #include "code\game\objects\explosion_recursive.dm" +#include "code\game\objects\items-carry_weight.dm" +#include "code\game\objects\items-defense.dm" #include "code\game\objects\items-interaction.dm" #include "code\game\objects\items.dm" #include "code\game\objects\materials.dm" #include "code\game\objects\misc.dm" #include "code\game\objects\mob_spawner.dm" -#include "code\game\objects\objs.dm" +#include "code\game\objects\obj-construction.dm" +#include "code\game\objects\obj-defense.dm" +#include "code\game\objects\obj.dm" #include "code\game\objects\structures.dm" #include "code\game\objects\stumble_into_vr.dm" #include "code\game\objects\topic.dm" @@ -1646,6 +1665,15 @@ #include "code\game\objects\items\id_cards\station_ids.dm" #include "code\game\objects\items\id_cards\syndicate_ids.dm" #include "code\game\objects\items\janitorial\soap.dm" +#include "code\game\objects\items\melee\melee.dm" +#include "code\game\objects\items\melee\types\misc.dm" +#include "code\game\objects\items\melee\types\ninja_energy_blade.dm" +#include "code\game\objects\items\melee\types\transforming.dm" +#include "code\game\objects\items\melee\types\transforming\energy.dm" +#include "code\game\objects\items\melee\types\transforming\hfmachete.dm" +#include "code\game\objects\items\melee\types\transforming\energy\axe.dm" +#include "code\game\objects\items\melee\types\transforming\energy\ionic_rapier.dm" +#include "code\game\objects\items\melee\types\transforming\energy\saber.dm" #include "code\game\objects\items\robot\gripper.dm" #include "code\game\objects\items\robot\robot_items.dm" #include "code\game\objects\items\robot\robot_parts.dm" @@ -1656,6 +1684,14 @@ #include "code\game\objects\items\scanners\reagent.dm" #include "code\game\objects\items\scanners\slime.dm" #include "code\game\objects\items\scanners\spectrometer.dm" +#include "code\game\objects\items\shield\shield.dm" +#include "code\game\objects\items\shield\types\shields_legacy.dm" +#include "code\game\objects\items\shield\types\shields_legacy_vr.dm" +#include "code\game\objects\items\shield\types\transforming.dm" +#include "code\game\objects\items\shield\types\transforming\energy.dm" +#include "code\game\objects\items\shield\types\transforming\telescopic.dm" +#include "code\game\objects\items\shield_projector\shield_matrix.dm" +#include "code\game\objects\items\shield_projector\shield_projector.dm" #include "code\game\objects\items\stacks\fifty_spawner.dm" #include "code\game\objects\items\stacks\marker_beacons.dm" #include "code\game\objects\items\stacks\matter_synth.dm" @@ -1737,7 +1773,6 @@ #include "code\game\objects\items\weapons\RPD.dm" #include "code\game\objects\items\weapons\RSF.dm" #include "code\game\objects\items\weapons\scrolls.dm" -#include "code\game\objects\items\weapons\shields.dm" #include "code\game\objects\items\weapons\stunbaton.dm" #include "code\game\objects\items\weapons\surgery_tools.dm" #include "code\game\objects\items\weapons\swords_axes_etc.dm" @@ -1789,10 +1824,6 @@ #include "code\game\objects\items\weapons\material\thrown.dm" #include "code\game\objects\items\weapons\material\twohanded.dm" #include "code\game\objects\items\weapons\material\whetstone.dm" -#include "code\game\objects\items\weapons\melee\deflect.dm" -#include "code\game\objects\items\weapons\melee\energy.dm" -#include "code\game\objects\items\weapons\melee\melee.dm" -#include "code\game\objects\items\weapons\melee\misc.dm" #include "code\game\objects\items\weapons\tanks\jetpack.dm" #include "code\game\objects\items\weapons\tanks\tank.dm" #include "code\game\objects\items\weapons\tanks\tank_types.dm" @@ -1983,8 +2014,8 @@ #include "code\game\rendering\plane_masters\plane_render.dm" #include "code\game\turfs\baseturfs.dm" #include "code\game\turfs\change_turf.dm" -#include "code\game\turfs\defense.dm" #include "code\game\turfs\simulated.dm" +#include "code\game\turfs\turf-construction.dm" #include "code\game\turfs\turf.dm" #include "code\game\turfs\turf_ao.dm" #include "code\game\turfs\turf_flick_animations.dm" @@ -2018,9 +2049,11 @@ #include "code\game\turfs\simulated\flooring\flooring_traps.dm" #include "code\game\turfs\simulated\flooring\shuttle_vr.dm" #include "code\game\turfs\simulated\misc\fancy_shuttles.dm" -#include "code\game\turfs\simulated\wall\defense.dm" #include "code\game\turfs\simulated\wall\materials.dm" #include "code\game\turfs\simulated\wall\rot.dm" +#include "code\game\turfs\simulated\wall\wall-construction.dm" +#include "code\game\turfs\simulated\wall\wall-damage.dm" +#include "code\game\turfs\simulated\wall\wall-defense.dm" #include "code\game\turfs\simulated\wall\wall.dm" #include "code\game\turfs\simulated\wall\wall_attacks.dm" #include "code\game\turfs\simulated\wall\wall_icon.dm" @@ -2173,6 +2206,7 @@ #include "code\modules\admin\view_variables\topic_list.dm" #include "code\modules\admin\view_variables\view_variables.dm" #include "code\modules\ai\ai_pathing.dm" +#include "code\modules\ai\ai_tracking.dm" #include "code\modules\ai\holders\ai_holder-movement.dm" #include "code\modules\ai\holders\ai_holder-pathfinding.dm" #include "code\modules\ai\holders\ai_holder-scheduling.dm" @@ -2357,7 +2391,6 @@ #include "code\modules\awaymissions\bluespaceartillery.dm" #include "code\modules\awaymissions\corpse.dm" #include "code\modules\awaymissions\exile.dm" -#include "code\modules\awaymissions\gateway.dm" #include "code\modules\awaymissions\loot.dm" #include "code\modules\awaymissions\loot_vr.dm" #include "code\modules\awaymissions\pamphlet.dm" @@ -2483,6 +2516,7 @@ #include "code\modules\clothing\masks\gasmask.dm" #include "code\modules\clothing\masks\miscellaneous.dm" #include "code\modules\clothing\masks\voice.dm" +#include "code\modules\clothing\sets\armor\ablative.dm" #include "code\modules\clothing\shoes\_shoes.dm" #include "code\modules\clothing\shoes\boots.dm" #include "code\modules\clothing\shoes\colour.dm" @@ -2826,6 +2860,10 @@ #include "code\modules\games\spaceball_cards.dm" #include "code\modules\games\tarot.dm" #include "code\modules\games\unus.dm" +#include "code\modules\gateway\stargate\stargate-away.dm" +#include "code\modules\gateway\stargate\stargate-destination.dm" +#include "code\modules\gateway\stargate\stargate-station.dm" +#include "code\modules\gateway\stargate\stargate.dm" #include "code\modules\genetics\side_effects.dm" #include "code\modules\ghostroles\instantiator.dm" #include "code\modules\ghostroles\menu.dm" @@ -2904,6 +2942,27 @@ #include "code\modules\hydroponics\trays\tray_update_icons.dm" #include "code\modules\identification\identification.dm" #include "code\modules\identification\item_procs.dm" +#include "code\modules\industry\destination_tagger.dm" +#include "code\modules\industry\package_wrapper.dm" +#include "code\modules\industry\conveyors\conveyor.dm" +#include "code\modules\industry\conveyors\conveyor_switch.dm" +#include "code\modules\industry\disposals\disposal.dm" +#include "code\modules\industry\disposals\disposal_frame.dm" +#include "code\modules\industry\disposals\disposal_holder.dm" +#include "code\modules\industry\disposals\disposal_pipe.dm" +#include "code\modules\industry\disposals\disposal\chute.dm" +#include "code\modules\industry\disposals\disposal\intake.dm" +#include "code\modules\industry\disposals\disposal\outlet.dm" +#include "code\modules\industry\disposals\disposal_pipe\broken.dm" +#include "code\modules\industry\disposals\disposal_pipe\down.dm" +#include "code\modules\industry\disposals\disposal_pipe\junction.dm" +#include "code\modules\industry\disposals\disposal_pipe\segment.dm" +#include "code\modules\industry\disposals\disposal_pipe\sortjunction.dm" +#include "code\modules\industry\disposals\disposal_pipe\tagger.dm" +#include "code\modules\industry\disposals\disposal_pipe\trunk.dm" +#include "code\modules\industry\disposals\disposal_pipe\up.dm" +#include "code\modules\industry\packages\large_parcel.dm" +#include "code\modules\industry\packages\small_parcel.dm" #include "code\modules\instruments\instrument_data\_instrument_data.dm" #include "code\modules\instruments\instrument_data\_instrument_key.dm" #include "code\modules\instruments\instrument_data\brass.dm" @@ -3215,6 +3274,7 @@ #include "code\modules\mapping\map_helpers\_map_helpers.dm" #include "code\modules\mapping\map_helpers\baseturf.dm" #include "code\modules\mapping\map_helpers\component_injector.dm" +#include "code\modules\mapping\map_helpers\electrochromatic_linker.dm" #include "code\modules\mapping\map_helpers\engine_loader.dm" #include "code\modules\mapping\map_helpers\gear_marker.dm" #include "code\modules\mapping\map_helpers\paint.dm" @@ -3424,7 +3484,6 @@ #include "code\modules\mob\animations.dm" #include "code\modules\mob\client.dm" #include "code\modules\mob\death.dm" -#include "code\modules\mob\defense.dm" #include "code\modules\mob\emote.dm" #include "code\modules\mob\floating_message.dm" #include "code\modules\mob\gender.dm" @@ -3436,9 +3495,12 @@ #include "code\modules\mob\life.dm" #include "code\modules\mob\login.dm" #include "code\modules\mob\logout.dm" +#include "code\modules\mob\mob-damage.dm" +#include "code\modules\mob\mob-defense.dm" #include "code\modules\mob\mob-keybind-triggers.dm" #include "code\modules\mob\mob.dm" #include "code\modules\mob\mob_defines.dm" +#include "code\modules\mob\mob-iff.dm" #include "code\modules\mob\mob_helpers.dm" #include "code\modules\mob\mob_transformation_simple.dm" #include "code\modules\mob\mobility.dm" @@ -3504,13 +3566,13 @@ #include "code\modules\mob\inventory\stripping.dm" #include "code\modules\mob\living\autohiss.dm" #include "code\modules\mob\living\butchering.dm" -#include "code\modules\mob\living\damage_procs.dm" #include "code\modules\mob\living\death.dm" #include "code\modules\mob\living\default_language.dm" -#include "code\modules\mob\living\defense.dm" #include "code\modules\mob\living\health.dm" #include "code\modules\mob\living\inventory.dm" #include "code\modules\mob\living\life.dm" +#include "code\modules\mob\living\living-damage.dm" +#include "code\modules\mob\living\living-defense-legacy.dm" #include "code\modules\mob\living\living-defense.dm" #include "code\modules\mob\living\living.dm" #include "code\modules\mob\living\living_defines.dm" @@ -3596,11 +3658,12 @@ #include "code\modules\mob\living\carbon\human\emote.dm" #include "code\modules\mob\living\carbon\human\examine.dm" #include "code\modules\mob\living\carbon\human\health.dm" +#include "code\modules\mob\living\carbon\human\human-damage-legacy.dm" +#include "code\modules\mob\living\carbon\human\human-damage.dm" +#include "code\modules\mob\living\carbon\human\human-defense-legacy.dm" #include "code\modules\mob\living\carbon\human\human-defense.dm" #include "code\modules\mob\living\carbon\human\human.dm" #include "code\modules\mob\living\carbon\human\human_attackhand.dm" -#include "code\modules\mob\living\carbon\human\human_damage.dm" -#include "code\modules\mob\living\carbon\human\human_defense.dm" #include "code\modules\mob\living\carbon\human\human_defines.dm" #include "code\modules\mob\living\carbon\human\human_helpers.dm" #include "code\modules\mob\living\carbon\human\human_modular_limbs.dm" @@ -3646,6 +3709,8 @@ #include "code\modules\mob\living\silicon\offense.dm" #include "code\modules\mob\living\silicon\perspective.dm" #include "code\modules\mob\living\silicon\say.dm" +#include "code\modules\mob\living\silicon\silicon-damage.dm" +#include "code\modules\mob\living\silicon\silicon-defense-legacy.dm" #include "code\modules\mob\living\silicon\silicon.dm" #include "code\modules\mob\living\silicon\subystems.dm" #include "code\modules\mob\living\silicon\translation.dm" @@ -3843,7 +3908,6 @@ #include "code\modules\mob\living\simple_mob\subtypes\humanoid\humanoid_vr.dm" #include "code\modules\mob\living\simple_mob\subtypes\humanoid\pirates.dm" #include "code\modules\mob\living\simple_mob\subtypes\humanoid\possessed.dm" -#include "code\modules\mob\living\simple_mob\subtypes\humanoid\russian.dm" #include "code\modules\mob\living\simple_mob\subtypes\humanoid\mercs\mercs.dm" #include "code\modules\mob\living\simple_mob\subtypes\humanoid\mercs\mercs_vr.dm" #include "code\modules\mob\living\simple_mob\subtypes\illusion\illusion.dm" @@ -3857,6 +3921,7 @@ #include "code\modules\mob\living\simple_mob\subtypes\mechanical\mechanical.dm" #include "code\modules\mob\living\simple_mob\subtypes\mechanical\viscerator.dm" #include "code\modules\mob\living\simple_mob\subtypes\mechanical\cyber_horror\cyber_horror.dm" +#include "code\modules\mob\living\simple_mob\subtypes\mechanical\hivebot\enigma_hivebot.dm" #include "code\modules\mob\living\simple_mob\subtypes\mechanical\hivebot\hivebot.dm" #include "code\modules\mob\living\simple_mob\subtypes\mechanical\hivebot\ranged_damage.dm" #include "code\modules\mob\living\simple_mob\subtypes\mechanical\hivebot\ranged_damage_vr.dm" @@ -3922,7 +3987,7 @@ #include "code\modules\mob\living\simple_mob\subtypes\vore\snake.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\solargrub.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\solargrub_larva.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\solarmoth_ch.dm" +#include "code\modules\mob\living\simple_mob\subtypes\vore\solarmoth.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\vore.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\wolf.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\wolfgirl.dm" @@ -3933,10 +3998,6 @@ #include "code\modules\mob\living\simple_mob\subtypes\vore\demon\demon_subtypes.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\demon\~defines.dm" #include "code\modules\mob\living\simple_mob\subtypes\vore\morph\morph.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\ability_objects.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\ability_procs.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\shadekin.dm" -#include "code\modules\mob\living\simple_mob\subtypes\vore\shadekin\types.dm" #include "code\modules\mob\living\voice\voice.dm" #include "code\modules\mob\new_player\join_menu.dm" #include "code\modules\mob\new_player\login.dm" @@ -4232,7 +4293,6 @@ #include "code\modules\power\fission\rods.dm" #include "code\modules\power\fusion\_setup.dm" #include "code\modules\power\fusion\fusion_circuits.dm" -#include "code\modules\power\fusion\fusion_particle_catcher.dm" #include "code\modules\power\fusion\fusion_reactions.dm" #include "code\modules\power\fusion\fusion_reagents.dm" #include "code\modules\power\fusion\magpower.dm" @@ -4330,8 +4390,6 @@ #include "code\modules\projectiles\gun.dm" #include "code\modules\projectiles\gun_item_renderer.dm" #include "code\modules\projectiles\gun_mob_renderer.dm" -#include "code\modules\projectiles\projectile-tracing.dm" -#include "code\modules\projectiles\projectile.dm" #include "code\modules\projectiles\ammunition\ammo_caliber.dm" #include "code\modules\projectiles\ammunition\ammo_casing.dm" #include "code\modules\projectiles\ammunition\ammo_magazine.dm" @@ -4429,25 +4487,32 @@ #include "code\modules\projectiles\guns\projectile\caseless\pellet.dm" #include "code\modules\projectiles\guns\projectile\sniper\collapsible_sniper.dm" #include "code\modules\projectiles\projectile\arc.dm" -#include "code\modules\projectiles\projectile\blob.dm" -#include "code\modules\projectiles\projectile\bullets.dm" -#include "code\modules\projectiles\projectile\bullets_vr.dm" -#include "code\modules\projectiles\projectile\change.dm" -#include "code\modules\projectiles\projectile\energy.dm" -#include "code\modules\projectiles\projectile\energy_vr.dm" -#include "code\modules\projectiles\projectile\explosive.dm" -#include "code\modules\projectiles\projectile\force.dm" -#include "code\modules\projectiles\projectile\hook.dm" -#include "code\modules\projectiles\projectile\magic.dm" -#include "code\modules\projectiles\projectile\magnetic.dm" -#include "code\modules\projectiles\projectile\pellets.dm" -#include "code\modules\projectiles\projectile\reusable.dm" -#include "code\modules\projectiles\projectile\scatter.dm" -#include "code\modules\projectiles\projectile\special.dm" -#include "code\modules\projectiles\projectile\trace.dm" -#include "code\modules\projectiles\projectile\beam\beams.dm" -#include "code\modules\projectiles\projectile\beam\beams_vr.dm" -#include "code\modules\projectiles\projectile\beam\blaster.dm" +#include "code\modules\projectiles\projectile\helpers.dm" +#include "code\modules\projectiles\projectile\projectile-hitscan_visuals.dm" +#include "code\modules\projectiles\projectile\projectile-physics.dm" +#include "code\modules\projectiles\projectile\projectile-tracing.dm" +#include "code\modules\projectiles\projectile\projectile.dm" +#include "code\modules\projectiles\projectile\projectile_effect.dm" +#include "code\modules\projectiles\projectile\subtypes\arc.dm" +#include "code\modules\projectiles\projectile\subtypes\blob.dm" +#include "code\modules\projectiles\projectile\subtypes\bullets.dm" +#include "code\modules\projectiles\projectile\subtypes\bullets_vr.dm" +#include "code\modules\projectiles\projectile\subtypes\change.dm" +#include "code\modules\projectiles\projectile\subtypes\energy.dm" +#include "code\modules\projectiles\projectile\subtypes\energy_vr.dm" +#include "code\modules\projectiles\projectile\subtypes\explosive.dm" +#include "code\modules\projectiles\projectile\subtypes\force.dm" +#include "code\modules\projectiles\projectile\subtypes\hook.dm" +#include "code\modules\projectiles\projectile\subtypes\magic.dm" +#include "code\modules\projectiles\projectile\subtypes\magnetic.dm" +#include "code\modules\projectiles\projectile\subtypes\pellet.dm" +#include "code\modules\projectiles\projectile\subtypes\reusable.dm" +#include "code\modules\projectiles\projectile\subtypes\scatter.dm" +#include "code\modules\projectiles\projectile\subtypes\special.dm" +#include "code\modules\projectiles\projectile\subtypes\trace.dm" +#include "code\modules\projectiles\projectile\subtypes\beam\beams.dm" +#include "code\modules\projectiles\projectile\subtypes\beam\beams_vr.dm" +#include "code\modules\projectiles\projectile\subtypes\beam\blaster.dm" #include "code\modules\projectiles\targeting\targeting_client.dm" #include "code\modules\projectiles\targeting\targeting_gun.dm" #include "code\modules\projectiles\targeting\targeting_mob.dm" @@ -4539,10 +4604,6 @@ #include "code\modules\reagents\reagent_containers\syringes_vr.dm" #include "code\modules\reagents\reagent_containers\unidentified_hypospray.dm" #include "code\modules\reagents\reagent_containers\glass\bottle_vr.dm" -#include "code\modules\recycling\conveyor2.dm" -#include "code\modules\recycling\disposal-construction.dm" -#include "code\modules\recycling\disposal.dm" -#include "code\modules\recycling\sortingmachinery.dm" #include "code\modules\research\design.dm" #include "code\modules\research\research.dm" #include "code\modules\research\designs\ai_holders.dm" @@ -4612,7 +4673,6 @@ #include "code\modules\sculpting\sculpting_block.dm" #include "code\modules\security levels\keycard authentication.dm" #include "code\modules\security levels\security levels.dm" -#include "code\modules\shieldgen\directional_shield.dm" #include "code\modules\shieldgen\emergency_shield.dm" #include "code\modules\shieldgen\energy_field.dm" #include "code\modules\shieldgen\energy_shield.dm" diff --git a/code/__DEFINES/_core.dm b/code/__DEFINES/_core.dm index 041c95757dcb..2a6a70722d89 100644 --- a/code/__DEFINES/_core.dm +++ b/code/__DEFINES/_core.dm @@ -12,5 +12,8 @@ /// * put the stack trace in stack trace storage #define stack_trace(message) _stack_trace(message, __FILE__, __LINE__) +/// get variable if not null or +#define VALUE_OR_DEFAULT(VAL, DEFAULT) (isnull(VAL)? (DEFAULT) : (VAL)) + /// byond bug https://secure.byond.com/forum/?post=2072419 #define BLOCK_BYOND_BUG_2072419 diff --git a/code/__DEFINES/_flags/atom_flags.dm b/code/__DEFINES/_flags/atom_flags.dm index 8b10246acf6f..8a7ccb364e17 100644 --- a/code/__DEFINES/_flags/atom_flags.dm +++ b/code/__DEFINES/_flags/atom_flags.dm @@ -59,7 +59,7 @@ DEFINE_BITFIELD(atom_flags, list( #define MOVABLE_NO_THROW_DAMAGE_SCALING (1<<1) /// Do not spin when thrown. #define MOVABLE_NO_THROW_SPIN (1<<2) -/// We are currently about to be yanked by a Moved() triggering a Move() +/// We are currently about to be yanked by a Moved(), Entered(), or Exited() triggering a Move() /// /// * used so things like projectile hitscans know to yield #define MOVABLE_IN_MOVED_YANK (1<<3) @@ -85,11 +85,13 @@ DEFINE_BITFIELD(movable_flags, list( #define ATOM_PASS_OVERHEAD_THROW (1<<7) /// let buckled mobs pass always #define ATOM_PASS_BUCKLED (1<<8) +/// "please don't interact with us" +#define ATOM_PASS_INCORPOREAL (1<<9) /// all actual pass flags / maximum pass #define ATOM_PASS_ALL (ATOM_PASS_TABLE | ATOM_PASS_GLASS | ATOM_PASS_GRILLE | \ ATOM_PASS_BLOB | ATOM_PASS_MOB | ATOM_PASS_THROWN | ATOM_PASS_CLICK | \ - ATOM_PASS_OVERHEAD_THROW | ATOM_PASS_BUCKLED) + ATOM_PASS_OVERHEAD_THROW | ATOM_PASS_BUCKLED | ATOM_PASS_INCORPOREAL) DEFINE_BITFIELD(pass_flags, list( BITFIELD(ATOM_PASS_TABLE), @@ -101,6 +103,7 @@ DEFINE_BITFIELD(pass_flags, list( BITFIELD(ATOM_PASS_CLICK), BITFIELD(ATOM_PASS_OVERHEAD_THROW), BITFIELD(ATOM_PASS_BUCKLED), + BITFIELD(ATOM_PASS_INCORPOREAL), )) DEFINE_BITFIELD(pass_flags_self, list( @@ -113,6 +116,7 @@ DEFINE_BITFIELD(pass_flags_self, list( BITFIELD(ATOM_PASS_CLICK), BITFIELD(ATOM_PASS_OVERHEAD_THROW), BITFIELD(ATOM_PASS_BUCKLED), + BITFIELD(ATOM_PASS_INCORPOREAL), )) //? /atom/movable movement_type - only one primary type should be on the atom at a time, but these are flags for quick checks. diff --git a/code/__DEFINES/_planes+layers.dm b/code/__DEFINES/_planes+layers.dm index 37eaaacbeb63..b38ab1a0fdd9 100644 --- a/code/__DEFINES/_planes+layers.dm +++ b/code/__DEFINES/_planes+layers.dm @@ -178,6 +178,7 @@ #define STAIRS_LAYER (TURF_LAYER+0.5) /// Layer for stairs. #define DOOR_OPEN_LAYER (TURF_LAYER+0.7) /// Under all objects if opened. 2.7 due to tables being at 2.6. #define TABLE_LAYER (TURF_LAYER+0.8) /// Just under stuff that wants to be slightly below common objects. +/// Below this layer, projectiles won't collide with things unless it's a directly clicked target. #define PROJECTILE_HIT_THRESHOLD_LAYER 2.8 #define UNDER_JUNK_LAYER (TURF_LAYER+0.9) /// Things that want to be slightly below common objects. diff --git a/code/__DEFINES/callbacks.dm b/code/__DEFINES/callbacks.dm index 33eda1bac26d..1c590425eb2a 100644 --- a/code/__DEFINES/callbacks.dm +++ b/code/__DEFINES/callbacks.dm @@ -1,10 +1,12 @@ /// Arbitrary sentinel value for global proc callbacks #define GLOBAL_PROC "some_magic_bullshit" +/// Arbitrary sentinel value for making sure a callback didn't sleep +#define CALLBACK_SLEEP_SENTINEL "___THE PROC SLEPT___" /// A shorthand for the callback datum, [documented here](datum/callback.html) #define CALLBACK new /datum/callback -///Per the DM reference, spawn(-1) will execute the spawned code immediately until a block is met. +/// Per the DM reference, spawn(-1) will execute the spawned code immediately until a block is met. #define MAKE_SPAWN_ACT_LIKE_WAITFOR -1 -///Create a codeblock that will not block the callstack if a block is met. +/// Create a codeblock that will not block the callstack if a block is met. #define ASYNC spawn(MAKE_SPAWN_ACT_LIKE_WAITFOR) #define INVOKE_ASYNC(proc_owner, proc_path, proc_arguments...) \ diff --git a/code/__DEFINES/coloration.dm b/code/__DEFINES/coloration.dm index 66a67f85f757..fb04c405e95a 100644 --- a/code/__DEFINES/coloration.dm +++ b/code/__DEFINES/coloration.dm @@ -1,4 +1,3 @@ - //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2024 Citadel Station developers. *// diff --git a/code/__DEFINES/combat/armor.dm b/code/__DEFINES/combat/armor.dm index f57d5e4f6f22..67ede87ad789 100644 --- a/code/__DEFINES/combat/armor.dm +++ b/code/__DEFINES/combat/armor.dm @@ -59,14 +59,26 @@ GLOBAL_REAL_LIST(armor_enums) = list( ARMOR_ACID, ) +GLOBAL_REAL_LIST(armor_types) = list( + ARMOR_MELEE, + ARMOR_BULLET, + ARMOR_LASER, + ARMOR_ENERGY, + ARMOR_BOMB, + ARMOR_BIO, + ARMOR_RAD, + ARMOR_FIRE, + ARMOR_ACID, +) + //? --- armor tiers --- -#define ARMOR_TIER_DEFAULT 0 +#define ARMOR_TIER_DEFAULT ARMOR_TIER_BASELINE #define ARMOR_TIER_LAUGHABLE -3 #define ARMOR_TIER_LOW -2 #define ARMOR_TIER_BELOW -1 -#define ARMOR_TIER_NORMAL 0 +#define ARMOR_TIER_BASELINE 0 #define ARMOR_TIER_ABOVE 1 #define ARMOR_TIER_HIGH 2 #define ARMOR_TIER_OVERWHELMING 3 @@ -76,37 +88,49 @@ GLOBAL_REAL_LIST(armor_enums) = list( //? melee -#define MELEE_TIER_DEFAULT ARMOR_TIER_DEFAULT +#define MELEE_TIER_DEFAULT MELEE_TIER_MEDIUM #define MELEE_TIER_UNARMED_DEFAULT ARMOR_TIER_LOW #define MELEE_TIER_UNARMED_FISTS ARMOR_TIER_LOW #define MELEE_TIER_UNARMED_CLAW ARMOR_TIER_BELOW -#define MELEE_TIER_LIGHT ARMOR_TIER_DEFAULT +#define MELEE_TIER_LIGHT ARMOR_TIER_BASELINE #define MELEE_TIER_MEDIUM ARMOR_TIER_ABOVE #define MELEE_TIER_HEAVY ARMOR_TIER_HIGH #define MELEE_TIER_EXTREME ARMOR_TIER_OVERWHELMING //? bullet -#define BULLET_TIER_DEFAULT ARMOR_TIER_DEFAULT - -#define BULLET_TIER_LAUGHABLE ARMOR_TIER_BELOW //! super improvised rounds / pistols / whatever. -#define BULLET_TIER_LOW ARMOR_TIER_DEFAULT //! pistols -#define BULLET_TIER_MEDIUM ARMOR_TIER_ABOVE //! smgs -#define BULLET_TIER_HIGH ARMOR_TIER_HIGH //! rifles -#define BULLET_TIER_EXTREME ARMOR_TIER_OVERWHELMING //! lmgs, light mech weapons -#define BULLET_TIER_RIDICULOUS ARMOR_TIER_RIDICULOUS //! heavy mech weapons +#define BULLET_TIER_DEFAULT BULLET_TIER_MEDIUM + +/// super improvised rounds / pistols / whatever. +#define BULLET_TIER_LAUGHABLE ARMOR_TIER_BELOW +/// pistols +#define BULLET_TIER_LOW ARMOR_TIER_BASELINE +/// smgs +#define BULLET_TIER_MEDIUM ARMOR_TIER_ABOVE +/// rifles +#define BULLET_TIER_HIGH ARMOR_TIER_HIGH +/// lmgs, light mech weapons +#define BULLET_TIER_EXTREME ARMOR_TIER_OVERWHELMING +/// heavy mech weapons +#define BULLET_TIER_RIDICULOUS ARMOR_TIER_RIDICULOUS //? laser -#define LASER_TIER_DEFAULT ARMOR_TIER_DEFAULT - -#define LASER_TIER_LAUGHABLE ARMOR_TIER_BELOW //! improvised laser focis / etc -#define LASER_TIER_LOW ARMOR_TIER_DEFAULT //! low tier lasers -#define LASER_TIER_MEDIUM ARMOR_TIER_ABOVE //! laser carbines, energy guns, etc -#define LASER_TIER_HIGH ARMOR_TIER_HIGH //! x-ray rifles, snipers -#define LASER_TIER_EXTREME ARMOR_TIER_OVERWHELMING //! mech weapons, usualy -#define LASER_TIER_RIDICULOUS ARMOR_TIER_RIDICULOUS //! power transmission laser? +#define LASER_TIER_DEFAULT LASER_TIER_MEDIUM + +/// improvised laser focis / etc +#define LASER_TIER_LAUGHABLE ARMOR_TIER_BELOW +/// low tier lasers +#define LASER_TIER_LOW ARMOR_TIER_BASELINE +/// laser carbines, energy guns, etc +#define LASER_TIER_MEDIUM ARMOR_TIER_ABOVE +/// x-ray rifles, snipers +#define LASER_TIER_HIGH ARMOR_TIER_HIGH +/// mech weapons, usualy +#define LASER_TIER_EXTREME ARMOR_TIER_OVERWHELMING +/// power transmission laser? +#define LASER_TIER_RIDICULOUS ARMOR_TIER_RIDICULOUS //? --- armor calculations --- diff --git a/code/__DEFINES/combat/attack_types.dm b/code/__DEFINES/combat/attack_types.dm index 26989b0a23c8..a0b4176d80ea 100644 --- a/code/__DEFINES/combat/attack_types.dm +++ b/code/__DEFINES/combat/attack_types.dm @@ -9,3 +9,10 @@ #define ATTACK_TYPE_THROWN (1<<2) /// damage source is /mob #define ATTACK_TYPE_UNARMED (1<<3) +/// we're being contacted by something +/// +/// * used internally by parry frames, mostly +/// * damage source is null +#define ATTACK_TYPE_TOUCH (1<<4) +/// a damage instance created by a block / parry frame transmuting damage and passing it to the user +#define ATTACK_TYPE_DEFENSIVE_PASSTHROUGH (1<<5) diff --git a/code/__DEFINES/combat/explosions.dm b/code/__DEFINES/combat/explosions.dm index c8709ccfd8a3..a60badb34c03 100644 --- a/code/__DEFINES/combat/explosions.dm +++ b/code/__DEFINES/combat/explosions.dm @@ -22,10 +22,9 @@ #define LEGACY_EXPLOSION_SEVERE_POWER EXPLOSION_CONSTANT_SEVERE #define LEGACY_EXPLOSION_MINOR_POWER EXPLOSION_CONSTANT_MINOR -#define LEGACY_EXPLOSION_DEVASTATE_INTEGRITY 1000 +#define LEGACY_EXPLOSION_DEVASTATE_INTEGRITY 500 #define LEGACY_EXPLOSION_SEVERE_INTEGRITY 180 #define LEGACY_EXPLOSION_MINOR_INTEGRITY 50 -#define LEGACY_EXPLOSION_INTEGRITY_MULT (0.01 * rand(50, 200)) // why the extra numbers? so if someone does weird math we don't out of bounds GLOBAL_REAL(_legacy_expowers, /list) = list( @@ -47,7 +46,6 @@ GLOBAL_REAL(_legacy_ex_atom_damage, /list) = list( 0 ) -#define LEGACY_EXPLOSION_ATOM_DAMAGE(P) (global._legacy_ex_atom_damage[P] * LEGACY_EXPLOSION_INTEGRITY_MULT) // this works out becuase epxlosions are 1-3 in legacy, so we can just use it as list indices #define LEGACY_EX_ACT(ATOM, POWER, TARGET) ATOM.legacy_ex_act(POWER, TARGET); ATOM.ex_act(_legacy_expowers[POWER]); diff --git a/code/__DEFINES/combat/shieldcall.dm b/code/__DEFINES/combat/shieldcall.dm index 889d27cad26f..965076dd8566 100644 --- a/code/__DEFINES/combat/shieldcall.dm +++ b/code/__DEFINES/combat/shieldcall.dm @@ -1,44 +1,198 @@ //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2023 Citadel Station developers. *// -//? keys for list/additional in atom_shieldcall +//* shieldcall return status *// -// None yet +/// terminate; either fully mitigated or we're done here +#define SHIELDCALL_FLAG_TERMINATE (1<<0) +/// terminate attacker swing entirely +/// +/// * usually you don't want this +#define SHIELDCALL_FLAG_CANCEL_SWING (1<<1) +/// stop attack effects +/// +/// * this is basically the [PROJECTILE_IMPACT_BLOCKED] for shieldcalls +/// * the thing hitting won't do direct damage but aftereffects like exploding rounds still explode +#define SHIELDCALL_FLAG_ATTACK_BLOCKED (1<<2) +/// attack redirected entirely +#define SHIELDCALL_FLAG_ATTACK_REDIRECT (1<<3) +/// attack goes through +/// +/// * both this and REDIRECT should be used if the original attack should keep going +/// * also use SHIELDCALL_FLAG_ATTACK_BLOCKED if original attack shouldn't process! otherwise it might be a pierce. +/// * example: reflecting a bullet +#define SHIELDCALL_FLAG_ATTACK_PASSTHROUGH (1<<4) +/// this attack already invoked a 'specialized' shieldcall proc, and is now invoking +/// the generalized atom_shieldcall() proc. +#define SHIELDCALL_FLAG_SECOND_CALL (1<<5) +/// asks the shieldcall nicely to not make a message +#define SHIELDCALL_FLAG_SUPPRESS_MESSAGE (1<<6) +/// asks the shieldcall nicely to not make a sound +#define SHIELDCALL_FLAG_SUPPRESS_SOUND (1<<7) +/// do not call armorcalls +#define SHIELDCALL_FLAG_SKIP_ARMORCALLS (1<<8) +/// do not call shieldcalls +#define SHIELDCALL_FLAG_SKIP_SHIELDCALLS (1<<9) +/// this is a passive / single parry +#define SHIELDCALL_FLAG_SINGLE_PARRY (1<<10) -//? shieldcall "struct" - this *must* match up with /atom/proc/run/check_shieldcall! +/// these flags mean to stop processing the attack +#define SHIELDCALL_FLAGS_BLOCK_ATTACK (SHIELDCALL_FLAG_ATTACK_BLOCKED) +/// these flags means that the attack should keep going after us, regardless of if we're hit +#define SHIELDCALL_FLAGS_PIERCE_ATTACK (SHIELDCALL_FLAG_ATTACK_PASSTHROUGH) +/// stop shieldcall chain +#define SHIELDCALL_FLAGS_SHOULD_TERMINATE (SHIELDCALL_FLAG_TERMINATE) +/// these flags means something happens / should happen +#define SHIELDCALL_FLAGS_SHOULD_PROCESS (SHIELDCALL_FLAGS_BLOCK_ATTACK | SHIELDCALL_FLAGS_PIERCE_ATTACK) + +/// flags set in a projectile reflect +/// +/// * you should be using /datum/shieldcall's bullet intercept / bullet signals if possible but this works too +#define SHIELDCALL_FLAGS_FOR_PROJECTILE_DEFLECT (SHIELDCALL_FLAG_TERMINATE | SHIELDCALL_FLAG_ATTACK_BLOCKED | SHIELDCALL_FLAG_ATTACK_REDIRECT | SHIELDCALL_FLAG_ATTACK_PASSTHROUGH) +/// flags set in a full block +#define SHIELDCALL_FLAGS_FOR_COMPLETE_BLOCK (SHIELDCALL_FLAG_TERMINATE | SHIELDCALL_FLAG_ATTACK_BLOCKED) + +//* Atom Shieldcall Args *// +//* *// +//* The shieldcall system is a very low-level 'damage instance' interception API. *// +//* It's used by the shieldcalls list in atoms to perform low-level intercepts, *// +//* as well as by default features like armor to perform their damage intercepts. *// +//* *// +//* For speed reasons, shieldcalls pass a single argument list down instead of *// +//* returning new lists every call, as shieldcalls need to be able to edit many *// +//* facets of a damage instance. *// +//* *// +//* Many of these are optional. *// +//* +//* Adding new arguments should be done sparingly. +//* Removing arguments requires every single proc with SHIELDCALL_PROC_HEADER *// +//* to be audited. *// /// damage amount #define SHIELDCALL_ARG_DAMAGE 1 /// damage type -#define SHIELDCALL_ARG_DAMTYPE 2 +#define SHIELDCALL_ARG_DAMAGE_TYPE 2 /// damage tier -#define SHIELDCALL_ARG_TIER 3 +#define SHIELDCALL_ARG_DAMAGE_TIER 3 /// armor flag -#define SHIELDCALL_ARG_FLAG 4 +#define SHIELDCALL_ARG_DAMAGE_FLAG 4 /// damage mode -#define SHIELDCALL_ARG_MODE 5 +#define SHIELDCALL_ARG_DAMAGE_MODE 5 /// attack type -#define SHIELDCALL_ARG_TYPE 6 -/// attacking weapon datum - same as used in armor +#define SHIELDCALL_ARG_ATTACK_TYPE 6 +/// attacking weapon datum +/// +/// * /obj/projectile if projectile +/// * /datum/unarmed_attack if unarmed melee +/// * /obj/item if item melee +/// * /datum/thrownthing if thrown +/// * null if touch #define SHIELDCALL_ARG_WEAPON 7 -/// list for additional data -#define SHIELDCALL_ARG_ADDITIONAL 8 -/// flags returned -#define SHIELDCALL_ARG_RETVAL 9 +/// flags returned from other shieldcalls +#define SHIELDCALL_ARG_FLAGS 8 +/// hit zone; this is usually a bodypart but this is also optional +#define SHIELDCALL_ARG_HIT_ZONE 9 +/// additional list returns; usually empty, but may exist +#define SHIELDCALL_ARG_ADDITIONAL 10 +/// the clickchain of a melee attack +/// +/// * this is passed in so you can grab data like who is doing it / who started the attack. +/// * filled in sometimes but not always if unarmed or item melee. +#define SHIELDCALL_ARG_CLICKCHAIN 11 + +/// A proc header with all the shieldcall args. +/// +/// * We use this so it's easy to check where shieldcall args are being used if shieldcalls need to be refactored. +#define SHIELDCALL_PROC_HEADER damage, damage_type, damage_tier, damage_flag, damage_mode, attack_type, datum/weapon, shieldcall_flags, hit_zone, list/additional, datum/event_args/actor/clickchain/clickchain -//? shieldcall additional data keys +//* list keys for list/additional in atom shieldcalls *// // none yet -//? shieldcall return flags - -/// abort further shieldcalls - hit is totally blocked or mitigated -#define SHIELDCALL_FULLY_MITIGATED (1<<0) -/// hit was partially blocked or mitigated -#define SHIELDCALL_PARTIALLY_MITIGATED (1<<1) -/// attack was forcefully missed e.g. by reactive teleport armor -#define SHIELDCALL_FORCED_MISS (1<<2) -/// fake; this is a check -#define SHIELDCALL_JUST_CHECKING (1<<3) -/// terminate further shieldcalls -#define SHIELDCALL_CEASE (1<<4) +//* Helpers to manipulate shieldcall args *// + +#define RESOLVE_SHIELDCALL_ATTACK_TEXT(SHIELDCALL) resolve_shieldcall_attack_text(SHIELDCALL) + +/proc/resolve_shieldcall_attack_text(list/shieldcall_args) + switch(shieldcall_args[SHIELDCALL_ARG_ATTACK_TYPE]) + if(ATTACK_TYPE_PROJECTILE) + . = shieldcall_args[SHIELDCALL_ARG_WEAPON] + if(ATTACK_TYPE_THROWN) + var/datum/thrownthing/thrown = shieldcall_args[SHIELDCALL_ARG_WEAPON] + if(thrown) + . = "the impact from [thrown.thrownthing]" + if(ATTACK_TYPE_MELEE, ATTACK_TYPE_UNARMED) + . = "the force of the blow" + + if(!.) + . = "the attack" + +#define RESOLVE_SHIELDCALL_WEAPON_DESCRIPTOR(SHIELDCALL) resolve_shieldcall_weapon_descriptor(SHIELDCALL) + +/proc/resolve_shieldcall_weapon_descriptor(list/shieldcall_args) + switch(shieldcall_args[SHIELDCALL_ARG_ATTACK_TYPE]) + if(ATTACK_TYPE_MELEE) + var/obj/item/weapon = shieldcall_args[SHIELDCALL_ARG_WEAPON] + return "a [weapon]" + if(ATTACK_TYPE_PROJECTILE) + var/obj/projectile/proj = shieldcall_args[SHIELDCALL_ARG_WEAPON] + return "a [proj]" + if(ATTACK_TYPE_THROWN) + var/datum/thrownthing/thrown = shieldcall_args[SHIELDCALL_ARG_WEAPON] + return "a [thrown.thrownthing]" + if(ATTACK_TYPE_MELEE, ATTACK_TYPE_UNARMED) + var/datum/unarmed_attack/style = shieldcall_args[SHIELDCALL_ARG_WEAPON] + return "a [style.attack_name]" + if(!.) + . = "an attack" + +//* handle_touch - contact_flags *//. + +// broad descriptors + +/// generally helpful actions (shake up, etc) +#define SHIELDCALL_CONTACT_FLAG_HELPFUL (1<<0) +/// potentially helpful but also potentially harmful actions +#define SHIELDCALL_CONTACT_FLAG_NEUTRAL (1<<1) +/// harmful actions +#define SHIELDCALL_CONTACT_FLAG_HARMFUL (1<<2) + +// categories + +/// medical items/techniques +#define SHIELDCALL_CONTACT_FLAG_MEDICAL (1<<23) + +//* handle_touch - contact_specific *// + +/// trying to inject someone +#define SHIELDCALL_CONTACT_SPECIFIC_SYRINGE_INJECTION "inject" +/// trying to shake someone up +#define SHIELDCALL_CONTACT_SPECIFIC_SHAKE_UP "help" +/// trying to drag someone +#define SHIELDCALL_CONTACT_SPECIFIC_PULL "pull" +/// trying to grab someone, **or** intensify a grab +#define SHIELDCALL_CONTACT_SPECIFIC_GRAB "grab" +/// trying to perform a surgery step - generic +#define SHIELDCALL_CONTACT_SPECIFIC_SURGERY "surgery" +/// being sprayed with chemicals +#define SHIELDCALL_CONTACT_SPECIFIC_CHEMICAL_SPRAY "spray" + +//* handle_touch - helpers *// + +/** + * Gets text to put in say, "blocks \the [text]" when someone has a blocked touch. + */ +#define RESOLVE_SHIELDCALL_TOUCH_TEXT(CONTACT_FLAGS, CONTACT_SPECIFIC) resolve_shieldcall_touch_text(CONTACT_FLAGS, CONTACT_SPECIFIC) + +/proc/resolve_shieldcall_touch_text(flags, specific) + switch(specific) + if(SHIELDCALL_CONTACT_SPECIFIC_SYRINGE_INJECTION) + return "syringe" + if(SHIELDCALL_CONTACT_SPECIFIC_SHAKE_UP) + return "help-up" + if(SHIELDCALL_CONTACT_SPECIFIC_GRAB) + return "grab" + if(SHIELDCALL_CONTACT_SPECIFIC_SURGERY) + return "operation" + if(SHIELDCALL_CONTACT_SPECIFIC_CHEMICAL_SPRAY) + return "spray" diff --git a/code/__DEFINES/datums/event_args.dm b/code/__DEFINES/datums/event_args.dm new file mode 100644 index 000000000000..2fa589bf4e6f --- /dev/null +++ b/code/__DEFINES/datums/event_args.dm @@ -0,0 +1,5 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +// make sure a var that is either event_args/actor or a single mob/user is event args; if it's not +#define E_ARGS_WRAP_USER_TO_ACTOR(USER) USER = ismob(USER)? new /datum/event_args/actor(USER) : USER diff --git a/code/__DEFINES/dcs/flags.dm b/code/__DEFINES/dcs/flags.dm index 47695369dcd7..f69925f96982 100644 --- a/code/__DEFINES/dcs/flags.dm +++ b/code/__DEFINES/dcs/flags.dm @@ -1,12 +1,10 @@ /// Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type. /// `parent` must not be modified if this is to be returned. /// This will be noted in the runtime logs -#define COMPONENT_INCOMPATIBLE (1<<0) -/// Returned in PostTransfer to prevent transfer, similar to `COMPONENT_INCOMPATIBLE` -#define COMPONENT_NOTRANSFER (1<<1) +#define COMPONENT_INCOMPATIBLE -1 /// Return value to cancel attaching -#define ELEMENT_INCOMPATIBLE (1<<0) +#define ELEMENT_INCOMPATIBLE -1 // /datum/element flags /// Causes the detach proc to be called when the host object is being deleted diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_buckling.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-buckling.dm similarity index 60% rename from code/__DEFINES/dcs/signals/signals_atom/signals_atom_buckling.dm rename to code/__DEFINES/dcs/signals/signals_atom/signals_atom-buckling.dm index 29d0bc4c9c04..640048265bb4 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_buckling.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-buckling.dm @@ -1,16 +1,23 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// -////! buckling. flags are always buckling opflags, see __DEFINES/procs/buckling.dm -/// called on mob buclked: (mob, flags, user, semantic) +//* -- Buckling -- *// +//* flags are always buckling opflags *// +//* see __DEFINES/procs/buckling.dm *// + +//* These are called when the buckle/unbuckle op have already happened *// + +/// called on the atom the mob buckled to: (mob, flags, user, semantic) #define COMSIG_MOVABLE_MOB_BUCKLED "mob_buckled" -/// called on mob unbuckled: (mob, flags, user, semantic) +/// called on the atom the mob unbuckled from: (mob, flags, user, semantic) #define COMSIG_MOVABLE_MOB_UNBUCKLED "mob_unbuckled" -//! weird names to be more distinct from movable signals /// called on the mob that just got buckled: (mob, flags, user, semantic) -#define COMSIG_MOB_BUCKLED "buckled" +#define COMSIG_MOB_BUCKLED_TO "buckled" /// called on the mob that just got unbuckled: (mob, flags, user, semantic) -#define COMSIG_MOB_UNBUCKLED "unbuckled" +#define COMSIG_MOB_UNBUCKLED_FROM "unbuckled" + +//* For the below, component_block/force_buckle_operation works to varying degrees on varying procs. *// -//! For the below, component_block/force_buckle_operation works to varying degrees on varying procs. /// called during mob buckling: (mob, flags, user, semantic) #define COMSIG_MOVABLE_PRE_BUCKLE_MOB "pre_buckle_mob" /// called during can buckle mob: (mob, flags, user, semantic) @@ -24,15 +31,18 @@ /// called on mob resist buckle: (mob, semantic). Can't force, can only block. #define COMSIG_MOVABLE_MOB_RESIST_BUCKLE "mob_resist_buckle" /// called during can buckle on mob: (AM, flags, user, semantic, movable_opinion) -#define COMSIG_MOB_CAN_BUCKLE "mob_can_buckle" +#define COMSIG_MOB_CAN_BUCKLE_TO "mob_can_buckle" /// called during can unbuckle on mob: (AM, flags, user, semantic, movable_opinion) -#define COMSIG_MOB_CAN_UNBUCKLE "mob_can_unbuckle" +#define COMSIG_MOB_CAN_UNBUCKLE_FROM "mob_can_unbuckle" /// block mob buckle/unbuckle **silently** - #define COMPONENT_BLOCK_BUCKLE_OPERATION (1<<0) + #define SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION (1<<0) + /// + /// * has priority over [SIGNAL_RAISE_FORCE_BUCKLE_OPERATION] where applicable /// force allow buckle/unbuckled **silently** - #define COMPONENT_FORCE_BUCKLE_OPERATION (1<<1) + #define SIGNAL_RAISE_FORCE_BUCKLE_OPERATION (1<<1) + +//* Interactions; only blocking default interactions work. *// -//! Interactions; only blocking default interactions work. /// called from drag_drop_buckle_interaction: (A, user) #define COMSIG_MOVABLE_DRAG_DROP_BUCKLE_INTERACTION "drag_drop_buckle" /// called from click_unbuckle_interaction: (user) @@ -40,4 +50,4 @@ /// called from resist_unbuckle_interaction(M) #define COMSIG_MOVABLE_RESIST_UNBUCKLE_INTERACTION "resist_unbuckle" /// cancel rest of procs - #define COMPONENT_HANDLED_BUCKLE_INTERACTION (1<<0) + #define SIGNAL_RAISE_BUCKLE_INTERACTION_HANDLED (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom-clickchain.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-clickchain.dm new file mode 100644 index 000000000000..989eb5321ab8 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-clickchain.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* All of these return CLICKCHAIN_* flags. *// + +/// from /obj/item/proc/item_attack_chain(): (obj/item/using, datum/event_args/actor/clickchain/e_args, clickchain_flags, datum/callback/reachability_check) +#define COMSIG_ATOM_USING_ITEM_ON "using_item_on" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_context_system.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-context_system.dm similarity index 95% rename from code/__DEFINES/dcs/signals/signals_atom/signals_atom_context_system.dm rename to code/__DEFINES/dcs/signals/signals_atom/signals_atom-context_system.dm index f6efa7cee461..a5fb0b6a8047 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_context_system.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-context_system.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// +//* Copyright (c) 2024 silicons *// /// from base of /atom/proc/context_query: (list/options, datum/event_args/actor/e_args) /// options list is the same format as /atom/proc/context_query, insert directly to it. diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom-defense.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-defense.dm new file mode 100644 index 000000000000..4d20c82cc267 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-defense.dm @@ -0,0 +1,37 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +// todo: integrity signals? + +/// called from bullet_act(): (args) +#define COMSIG_ATOM_BULLET_ACT "bullet_act" + #define BULLET_ACT_ARG_PROJECTILE 1 + /// index of arg for PROJECTILE_IMPACT_* flags + #define BULLET_ACT_ARG_FLAGS 2 + #define BULLET_ACT_ARG_ZONE 3 + #define BULLET_ACT_ARG_EFFICIENCY 4 + +/// called from run_armorcalls(): (list/shieldcall_args, fake_attack) +/// +/// * This is an extremely low-level signal. Handle with care. +#define COMSIG_ATOM_ARMORCALL "atom-armorcalls" +/// called from run_shieldcalls(): (list/shieldcall_args, fake_attack) +/// +/// * This is an extremely low-level signal. Handle with care. +#define COMSIG_ATOM_SHIELDCALL "atom-shieldcalls" + +/// called from atom_shieldcall_handle_*l: (shieldcall_type) +/// +/// * use this for stuff that should spin up a full shield when attacked but is usually inactive. +/// * this is not used for base of /atom_shieldcall(), as it already has a signal! +#define COMSIG_ATOM_SHIELDCALL_ITERATION "atom-shieldcall-iteration" + /// atom_shieldcall_handle_unarmed_melee() + #define ATOM_SHIELDCALL_ITERATING_UNARMED_MELEE (1<<1) + /// atom_shieldcall_handle_item_melee() + #define ATOM_SHIELDCALL_ITERATING_ITEM_MELEE (1<<2) + /// atom_shieldcall_handle_bullet_act() + #define ATOM_SHIELDCALL_ITERATING_BULLET_ACT (1<<3) + /// atom_shieldcall_handle_touch() + #define ATOM_SHIELDCALL_ITERATING_TOUCH (1<<4) + /// atom_shieldcall_handle_throw_impact() + #define ATOM_SHIELDCALL_ITERATING_THROW_IMPACT (1<<5) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom-reachability.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-reachability.dm new file mode 100644 index 000000000000..3b98fa4b0f7d --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-reachability.dm @@ -0,0 +1,10 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +/** + * These are all **very** low level signals. + * Handle with care. + */ + +/// from base of /atom/movable/proc/DirectAccess(): (list/accessible) +#define COMSIG_ATOM_REACHABILITY_DIRECTACCESS "atom-directaccess" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_throwing.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-throwing.dm similarity index 79% rename from code/__DEFINES/dcs/signals/signals_atom/signals_atom_throwing.dm rename to code/__DEFINES/dcs/signals/signals_atom/signals_atom-throwing.dm index 48c615ad3e2f..ae1ba120b00d 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_throwing.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-throwing.dm @@ -1,8 +1,9 @@ -//! Wanna add hitpush signals? TOO BAD, DON'T. Modify the thrownthing datum these pass in! +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// -/// from base of /atom/proc/throw_impacted: (AM, thrownthing) +/// from /atom/movable/proc/_throw_do_hit: (impactor, thrownthing) #define COMSIG_ATOM_THROW_IMPACTED "throw_impacted" -/// from base of /atom/movable/proc/throw_impact: (AM, thrownthing) +/// from /atom/movable/proc/_throw_do_hit: (blocker, thrownthing) #define COMSIG_MOVABLE_THROW_IMPACT "throw_impact" // This set of returns can be for both of the above! /// cancel further actions in this hit @@ -12,9 +13,9 @@ /// completely terminate throw silently immediately. Use if you're deleting the atom. #define COMPONENT_THROW_HIT_TERMINATE (1<<2) -/// called on throws landing on something: (landed_on, thrownthing) +/// from /atom/movable/proc/_throw_finalize: (landed_on, thrownthing) #define COMSIG_MOVABLE_THROW_LAND "throw_land" -/// called on something landing on us from a throw +/// from /atom/movable/proc/_throw_finalize: (landing_movable, thrownthing) #define COMSIG_ATOM_THROW_LANDED "throw_landed" // This set of returns can be for both of the above! /// cancel further actions diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_tool_system.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-tool_system.dm similarity index 91% rename from code/__DEFINES/dcs/signals/signals_atom/signals_atom_tool_system.dm rename to code/__DEFINES/dcs/signals/signals_atom/signals_atom-tool_system.dm index 5d9c7ab9c1de..3bb7af39fc40 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_tool_system.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom-tool_system.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// +//* Copyright (c) 2024 silicons *// /// from base of _tool_act: (I, user, function, flags, hint) where I = item, e_args = clickchain data, function = tool behaviour, flags = tool operation flags, hint = set by dynamic tool system /// return CLICKCHAIN_COMPONENT_SIGNAL_HANDLED to abort normal tool_act handling. diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_defense.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_defense.dm deleted file mode 100644 index 89d0a90cfe37..000000000000 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_defense.dm +++ /dev/null @@ -1,5 +0,0 @@ -//* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// - -// todo: this file left intentionally empty since shieldcalls were moved to datums -// todo: add signals for integrity diff --git a/code/__DEFINES/dcs/signals/signals_item/signals_item-clickchain.dm b/code/__DEFINES/dcs/signals/signals_item/signals_item-clickchain.dm new file mode 100644 index 000000000000..a6813cd46db2 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_item/signals_item-clickchain.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* All of these return CLICKCHAIN_* flags. *// + +/// from /obj/item/proc/item_attack_chain(): (atom/target, datum/event_args/actor/clickchain/e_args, clickchain_flags, datum/callback/reachability_check) +#define COMSIG_ITEM_USING_AS_ITEM "using_as_item" diff --git a/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm b/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm index 070449036739..77773426f55c 100644 --- a/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm +++ b/code/__DEFINES/dcs/signals/signals_item/signals_item_inventory.dm @@ -1,10 +1,10 @@ -/// From base of obj/item/dropped: (mob/user, flags, atom/newLoc) +/// From base of obj/item/dropped: (mob/user, inv_op_flags, atom/new_loc) #define COMSIG_ITEM_DROPPED "item_drop" #define COMPONENT_ITEM_DROPPED_RELOCATE (1<<0) #define COMPONENT_ITEM_DROPPED_SUPPRESS_SOUND (1<<1) -/// From base of obj/item/pickup: (mob/user, flags, atom/oldLoc) +/// From base of obj/item/pickup: (mob/user, inv_op_flags, atom/old_loc) #define COMSIG_ITEM_PICKUP "item_pickup" -/// From base of obj/item/equipped(): (/mob/equipper, slot, accessory) +/// From base of obj/item/equipped(): (/mob/equipper, slot, inv_op_flags) #define COMSIG_ITEM_EQUIPPED "item_equip" -/// From base of obj/item/unequipped(): (/mob/unequipped, slot, accessory) +/// From base of obj/item/unequipped(): (/mob/unequipped, slot, inv_op_flags) #define COMSIG_ITEM_UNEQUIPPED "item_unequip" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_inventory.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob-inventory.dm similarity index 85% rename from code/__DEFINES/dcs/signals/signals_mob/signals_mob_inventory.dm rename to code/__DEFINES/dcs/signals/signals_mob/signals_mob-inventory.dm index 1aff02f288d3..ffdc89ec7470 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_inventory.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob-inventory.dm @@ -1,3 +1,6 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + /// A mob has just equipped an item. Called on [/mob] from base of [/obj/item/equipped()]: (/obj/item/equipped_item, slot, inv_op_flags) #define COMSIG_MOB_ITEM_EQUIPPED "mob_equipped_item" /// A mob has just unequipped an item. Called on [/mob] from base of [/obj/item/unequipped()]: (/obj/item/equipped_item, slot, inv_op_flags) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob-perspective.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob-perspective.dm new file mode 100644 index 000000000000..971e9b9a00d6 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob-perspective.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/// emitted by reset_perspective, but only if the perspective needed switching: (perspective) +#define COMSIG_MOB_RESET_PERSPECTIVE "reset_perspective" +/// emitted by update_perspective: () +#define COMSIG_MOB_UPDATE_PERSPECTIVE "update_perspective" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm new file mode 100644 index 000000000000..374ffdcdbb66 --- /dev/null +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_legacy.dm @@ -0,0 +1,13 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +// todo: burn all this shit with fire + +/// used by passive parry to detect +/// the entire mob item attack system is a dumpster fire and needs rewritten +/// for now, this is a signal with (item, user, hit_zone) +#define COMSIG_MOB_LEGACY_RESOLVE_ITEM_ATTACK "legacy-mob-item-resolve-attack" +/// used by passive parry to detect +/// signal with (user, datum/event_args/actor/clickchain/e_args) +/// :skull: +#define COMSIG_MOB_LEGACY_ATTACK_HAND_INTERCEPT "legacy-mob-legacy-attack-hand" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_perspectiive.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_perspectiive.dm deleted file mode 100644 index ecaa7708fb5e..000000000000 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_perspectiive.dm +++ /dev/null @@ -1,4 +0,0 @@ -/// emitted by reset_perspective, but only if the perspective needed switching: (perspective) -#define COMSIG_MOB_RESET_PERSPECTIVE "reset_perspective" -/// emitted by update_perspective: () -#define COMSIG_MOB_UPDATE_PERSPECTIVE "update_perspective" diff --git a/code/__DEFINES/event_args.dm b/code/__DEFINES/event_args.dm deleted file mode 100644 index cfbbc03c37c3..000000000000 --- a/code/__DEFINES/event_args.dm +++ /dev/null @@ -1,7 +0,0 @@ -//? for /datum/event_args/actor - -#define WRAP_MOB_TO_ACTOR_EVENT_ARGS(VARNAME) VARNAME = ismob(VARNAME)? new /datum/event_args/actor(VARNAME) : VARNAME - -//? for /datum/event_args/actor/clickchain - -#define WRAP_MOB_TO_CLICKCHAIN_EVENT_ARGS(VARNAME) VARNAME = ismob(VARNAME)? new /datum/event_args/actor/clickchain(VARNAME) : VARNAME diff --git a/code/__DEFINES/loadout.dm b/code/__DEFINES/loadout.dm index 97541ba9432d..e8fcbfcf7f4a 100644 --- a/code/__DEFINES/loadout.dm +++ b/code/__DEFINES/loadout.dm @@ -33,9 +33,9 @@ DEFINE_BITFIELD(loadout_customize_flags, list( )) // *DO NOT RAISE THIS. This is for performance reasons.* // -#define LOADOUT_MAX_SLOTS 10 +#define LOADOUT_MAX_SLOTS 16 #define LOADOUT_SLOT_NAME_LENGTH 32 -#define LOADOUT_MAX_ITEMS 30 +#define LOADOUT_MAX_ITEMS 32 /// Used in chargen for accessory loadout limit. #define LOADOUT_MAX_COST 20 /// Used in chargen for accessory loadout limit on holidays. diff --git a/code/__DEFINES/math.dm b/code/__DEFINES/math.dm index d23d6c4f7d88..4fe4d17dee5e 100644 --- a/code/__DEFINES/math.dm +++ b/code/__DEFINES/math.dm @@ -2,13 +2,22 @@ // This file is quadruple wrapped for your pleasure // ( +/// The value of the mathematical constant 'e' #define NUM_E 2.71828183 - +/// The value of sqrt(2); defined for speed #define SQRT_2 1.414214 - -#define M_PI (3.14159265) -///closer then enough -#define INFINITY (1.#INF) +/// The value of the mathematical constant 'Pi' +#define M_PI 3.14159265 + +/// A quick way to write (1.#INF) +#define INFINITY (1.#INF) + +/// the highest number that does not lose precision when only using the one's place +/// +/// * floating point has serious inaccuracies; after this limit, we can no longer track to one's place +/// * you usually don't have to worry about this if you're not writing anything that requires accuracy +/// * if you are, note that accuracy is lost even below this limit for fractionals. +/// * please look up how IEEE single precision floats work for more details. #define SHORT_REAL_LIMIT 16777216 //"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks @@ -28,13 +37,21 @@ #define SIGN(x) ( (x)!=0 ? (x) / abs(x) : 0 ) /// ceil() +// +// todo: get rid of this, this is native now #define ROUND_UP(x) ( -round(-(x))) /// floor() +// +// todo: get rid of this, this is native now #define ROUND_DOWN(x) (round(x)) -// x to the nearest higher multiple of y +/// x to the nearest higher multiple of y +/// +/// * This is not replaced by native ceil(), as that is always CEILING(x, 1)! #define CEILING(x, y) ( -round(-(x) / (y)) * (y) ) -// x to the nearest lower multiple of y +/// x to the nearest lower multiple of y +/// +/// * This is not replaced by native floor(), as that is always FLOOR(x, 1)! #define FLOOR(x, y) ( round((x) / (y)) * (y) ) // Similar to clamp but the bottom rolls around to the top and vice versa. min is inclusive, max is exclusive @@ -43,16 +60,15 @@ // Real modulus that handles decimals #define MODULUS_F(x, y) ( (x) - (y) * round((x) / (y)) ) -// Cotangent +/// Cotangent #define COT(x) (1 / tan(x)) - -// Secant +/// Secant #define SEC(x) (1 / cos(x)) - -// Cosecant +/// Cosecant #define CSC(x) (1 / sin(x)) // ArcTan2. Returns the degree between two points in an x and y system. +// todo: get rid of this, this is native now /proc/arctantwo(x1,y1,x2,y2) var/dx = x2-x1 var/dy = y2-y1 diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 34d76cb7f739..eb8d894025cf 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -192,14 +192,6 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s #define NTOS_EMAIL_NOTIFALREADY 1 #define NTOS_EMAIL_NEWMESSAGE 2 - -// Special return values from bullet_act(). Positive return values are already used to indicate the blocked level of the projectile. -/// If the projectile should continue flying after calling bullet_act() -#define PROJECTILE_CONTINUE -1 -/// If the projectile should treat the attack as a miss (suppresses attack and admin logs) - only applies to mobs. -#define PROJECTILE_FORCE_MISS -2 - - // Vending stuff #define CAT_NORMAL 1 #define CAT_HIDDEN 2 @@ -275,8 +267,6 @@ var/list/economy_station_departments = list( ///The number of deciseconds in a day #define MIDNIGHT_ROLLOVER 864000 -///Needed for the R-UST port -#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 /// Maximum effective value of client.view (According to DM references) #define MAX_CLIENT_VIEW 34 diff --git a/code/__DEFINES/mobs/iff.dm b/code/__DEFINES/mobs/iff.dm new file mode 100644 index 000000000000..4edc9d365aa2 --- /dev/null +++ b/code/__DEFINES/mobs/iff.dm @@ -0,0 +1,109 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +//* IFF Factions *// + +//? -- Special; These must start with '!' -- ?// +//? ?// +//? These are only valid syntax in places ?// +//? where you should be using them, aka ?// +//? initializers. ?// + +/// get an arbitrary faction that's the same on a given /datum/map_level +#define MOB_IFF_FACTION_BIND_TO_LEVEL "!bind-level" +/// get an arbitrary faction that's the same on a given /datum/map +/// +/// * acts like BIND_TO_LEVEL if there's no parent /datum/map for a /datum/map_level +#define MOB_IFF_FACTION_BIND_TO_MAP "!bind-map" +/// get an arbitrary faction that's the same on a given /datum/map_level for a given key +/// +/// * GROUP must be a string +#define MOB_IFF_FACTION_BIND_TO_LEVEL_GROUP(GROUP) list(MOB_IFF_FACTION_BIND_TO_LEVEL = GROUP) +/// get an arbitrary faction that's the same on a given /datum/map for a given key +/// +/// * GROUP must be a string +/// * acts like BIND_TO_LEVEL if there's no parent /datum/map for a /datum/map_level +#define MOB_IFF_FACTION_BIND_TO_MAP_GROUP list(MOB_IFF_FACTION_BIND_TO_MAP = GROUP) + +// todo: "bind to map template" (?) +// todo: "bind to /area" (?) +// todo: "bind to specific type" handling; maybe just input typepath? + +/// automatically detect what we should bind to +/// +/// * submap +/// * map +/// todo: impl +#define MOB_IFF_FACTION_BIND_AUTO MOB_IFF_FACTION_BIND_TO_MAP +/// automatically detect what we should bind to, and use a specific separated group +/// +/// * submap +/// * map +/// todo: impl +#define MOB_IFF_FACTION_BIND_AUTO_GROUP(GROUP) list(MOB_IFF_FACTION_BIND_TO_MAP = GROUP) + +//? Default factions *// + +/// mobs have this by default +/// +/// * this makes a lot of things assume that the mob is nonhostile. +/// * this should be removed for hostile mobs +#define MOB_IFF_FACTION_NEUTRAL "neutral" +/// generic hostile mob faction +/// +/// * do not check for this; most hostile mobs do not have this +/// * having FACTION_NEUTRAL is an effect; having this is not, this is just a generic one so the mob has a faction. +/// * you probably shouldn't even be using this unless you're doing BIND_TO_LEVEL/MAP_GROUP with this. +#define MOB_IFF_FACTION_HOSTILE "hostile" + +//? AI / machine intelligence factions ?// + +#define MOB_IFF_FACTION_HIVEBOT "ai-hivebot" +#define MOB_IFF_FACTION_SWARMER "ai-swarmer" + +//? Alien factions + +#define MOB_IFF_FACTION_BLOB "alien-blob" +#define MOB_IFF_FACTION_CHIMERIC "alien-chimeric" +#define MOB_IFF_FACTION_SLIME "alien-slime" +#define MOB_IFF_FACTION_STATUE "alien-statue" +#define MOB_IFF_FACTION_XENOMORPH "alien-xenomorph" + +//? Animal factions ?// +//* Farm refers to 'this would not be out of place in a normal earth farm that isn't in a horror series' + +/// goats, cows, sheep +#define MOB_IFF_FACTION_FARM_ANIMAL "farm-animal" +/// ducks, other 'non producing' (canonically, anyways) +#define MOB_IFF_FACTION_FARM_NEUTRAL "farm-neutral" +/// mice and similar +#define MOB_IFF_FACTION_FARM_PEST "farm-pest" +/// cats, dogs +#define MOB_IFF_FACTION_FARM_PET "pet" + +/// man's worst enemy (spiders) +#define MOB_IFF_FACTION_SPIDER "spider" +/// fallout gone wrong - wait what?! (cockroaches) +#define MOB_IFF_FACTION_ROACH "roach" +/// biotech gone wrong (genetic horrors) +#define MOB_IFF_FACTION_MUTANT "mutant" +/// hydroponics gone wrong (literally any hostile plane) +#define MOB_IFF_FACTION_PLANT "plant" +/// is this a dune reference??? (space worms) +#define MOB_IFF_FACTION_WORM "worm" +/// we're going whaling! (space carps) +#define MOB_IFF_FACTION_CARP "carp" +/// the bane of engineering (solargrubs) +#define MOB_IFF_FACTION_GRUB "grubs" + +//? Human factions ?// + +#define MOB_IFF_FACTION_MERCENARY "mercenary" +#define MOB_IFF_FACTION_MERCENARY_GROUP(GROUP) ("mercenary-" + GROUP) +#define MOB_IFF_FACTION_PIRATE "mercenary" +#define MOB_IFF_FACTION_PIRATE_GROUP(GROUP) ("mercenary-" + GROUP) + +//? Paracausal factions ?// + +#define MOB_IFF_FACTION_CLOCKWORK_CULT "clock-cult" +#define MOB_IFF_FACTION_SANGUINE_CULT "blood-cult" diff --git a/code/__DEFINES/procs/clickcode.dm b/code/__DEFINES/procs/clickcode.dm index 6293c3498e06..5eaa74894e1a 100644 --- a/code/__DEFINES/procs/clickcode.dm +++ b/code/__DEFINES/procs/clickcode.dm @@ -19,7 +19,9 @@ */ /// stop the click chain from proceeding past this point; usually used if we're deleting or being inserted -/// DO NOT ABUSE THIS PROC TO INTERRUPT AFTERATTACKS WITHOUT CARE; this is NOT what this is here for! +/// +/// * This is an unconditional abort. +/// * DO NOT ABUSE THIS PROC TO INTERRUPT AFTERATTACKS WITHOUT CARE; this is NOT what this is here for! #define CLICKCHAIN_DO_NOT_PROPAGATE (1<<0) /// person can reach us normally #define CLICKCHAIN_HAS_PROXIMITY (1<<1) @@ -38,10 +40,21 @@ #define CLICKCHAIN_DO_NOT_ATTACK (1<<7) /// intercepted by component #define CLICKCHAIN_COMPONENT_SIGNAL_HANDLED (1<<8) +/// this is a reflex counterattack by something +/// +/// * used to prevent loops where both parties reactively attack each other instantly. +#define CLICKCHAIN_REFLEX_COUNTER (1<<9) +/// put this in if we should entirely abort the attack +#define CLICKCHAIN_FULL_BLOCKED (1<<10) + +/// check these for 'unconditional abort' +#define CLICKCHAIN_FLAGS_UNCONDITIONAL_ABORT (CLICKCHAIN_DO_NOT_PROPAGATE) +/// check these for 'abort attack' +#define CLICKCHAIN_FLAGS_ATTACK_ABORT (CLICKCHAIN_DO_NOT_PROPAGATE | CLICKCHAIN_FULL_BLOCKED) //! Reachability Depths - checked from level of DirectAccess and turf adjacency. /// default reachability depth -#define DEFAULT_REACHABILITY_DEPTH 3 // enough to reach into pill bottles in box in backpack +#define DEFAULT_REACHABILITY_DEPTH 4 //! Reachability /// can't reach - this *must* be a fals-y value. diff --git a/code/__DEFINES/projectiles/projectile.dm b/code/__DEFINES/projectiles/projectile.dm new file mode 100644 index 000000000000..f87cff191198 --- /dev/null +++ b/code/__DEFINES/projectiles/projectile.dm @@ -0,0 +1,119 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* pre_impact(), impact(), bullet_act(), on_impact() impact_flags *// +/// pre_impact, bullet_act, on_impact are called in that order /// + +/// pointblank hit +#define PROJECTILE_IMPACT_POINT_BLANK (1<<0) +/// piercing hit; if returned, forces pierce for current impact +/// +/// * projectile has the right to perform special behavior like reducing damage after the impact +#define PROJECTILE_IMPACT_PIERCE (1<<1) +/// was blocked from directly hitting target +/// +/// * on impact probably shouldn't do direct damage, but explosive rounds will explode, etc +#define PROJECTILE_IMPACT_BLOCKED (1<<2) +/// if sensing this flag, **immediately** destroy the projectile without elaboration +/// +/// * overrides everything else +#define PROJECTILE_IMPACT_DELETE (1<<3) +/// do not hit; if this is present, we phase through without interaction +/// +/// * bullet_act(), and on_impact() will be cancelled by this. +/// * on_phase() is called instead to allow for standard hooks to fire +#define PROJECTILE_IMPACT_PHASE (1<<4) +/// signifies that the projectile is reflected. +/// +/// * projectile is not deleted like in PIERCING or PHASE +/// * fires off on_reflect() +#define PROJECTILE_IMPACT_REFLECT (1<<5) +/// we should pass through without interaction +/// +/// * bullet_act(), on_impact(), on_reflect(), and on_phase() will all be cancelled by this. +#define PROJECTILE_IMPACT_PASSTHROUGH (1<<6) +/// instructs piercing projectiles that support this +/// to not reduce damage because the impact was so trivial +/// compared to the force of the projectile +#define PROJECTILE_IMPACT_TRIVIAL (1<<7) +/// aborting duplicate impact due to already being in impacted list of projectile +#define PROJECTILE_IMPACT_DUPLICATE (1<<8) +/// passed from another bullet_act(), +/// like from a target stake to the mounted target +#define PROJECTILE_IMPACT_INDIRECTED (1<<9) +/// used by /impact() on projectile to signal to impact_loop() +/// that the projectile should keep impacting everything on the turf it was trying to hit +#define PROJECTILE_IMPACT_CONTINUE_LOOP (1<<10) +/// target was deleted, stop processing on target side but not projectile side +#define PROJECTILE_IMPACT_TARGET_DELETED (1<<11) +/// this is an impact (usually on the ground) from a projectile expiring +#define PROJECTILE_IMPACT_IS_EXPIRING (1<<12) +/// requests that no sound is made +/// +/// * this is separate from suppression / silencing projectile-side! +/// * notably, this is an absolute that should be always obeyed; while things like special round effects can ignore normal suppression. +#define PROJECTILE_IMPACT_SUPPRESS_SOUND (1<<13) +/// requests that no message is made +/// +/// * this is separate from suppression / silencing projectile-side! +/// * notably, this is an absolute that should be always obeyed; while things like special round effects can ignore normal suppression. +#define PROJECTILE_IMPACT_SUPPRESS_MESSAGE (1<<14) // phasing? +/// do not process damage normally +/// +/// * stops generic 'inflict damage instance' from being procced automatically. +#define PROJECTILE_IMPACT_SKIP_STANDARD_DAMAGE (1<<15) + +/// any of these means the projectile should delete immediately +#define PROJECTILE_IMPACT_FLAGS_SHOULD_DELETE (PROJECTILE_IMPACT_DELETE) +/// any of these means the projectile should not impact +#define PROJECTILE_IMPACT_FLAGS_SHOULD_NOT_HIT (PROJECTILE_IMPACT_REFLECT | PROJECTILE_IMPACT_PHASE | PROJECTILE_IMPACT_PASSTHROUGH) +/// any of these means don't just delete after hit +#define PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH (PROJECTILE_IMPACT_REFLECT | PROJECTILE_IMPACT_PHASE | PROJECTILE_IMPACT_PASSTHROUGH | PROJECTILE_IMPACT_PIERCE) +/// any of these means the projectile should abort bullet_act +#define PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT (PROJECTILE_IMPACT_DELETE | PROJECTILE_IMPACT_REFLECT | PROJECTILE_IMPACT_PHASE | PROJECTILE_IMPACT_PASSTHROUGH) +/// any of these means the projectile should abort bullet_act, but not on_impact() for the projectile +#define PROJECTILE_IMPACT_FLAGS_TARGET_ABORT (PROJECTILE_IMPACT_DELETE | PROJECTILE_IMPACT_REFLECT | PROJECTILE_IMPACT_PHASE | PROJECTILE_IMPACT_PASSTHROUGH | PROJECTILE_IMPACT_TARGET_DELETED) + +//* projectile_type bitfield *// + +//? base types; all projectiles should have one of these ?// + +/// kinetic matter, basically +#define PROJECTILE_TYPE_KINETIC (1<<0) +/// energy projectiles that aren't a beam +#define PROJECTILE_TYPE_ENERGY (1<<1) +/// particle beam, basically +#define PROJECTILE_TYPE_BEAM (1<<2) + +//? specific types; projectiles may have one or more of these in addition to the above ?// + +/// photonic energy, basically (yes yes lasers are unrealistic i don't care) +#define PROJECTILE_TYPE_PHOTONIC (1<<22) +/// exotic energy or exotic matter +#define PROJECTILE_TYPE_EXOTIC (1<<23) + +//? special types + +/// trace projectile, aka "always let this through shields so stuff knows to fire at it" +#define PROJECTILE_TYPE_TRACE (1<<24) + +DEFINE_BITFIELD_NEW(projectile_types, list( + /obj/projectile = list( + "projectile_type", + ), + /obj/structure/prop/prism = list( + "projectile_type", + "projectile_type_cant", + ), +), list( + BITFIELD_NEW("Base - Kinetic", PROJECTILE_TYPE_KINETIC), + BITFIELD_NEW("Base - Energy", PROJECTILE_TYPE_ENERGY), + BITFIELD_NEW("Base - Beam", PROJECTILE_TYPE_BEAM), + BITFIELD_NEW("Flag - Photonic Energy", PROJECTILE_TYPE_PHOTONIC), + BITFIELD_NEW("Flag - Exotic Energy / Matter", PROJECTILE_TYPE_EXOTIC), +)) + +//* helpers *// + +/// tiles per second to pixels per decisecond +#define PROJECTILE_SPEED_FOR_TPS(tiles) (tiles * WORLD_ICON_SIZE) diff --git a/code/__DEFINES/turfs/turfs.dm b/code/__DEFINES/turfs/turfs.dm new file mode 100644 index 000000000000..5ec000ffce90 --- /dev/null +++ b/code/__DEFINES/turfs/turfs.dm @@ -0,0 +1,12 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/// Turf crowding limit. +/// +/// Modules that move a lot of objects should respect this, +/// as performance falls quadratically as turfs get more full. +#define TURF_CROWDING_SOFT_LIMIT 15 +/// Turf catastrophic crowding limit. +/// +/// Modules that move a lot of objects should flat out refuse to operate on more objects than this. +#define TURF_CROWDING_HARD_LIMIT 75 diff --git a/code/__HELPERS/game/combat/arc.dm b/code/__HELPERS/game/combat/arc.dm new file mode 100644 index 000000000000..e51718e2f66b --- /dev/null +++ b/code/__HELPERS/game/combat/arc.dm @@ -0,0 +1,58 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/** + * Checks if an attacking atom is within the defensive arc of a defending atom. + * + * * This does not support pixel movement. + * * A null source is always inside defensive arc. + * + * todo: verify behavior. + * + * Attacking entity can be: + * + * * /obj/projectile - treated as projectile + * * /datum/thrownthing - treated as thrown + * * anything else - treated as an /atom-ish source. + * + * @params + * * defending - the defending atom + * * attacking - the attacking entity + * * arc - the arc to check + * * round_up_arc - if the attacking atom is not a projectile or something with angle sim, should we round their defensive angle up or down? + * * use_dir - use this dir, not the defending atom's dir + * + * @return TRUE if they're within arc, FALSE otherwise + */ +/proc/check_defensive_arc_tile(atom/defending, attacking, arc, round_up_arc, use_dir = defending.dir) + // clockwise from north + var/our_angle = dir2angle(use_dir) + // clockwise from north + var/their_angle + if(istype(attacking, /obj/projectile)) + // projectile source + var/obj/projectile/proj = attacking + // projectile angle var is clockwise from north + // turn it around to get the angle from our PoV + their_angle = (proj.angle + 180) % 360 + else + // atom-ish source + var/atom/atom_source + if(istype(attacking, /datum/thrownthing)) + var/datum/thrownthing/thrown = attacking + atom_source = thrown.thrownthing + else if(isatom(attacking)) + atom_source = attacking + else + return TRUE + their_angle = dir2angle(get_dir(defending, atom_source)) + // if we're rounding up our arc, we boost our arc since it's an atom source to nearest 45 + if(round_up_arc) + arc = CEILING(arc, 45) + // normalize it to +- of our angle + their_angle -= our_angle + if(their_angle > 180) + their_angle -= 360 + return abs(their_angle) <= arc + +// todo: pixel movement variant for overmaps and others. diff --git a/code/__HELPERS/lists/string.dm b/code/__HELPERS/lists/string.dm index c6317712253e..f19028b98fad 100644 --- a/code/__HELPERS/lists/string.dm +++ b/code/__HELPERS/lists/string.dm @@ -1,8 +1,22 @@ /** * Returns a list in plain english as a string. + * + * @params + * * input - the list to interpolate into strings + * * nothing_text - the text to emit if the list is empty + * * and_text - the text to emit on the last element instead of a comma + * * comma_text - the glue between elements + * * final_comma_text - the glue between the last two elements; usually empty for use with 'and_text' + * * limit - limit the entries processed. if the limit was reached, the last two elements will not use the usual glue text. + * * limit_text - text to append at the end if we were limited. defaults to "..." */ -/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) - var/total = input.len +/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "", limit, limit_text = "...") + var/total = length(input) + var/limited = FALSE + if(!isnull(limit)) + if(total > limit) + total = limit + limited = TRUE if (!total) return "[nothing_text]" else if (total == 1) @@ -13,13 +27,13 @@ var/output = "" var/index = 1 while (index < total) - if (index == total - 1) + if ((index == (total - 1)) && !limited) comma_text = final_comma_text output += "[input[index]][comma_text]" index++ - return "[output][and_text][input[index]]" + return "[output][limited ? "" : and_text][input[index]][limited? limit_text : ""]" /** * Removes a string from a list. diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index f9f5a1e3a709..5851e9a63a31 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -2,7 +2,7 @@ return /obj/vehicle/get_mob() - return occupants + return SAFEPICK(occupants) /obj/vehicle_old/train/get_mob() return SAFEPICK(buckled_mobs) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 08ba49b5fd2e..7fcdc575e3a9 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -36,7 +36,7 @@ if(extra) var/temp_input = replace_characters(input, list("\n"=" ","\t"=" "))//one character is replaced by two - if(length_char(input) < (length_char(temp_input) - (6 * 2))) //12 is the number of linebreaks allowed per message + if(length_char(input) < (length_char(temp_input) - 24)) //24 is the number of linebreaks allowed per message input = replace_characters(temp_input,list(" "=" "))//replace again, this time the double spaces with single ones if(encode) diff --git a/code/__HELPERS/type2type/type2type.dm b/code/__HELPERS/type2type/type2type.dm index ca1a90903354..5b82e5f88c12 100644 --- a/code/__HELPERS/type2type/type2type.dm +++ b/code/__HELPERS/type2type/type2type.dm @@ -133,7 +133,9 @@ return 10 /** - * Converts an angle (degrees) into an ss13 direction. + * Gets the direction of an angle, in degrees, that is clockwise of north + * + * todo: verify math and do documentation * * @params * * degree - angle clockwise of north @@ -154,10 +156,12 @@ return SOUTHWEST if (degree < 315) return WEST - return NORTH|WEST + return NORTHWEST /** - * Returns the north-zero clockwise angle in degrees, given a direction. + * Gets the angle, in degrees, clockwise of north of a direction + * + * @return angle */ /proc/dir2angle(direction) switch (direction) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index cbfb0473ff91..20b933d326dc 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -997,7 +997,7 @@ return FALSE if(/obj/item/pickaxe/plasmacutter) return 3800 - if(/obj/item/melee/energy) + if(/obj/item/melee/transforming/energy) return 3500 else return FALSE diff --git a/code/__global_init.dm b/code/__global_init.dm index 97bb1f766de0..b9b52380fb0d 100644 --- a/code/__global_init.dm +++ b/code/__global_init.dm @@ -33,3 +33,15 @@ var/datum/world_debug_enabler/world_debug_enabler = new if (debug_server) LIBCALL(debug_server, "auxtools_init")() enable_debugging() + debug_loop() + +/datum/world_debug_enabler/proc/debug_loop() + set waitfor = FALSE + debug_loop_impl() + +/** + * the sole job of this is keep ticking so the debug server can still do stuff while no clients are conencted + */ +/datum/world_debug_enabler/proc/debug_loop_impl() + while(TRUE) + sleep(world.tick_lag) diff --git a/code/controllers/subsystem/ai_movement.dm b/code/controllers/subsystem/ai_movement.dm index d9540ab26a03..619744ad82fb 100644 --- a/code/controllers/subsystem/ai_movement.dm +++ b/code/controllers/subsystem/ai_movement.dm @@ -83,15 +83,14 @@ SUBSYSTEM_DEF(ai_movement) var/reschedule_delay = being_processed.move(++being_processed.movement_cycle) // check if we are still ticking; if not, we got ejected, so we abort as we don't need to eject or insert again if(buckets[bucket_offset] == being_processed) - // eject; we don't change being_processed.ticking_(next|previous) - if(being_processed.movement_bucket_next == being_processed) - buckets[bucket_offset] = null - else - buckets[bucket_offset] = being_processed.movement_bucket_next - being_processed.movement_bucket_next.movement_bucket_prev = being_processed.movement_bucket_prev - being_processed.movement_bucket_prev.movement_bucket_next = being_processed.movement_bucket_next - if(reschedule_delay) + // eject; we don't change being_processed.ticking_(next|previous) + if(being_processed.movement_bucket_next == being_processed) + buckets[bucket_offset] = null + else + buckets[bucket_offset] = being_processed.movement_bucket_next + being_processed.movement_bucket_next.movement_bucket_prev = being_processed.movement_bucket_prev + being_processed.movement_bucket_prev.movement_bucket_next = being_processed.movement_bucket_next // insert; we now set its ticking_(next|previous) // note that we don't do catchup var/inject_offset = ((now_index_raw + round(DS2TICKS(reschedule_delay))) % bucket_amount) + 1 diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 8609e1f99b1a..a7b59ec711b0 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -137,7 +137,7 @@ SUBSYSTEM_DEF(air) var/timer if(!resumed) if(LAZYLEN(currentrun) != 0) - stack_trace("Currentrun not empty before processing cycle when it should be. [english_list(currentrun)]") + stack_trace("Currentrun not empty before processing cycle when it should be. [english_list(currentrun, limit = 5)]") currentrun = list() if(current_step != null) stack_trace("current_step before processing cycle was [current_step] instead of null") @@ -152,7 +152,7 @@ SUBSYSTEM_DEF(air) // Okay, we're done! Woo! Got thru a whole SSair cycle! if(LAZYLEN(currentrun) != 0) - stack_trace("Currentrun not empty after processing cycle when it should be. [english_list(currentrun.Copy(1, min(currentrun.len, 5)))]") + stack_trace("Currentrun not empty after processing cycle when it should be. [english_list(currentrun, limit = 5)]") currentrun = null if(current_step != SSAIR_DONE) stack_trace("current_step after processing cycle was [current_step] instead of [SSAIR_DONE]") diff --git a/code/controllers/subsystem/processing/fastprocess.dm b/code/controllers/subsystem/processing/fastprocess.dm deleted file mode 100644 index 9622e0214692..000000000000 --- a/code/controllers/subsystem/processing/fastprocess.dm +++ /dev/null @@ -1,6 +0,0 @@ -//Fires five times every second. - -PROCESSING_SUBSYSTEM_DEF(fastprocess) - name = "Fast Processing" - wait = 2 - stat_tag = "FP" diff --git a/code/controllers/subsystem/processing/process_20fps.dm b/code/controllers/subsystem/processing/process_20fps.dm new file mode 100644 index 000000000000..3afd5b018d4d --- /dev/null +++ b/code/controllers/subsystem/processing/process_20fps.dm @@ -0,0 +1,7 @@ +/** + * Fires 20 times a second. Kind of on the nose, huh? + */ +PROCESSING_SUBSYSTEM_DEF(process_20fps) + name = "Processing - 20 fps" + wait = 0.5 + stat_tag = "P20" diff --git a/code/controllers/subsystem/processing/process_5fps.dm b/code/controllers/subsystem/processing/process_5fps.dm new file mode 100644 index 000000000000..05dc2d48d895 --- /dev/null +++ b/code/controllers/subsystem/processing/process_5fps.dm @@ -0,0 +1,7 @@ +/** + * Fires 5 times a second. Kind of on the nose, huh? + */ +PROCESSING_SUBSYSTEM_DEF(process_5fps) + name = "Processing - 5 FPS" + wait = 2 + stat_tag = "P5" diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm index 007def420e51..edab5fc5e61f 100644 --- a/code/controllers/subsystem/throwing.dm +++ b/code/controllers/subsystem/throwing.dm @@ -390,6 +390,9 @@ SUBSYSTEM_DEF(throwing) /** * get damage scaling - default handling + * + * @params + * * target - (optional) thing being hit */ /datum/thrownthing/proc/get_damage_multiplier(atom/target) if(!resist) diff --git a/code/__HELPERS/datastructs/armor.dm b/code/datums/armor/armor.dm similarity index 96% rename from code/__HELPERS/datastructs/armor.dm rename to code/datums/armor/armor.dm index 929eb064b56a..7465bc2fb1e4 100644 --- a/code/__HELPERS/datastructs/armor.dm +++ b/code/datums/armor/armor.dm @@ -184,6 +184,16 @@ else return 0 +/** + * The big, bad proc that deals with inbound shieldcalls. + */ +/datum/armor/proc/handle_shieldcall(list/shieldcall_args, fake_attack) + shieldcall_args[SHIELDCALL_ARG_DAMAGE] = resultant_damage( + shieldcall_args[SHIELDCALL_ARG_DAMAGE], + shieldcall_args[SHIELDCALL_ARG_DAMAGE_TIER], + shieldcall_args[SHIELDCALL_ARG_DAMAGE_FLAG], + ) + /datum/armor/proc/resultant_damage(damage, tier, flag) switch(flag) if(ARMOR_MELEE) diff --git a/code/datums/callback.dm b/code/datums/callback.dm index 721d050b5c39..2087fd6ef448 100644 --- a/code/datums/callback.dm +++ b/code/datums/callback.dm @@ -70,6 +70,9 @@ if(usr) user = WEAKREF(usr) +/datum/callback/proc/operator""() + return "callback [object] ([ref(object)])[isdatum(object) ? " ([object.type])" : ""] args: \[[english_list(arguments)]\]" + /** * Invoke this callback * @@ -103,6 +106,19 @@ return call(delegate)(arglist(calling_arguments)) return call(object, delegate)(arglist(calling_arguments)) +/** + * Invoke this callback and crash if it sleeps. + * + * * Use when a callback should never sleep, as call() cannot be verified by static analysis. + */ +/datum/callback/proc/invoke_no_sleep(...) + . = CALLBACK_SLEEP_SENTINEL + ASYNC + . = Invoke(arglist(args)) + if(. == CALLBACK_SLEEP_SENTINEL) + . = null + CRASH("Callback [src] slept on a no-sleeping invoke.") + /** * Invoke this callback async (waitfor=false) * diff --git a/code/datums/components/atoms/fishing_spot.dm b/code/datums/components/atoms/fishing_spot.dm index 77e800f7d878..3e43fd412dc4 100644 --- a/code/datums/components/atoms/fishing_spot.dm +++ b/code/datums/components/atoms/fishing_spot.dm @@ -1,12 +1,12 @@ // A thing you can fish in /datum/component/fishing_spot registered_type = /datum/component/fishing_spot - + /// Defines the probabilities and fish availibilty var/datum/fish_source/fish_source /datum/component/fishing_spot/Initialize(configuration) - if(!isatom(parent) || ((. = ..()) & COMPONENT_INCOMPATIBLE)) + if(!isatom(parent) || ((. = ..()) == COMPONENT_INCOMPATIBLE)) return COMPONENT_INCOMPATIBLE if(ispath(configuration, /datum/fish_source)) // Create new one of the given type diff --git a/code/datums/components/gps_signal.dm b/code/datums/components/gps_signal.dm index 5171dce10212..e54553a884b4 100644 --- a/code/datums/components/gps_signal.dm +++ b/code/datums/components/gps_signal.dm @@ -43,7 +43,7 @@ GLOBAL_LIST_EMPTY(gps_transmitters) var/registered = FALSE /datum/component/gps_signal/Initialize(gps_tag = "COM0", disabled = FALSE) - if(!isatom(parent) || ((. = ..()) & COMPONENT_INCOMPATIBLE)) + if(!isatom(parent) || ((. = ..()) == COMPONENT_INCOMPATIBLE)) return COMPONENT_INCOMPATIBLE src.gps_tag = gps_tag src.disabled = disabled diff --git a/code/datums/components/horror_aura.dm b/code/datums/components/horror_aura.dm index ea835d879743..45581a602425 100644 --- a/code/datums/components/horror_aura.dm +++ b/code/datums/components/horror_aura.dm @@ -11,7 +11,7 @@ It also serves the purposes of portraying the Lore accurate effect of "Acausal L /datum/component/horror_aura/Initialize(radius) if(radius) src.radius = radius - if(. & COMPONENT_INCOMPATIBLE) + if(. == COMPONENT_INCOMPATIBLE) return else if(!istype(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/items/active_parry.dm b/code/datums/components/items/active_parry.dm new file mode 100644 index 000000000000..1a5dc7dadb9e --- /dev/null +++ b/code/datums/components/items/active_parry.dm @@ -0,0 +1,10 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +/** + * generic parry provider on items + */ +/datum/component/active_parry + registered_type = /datum/component/active_parry + +// todo: default implementation via active defensive hotkey diff --git a/code/datums/components/items/passive_parry.dm b/code/datums/components/items/passive_parry.dm new file mode 100644 index 000000000000..678aa914dfca --- /dev/null +++ b/code/datums/components/items/passive_parry.dm @@ -0,0 +1,348 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +/** + * Shieldcall used as a listener for [/datum/component/passive_parry] + */ +/datum/shieldcall/bound/passive_parry + expected_type = /datum/component/passive_parry + +/** + * generic parry provider on items + * + * this is effectively the old rng block system + * + * also known as: autoparry. + */ +/datum/component/passive_parry + registered_type = /datum/component/passive_parry + + /// passive parry data + var/datum/passive_parry/parry_data + /// callback to invoke before the parry is initiated + /// + /// * must return /datum/parry_frame or null. + /// * if it returns a parry frame, it'll override the frame provided by the passive parry datum + /// * if it returns null, it'll cancel the parry + /// * invoked, if existing, with (obj/item/parent, mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + /// * this allows you to construct a custom parry frame + /// * this will be null'd, not qdel'd, when the component is qdel'd + var/datum/callback/parry_intercept + /// our registered shieldcall + var/datum/shieldcall/bound/passive_parry/hooked_shieldcall + /// the mob we're registered with right now + var/mob/hooked + +/datum/component/passive_parry/Initialize(datum/passive_parry/data, datum/callback/intercept) + . = ..() + if(. == COMPONENT_INCOMPATIBLE) + return + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + data = fetch_data(data) + if(!data) + stack_trace("invalid data") + return COMPONENT_INCOMPATIBLE + src.parry_data = data + src.parry_intercept = intercept + +/datum/component/passive_parry/Destroy() + parry_data = null // parry data holds no refs + parry_intercept = null // i'd hope this doesn't hold a ref to us. + return ..() + +/datum/component/passive_parry/proc/fetch_data(datum/passive_parry/datalike) + if(IS_ANONYMOUS_TYPEPATH(datalike)) + return new datalike + if(ispath(datalike)) + return resolve_passive_parry_data(datalike) + if(istype(datalike)) + return datalike + +/** + * About to start a parry. Resolve parry_frame datum. + */ +/datum/component/passive_parry/proc/ignite(atom/defending, attack_type, datum/weapon) + RETURN_TYPE(/datum/parry_frame) + if(parry_intercept) + return parry_intercept.invoke_no_sleep(parent, defending, attack_type, weapon, parry_data) + else + var/obj/item/item = parent + return item.passive_parry_intercept(defending, attack_type, weapon, parry_data) + +/datum/component/passive_parry/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equipped)) + RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_dropped)) + if(!hooked) + var/obj/item/item = parent + if(item.worn_mob()) + on_equipped(item, item.worn_mob(), item.worn_slot) + +/datum/component/passive_parry/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, COMSIG_ITEM_EQUIPPED) + if(hooked) + var/obj/item/item = parent + on_unequipped(item, hooked) + +/datum/component/passive_parry/proc/on_dropped(obj/item/source, inv_op_flags, atom/new_loc) + // delete on drop to save memory + qdel(src) + +/datum/component/passive_parry/proc/on_equipped(obj/item/source, mob/user, slot) + if(!check_slot(slot)) + return + if(hooked) + return + ASSERT(user) + hooked = user + hooked_shieldcall = new(src) + user.register_shieldcall(hooked_shieldcall) + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL_ITERATION, PROC_REF(shieldcall_iterating)) + +/datum/component/passive_parry/proc/on_unequipped(obj/item/source, mob/user) + ASSERT(user == hooked) + hooked = null + user.unregister_shieldcall(hooked_shieldcall) + QDEL_NULL(hooked_shieldcall) + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL_ITERATION, PROC_REF(shieldcall_iterating)) + +/datum/component/passive_parry/proc/check_slot(slot_id) + return islist(parry_data.parry_slot_id)? (slot_id in parry_data.parry_slot_id) : (!parry_data.parry_slot_id || (parry_data.parry_slot_id == slot_id)) + +/datum/component/passive_parry/proc/shieldcall_iterating(mob/source, shieldcall_type) + SIGNAL_HANDLER + ASSERT(source == hooked) + var/datum/passive_parry/data = fetch_data(parry_data) + // normal shieldcall handlers handle it + if(!data.parry_frame_simulated) + return + var/datum/parry_frame/resolved = ignite(source) + if(!resolved) + return + // for now, we only care about if they already have a frame + // in the future, maybe this can fire as long as we aren't the source of a parry frame on them. + // todo: cooldown enforcement + // todo: mobility enforcement + // todo: full parry swing cycle? + if(!source.GetComponent(/datum/component/parry_frame)) + source.AddComponent(/datum/component/parry_frame, resolved, data.parry_frame_timing) + +//* Bindings - Bullet *// + +/datum/shieldcall/bound/passive_parry/handle_bullet(atom/defending, shieldcall_returns, fake_attack, list/bullet_act_args) + // todo: no support for fake attacks yet + if(fake_attack) + return + // this is a definite 'do as i say, not as i do' moment + // this works because the proc names and args and types are **exactly** matching + // this is why the procs are all together + // do NOT try this at home. + return bound:handle_bullet(arglist(args)) + +/datum/component/passive_parry/proc/handle_bullet(atom/defending, shieldcall_returns, fake_attack, list/bullet_act_args) + var/datum/passive_parry/data = parry_data + if(data.parry_frame_simulated) + return + if(!prob(isnull(data.parry_chance_projectile) ? data.parry_chance_default : data.parry_chance_projectile)) + return + if(!check_defensive_arc_tile(defending, bullet_act_args[BULLET_ACT_ARG_PROJECTILE], data.parry_arc, !data.parry_arc_round_down)) + return + // - Projectile-specific - + var/obj/projectile/proj = bullet_act_args[BULLET_ACT_ARG_PROJECTILE] + if(!(proj.projectile_type & data.parry_projectile_types)) + return + // - End - + var/datum/parry_frame/resolved = ignite(defending, ATTACK_TYPE_PROJECTILE, bullet_act_args[BULLET_ACT_ARG_PROJECTILE]) + if(!resolved) + return + return resolved.handle_bullet(defending, shieldcall_returns | SHIELDCALL_FLAG_SINGLE_PARRY, fake_attack, data.parry_frame_efficiency, bullet_act_args, parent) + +//* Bindings - Melee *// + +/datum/shieldcall/bound/passive_parry/handle_item_melee(atom/defending, shieldcall_returns, fake_attack, obj/item/weapon, datum/event_args/actor/clickchain/e_args) + // todo: no support for fake attacks yet + if(fake_attack) + return + // this is a definite 'do as i say, not as i do' moment + // this works because the proc names and args and types are **exactly** matching + // this is why the procs are all together + // do NOT try this at home. + return bound:handle_item_melee(arglist(args)) + +/datum/component/passive_parry/proc/handle_item_melee(atom/defending, shieldcall_returns, fake_attack, obj/item/weapon, datum/event_args/actor/clickchain/e_args) + var/datum/passive_parry/data = parry_data + if(data.parry_frame_simulated) + return + if(!prob(isnull(data.parry_chance_melee) ? data.parry_chance_default : data.parry_chance_melee)) + return + if(!check_defensive_arc_tile(defending, e_args.performer, data.parry_arc, !data.parry_arc_round_down)) + return + var/datum/parry_frame/resolved = ignite(defending, ATTACK_TYPE_MELEE, weapon) + if(!resolved) + return + return resolved.handle_item_melee(defending, shieldcall_returns | SHIELDCALL_FLAG_SINGLE_PARRY, fake_attack, data.parry_frame_efficiency, weapon, e_args, parent) + +/datum/shieldcall/bound/passive_parry/handle_unarmed_melee(atom/defending, shieldcall_returns, fake_attack, datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args) + // todo: no support for fake attacks yet + if(fake_attack) + return + // this is a definite 'do as i say, not as i do' moment + // this works because the proc names and args and types are **exactly** matching + // this is why the procs are all together + // do NOT try this at home. + return bound:handle_unarmed_melee(arglist(args)) + +/datum/component/passive_parry/proc/handle_unarmed_melee(atom/defending, shieldcall_returns, fake_attack, datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args) + var/datum/passive_parry/data = parry_data + if(data.parry_frame_simulated) + return + if(!prob(isnull(data.parry_chance_melee) ? data.parry_chance_default : data.parry_chance_melee)) + return + if(!check_defensive_arc_tile(defending, e_args.performer, data.parry_arc, !data.parry_arc_round_down)) + return + var/datum/parry_frame/resolved = ignite(defending, ATTACK_TYPE_UNARMED, style) + if(!resolved) + return + return resolved.handle_unarmed_melee(defending, shieldcall_returns | SHIELDCALL_FLAG_SINGLE_PARRY, fake_attack, data.parry_frame_efficiency, style, e_args, parent) + +/datum/shieldcall/bound/passive_parry/handle_touch(atom/defending, shieldcall_returns, fake_attack, datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific) + // todo: no support for fake attacks yet + if(fake_attack) + return + // this is a definite 'do as i say, not as i do' moment + // this works because the proc names and args and types are **exactly** matching + // this is why the procs are all together + // do NOT try this at home. + return bound:handle_touch(arglist(args)) + +/datum/component/passive_parry/proc/handle_touch(atom/defending, shieldcall_returns, fake_attack, datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific) + var/datum/passive_parry/data = parry_data + if(data.parry_frame_simulated) + return + if(!prob(isnull(data.parry_chance_touch) ? data.parry_chance_default : data.parry_chance_touch)) + return + if(!check_defensive_arc_tile(defending, e_args.performer, data.parry_arc, !data.parry_arc_round_down)) + return + var/datum/parry_frame/resolved = ignite(defending, ATTACK_TYPE_TOUCH, null) + if(!resolved) + return + return resolved.handle_touch(defending, shieldcall_returns | SHIELDCALL_FLAG_SINGLE_PARRY, fake_attack, data.parry_frame_efficiency, e_args, contact_flags, contact_specific, parent) + +//* Bindings - Thrown *// + +/datum/shieldcall/bound/passive_parry/handle_throw_impact(atom/defending, shieldcall_returns, fake_attack, datum/thrownthing/thrown) + // todo: no support for fake attacks yet + if(fake_attack) + return + // this is a definite 'do as i say, not as i do' moment + // this works because the proc names and args and types are **exactly** matching + // this is why the procs are all together + // do NOT try this at home. + return bound:handle_throw_impact(arglist(args)) + +/datum/component/passive_parry/proc/handle_throw_impact(atom/defending, shieldcall_returns, fake_attack, datum/thrownthing/thrown) + var/datum/passive_parry/data = parry_data + if(data.parry_frame_simulated) + return + if(!prob(isnull(data.parry_chance_thrown) ? data.parry_chance_default : data.parry_chance_thrown)) + return + if(!check_defensive_arc_tile(defending, thrown, data.parry_arc, !data.parry_arc_round_down)) + return + var/datum/parry_frame/resolved = ignite(defending, ATTACK_TYPE_THROWN, thrown) + if(!resolved) + return + return resolved.handle_throw_impact(defending, shieldcall_returns | SHIELDCALL_FLAG_SINGLE_PARRY, fake_attack, data.parry_frame_efficiency, thrown, parent) + +//* Item *// + +/** + * Called by /datum/component/passive_parry when we're about to start up the parry frame + * Called if parry intercept callback isn't set. + * + * @params + * * defending - mob being defended + * * attack_type - (optional) attack type + * * weapon - (optional) the weapon + * * parry_data - (optional) the existing parry data + * + * @return parry frame datum to use, or null to cancel + */ +/obj/item/proc/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + return parry_data.parry_frame + +//* Data *// + +GLOBAL_LIST_EMPTY(passive_parry_data) +/** + * get a cached version of a passive paary datum + */ +/proc/resolve_passive_parry_data(datum/passive_parry/datalike) + if(isnull(datalike)) + return + if(IS_ANONYMOUS_TYPEPATH(datalike)) + return new datalike + if(istype(datalike)) + return datalike + if(!GLOB.passive_parry_data[datalike]) + GLOB.passive_parry_data[datalike] = new datalike + return GLOB.passive_parry_data[datalike] + +/** + * datum for holding data on passive parrying + */ +/datum/passive_parry + /// parry chance for harmful melee: [0, 100] + var/parry_chance_melee + /// parry chance for (seemingly) benign melee: [0, 100] + var/parry_chance_touch + /// parry chance for inbound projectile: [0, 100] + var/parry_chance_projectile + /// parry chance for inbound throw + var/parry_chance_thrown + /// default parry chance if one of the above is null + var/parry_chance_default = 0 + + /// passive parry arc + var/parry_arc = 180 + /// passive parry arc should round down for non-projectiles + /// + /// * at 136 (1 more than 135), having this TRUE means non-projectiles can hit them from behind as behind is 180. + /// * at 136 (1 more than 135), having this be FALSE means non-projectiles **cannot** hit them from behind as behind is 180. + var/parry_arc_round_down = TRUE + + /// valid slot ids; null for all, list for multiple, singular for single + var/parry_slot_id = SLOT_ID_HANDS + + /// projectile types we autoparry on + var/parry_projectile_types = ALL + + /// parry frame data to use by default + /// + /// * can be a typepath + /// * can be an anonymous typepath + /// * can be a datum + var/parry_frame = /datum/parry_frame/passive_block + /// simulate a full parry frame with carry-through and duration, or just run the frame once + var/parry_frame_simulated = FALSE + /// if not simulated, what's our efficiency? [0, 1] + /// + /// * only used if not simulated + var/parry_frame_efficiency = 1 + /// if simulated, how far in do we start? [0, infinity] + /// + /// * only used if simulated + var/parry_frame_timing = 0 + +/datum/passive_parry/New() + if(IS_ANONYMOUS_TYPEPATH(parry_frame)) + parry_frame = new parry_frame + else if(ispath(parry_frame)) + parry_frame = new parry_frame + else if(istype(parry_frame, /datum/parry_frame)) + else + CRASH("invalid parry frame") + +/datum/parry_frame/passive_block + parry_can_prevent_contact = TRUE diff --git a/code/datums/components/items/shield_block.dm b/code/datums/components/items/shield_block.dm new file mode 100644 index 000000000000..9e1adc211689 --- /dev/null +++ b/code/datums/components/items/shield_block.dm @@ -0,0 +1,11 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +/** + * generic shield-like block provider on items + */ +/datum/component/shield_block + registered_type = /datum/component/shield_block + +// todo: default implementation via toggled defensive hotkey +// todo: default 'passive' mode for when you have it held but haven't toggled defensive mode. diff --git a/code/datums/components/items/wielding.dm b/code/datums/components/items/wielding.dm index 1294b37e5b26..96fc1e4bfe77 100644 --- a/code/datums/components/items/wielding.dm +++ b/code/datums/components/items/wielding.dm @@ -1,7 +1,7 @@ // todo: can element this by usign 3 signals instead of 2, one to receive a keybind signal. /datum/component/wielding registered_type = /datum/component/wielding - + /// hands needed var/hands /// lazylist @@ -16,7 +16,7 @@ /datum/component/wielding/Initialize(hands = 2, datum/callback/on_wield, datum/callback/on_unwield) if(!isitem(parent)) return COMPONENT_INCOMPATIBLE - if((. = ..()) & COMPONENT_INCOMPATIBLE) + if((. = ..()) == COMPONENT_INCOMPATIBLE) return src.hands = hands src.on_wield = on_wield @@ -91,6 +91,9 @@ /datum/component/wielding/proc/offhand_destroyed(obj/item/offhand/wielding/I) unwield() + +//* Offhands *// + /obj/item/offhand/wielding name = "wielding offhand" desc = "You shouldn't be able to see this." @@ -103,7 +106,8 @@ host = null return ..() -// item procs +//* Item Hooks *// + /obj/item/proc/on_wield(mob/user, hands) return diff --git a/code/datums/components/mobs/block_frame.dm b/code/datums/components/mobs/block_frame.dm new file mode 100644 index 000000000000..bbbbedd27b3d --- /dev/null +++ b/code/datums/components/mobs/block_frame.dm @@ -0,0 +1,75 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +/** + * ## Active Defensives + * + * Datastructure for active block on mobs. + * + * * One, or more, may exist at a time; all of them will be shieldcall-registered, be very careful when doing this. + * * Items should generally not allow adding another packet while one is active + */ +/datum/component/block_frame + registered_type = /datum/component/block_frame + + /// active defensive data + var/datum/block_frame/active_block + /// current number of processed hits + var/hit_count = 0 + /// world.time of start + var/start_time + +/datum/shieldcall/bound/block_frame + expected_type = /datum/component/parry_frame + +// todo: default implementation of a hold-down blocking system. + +/** + * Datastructure for block data, now far more simplified. + * + * * Please avoid anonymous typing this where possible, this is a heavy datum and caching helps a lot. + * * The reason this is separate from parrying is because block system is far more focused on exact damage simulation, while parrying is focused on deflecting a hit and handling the effects of that. + * + * todo: this should be a serializable prototype + */ +/datum/block_frame + /// shield arc, in both CW/CCW from user facing direction + /// + /// * given RP doesn't have combat mode, you should really just keep this at 180 + /// * realistically the cutoffs are 45, 90, 135, and 180 for anything that's not a projectile as only those sim physics + var/block_arc = 180 + /// maximum block per attack instance + var/block_damage_max = INFINITY + /// damage block % above minimum + var/block_damage_ratio = 0 + /// damage block minimum + var/block_damage_min = 0 + /// if set, use this armor datum for processing how much damage to block + /// + /// * use this for tiered simulation + /// * [block_damage_max] is the only other variable used for calculations if this is set, all others are on armor already + /// * set to typepath or instance + var/datum/armor/block_via_armor + + /// attack types we are allowed to parry + var/block_attack_types = NONE + + /// if 100% of damage is blocked, do we set SHIELDCALL_BLOCKED and similar flags? + /// + /// * this means things like syringes would be blocked from injecting. + var/block_can_prevent_contact = FALSE + /// always add BLOCKED, even if not 100% mitigated / transmuted + var/block_always_prevents_contact = FALSE + + /// ratio [0, INFINITY] of inbound damage to convert to another type + var/block_transmute = 0 + /// damage type to transmute to + var/block_transmute_type = HALLOSS + /// damage flag the transmuted damage counts as; null = inherit from attack + /// + /// * only used if block_transmute_simulation is on + var/block_transmute_flag = null + /// the transmuted damage is directly applied with full melee sim, instead of just a damage instance + /// + /// * DO NOT TURN THIS ON WITHOUT GOOD REASON. Melee sim is several times more expensive than armor / low-level intercepts for damage instances. + var/block_transmute_simulation = FALSE diff --git a/code/datums/components/mobs/parry_frame.dm b/code/datums/components/mobs/parry_frame.dm new file mode 100644 index 000000000000..d3fd3c85a772 --- /dev/null +++ b/code/datums/components/mobs/parry_frame.dm @@ -0,0 +1,561 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) Citadel Station Developers *// + +/** + * ## Active Parry + * + * Datastructure for active parry on mobs. + * + * * One, or more, may exist at a time; all of them will be shieldcall-registered, be very careful when doing this. + * * Items should generally not allow adding another parry frame while one is active + */ +/datum/component/parry_frame + registered_type = /datum/component/parry_frame + + /// active defensive data + var/datum/parry_frame/active_parry + /// current number of processed hits + var/hit_count = 0 + /// world.time of start + var/start_time + /// world time of drop + var/drop_time + /// registered shieldcall + var/datum/shieldcall/bound/parry_frame/shieldcall + +/** + * * frame - the parry frame + * * kick_time_forwards - start this many deciseconds into the frame. + */ +/datum/component/parry_frame/Initialize(datum/parry_frame/frame, kick_time_forwards) + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + . = ..() + if(. == COMPONENT_INCOMPATIBLE) + return + src.active_parry = frame + src.start_time = world.time - kick_time_forwards + src.drop_time = src.start_time + max(frame.parry_timing_active, frame.parry_timing_perfect) + frame.parry_timing_stop + if(src.drop_time < world.time) + . = COMPONENT_INCOMPATIBLE + CRASH("attempted to start a parry that ended in the past.") + shieldcall = new(src) + shieldcall.tool_text = parent + var/delete_in = src.drop_time - world.time + QDEL_IN(src, delete_in) + // this is non-transferable, duh + new frame.parry_vfx(null, parent, frame) + +/datum/component/parry_frame/Destroy() + . = ..() + // shieldcall must be deleted after unregister + QDEL_NULL(shieldcall) + +/datum/component/parry_frame/RegisterWithParent() + var/atom/movable/AM = parent + AM.register_shieldcall(shieldcall) + +/datum/component/parry_frame/UnregisterFromParent() + var/atom/movable/AM = parent + AM.unregister_shieldcall(shieldcall) + +/datum/component/parry_frame/proc/on_parry(attack_type, datum/weapon, shieldcall_returns, efficiency) + ++hit_count + // check drop + if(hit_count > active_parry.parry_drop_after_hits) + qdel(src) + return + +//* -- Shieldcall -- *// + +/** + * Shieldcall used as a listener for [/datum/component/parry_frame] + */ +/datum/shieldcall/bound/parry_frame + expected_type = /datum/component/parry_frame + + /// text to describe the used tool, if any + var/tool_text + +//* -- Parry Frame -- *// + +/** + * Datastructure for parry data, now far more simplified. + * + * * Please avoid anonymous typing this where possible, this is a heavy datum and caching helps a lot. + * * While this is very close to /datum/block_frame, it is different in separate major ways. + * * Parrying tends to be more powerful and complex, as it's meant to simulate a very dynamic action. + * * Parrying is more expensive to deal with than blocking. + * + * todo: this should be a serializable prototype + * todo: estimated_severity for audiovisuals needs a revisit; sound is not linear. + */ +/datum/parry_frame + //* Arc *// + /// shield arc, in both CW/CCW from user facing direction + /// + /// * given RP doesn't have combat mode, you should really just keep this at 180 + /// * realistically the cutoffs are 45, 90, 135, and 180 for anything that's not a projectile as only those sim physics + var/parry_arc = 180 + /// should we round parry arc down for non-projectiles? + /// + /// * this means 179 can't cover behind us + /// * this is needed because non-projectiles don't have exact angles + var/parry_arc_round_down = TRUE + + //* Timing *// + /// spinup time + /// + /// * keep this at 0 in most cases + /// * the parry does nothing while it's spinning up + /// * parries landing on the tick this ends count as having spun up + /// * overrules both perfect and active timing + var/parry_timing_start = 0 SECONDS + /// perfect time + /// + /// * this is the amount of time we are a perfect parry after tick 0 + /// * this means that this overlaps with [parry_timing_start]! + /// * this is done for performance reasons. + /// * parries landing on the tick this ends still count as perfect + /// * overrules active timing + /// * overruled by start timing + var/parry_timing_perfect = 0 SECONDS + /// no-falloff time + /// + /// * this is the amount of time we are at full [parry_efficiency_active] after tick 0 + /// * this means that this overlaps with both [parry_timing_start] and [parry_timing_perfect]. + /// * this is done for performance reasons. + /// * parries landing on the tick this ends still count as fully active + /// * overruled by start and perfect timing + var/parry_timing_active = 0 SECONDS + /// end time + /// + /// * this is the amount of time we are still active after [parry_timing_active] or [parry_timing_perfect], whatever is longer + /// * efficiency linearly drops from active efficiency to 0 during this time + /// * the parry immediately drops after + /// * parries landing on the tick this ends are dropped + var/parry_timing_stop = 0 SECONDS + + //* Attack Types *// + /// attack types we are allowed to parry + var/parry_attack_types = NONE + + //* Efficiency *// + /// parry efficiency at perfect; [0, 1] + /// + /// * parry efficiency is ratio of damage to block + var/parry_efficiency_perfect = 1 + /// parry efficiency at active; [0, 1] + /// + /// * parry efficiency is ratio of damage to block + var/parry_efficiency_active = 1 + /// minimum efficiency to drop to + var/parry_efficiency_floor = 0 + /// parry efficiency at which we count as a full block + var/parry_efficiency_blocked = 1 + /// parry efficiency at which redirection occurs + var/parry_efficiency_redirection = 1 + + //* Defender Effects *// + /// action-lock the defender while parrying + // todo: implement + var/parry_lock_defender = TRUE + /// drop action-lock on defender when a parry succeeds + // todo: implement + var/parry_free_defender_on_success = TRUE + + //* Configuration *// + /// immediately drop the parry after this many hits + var/parry_drop_after_hits = 1 + + //* Counter Effects *// + /// counterattack on hit + /// + /// * keep this off, this is a good exercise in 'just because you can doesn't mean you should' + // todo: implement + var/parry_counter_attack = FALSE + /// status effects to apply on hit to attacker + /// + /// supports: + /// * /datum/status_effect status effects; associate to duration + /// + /// does not support: + /// * any status effect supertype that isn't listed above. right now, that's grouped and stacking. + var/list/parry_counter_effects + + //* Counter Effects - Projectiles / Vector *// + /// default handling: reflect attack types + /// + /// * yeah you probably shouldn't put anything other than ATTACK_TYPE_PROJECTILE in here. + var/parry_redirect_attack_types = NONE + /// default handling: reflect attack back at attacker + /// + /// * yeah you probably should leave this off + var/parry_redirect_return_to_sender = FALSE + /// redirection arc CW/CCW of angle of incidence + /// + /// * if return_to_sender is off, this is the valid arc from attack source it can be reflected to + /// * if return_to_sender is on, this is the arc in error from attack source we can reflect to + var/parry_redirect_arc = 45 + + //* Defense - Damage *// + /// if 100% of damage is blocked, do we set SHIELDCALL_BLOCKED and similar flags? + /// + /// * this means things like syringes would be blocked from injecting. + var/parry_can_prevent_contact = FALSE + /// always add BLOCKED, even if not 100% mitigated / transmuted + var/parry_always_prevents_contact = FALSE + /// maximum damage blocked per attack instance + // todo: implement + var/parry_damage_max = INFINITY + + //* Defense - Transmute *// + // todo: implement + /// ratio [0, INFINITY] of **blocked** damage to convert to another type + var/parry_transmute = 0 + /// damage type to transmute to; null to default to attacking damage type + var/parry_transmute_type = null + /// damage tier used for transmuted damage; null to default to attacking tier + var/parry_transmute_tier = null + /// damage mode used for transmuted damage; null to default to attacking mode + var/parry_transmute_mode = null + /// damage flag the transmuted damage counts as; null = inherit from attack + var/parry_transmute_flag = null + /// the transmuted damage should be simulated as close to a proper melee hit as possible, + /// instead of just going through run_damage_instance() + /// + /// * DO NOT TURN THIS ON WITHOUT GOOD REASON. Melee sim is several times more expensive than armor / low-level intercepts for damage instances. + /// * Currently does nothing, as we do not have a way to simulate a standard melee hit arbitrarily without side effects. + var/parry_transmute_simulation = FALSE + + //* Defender Cooldown *// + /// hard cooldown to apply to parrying with the thing parrying wth + // todo: implement + var/parry_cooldown_tool = 2 SECONDS + /// hard cooldown to apply to parrying at all as the mob + // todo: implement + var/parry_cooldown_user = 0 SECONDS + /// is the parry cooldown ignored if a successful parry was made + // todo: implement + var/parry_cooldown_on_success = FALSE + + //* Audiovisuals & Feedback *// + /// a sound, or a list of sounds that can be played when we're hit + /// list can be weighted by associated number for relative chance + /// + /// * sound can be a file + /// * sound can be a /datum/soundbyte typepath or instance + var/list/parry_sfx = /datum/soundbyte/grouped/metal_parry + /// a typepath of /atom/movable/parry_frame to use as our visual; this is placed in the defending atom's vis_contents + var/parry_vfx = /atom/movable/render/parry_frame/default + /// "[person] [start_verb] with [item]" + // todo: implement + var/start_verb = "shifts into a defensive stance" + /// "[person] [block_verb] [attack_source_descriptor] with [item]" + var/block_verb = "parries" + /// "[person] [deflect_verb] [attack_source_descriptor] with [item]" + /// + /// * used if an attack was redirected and not just blocked + var/deflect_verb = "deflects" + +/** + * * 0 if in spinup + * * perfect efficiency if in perfect (from 0) + * * active efficiency if in active (from 0) + * * linear falloff to 0 if in spindown (from active time end) + */ +/datum/parry_frame/proc/calculate_parry_efficiency(time_into_parry) + if(time_into_parry < parry_timing_start) + return 0 + if(time_into_parry <= parry_timing_perfect) + return parry_timing_perfect + if(time_into_parry <= parry_timing_active) + return parry_timing_active + var/drop_time = parry_timing_active + parry_timing_stop + if(time_into_parry >= drop_time) + return 0 + var/time_into_drop = time_into_parry - parry_timing_active + return parry_efficiency_active - (parry_efficiency_active - parry_efficiency_floor) * ((time_into_drop) / parry_timing_stop) + +/** + * @params + * * time_into_parry - ds elapsed since parry start + * + * @return TRUE / FALSE + */ +/datum/parry_frame/proc/is_parry_perfect(time_into_parry) + return (time_into_parry >= parry_timing_start) && (time_into_parry <= parry_timing_perfect) + +/** + * Called when parrying something + * + * @params + * * defending - thing being defended against an attack + * * attack_type - (optional) type of attack + * * efficiency - (optional) parry efficiency + * * weapon - (optional) incoming weapon, depends on ATTACK_TYPE + * * shieldcall_flags - (optional) the attack's shieldcall flags + * * severity - (optional) arbitrary 0 to 100 severity of how bad the hit is estimated to be + * * attack_source_descriptor - (optional) text, or an entity to describe the attack. entities will be automatically handled. + * * tool_text - (optional) text to describe the parry tool + */ +/datum/parry_frame/proc/perform_audiovisuals(atom/defending, attack_type, efficiency, datum/weapon, shieldcall_flags, severity = 75, attack_source_descriptor, tool_text) + playsound(defending, (islist(parry_sfx) && length(parry_sfx)) ? pick(parry_sfx) : parry_sfx , severity, TRUE) + new parry_vfx(null, defending, src, shieldcall_flags & SHIELDCALL_FLAG_SINGLE_PARRY) + + var/parry_verb + if((attack_type & parry_redirect_attack_types) && (efficiency >= parry_efficiency_redirection)) + parry_verb = deflect_verb + else + parry_verb = block_verb + + var/attack_descriptor = "the attack" + if(attack_source_descriptor) + // generic processing + attack_descriptor = "\the [attack_source_descriptor]" + // item processing + if(isitem(attack_source_descriptor)) + var/obj/item/item_source_descriptor = attack_source_descriptor + var/mob/mob_holding_item = item_source_descriptor.worn_mob() + if(mob_holding_item) + attack_descriptor = "[mob_holding_item]'s [item_source_descriptor]" + + defending.visible_message( + SPAN_DANGER("[defending] [parry_verb] [attack_descriptor][tool_text && " with \the [tool_text]"]!"), + ) + +/** + * Called when something is parried + * + * @params + * * defending - thing being defended against an attack + * * attack_type - (optional) type of attack + * * efficiency - (optional) parry efficiency + * * weapon - (optional) incoming weapon, depends on ATTACK_TYPE + * * shieldcall_flags - (optional) the attack's shieldcall flags + * * e_args - (optional) for melee, the event args of the attack + * + * @return SHIELDCALL_* flags; these override the caller's! + */ +/datum/parry_frame/proc/perform_aftereffects(atom/defending, attack_type, efficiency, datum/weapon, shieldcall_flags, datum/event_args/actor/clickchain/e_args) + . = shieldcall_flags + + var/atom/movable/aggressor + + // detect aggressor + if(istype(weapon, /obj/projectile)) + var/obj/projectile/weapon_proj = weapon + aggressor = weapon_proj.firer + else if(e_args) + aggressor = e_args.performer + else if(istype(weapon, /datum/thrownthing)) + var/datum/thrownthing/weapon_thrown = weapon + aggressor = weapon_thrown.thrower + + switch(attack_type) + if(ATTACK_TYPE_PROJECTILE) + var/obj/projectile/proj = weapon + if((parry_redirect_attack_types & ATTACK_TYPE_PROJECTILE) && (efficiency >= parry_efficiency_redirection)) + var/outgoing_angle + if(parry_redirect_return_to_sender && aggressor) + outgoing_angle = arctan(aggressor.y - defending.y, aggressor.x - defending.x) + else + // todo: this should be angle of incidence maybe? + outgoing_angle = turn(proj.angle, 180) + rand(-parry_redirect_arc, parry_redirect_arc) + proj.set_angle(outgoing_angle) + . |= SHIELDCALL_FLAG_ATTACK_REDIRECT + + // effects that are only valid if we have a retaliation target + if(aggressor) + if(ismob(aggressor)) + var/mob/aggressor_mob = aggressor + for(var/key in parry_counter_effects) + var/value = parry_counter_effects[key] + if(ispath(key, /datum/status_effect)) + aggressor_mob.apply_status_effect(key, value) + + if(parry_counter_attack) + // RIP AND TEAR + // todo: counterattacks are offline due to clickchain not being entirely nailed down + // we need clickchain flags to be actually checked in active block/parry so that REFLEX_COUNTER flagged clicks don't get re-parried. + pass() + +/** + * Called to transmute an instance of damage into another instance of damage and apply it to the defender. + */ +/datum/parry_frame/proc/perform_transmuted_damage(atom/defending, damage, damage_tier, damage_type, damage_mode, damage_flag, hit_zone, shieldcall_flags) + // todo: parry_transmute_simulation + defending.run_damage_instance( + parry_transmute * damage, + isnull(parry_transmute_type) ? damage_type : parry_transmute_type, + isnull(parry_transmute_tier) ? damage_tier : parry_transmute_tier, + isnull(parry_transmute_flag) ? damage_flag : parry_transmute_flag, + isnull(parry_transmute_mode) ? damage_mode : parry_transmute_mode, + ATTACK_TYPE_DEFENSIVE_PASSTHROUGH, + null, + SHIELDCALL_FLAG_SECOND_CALL, + hit_zone, + ) + +//* Bindings - Bullet *// + +/datum/shieldcall/bound/parry_frame/handle_bullet(atom/defending, shieldcall_returns, fake_attack, list/bullet_act_args) + var/datum/component/parry_frame/frame = bound + if(!(frame.active_parry.parry_attack_types & ATTACK_TYPE_PROJECTILE)) + return + if(!check_defensive_arc_tile(defending, bullet_act_args[BULLET_ACT_ARG_PROJECTILE], frame.active_parry.parry_arc, !frame.active_parry.parry_arc_round_down)) + return + var/efficiency = frame.active_parry.calculate_parry_efficiency(frame.start_time - world.time) + . = frame.active_parry.handle_bullet(defending, shieldcall_returns, fake_attack, efficiency, bullet_act_args, tool_text) + frame.on_parry(ATTACK_TYPE_PROJECTILE, bullet_act_args[BULLET_ACT_ARG_PROJECTILE], ., efficiency) + +/datum/parry_frame/proc/handle_bullet(atom/defending, shieldcall_returns, fake_attack, efficiency, list/bullet_act_args, tool_text) + . = shieldcall_returns + // todo: doesn't take into account any damage randomization + var/obj/projectile/proj = bullet_act_args[BULLET_ACT_ARG_PROJECTILE] + var/estimated_severity = clamp(proj.damage / 20 * 75, 0, 100) + bullet_act_args[BULLET_ACT_ARG_EFFICIENCY] = bullet_act_args[BULLET_ACT_ARG_EFFICIENCY] * clamp(1 - efficiency, 0, 1) + . = perform_aftereffects(defending, ATTACK_TYPE_PROJECTILE, efficiency, proj, .) + perform_audiovisuals(defending, ATTACK_TYPE_PROJECTILE, efficiency, proj, ., estimated_severity, proj, tool_text) + if(. & (SHIELDCALL_FLAG_ATTACK_PASSTHROUGH | SHIELDCALL_FLAG_ATTACK_REDIRECT)) + bullet_act_args[BULLET_ACT_ARG_FLAGS] |= PROJECTILE_IMPACT_REFLECT + if(parry_always_prevents_contact || (parry_can_prevent_contact && (efficiency >= parry_efficiency_blocked))) + bullet_act_args[BULLET_ACT_ARG_FLAGS] |= PROJECTILE_IMPACT_BLOCKED + +//* Bindings - Melee *// + +/datum/shieldcall/bound/parry_frame/handle_item_melee(atom/defending, shieldcall_returns, fake_attack, obj/item/weapon, datum/event_args/actor/clickchain/e_args) + var/datum/component/parry_frame/frame = bound + if(!(frame.active_parry.parry_attack_types & ATTACK_TYPE_MELEE)) + return + if(e_args && !check_defensive_arc_tile(defending, e_args.performer, frame.active_parry.parry_arc, !frame.active_parry.parry_arc_round_down)) + return + var/efficiency = frame.active_parry.calculate_parry_efficiency(frame.start_time - world.time) + . = frame.active_parry.handle_item_melee(defending, shieldcall_returns, fake_attack, efficiency, weapon, e_args, tool_text) + frame.on_parry(ATTACK_TYPE_MELEE, weapon, ., efficiency) + +/datum/parry_frame/proc/handle_item_melee(atom/defending, shieldcall_returns, fake_attack, efficiency, obj/item/weapon, datum/event_args/actor/clickchain/e_args, tool_text) + . = shieldcall_returns + // todo: doesn't take into account any damage randomization + var/estimated_severity = clamp(weapon.damage_force * e_args.damage_multiplier / 20 * 75, 0, 100) + e_args.damage_multiplier *= clamp(1 - efficiency, 0, 1) + . = perform_aftereffects(defending, ATTACK_TYPE_MELEE, efficiency, weapon, ., e_args) + perform_audiovisuals(defending, ATTACK_TYPE_MELEE, efficiency, weapon, ., estimated_severity, weapon, tool_text) + if(parry_always_prevents_contact || (parry_can_prevent_contact && (efficiency >= parry_efficiency_blocked))) + . |= SHIELDCALL_FLAG_ATTACK_BLOCKED + +/datum/shieldcall/bound/parry_frame/handle_unarmed_melee(atom/defending, shieldcall_returns, fake_attack, datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args) + var/datum/component/parry_frame/frame = bound + if(!(frame.active_parry.parry_attack_types & ATTACK_TYPE_UNARMED)) + return + if(e_args && !check_defensive_arc_tile(defending, e_args.performer, frame.active_parry.parry_arc, !frame.active_parry.parry_arc_round_down)) + return + var/efficiency = frame.active_parry.calculate_parry_efficiency(frame.start_time - world.time) + . = frame.active_parry.handle_unarmed_melee(defending, shieldcall_returns, fake_attack, efficiency, style, e_args, tool_text) + frame.on_parry(ATTACK_TYPE_UNARMED, style, ., efficiency) + +/datum/parry_frame/proc/handle_unarmed_melee(atom/defending, shieldcall_returns, fake_attack, efficiency, datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args, tool_text) + . = shieldcall_returns + // todo: doesn't take into account any damage randomization + var/estimated_severity = clamp(style.damage * e_args.damage_multiplier / 20 * 75, 0, 100) + e_args.damage_multiplier *= clamp(1 - efficiency, 0, 1) + . = perform_aftereffects(defending, ATTACK_TYPE_UNARMED, efficiency, style, ., e_args) + perform_audiovisuals(defending, ATTACK_TYPE_UNARMED, efficiency, style, ., estimated_severity, style, tool_text) + if(parry_always_prevents_contact || (parry_can_prevent_contact && (efficiency >= parry_efficiency_blocked))) + . |= SHIELDCALL_FLAG_ATTACK_BLOCKED + +/datum/shieldcall/bound/parry_frame/handle_touch(atom/defending, shieldcall_returns, fake_attack, datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific) + var/datum/component/parry_frame/frame = bound + if(!(frame.active_parry.parry_attack_types & ATTACK_TYPE_TOUCH)) + return + if(e_args && !check_defensive_arc_tile(defending, e_args.performer, frame.active_parry.parry_arc, !frame.active_parry.parry_arc_round_down)) + return + var/efficiency = frame.active_parry.calculate_parry_efficiency(frame.start_time - world.time) + . = frame.active_parry.handle_touch(defending, shieldcall_returns, fake_attack, efficiency, e_args, contact_flags, contact_specific, tool_text) + frame.on_parry(ATTACK_TYPE_TOUCH, null, ., efficiency) + +/datum/parry_frame/proc/handle_touch(atom/defending, shieldcall_returns, fake_attack, efficiency, datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific, tool_text) + . = shieldcall_returns + // todo: doesn't take into account any damage randomization + var/estimated_severity = 50 + e_args.damage_multiplier *= clamp(1 - efficiency, 0, 1) + . = perform_aftereffects(defending, ATTACK_TYPE_TOUCH, efficiency, null, ., e_args) + perform_audiovisuals(defending, ATTACK_TYPE_TOUCH, efficiency, null, ., estimated_severity, e_args.performer, tool_text) + if(parry_always_prevents_contact || (parry_can_prevent_contact && (efficiency >= parry_efficiency_blocked))) + . |= SHIELDCALL_FLAG_ATTACK_BLOCKED + +//* Bindings - Thrown *// + +/datum/shieldcall/bound/parry_frame/handle_throw_impact(atom/defending, shieldcall_returns, fake_attack, datum/thrownthing/thrown) + var/datum/component/parry_frame/frame = bound + if(!(frame.active_parry.parry_attack_types & ATTACK_TYPE_THROWN)) + return + if(!check_defensive_arc_tile(defending, thrown, frame.active_parry.parry_arc, !frame.active_parry.parry_arc_round_down)) + return + var/efficiency = frame.active_parry.calculate_parry_efficiency(frame.start_time - world.time) + . = frame.active_parry.handle_throw_impact(defending, shieldcall_returns, fake_attack, efficiency, thrown, tool_text) + frame.on_parry(ATTACK_TYPE_THROWN, thrown, ., efficiency) + +/datum/parry_frame/proc/handle_throw_impact(atom/defending, shieldcall_returns, fake_attack, efficiency, datum/thrownthing/thrown, tool_text) + . = shieldcall_returns + // todo: doesn't take into account any damage randomization + // todo: why isn't thrownthing just with a get_damage() or a better inflict_damage() and get_damage_tuple() idfk man + var/estimated_severity = clamp(thrown.thrownthing.throw_force * thrown.get_damage_multiplier() / 20 * 75, 0, 100) + thrown.damage_multiplier *= clamp(1 - efficiency, 0, 1) + . = perform_aftereffects(defending, ATTACK_TYPE_THROWN, efficiency, thrown, ., thrown.thrownthing, tool_text) + perform_audiovisuals(defending, ATTACK_TYPE_THROWN, efficiency, thrown, ., estimated_severity) + if(parry_always_prevents_contact || (parry_can_prevent_contact && (efficiency >= parry_efficiency_blocked))) + . |= SHIELDCALL_FLAG_ATTACK_BLOCKED + +//* -- VFX Render -- *// + +INITIALIZE_IMMEDIATE(/atom/movable/render/parry_frame) +/** + * A visualizer for a parry frame. + */ +/atom/movable/render/parry_frame + var/atom/movable/bound + /// set this in your custom procs for cycle and single/spinup/spindown + var/qdel_time = 1 SECONDS + +/atom/movable/render/parry_frame/Initialize(mapload, atom/movable/bind_to, datum/parry_frame/frame, single_deflect) + SHOULD_CALL_PARENT(FALSE) + if(!istype(frame)) + . = INITIALIZE_HINT_QDEL + CRASH("no valid frame, this is bad") + src.bound = bind_to + bind_to.vis_contents += src + cycle(frame, single_deflect) + QDEL_IN(src, qdel_time) + return INITIALIZE_HINT_NORMAL + +/atom/movable/render/parry_frame/Destroy() + bound.vis_contents -= src + bound = null + return ..() + +/atom/movable/render/parry_frame/proc/cycle(datum/parry_frame/frame, single_deflect) + if(single_deflect) + single() + return + spinup(frame.parry_timing_start) + addtimer(CALLBACK(src, PROC_REF(spindown), frame.parry_timing_stop), max(frame.parry_timing_active, frame.parry_timing_perfect)) + +/atom/movable/render/parry_frame/proc/single() + return + +/atom/movable/render/parry_frame/proc/spinup(start_time) + return + +/atom/movable/render/parry_frame/proc/spindown(stop_time) + return + +//* -- VFX Default -- *// + +/atom/movable/render/parry_frame/default + icon = 'icons/effects/defensive/main_parry.dmi' + icon_state = "hold" + +/atom/movable/render/parry_frame/default/single() + animate(src, time = 0.3 SECONDS, alpha = 0) + qdel_time = 0.3 SECONDS diff --git a/code/datums/components/movable/spatial_grid.dm b/code/datums/components/movable/spatial_grid.dm index a549c642d1ae..ca650de950ce 100644 --- a/code/datums/components/movable/spatial_grid.dm +++ b/code/datums/components/movable/spatial_grid.dm @@ -16,7 +16,7 @@ /datum/component/spatial_grid/Initialize(datum/spatial_grid/grid) . = ..() - if(. & COMPONENT_INCOMPATIBLE) + if(. == COMPONENT_INCOMPATIBLE) return if(!ismovable(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/riding/riding_filter.dm b/code/datums/components/riding/riding_filter.dm index 464e7c9f7fab..8ab7fcbf0116 100644 --- a/code/datums/components/riding/riding_filter.dm +++ b/code/datums/components/riding/riding_filter.dm @@ -43,7 +43,7 @@ /datum/component/riding_filter/Initialize(handler_typepath) . = ..() - if(. & COMPONENT_INCOMPATIBLE) + if(. == COMPONENT_INCOMPATIBLE) return if(!istype(parent, expected_typepath)) return COMPONENT_INCOMPATIBLE @@ -80,11 +80,11 @@ /datum/component/riding_filter/proc/signal_hook_user_buckle(atom/movable/source, mob/M, flags, mob/user, semantic) SIGNAL_HANDLER_DOES_SLEEP - return check_user_mount(M, flags, user, semantic)? COMPONENT_FORCE_BUCKLE_OPERATION : COMPONENT_BLOCK_BUCKLE_OPERATION + return check_user_mount(M, flags, user, semantic)? SIGNAL_RAISE_FORCE_BUCKLE_OPERATION : SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION /datum/component/riding_filter/proc/signal_hook_pre_buckle(atom/movable/source, mob/M, flags, mob/user, semantic) SIGNAL_HANDLER - return on_mount_attempt(M, flags, user, semantic)? COMPONENT_FORCE_BUCKLE_OPERATION : COMPONENT_BLOCK_BUCKLE_OPERATION + return on_mount_attempt(M, flags, user, semantic)? SIGNAL_RAISE_FORCE_BUCKLE_OPERATION : SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION /datum/component/riding_filter/proc/signal_hook_post_buckle(atom/movable/source, mob/M, flags, mob/user, semantic) SIGNAL_HANDLER @@ -106,7 +106,7 @@ */ /datum/component/riding_filter/proc/signal_hook_can_buckle(atom/movable/source, mob/M, flags, mob/user, semantic) SIGNAL_HANDLER - return check_mount_attempt(M, flags, user, semantic)? COMPONENT_FORCE_BUCKLE_OPERATION : COMPONENT_BLOCK_BUCKLE_OPERATION + return check_mount_attempt(M, flags, user, semantic)? SIGNAL_RAISE_FORCE_BUCKLE_OPERATION : SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION /** * called on buckling process right before point of no return diff --git a/code/datums/components/riding/riding_handler.dm b/code/datums/components/riding/riding_handler.dm index 10230638e7fd..0b260810dbbe 100644 --- a/code/datums/components/riding/riding_handler.dm +++ b/code/datums/components/riding/riding_handler.dm @@ -77,7 +77,7 @@ /datum/component/riding_handler/Initialize() . = ..() - if(. & COMPONENT_INCOMPATIBLE) + if(. == COMPONENT_INCOMPATIBLE) return if(!istype(parent, expected_typepath)) return COMPONENT_INCOMPATIBLE @@ -127,7 +127,7 @@ /datum/component/riding_handler/proc/signal_hook_pre_buckle_mob(atom/movable/source, mob/M, flags, mob/user, semantic) SIGNAL_HANDLER_DOES_SLEEP if(!check_rider(M, semantic, TRUE, user = user)) - return COMPONENT_BLOCK_BUCKLE_OPERATION + return SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION /datum/component/riding_handler/proc/signal_hook_pixel_offset_changed(atom/movable/source) full_update_riders(null, TRUE) diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 6909f279a409..c93c2373e7b9 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -11,7 +11,12 @@ datum_flags |= DF_VAR_EDITED return TRUE -/datum/proc/vv_get_var(var_name) +/** + * @params + * * var_name - name of the variable + * * resolve - automatically resolve the variable if it's lazy loaded? + */ +/datum/proc/vv_get_var(var_name, resolve) switch(var_name) if ("vars") return debug_variable(var_name, list(), 0, src) diff --git a/code/datums/elements/conflict_checking.dm b/code/datums/elements/conflict_checking.dm index f298d184f017..8c8e35db6982 100644 --- a/code/datums/elements/conflict_checking.dm +++ b/code/datums/elements/conflict_checking.dm @@ -1,5 +1,7 @@ /** * Simple conflict checking for getting number of conflicting things on someone with the same ID. + * + * todo: this is a bit slow innit. */ /datum/element/conflict_checking element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH diff --git a/code/datums/event_args/clickchain.dm b/code/datums/event_args/clickchain.dm index 50858fa0553c..32ec7f0bdae5 100644 --- a/code/datums/event_args/clickchain.dm +++ b/code/datums/event_args/clickchain.dm @@ -2,6 +2,9 @@ * used to hold data about a click (melee/ranged/other) action * * the click may be real or fake. + * + * * clickchain flags are deliberately not stored in here; you're supposed to modify and return them a ton, so it's inefficient to put it in here. + * * This is required for item swings / interaction, usually, not just base /event_args/actor. */ /datum/event_args/actor/clickchain /// optional: attack intent @@ -11,6 +14,15 @@ /// optional: target atom var/atom/target + //* Attack Data *// + + /// Overall damage multiplier + /// + /// todo: implement; needs slight clickchain/melee overhaul + /// + /// * Allowed to be changed by shieldcalls and other intercepts + var/damage_multiplier = 1 + /datum/event_args/actor/clickchain/New(mob/performer, mob/initiator, atom/target, list/params, intent) ..() src.target = target diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index 1f08f52c6c2c..d7e04d7a14cf 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -4,11 +4,10 @@ // Mutable appearances are children of images, just so you know. -/mutable_appearance/New(copy_from, ...) - ..() - if(!copy_from) - plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE - // And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var +/** + * * Mutable appearances do **not** have FLOAT_PLANE by default. Set it manually if you aren't using [mutable_appearance()] + */ +/mutable_appearance // Helper similar to image() /proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE) diff --git a/code/datums/outfits/ghostrole.dm b/code/datums/outfits/ghostrole.dm index 896dc9c57267..7ab69512758a 100644 --- a/code/datums/outfits/ghostrole.dm +++ b/code/datums/outfits/ghostrole.dm @@ -69,13 +69,13 @@ /datum/outfit/pirate/immigrant name = "Pirate - Immigrant" belt = /obj/item/gun/ballistic/pirate - r_pocket = /obj/item/melee/energy/sword/pirate + r_pocket = /obj/item/melee/transforming/energy/sword/cutlass /datum/outfit/pirate/dilettante name = "Pirate - Dilettante" uniform = /obj/item/clothing/under/surplus shoes = /obj/item/clothing/shoes/boots/jackboots - belt = /obj/item/melee/energy/sword/pirate + belt = /obj/item/melee/transforming/energy/sword/cutlass l_hand = /obj/item/shield/makeshift /datum/outfit/pirate/professional @@ -85,5 +85,5 @@ shoes = /obj/item/clothing/shoes/boots/jackboots mask = /obj/item/clothing/mask/balaclava belt = /obj/item/gun/energy/zip - r_pocket = /obj/item/melee/energy/sword/pirate + r_pocket = /obj/item/melee/transforming/energy/sword/cutlass r_hand = /obj/item/shield/makeshift diff --git a/code/datums/outfits/horror_killers.dm b/code/datums/outfits/horror_killers.dm index 87ce4e992701..a1f7e22b70c8 100644 --- a/code/datums/outfits/horror_killers.dm +++ b/code/datums/outfits/horror_killers.dm @@ -41,7 +41,7 @@ gloves = /obj/item/clothing/gloves/black l_ear = /obj/item/radio/headset glasses = /obj/item/clothing/glasses/sunglasses - l_pocket = /obj/item/melee/energy/sword + l_pocket = /obj/item/melee/transforming/energy/sword mask = /obj/item/clothing/mask/gas/clown_hat id_slot = SLOT_ID_WORN_ID diff --git a/code/datums/outfits/outfit.dm b/code/datums/outfits/outfit.dm index e9f5223c5e08..85823242f516 100644 --- a/code/datums/outfits/outfit.dm +++ b/code/datums/outfits/outfit.dm @@ -263,8 +263,8 @@ belt = /obj/item/storage/belt/security/tactical/bandolier l_pocket = /obj/item/cell/device/weapon r_pocket = /obj/item/cell/device/weapon - r_hand = /obj/item/melee/energy/sword/imperial - l_hand = /obj/item/shield/energy/imperial + r_hand = /obj/item/melee/transforming/energy/sword/imperial + l_hand = /obj/item/shield/transforming/energy/imperial suit_store = /obj/item/gun/energy/imperial /datum/outfit/imperial/officer @@ -279,6 +279,6 @@ belt = /obj/item/storage/belt/security/tactical/bandolier l_pocket = /obj/item/cell/device/weapon r_pocket = /obj/item/cell/device/weapon - r_hand = /obj/item/melee/energy/sword/imperial - l_hand = /obj/item/shield/energy/imperial + r_hand = /obj/item/melee/transforming/energy/sword/imperial + l_hand = /obj/item/shield/transforming/energy/imperial suit_store = /obj/item/gun/energy/imperial diff --git a/code/datums/outfits/pirates.dm b/code/datums/outfits/pirates.dm index 3208e284a182..c2a061eb2772 100644 --- a/code/datums/outfits/pirates.dm +++ b/code/datums/outfits/pirates.dm @@ -6,7 +6,7 @@ shoes = /obj/item/clothing/shoes/brown head = /obj/item/clothing/head/bandana glasses = /obj/item/clothing/glasses/eyepatch - l_hand = /obj/item/melee/energy/sword/pirate + l_hand = /obj/item/melee/transforming/energy/sword/cutlass /datum/outfit/pirate/norm @@ -25,7 +25,7 @@ gloves = /obj/item/clothing/gloves/light_brown mask = /obj/item/clothing/mask/breath back = /obj/item/tank/vox - l_hand = /obj/item/melee/energy/sword/pirate + l_hand = /obj/item/melee/transforming/energy/sword/cutlass r_hand = /obj/item/gun/ballistic/shotgun/pump/rifle/vox_hunting l_pocket = /obj/item/ammo_magazine/a7_62mm/clip r_pocket = /obj/item/ammo_magazine/a7_62mm/clip diff --git a/code/datums/position_point_vector.dm b/code/datums/position_point_vector.dm index 6f9121469b92..3babfe0fb1df 100644 --- a/code/datums/position_point_vector.dm +++ b/code/datums/position_point_vector.dm @@ -119,10 +119,13 @@ /** * angle is clockwise from north + * + * @return self */ /datum/point/proc/shift_in_projectile_angle(angle, distance) x += sin(angle) * distance y += cos(angle) * distance + return src /** * doesn't use set base pixel x/y diff --git a/code/datums/shieldcall.dm b/code/datums/shieldcall.dm index 8f66b776107d..37f612450fe7 100644 --- a/code/datums/shieldcall.dm +++ b/code/datums/shieldcall.dm @@ -1,15 +1,43 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// +//* Copyright (c) 2024 silicons *// + +GLOBAL_LIST_EMPTY(cached_shieldcall_datums) + +/** + * return a globally cached shieldcall + * mostly for hardcoded ones that always work a certain way regardless of what it's actually representing + */ +/proc/fetch_cached_shieldcall(path) + if(GLOB.cached_shieldcall_datums[path]) + return GLOB.cached_shieldcall_datums[path] + GLOB.cached_shieldcall_datums[path] = new path + return GLOB.cached_shieldcall_datums[path] /** * Shieldcall handling datums + * + * These are semi-expensive datums that act as hooks + * to intercept arbitrary attacks at the /atom level. + * + * Please be careful with their usage. + * They hook most attacks, and as such are pretty expensive to have on an atom, compute-wise. + * This is pretty much only efficient for single-target; if it's possible, any suppression hook for attacks + * is better than using this on a large number of targets. */ /datum/shieldcall /// priority ; should never change once we're registered on something. lower has higher priority. var/priority = 0 /// goes to mob when in inventory - whether equipped to slot or in hand /// do not modify while applied. it will not un/register properly. + /// + /// * Turn this off if you're doing your own handling. var/shields_in_inventory = TRUE + /// Allow interception of `atom_shieldcall`. + /// + /// * "Yes, I read and understand the terms and conditions" + /// * "Yes, I will handle SHIELDCALL_FLAG_SECOND_CALL to prevent a double-block scenario" + /// * "Yes, I understand that atom_shieldcall is low level and is called in addition to other shieldcall handling procs" + var/low_level_intercept = FALSE /** * sent over from the atom @@ -17,6 +45,127 @@ * @params * * defending - the atom in question * * shieldcall_args - indexed list of shieldcall args. + * * fake_attack - just checking! + * + * @return nothing, because you should be modifying the return flags via shieldcall_args list! + */ +/datum/shieldcall/proc/handle_shieldcall(atom/defending, list/shieldcall_args, fake_attack) + return + +//* Melee Handling *// + +/** + * sent over from the atom + * + * * this is generic pre-intercept for melee; please keep this cheap + * * for stuff like reactive teleport armor, use this because it will stop the hit entirely. + * * for damage modification, inject directly into e_args + * + * @params + * * defending - the atom being attacked + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * weapon - the item being used to swing with + * * e_args - (optional) the clickchain event, if any; **This is mutable.** + * + * @return SHIELDCALL_FLAG_* flags + */ +/datum/shieldcall/proc/handle_item_melee(atom/defending, shieldcall_returns, fake_attack, obj/item/weapon, datum/event_args/actor/clickchain/e_args) + return NONE + +/** + * sent over from the atom + * + * * this is generic pre-intercept for melee; please keep this cheap + * * for stuff like reactive teleport armor, use this because it will stop the hit entirely. + * * for damage modification, inject directly into e_args + * + * @params + * * defending - the atom in question + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * style - the unarmed_attack datum being used + * * e_args (optional) the clickchain event, if any; **This is mutable.** + * + * @return SHIELDCALL_FLAG_* flags + */ +/datum/shieldcall/proc/handle_unarmed_melee(atom/defending, shieldcall_returns, fake_attack, datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args) + return NONE + +//* Interaction Handling *// + +/** + * sent over from the atom + * + * * for generic 'tried to touch' things + * * this is really funny because it lets us do things like teleport the RD on a hug + * + * @params + * * defending - the atom in question + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * e_args (optional) the clickchain event, if any; **This is mutable.** + * * contact_flags - SHIELDCALL_CONTACT_FLAG_* + * * contact_specific - SHIELDCALL_CONTACT_SPECIFIC_* + * + * @return SHIELDCALL_FLAG_* flags + */ +/datum/shieldcall/proc/handle_touch(atom/defending, shieldcall_returns, fake_attack, datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific) + return NONE + +//* Projectile Handling *// + +/** + * sent over from the atom + * + * * this is pre-intercept for projectiles; please keep this cheap. + * * for stuff like reactive teleport armor, use this because it will stop the hit entirely. + * * passed in bullet act args is mutable. + * * we DO NOT process SHIELDCALL_FLAG flags other than _TERMINATE, because we have direct access to impact_flags of the bullet! + * + * @params + * * defending - the atom in question + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * bullet_act_args - indexed list of bullet_act args. + * + * @return SHIELDCALL_FLAG_TERMINATE or NONE */ -/datum/shieldcall/proc/handle_shieldcall(atom/defending, list/shieldcall_args) +/datum/shieldcall/proc/handle_bullet(atom/defending, shieldcall_returns, fake_attack, list/bullet_act_args) + return NONE + +//* Throw Handling *// + +/** + * sent over from the atom + * + * * this is pre-intercept for throwns + * * for stuff like reactive teleport armor, use this because it will stop the hit entirely + * + * todo: implement for turf + * todo: implement for obj + * + * @params + * * defending - the thing being hit + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * thrown - the thrown object's data + * + * @return SHIELDCALL_FLAG_* flags + */ +/datum/shieldcall/proc/handle_throw_impact(atom/defending, shieldcall_returns, fake_attack, datum/thrownthing/thrown) return + +//* Bound Variant *// + +/datum/shieldcall/bound + var/expected_type + var/datum/bound + +/datum/shieldcall/bound/New(datum/binding) + ASSERT(expected_type && istype(binding, expected_type)) + src.bound = binding + +/datum/shieldcall/bound/Destroy() + src.bound = null + return ..() diff --git a/code/datums/soundbytes/effects/combat.dm b/code/datums/soundbytes/effects/combat.dm new file mode 100644 index 000000000000..dd1dd5ef5dd9 --- /dev/null +++ b/code/datums/soundbytes/effects/combat.dm @@ -0,0 +1,47 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/datum/soundbyte/parry_start + name = "Active Parry (Start)" + is_sfx = TRUE + path = 'sound/soundbytes/effects/combat/parry-cycle.ogg' + +/datum/soundbyte/grouped/metal_parry + name = "Parry (Metal-Metal)" + is_sfx = TRUE + path = list( + 'sound/soundbytes/effects/combat/parry-metal1.ogg', + 'sound/soundbytes/effects/combat/parry-metal2.ogg', + ) + +/datum/soundbyte/grouped/wood_parry + name = "Parry (Wood-Wood)" + is_sfx = TRUE + path = list( + 'sound/soundbytes/effects/combat/parry-wood1.ogg', + 'sound/soundbytes/effects/combat/parry-wood2.ogg', + ) + +/datum/soundbyte/grouped/block_metal_with_wood + name = "Block (Metal-Wood)" + is_sfx = TRUE + path = list( + 'sound/soundbytes/effects/combat/block-metal-on-wood-1.ogg', + 'sound/soundbytes/effects/combat/block-metal-on-wood-2.ogg', + ) + +/datum/soundbyte/grouped/block_metal_with_metal + name = "Block (Metal-Metal)" + is_sfx = TRUE + path = list( + 'sound/soundbytes/effects/combat/block-metal-on-metal-1.ogg', + 'sound/soundbytes/effects/combat/block-metal-on-metal-2.ogg', + ) + +/datum/soundbyte/grouped/block_wood_with_wood + name = "Block (Wood-Wood)" + is_sfx = TRUE + path = list( + 'sound/soundbytes/effects/combat/block-wood-on-wood-1.ogg', + 'sound/soundbytes/effects/combat/block-wood-on-wood-2.ogg', + ) diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index e0980c2b8def..010e8d88d0c2 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -6,6 +6,9 @@ * * each effect potentially has its own amount of variable arguments that * can be passed into apply_status_effect. they will be detailed per-file. + * + * todo: /datum/prototype/status_effect + * todo: /datum/prototype/status_effect/simple for normal ones. */ /datum/status_effect abstract_type = /datum/status_effect @@ -196,9 +199,11 @@ /** * remove a status effect * + * * will remove grouped effects entirely. + * * @params * * path - path to effect - * * stacks - stacks to remove for grouped and stacking, default is all. + * * stacks - stacks to remove for stacking, default is all. * * @return stacks **left**. for single effects this is probably 0. */ diff --git a/code/datums/unarmed_attack.dm b/code/datums/unarmed_attack.dm index 6179ef081fe7..ff6e629e7b91 100644 --- a/code/datums/unarmed_attack.dm +++ b/code/datums/unarmed_attack.dm @@ -52,6 +52,9 @@ GLOBAL_LIST_EMPTY(unarmed_attack_cache) var/eye_attack_text var/eye_attack_text_victim +/datum/unarmed_attack/proc/operator""() + return pick(attack_verb_legacy) + //* Feedback /datum/unarmed_attack/proc/get_sparring_variant() diff --git a/code/datums/uplink/visible_weapons.dm b/code/datums/uplink/visible_weapons.dm index 04a3acad2112..776ebc499b9e 100644 --- a/code/datums/uplink/visible_weapons.dm +++ b/code/datums/uplink/visible_weapons.dm @@ -17,17 +17,17 @@ /datum/uplink_item/item/visible_weapons/energy_sword name = "Energy Sword, Colorable" item_cost = 40 - path = /obj/item/melee/energy/sword + path = /obj/item/melee/transforming/energy/sword /datum/uplink_item/item/visible_weapons/energy_sword_pirate name = "Energy Cutlass, Colorable" item_cost = 40 - path = /obj/item/melee/energy/sword/pirate + path = /obj/item/melee/transforming/energy/sword/cutlass -/datum/uplink_item/item/visible_weapons/energy_spear - name = "Energy Spear, Colorable" - item_cost = 50 - path = /obj/item/melee/energy/spear +// /datum/uplink_item/item/visible_weapons/energy_spear +// name = "Energy Spear, Colorable" +// item_cost = 50 +// path = /obj/item/melee/transforming/energy/spear /datum/uplink_item/item/visible_weapons/claymore name = "Claymore" diff --git a/code/game/antagonist/outsider/deathsquad.dm b/code/game/antagonist/outsider/deathsquad.dm index 931ae8626d41..26517f966973 100644 --- a/code/game/antagonist/outsider/deathsquad.dm +++ b/code/game/antagonist/outsider/deathsquad.dm @@ -49,7 +49,7 @@ var/datum/antagonist/deathsquad/deathsquad player.equip_to_slot_or_del(new /obj/item/gun/ballistic/revolver/combat(player), SLOT_ID_BELT) player.equip_to_slot_or_del(new /obj/item/gun/energy/pulse_rifle(player), /datum/inventory_slot/abstract/hand/right) player.equip_to_slot_or_del(new /obj/item/hardsuit/ert/assetprotection(player), SLOT_ID_BACK) - player.equip_to_slot_or_del(new /obj/item/melee/energy/sword(player), SLOT_ID_SUIT_STORAGE) + player.equip_to_slot_or_del(new /obj/item/melee/transforming/energy/sword(player), SLOT_ID_SUIT_STORAGE) // player.implant_loyalty() var/obj/item/card/id/id = create_id("Asset Protection", player) diff --git a/code/game/atoms/atom-construction.dm b/code/game/atoms/atom-construction.dm new file mode 100644 index 000000000000..94d85815a35e --- /dev/null +++ b/code/game/atoms/atom-construction.dm @@ -0,0 +1,60 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Deconstruction *// + +/** + * called to semantically deconstruct an atom + * + * @params + * * method - how we were deconstructed + */ +/atom/proc/deconstruct(method = ATOM_DECONSTRUCT_DISASSEMBLED) + SHOULD_NOT_OVERRIDE(TRUE) + + // send signal + // todo: signal + // do da funny logic + deconstructed(method) + // drop things after so things that rely on having objects don't break + drop_products(method, drop_location()) + // goodbye, cruel world + break_apart(method) + +/** + * called to actually destroy ourselves + */ +/atom/proc/break_apart(method) + qdel(src) + +/** + * called when we are deconstructed + * + * **do not drop products in here** + * + * @params + * - method - how we were deconstructed + */ +/atom/proc/deconstructed(method) + return + +/** + * called to drop the products of deconstruction + * + * @params + * * method - how we were deconstructed + * * where - where to drop products; set in base if null to drop_location(). + */ +/atom/proc/drop_products(method, atom/where = drop_location()) + return + +/** + * called to move a product to a place + * + * @params + * * method - how we were deconstructed + * * dropping - movable in question + * * where - where to move to + */ +/atom/proc/drop_product(method, atom/movable/dropping, atom/where) + dropping.forceMove(where || drop_location()) diff --git a/code/game/atoms/atom-damage.dm b/code/game/atoms/atom-damage.dm new file mode 100644 index 000000000000..61ebc475db1c --- /dev/null +++ b/code/game/atoms/atom-damage.dm @@ -0,0 +1,333 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Damage Instance Handling *// + +/** + * standard damage handling - process an instance of damage + * + * args are the same as shieldcall args, because this directly invokes shield/armorcalls + * + * * additional args past the normal shieldcall args are allowed, but not before! + * * the entire args list is extracted via return, allowing for handling by caller. + * * damage is assumed to be zone'd if def_zone is set; otherwise it's overall + * * please note that overall damage generally doesn't check armor properly for speed reasons! + * + * @return modified args + */ +/atom/proc/run_damage_instance(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + process_damage_instance(args, hit_zone) + if(shieldcall_flags & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return args.Copy() // args are only valid during a call; it's destroyed after. + inflict_damage_instance(arglist(args)) + return args.Copy() // args are only valid during a call; it's destroyed after. + +/** + * [/atom/proc/run_damage_instance()], but doesn't actually do damage. + * + * @return modified args + */ +/atom/proc/check_damage_instance(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + process_damage_instance(args, hit_zone, TRUE) + return args.Copy() // args are only valid during a call; it's destroyed after. + +/** + * process an instance of damage through defense handling. + */ +/atom/proc/process_damage_instance(list/shieldcall_args, filter_zone, fake_attack) + if(!(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_SKIP_SHIELDCALLS)) + run_shieldcalls(shieldcall_args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & (SHIELDCALL_FLAGS_SHOULD_TERMINATE | SHIELDCALL_FLAGS_BLOCK_ATTACK)) + return + if(!(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_SKIP_ARMORCALLS)) + run_armorcalls(shieldcall_args, fake_attack, filter_zone) + +/** + * inflict an instance of damage. + * + * * this happens after shieldcalls, armor checks, etc, all resolve. + * * at this point, nothing should modify damage + * * for things like limb damage and armor handling, check the armor/etc in process_damage_instance + * * for this reason, we do not allow any returns. + * * if hit_zone is not specified, this is considered overall damage. + * * overall damage is implementation-defined, so it's recommended to, ironically, not try to standardize that too much. + * * this is pretty much the hand-off proc where damage goes from hit processing / defense checks to the damage system for an entity + * * the damage system can be a medical system or just the atom integrity system. + */ +/atom/proc/inflict_damage_instance(SHIELDCALL_PROC_HEADER) + if(!integrity_enabled) + return + if(inflict_damage_type_special(args)) + return + switch(damage_type) + if(BRUTE) + if(BURN) + else + return // normal atoms can't take non brute-burn damage + // default atom damage handling + inflict_atom_damage( + damage, + damage_type, + damage_tier, + damage_flag, + damage_mode, + hit_zone, + attack_type, + weapon, + ) + +/** + * decodes damage type to what it should actually do + * + * * this is for hybrid / semantic damage types like bio-acid and searing damage to work + * + * @return TRUE to handle the damage type. + */ +/atom/proc/inflict_damage_type_special(list/shieldcall_args) + return FALSE + +//* Damage Processing API *// + +/** + * takes damage from a generic attack, taking into account armor but not shields. + * this does not handle playing sounds / anything, this is strictly generic damage handling + * usable by anything. + * + * * This does **not** invoke the shieldcall API! + * * This does **not** invoke the armor API! + * * This is because damage instance processing should be processing that. + * + * @params + * * damage - raw damage + * * damage_type - (optional) damage type to inflict + * * damage_tier - (optional) resulting damage tier + * * damage_flag - (optional) resulting damage armor flag from [code/__DEFINES/combat/armor.dm] + * * damage_mode - (optional) DAMAGE_MODE_* flags + * * hit_zone - (optional) the zone being hit + * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] + * * weapon - (optional) attacking datum; same format as shieldcall API. See shieldcalls for more information. + * + * @return raw damage taken + */ +/atom/proc/inflict_atom_damage(damage, damage_type, damage_tier, damage_flag, damage_mode, hit_zone, attack_type, datum/weapon) + if(!integrity_enabled) + return 0 + if(integrity_flags & INTEGRITY_INDESTRUCTIBLE) + return 0 + if(!damage) + return 0 + . = integrity + damage_integrity(damage) + . = . - integrity + + +//* Integrity - Direct Manipulation *// + +/** + * damages integrity directly, ignoring armor / shields + * + * @params + * * amount - how much + * * gradual - burst or gradual? if you want to play a sound or something, you usually want to check this. + * * do_not_break - skip calling atom_break + */ +/atom/proc/damage_integrity(amount, gradual, do_not_break) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + var/was_working = integrity > integrity_failure + integrity = max(0, integrity - amount) + if(was_working && integrity <= integrity_failure && !do_not_break) + atom_break() + if(integrity <= 0) + atom_destruction() + +/** + * heals integrity directly + * + * @params + * * amount - how much + * * gradual - burst or gradual? if you want to play a sound or something, you usually want to check this. + * * do_not_fix - skip calling atom_fix + * + * @return amount healed + */ +/atom/proc/heal_integrity(amount, gradual, do_not_fix) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + var/was_failing = integrity <= integrity_failure + . = integrity + integrity = min(integrity_max, integrity + amount) + . = integrity - . + if(was_failing && integrity > integrity_failure && !do_not_fix) + atom_fix() + +/** + * directly sets integrity - ignores armor / sihelds + * + * will not call [damage_integrity] or [heal_integrity] + * will call [atom_break], [atom_fix], [atom_destruction] + * + * @params + * * amount - how much to set to? + */ +/atom/proc/set_integrity(amount) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + var/was_failing = integrity <= integrity_failure + integrity = clamp(amount, 0, integrity_max) + if(!was_failing && integrity <= integrity_failure) + atom_break() + else if(was_failing && integrity > integrity_failure) + atom_fix() + if(integrity <= 0) + atom_destruction() + +/** + * sets max integrity - will automatically reduce integrity if it's above max. + * + * will not call [damage_integrity] + * will call [atom_break], [atom_fix], [atom_destruction] + * + * @params + * * amount - how much to set to + */ +/atom/proc/set_max_integrity(amount) + integrity_max = max(amount, 0) + if(integrity < integrity_max) + return + var/was_broken = integrity <= integrity_failure + integrity = integrity_max + if(!was_broken && (integrity <= integrity_failure)) + atom_break() + if(integrity <= 0) + atom_destruction() + +/** + * sets integrity and max integrity - will automatically reduce integrity if it's above max. + * + * will not call [damage_integrity] + * will call [atom_break], [atom_fix], [atom_destruction] + * + * @params + * * integrity - how much to set integrity to + * * integrity_max - how much to set integrity_max to + */ +/atom/proc/set_full_integrity(integrity, integrity_max) + src.integrity_max = max(integrity_max, 0) + var/was_broken = src.integrity <= integrity_failure + src.integrity = clamp(integrity, 0, integrity_max) + var/now_broken = integrity <= integrity_failure + if(!was_broken && now_broken) + atom_break() + else if(was_broken && !now_broken) + atom_fix() + if(integrity <= 0) + atom_destruction() + +/** + * Set integrity to a multiple of initial. + * + * And fully restore it if specified. + * Otherwise, will retain the last percentage. + */ +/atom/proc/set_multiplied_integrity(factor, restore) + var/was_broken = src.integrity <= integrity_failure + if(restore) + integrity = integrity_max = initial(integrity_max) * factor + if(was_broken && integrity > integrity_failure) + atom_fix() + return + var/ratio = integrity / integrity_max + integrity_max = initial(integrity_max) * factor + integrity = integrity_max * ratio + var/now_broken = integrity <= integrity_failure + if(!was_broken && now_broken) + atom_break() + else if(was_broken && !now_broken) + atom_fix() + if(integrity <= 0) + atom_destruction() + +/** + * adjusts integrity - routes directly to [damage_integrity] and [heal_integrity] + * + * will call [damage_integrity] + * will call [atom_break], [atom_fix], [atom_destruction] + * + * @params + * * amount - how much + * * gradual - burst or gradual? + * * no_checks - do not call fix/break + */ +/atom/proc/adjust_integrity(amount, gradual, no_checks) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + if(amount > 0) + return heal_integrity(amount, gradual, no_checks) + else + return damage_integrity(amount, gradual, no_checks) + +/** + * adjusts max integrity - will automatically reduce integrity if it's above max. + * + * will call [damage_integrity] + * will call [atom_break], [atom_fix], [atom_destruction] + * + * @params + * * amount - how much to adjust by + * * gradual - burst or gradual? + */ +/atom/proc/adjust_max_integrity(amount, gradual) + // lazy route lmao + set_max_integrity(integrity_max + amount) + +//* Integrity - Check / Getters *// + +/** + * percent integrity, rounded. + */ +/atom/proc/percent_integrity(round_to = 0.1) + return integrity_max? round(integrity / integrity_max, round_to) : 0 + +/atom/proc/is_integrity_broken() + return atom_flags & ATOM_BROKEN + +/atom/proc/is_integrity_damaged() + return integrity < integrity_max + +//* Integrity - Events *// + +/** + * called when integrity reaches 0 from a non 0 value + */ +/atom/proc/atom_destruction() + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + if(!(integrity_flags & INTEGRITY_NO_DECONSTRUCT)) + deconstruct(ATOM_DECONSTRUCT_DESTROYED) + +/** + * called when integrity drops below or at integrity_failure + * + * if integrity_failure is 0, this is called before destruction. + */ +/atom/proc/atom_break() + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + if(integrity > integrity_failure) + damage_integrity(integrity - integrity_failure, do_not_break = TRUE) + atom_flags |= ATOM_BROKEN + +/** + * called when integrity rises above integrity_failure + * + * if integrity_failure is 0, this still works. + */ +/atom/proc/atom_fix() + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + if(integrity < integrity_failure) + heal_integrity(integrity_failure - integrity + 1, do_not_fix = TRUE) + atom_flags &= ~ATOM_BROKEN diff --git a/code/game/atoms/atom-defense.dm b/code/game/atoms/atom-defense.dm new file mode 100644 index 000000000000..3f2f7141d7be --- /dev/null +++ b/code/game/atoms/atom-defense.dm @@ -0,0 +1,532 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//! Welcome to hell. + +// todo: everything needs comsigs comsigs comsigs + +//* External API / Damage Receiving *// + +/** + * todo: implement on most atoms/generic damage system + * todo: replace legacy_ex_act entirely with this + * + * React to being hit by an explosive shockwave + * + * ? Tip for overrides: . = ..() when you want signal to be sent, mdify power before if you need to; to ignore parent + * ? block power, just `return power` in your proc after . = ..(). + * + * @params + * - power - power our turf was hit with + * - direction - DIR_BIT bits; can bwe null if it wasn't a wave explosion!! + * - explosion - explosion automata datum; can be null + * + * @return power after falloff (e.g. hit with 30 power, return 20 to apply 10 falloff) + */ +/atom/proc/ex_act(power, dir, datum/automata/wave/explosion/E) + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, power, dir, E) + return power + +/** + * called on melee hit + * + * todo: add clickchain datum, instead of multiplier + * + * * check CLICKCHAIN_FLAGS_* as needed, especially UNCONDITIONAL_ABORT and ATTACK_ABORT + * * clickchain flags are sent down through parent calls. + * + * @params + * * user - person attacking + * * weapon - weapon used + * * target_zone - zone targeted + * * mult - damage multiplier + * + * @return clickchain flags to append + */ +/atom/proc/melee_act(mob/user, obj/item/weapon, target_zone, datum/event_args/actor/clickchain/clickchain) + return CLICKCHAIN_DO_NOT_ATTACK + +/** + * called on unarmed melee hit + * + * todo: add clickchain datum, instead of multiplier + * + * @params + * * user - person attacking + * * style - unarmed attack datum + * * target_zone - zone targeted + * * mult - damage multiplier + * + * @return clickchain flags to append + */ +/atom/proc/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, datum/event_args/actor/clickchain/clickchain) + return CLICKCHAIN_DO_NOT_ATTACK + +/** + * Because this is the proc that calls on_impact(), handling is necessarily + * done in here for a lot of things. + * + * * Overrides should modify proc args directly (e.g. impact_flags) and then call ..() + * if it needs to be taken account by default handling. + * * Overrides should not edit args after ..(), as args are only passed up, not down. + * * Overrides should, for that reason, always call ..() last. + * * This semantically means 'we are **about** to be hit, do anything for special processing'. + * * If you need to delete a projectile on impact, use `on_bullet_act()`; that's called after the contact actually happens. + * + * Things to keep in mind + * * 'efficiency' arg is **extremely** powerful. Please don't lower it to dismal values for no reason. + * * use PROJECTILE_IMPACT_BLOCKED instead of setting efficiency to 0 if an impact is entirely blocked + * * semantically, efficiency 0 means shield from all damages, IMPACT_BLOCKED means it hit something else + * * bullet_act is this way because we don't make a `/datum/event_args/actor/clickchain` with every call, so it needs a way of propagating blocking behavior and impact flags up/down the chain. + * + * @params + * * proj - the projectile + * * impact_flags - PROJECTILE_IMPACT_* flags + * * def_zone - impacting zone; calculated by projectile side, usually + * * efficiency - 0 to 1, inclusive. ratio of effects, including damage, to pass through. + * + * todo: add PROJECTILE_IMPACT_DELETE_AFTER as opposed to DELETE? so rest of effects can still run + * todo: shieldcalls still fire if target aborts without unconditional abort, they should not do that. + * + * @return new impact_flags + */ +/atom/proc/bullet_act(obj/projectile/proj, impact_flags, def_zone, efficiency = 1) + SHOULD_NOT_SLEEP(TRUE) + SHOULD_CALL_PARENT(TRUE) + // lower calls can change flags before we trigger + // check if we're still hitting + if(impact_flags & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return impact_flags + // 0. fire signal + SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, args) + // check if we're still hitting + if(impact_flags & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return impact_flags + // 1. fire shieldcalls + atom_shieldcall_handle_bullet(args, FALSE, NONE) + // check if we're still hitting + if(impact_flags & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return impact_flags + // 2. fire on_bullet_act + impact_flags |= on_bullet_act(proj, impact_flags, args) + if(impact_flags & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return impact_flags + // 3. fire projectile-side on_impact + return proj.on_impact(src, impact_flags, def_zone, efficiency) + +/** + * So, turns out, BYOND passes `args` list **down** on a ..() via arglist(args) equivalent but not back up. + * + * This means that modified bullet act args can't actually propagate through. + * To handle this, we have this function to actually perform the effects of said bullet act. + * + * * This basically semantically means 'we are being hit, do effects for it'. + * * This is called before the projectile-side impact, which is where the damage is usually inflicted. + * * Modify `impact_flags` directly before ..(), and `.` after ..() + * * Check `.` after ..() if it isn't the last call so you know when to abort the processing as needed. + * * Args will propagate **up** (closer to /atom) `..()` calls, but not back down (if base `/atom` changes something you won't get it on your sub-type) + * * For this reason `bullet_act_args` is provided so you can mutably edit it. Do **not** edit the projectile or the impact flags; return the impact flags for automatic addition. + * + * @params + * * proj - hitting projectile; immutable + * * impact_flags - impact flags; immutable. edit directly before ..() call, return edited values after. + * * bullet_act_args - access to the rest of the args in `bullet_act`. Mutable, except for the projectile and the impact flags. + */ +/atom/proc/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + return impact_flags + +//* Hitsound API *// + +// todo: stuff like metal limbs punching walls making special sounds +// todo: this probably needs a rework + +/** + * gets hitsound override. return a value to be fed into playsound, or null for default. + * + * @params + * * damage_type - damage type like brute / burn / etc + * * damage_mode - damage mode for piercing / whatnot + * * attack_type - attack type enum like melee / projectile / thrown / unarmed / etc + * * weapon - attacking /obj/item for melee / thrown, /obj/projectile for ranged, /mob for unarmed + */ +/atom/proc/hitsound_override(damage_type, damage_mode, attack_type, datum/weapon) + return // default is null + +/atom/proc/hitsound_melee(obj/item/I) + . = I.attacksound_override(src, ATTACK_TYPE_MELEE) + if(!isnull(.)) + return + . = hitsound_override(I.damtype, I.damage_mode, ATTACK_TYPE_MELEE, I) + if(.) + return + . = (I.damtype == BURN? hit_sound_burn : hit_sound_brute) || I.attack_sound + if(.) + return + switch(I.damtype) + if(BRUTE) + return "swing_hit" + if(BURN) + return "'sound/items/welder.ogg" + else + return "swing_hit" + +/atom/proc/hitsound_projectile(obj/projectile/P) + //? todo: projectile gets final say + . = hitsound_override(P.damtype, P.damage_mode, ATTACK_TYPE_PROJECTILE, P) + if(.) + return + return islist(P.impact_sounds)? pick(P.impact_sounds) : P.impact_sounds + +/atom/proc/hitsound_throwhit(obj/item/I) + . = I.attacksound_override(src, ATTACK_TYPE_THROWN) + if(!isnull(.)) + return + . = hitsound_override(I.damtype, I.damage_mode, ATTACK_TYPE_THROWN, I) + if(.) + return + . = (I.damtype == BURN? hit_sound_burn : hit_sound_brute) || I.attack_sound + if(.) + return + switch(I.damtype) + if(BRUTE) + return "swing_hit" + if(BURN) + return 'sound/items/welder.ogg' + else + return "swing_hit" + +/atom/proc/hitsound_unarmed(mob/M, datum/unarmed_attack/style) + //? todo: style gets final say + . = hitsound_override(M, style.damage_mode, ATTACK_TYPE_UNARMED, style) + if(.) + return + // todo: way to override this from style side? we don't just want hitsound brute/burn. + . = (style.damage_type == BURN? hit_sound_burn : hit_sound_brute) || style.attack_sound + + +//* Armor *// + +/** + * resets our armor to initial values + */ +/atom/proc/reset_armor() + set_armor(initial(armor_type)) + +/** + * sets our armor + * + * @params + * * what - list of armor values or a /datum/armor path + */ +/atom/proc/set_armor(what) + armor = fetch_armor_struct(what) + +/** + * gets our armor datum or otherwise make sure it exists + */ +/atom/proc/fetch_armor() + RETURN_TYPE(/datum/armor) + return armor || (armor = generate_armor()) + +/** + * get default armor datum + */ +/atom/proc/generate_armor() + return fetch_armor_struct(armor_type) + +/** + * runs an attack against armor + * + * * side effects are **not** allowed + * * this is the 'just checking' version. + * + * params are modified and then returned as a list + * + * * See [atom_shieldcall()] for what is going on here. + * * SHIELDCALL_ARG_* are used as the return list's indices. + * + * @params + * * damage - raw damage + * * damtype - damage type + * * tier - penetration / attack tier + * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] + * * mode - damage_mode + * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] + * * weapon - (optional) the attacking weapon datum; see [code/__DEFINES/combat/shieldcall.dm] + * * flags - shieldcall flags passed through components. [code/__DEFINES/combat/shieldcall.dm] + * * hit_zone - where were they hit? + * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain - the clickchain for melee attacks. + * + * @return args, modified, as list. + */ +/atom/proc/check_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_SLEEP(TRUE) + run_armorcalls(args, TRUE) + return args.Copy() + +/** + * runs an attack against armor + * + * * side effects are allowed + * + * params are modified and then returned as a list + * + * * See [atom_shieldcall()] for what is going on here. + * * SHIELDCALL_ARG_* are used as the return list's indices. + * + * @params + * * damage - raw damage + * * damtype - damage type + * * tier - penetration / attack tier + * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] + * * mode - damage_mode + * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] + * * weapon - (optional) the attacking weapon datum; see [code/__DEFINES/combat/shieldcall.dm] + * * flags - shieldcall flags passed through components. [code/__DEFINES/combat/shieldcall.dm] + * * hit_zone - where were they hit? + * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain - the clickchain for melee attacks. + * + * @return args, modified, as list. + */ +/atom/proc/run_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_SLEEP(TRUE) + run_armorcalls(args, FALSE) + return args.Copy() + +//* Shieldcalls *// + +/** + * runs an attack against shields + * + * * side effects are **not** allowed + * * this is the 'just checking' version. + * + * params are modified and then returned as a list + * + * * This is the dynamic shieldcall system. It's best to do what you want in specific shieldcall hooks if possible. + * * What this means is that this can't, say, redirect or delete a projectile, because bullet act handling is where that happens. + * * This more or less just lets you modify incoming damage instances sometimes. + * * The args are not copied! They're passed back directly. This has implications. + * * Make sure you pass in SHIELDCALL_FLAG_SECOND_CALL if **any** kind of shieldcall invocation has happened during this attack. + * * SECOND_CALL is required to tell things that something is not the first time, so you don't get doubled blocking efficiency. + * + * @params + * * damage - raw damage + * * damtype - damage type + * * damage_tier - penetration / attack tier + * * damage_flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] + * * damage_mode - damage_mode + * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] + * * weapon - (optional) the attacking weapon datum; see [code/__DEFINES/combat/shieldcall.dm] + * * shieldcall_flags - shieldcall flags passed through components. [code/__DEFINES/combat/shieldcall.dm] + * * hit_zone - where were they hit? + * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain - the clickchain for melee attacks. + * + * @return args, modified, as list. + */ +/atom/proc/atom_shieldcheck(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_SLEEP(TRUE) + run_shieldcalls(args, TRUE) + return args.Copy() + +/** + * runs an attack against shields + * + * * side effects are allowed + * * this is run during an actual attack + * + * params are modified and then returned as a list + * + * * This is the dynamic shieldcall system. It's best to do what you want in specific shieldcall hooks if possible. + * * What this means is that this can't, say, redirect or delete a projectile, because bullet act handling is where that happens. + * * This more or less just lets you modify incoming damage instances sometimes. + * * The args are not copied! They're passed back directly. This has implications. + * * Make sure you pass in SHIELDCALL_FLAG_SECOND_CALL if **any** kind of shieldcall invocation has happened during this attack. + * * SECOND_CALL is required to tell things that something is not the first time, so you don't get doubled blocking efficiency. + * + * @params + * * damage - raw damage + * * damtype - damage type + * * damage_tier - penetration / attack tier + * * damage_flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] + * * damage_mode - damage_mode + * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] + * * weapon - (optional) the attacking weapon datum; see [code/__DEFINES/combat/shieldcall.dm] + * * shieldcall_flags - shieldcall flags passed through components. [code/__DEFINES/combat/shieldcall.dm] + * * hit_zone - where were they hit? + * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain - the clickchain for melee attacks. + * + * @return args, modified, as list. + */ +/atom/proc/atom_shieldcall(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_SLEEP(TRUE) + run_shieldcalls(args, FALSE) + return args.Copy() + +/** + * Runs a damage instance against shieldcalls + * + * * This is a low level proc. Make sure you undersatnd how shieldcalls work [__DEFINES/combat/shieldcall.dm]. + */ +/atom/proc/run_shieldcalls(list/shieldcall_args, fake_attack) + SHOULD_NOT_SLEEP(TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL, shieldcall_args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_TERMINATE) + return + for(var/datum/shieldcall/calling as anything in shieldcalls) + if(!calling.low_level_intercept) + continue + calling.handle_shieldcall(src, args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_TERMINATE) + break + +/** + * Runs a damage instance against armor + * + * * This is a low level proc. Make sure you undersatnd how shieldcalls work [__DEFINES/combat/shieldcall.dm]. + */ +/atom/proc/run_armorcalls(list/shieldcall_args, fake_attack) + SHOULD_NOT_SLEEP(TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_ARMORCALL, shieldcall_args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_TERMINATE) + return + var/datum/armor/our_armor = fetch_armor() + our_armor.handle_shieldcall(shieldcall_args, fake_attack) + +/atom/proc/register_shieldcall(datum/shieldcall/delegate) + SHOULD_NOT_SLEEP(TRUE) + LAZYINITLIST(shieldcalls) + BINARY_INSERT(delegate, shieldcalls, /datum/shieldcall, delegate, priority, COMPARE_KEY) + +/atom/proc/unregister_shieldcall(datum/shieldcall/delegate) + SHOULD_NOT_SLEEP(TRUE) + LAZYREMOVE(shieldcalls, delegate) + +//* Shieldcalls - Focused / High-Level *// + +/** + * Runs shieldcalls for handle_touch + * + * * Use this instead of manually looping, as it fires a signal that makes things like /datum/passive_parry spin up. + * + * @params + * * e_args (optional) the clickchain event, if any; **This is mutable.** + * * contact_flags - SHIELDCALL_CONTACT_FLAG_* + * * contact_specific - SHIELDCALL_CONTACT_SPECIFIC_* + * * fake_attack - just checking! + * * shieldcall_flags - shieldcall flags. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain_flags - clickchain flags. [code/__DEFINES/procs/clickcode.dm] + * + * @return SHIELDCALL_FLAG_* flags + */ +/atom/proc/atom_shieldcall_handle_touch(datum/event_args/actor/clickchain/e_args, contact_flags, contact_specific, fake_attack, shieldcall_flags) + SHOULD_NOT_SLEEP(TRUE) + // cannot parry yourself + if(e_args.performer == src) + return shieldcall_flags + // send query signal + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_TOUCH) + . = shieldcall_flags + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + . |= shieldcall.handle_touch(src, ., fake_attack, e_args, contact_flags, contact_specific) + +/** + * Runs shieldcalls for handle_unarmed_melee + * + * * Use this instead of manually looping, as it fires a signal that makes things like /datum/passive_parry spin up. + * + * @params + * * style - the unarmed_attack datum being used + * * e_args (optional) the clickchain event, if any; **This is mutable.** + * * fake_attack - (optional) just checking! + * * shieldcall_flags - (optional) shieldcall flags. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain_flags - (optional) clickchain flags. [code/__DEFINES/procs/clickcode.dm] + * + * @return SHIELDCALL_FLAG_* flags + */ +/atom/proc/atom_shieldcall_handle_unarmed_melee(datum/unarmed_attack/style, datum/event_args/actor/clickchain/e_args, fake_attack, shieldcall_flags, clickchain_flags) + SHOULD_NOT_SLEEP(TRUE) + // cannot parry yourself + if(e_args.performer == src) + return shieldcall_flags + // send query signal + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_UNARMED_MELEE) + . = shieldcall_flags + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + . |= shieldcall.handle_unarmed_melee(src, ., fake_attack, style, e_args) + +/** + * Runs shieldcalls for handle_item_melee + * + * * Use this instead of manually looping, as it fires a signal that makes things like /datum/passive_parry spin up. + * + * @params + * * weapon - the item being used to swing with + * * e_args - (optional) the clickchain event, if any; **This is mutable.** + * * fake_attack - (optional) just checking! + * * shieldcall_flags - (optional) shieldcall flags. [code/__DEFINES/combat/shieldcall.dm] + * * clickchain_flags - (optional) clickchain flags. [code/__DEFINES/procs/clickcode.dm] + * + * @return SHIELDCALL_FLAG_* flags + */ +/atom/proc/atom_shieldcall_handle_item_melee(obj/item/weapon, datum/event_args/actor/clickchain/e_args, fake_attack, shieldcall_flags, clickchain_flags) + SHOULD_NOT_SLEEP(TRUE) + // cannot parry yourself + if(e_args.performer == src) + return shieldcall_flags + // send query signal + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_ITEM_MELEE) + . = shieldcall_flags + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + . |= shieldcall.handle_item_melee(src, ., fake_attack, weapon, e_args) + +/** + * Runs shieldcalls for handle_bullet + * + * * Use this instead of manually looping, as it fires a signal that makes things like /datum/passive_parry spin up. + * + * @params + * * bullet_act_args - indexed list of bullet_act args. + * * shieldcall_returns - existing returns from other shieldcalls + * * fake_attack - just checking! + * * shieldcall_flags - shieldcall flags. [code/__DEFINES/combat/shieldcall.dm] + * + * @return SHIELDCALL_FLAG_TERMINATE or NONE + */ +/atom/proc/atom_shieldcall_handle_bullet(list/bullet_act_args, fake_attack, shieldcall_flags) + SHOULD_NOT_SLEEP(TRUE) + // cannot parry yourself + var/obj/projectile/proj = bullet_act_args[BULLET_ACT_ARG_PROJECTILE] + if(proj.firer == src && (bullet_act_args[BULLET_ACT_ARG_FLAGS] & PROJECTILE_IMPACT_POINT_BLANK)) + return shieldcall_flags + // send query signal + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_BULLET_ACT) + . = shieldcall_flags + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + . |= shieldcall.handle_bullet(src, ., fake_attack, bullet_act_args) + +/** + * Runs shieldcalls for handle_throw_impact + * + * @params + * * thrown - the thrown object's data + * * fake_attack - just checking! + * * shieldcall_flags - shieldcall flags. [code/__DEFINES/combat/shieldcall.dm] + * + * @return SHIELDCALL_FLAG_* flags + */ +/atom/proc/atom_shieldcall_handle_throw_impact(datum/thrownthing/thrown, fake_attack, shieldcall_flags) + SHOULD_NOT_SLEEP(TRUE) + // cannot parry yourself + if(thrown.thrower == src && thrown.dist_travelled <= 1) + return shieldcall_flags + // send query signal + SEND_SIGNAL(src, COMSIG_ATOM_SHIELDCALL_ITERATION, ATOM_SHIELDCALL_ITERATING_THROW_IMPACT) + . = shieldcall_flags + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + . |= shieldcall.handle_throw_impact(src, ., fake_attack, thrown) diff --git a/code/game/atoms/atom.dm b/code/game/atoms/atom.dm index b9bcee5fe40d..95403d73eb59 100644 --- a/code/game/atoms/atom.dm +++ b/code/game/atoms/atom.dm @@ -882,6 +882,7 @@ return T.has_gravity() +// todo: annihilate this in favor of ATOM_PASS_INCORPOREAL /atom/proc/is_incorporeal() return FALSE diff --git a/code/game/atoms/buckling.dm b/code/game/atoms/buckling.dm index 45e68574979c..0068578f2ab7 100644 --- a/code/game/atoms/buckling.dm +++ b/code/game/atoms/buckling.dm @@ -1,12 +1,12 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// +//* Copyright (c) 2024 silicons *// /atom/movable/MouseDroppedOn(atom/dropping, mob/user, proximity, params) if(drag_drop_buckle_interaction(dropping, user)) return CLICKCHAIN_DO_NOT_PROPAGATE return ..() -/atom/movable/attack_hand(mob/user, list/params) +/atom/movable/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() . = ..() @@ -40,7 +40,7 @@ return FALSE if(!user.Adjacent(src) || !A.Adjacent(src)) return FALSE - if(SEND_SIGNAL(src, COMSIG_MOVABLE_DRAG_DROP_BUCKLE_INTERACTION, A, user) & COMPONENT_HANDLED_BUCKLE_INTERACTION) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_DRAG_DROP_BUCKLE_INTERACTION, A, user) & SIGNAL_RAISE_BUCKLE_INTERACTION_HANDLED) return TRUE if(!buckle_allowed || (buckle_flags & BUCKLING_NO_DEFAULT_BUCKLE)) return FALSE @@ -69,7 +69,7 @@ // todo: refactor below if(user.incapacitated()) return TRUE - if(SEND_SIGNAL(src, COMSIG_MOVABLE_CLICK_UNBUCKLE_INTERACTION, user) & COMPONENT_HANDLED_BUCKLE_INTERACTION) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_CLICK_UNBUCKLE_INTERACTION, user) & SIGNAL_RAISE_BUCKLE_INTERACTION_HANDLED) return TRUE if(!buckle_allowed || (buckle_flags & BUCKLING_NO_DEFAULT_UNBUCKLE)) return FALSE @@ -92,7 +92,7 @@ /atom/movable/proc/user_unbuckle_mob(mob/M, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) . = SEND_SIGNAL(src, COMSIG_MOVABLE_USER_UNBUCKLE_MOB, M, flags, user, semantic) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE . = unbuckle_mob(M, flags, user, semantic) if(!.) @@ -126,7 +126,7 @@ /atom/movable/proc/user_buckle_mob(mob/M, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) . = SEND_SIGNAL(src, COMSIG_MOVABLE_USER_BUCKLE_MOB, M, flags, user, semantic) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE if((buckle_flags & BUCKLING_NO_USER_BUCKLE_OTHER_TO_SELF) && (user == src)) return FALSE @@ -166,7 +166,7 @@ if(M == src) return FALSE - if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_BUCKLE_MOB, M, flags, user, semantic) & COMPONENT_BLOCK_BUCKLE_OPERATION) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_BUCKLE_MOB, M, flags, user, semantic) & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE if(!(flags & BUCKLE_OP_FORCE) && !can_buckle_mob(M, flags, user, semantic)) @@ -244,9 +244,9 @@ /atom/movable/proc/can_buckle_mob(mob/M, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) . = SEND_SIGNAL(src, COMSIG_MOVABLE_CAN_BUCKLE_MOB, M, flags, user, semantic) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE - else if(. & COMPONENT_FORCE_BUCKLE_OPERATION) + else if(. & SIGNAL_RAISE_FORCE_BUCKLE_OPERATION) return TRUE if(!(flags & BUCKLE_OP_IGNORE_LOC) && !M.Adjacent(src)) return FALSE @@ -276,9 +276,9 @@ /atom/movable/proc/can_unbuckle_mob(mob/M, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) . = SEND_SIGNAL(src, COMSIG_MOVABLE_CAN_UNBUCKLE_MOB, M, flags, user, semantic) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE - else if(. & COMPONENT_FORCE_BUCKLE_OPERATION) + else if(. & SIGNAL_RAISE_FORCE_BUCKLE_OPERATION) return TRUE return TRUE /** @@ -329,7 +329,7 @@ /atom/movable/proc/resist_unbuckle_interaction(mob/M) set waitfor = FALSE ASSERT(M in buckled_mobs) - if(SEND_SIGNAL(src, COMSIG_MOVABLE_RESIST_UNBUCKLE_INTERACTION, M) & COMPONENT_HANDLED_BUCKLE_INTERACTION) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_RESIST_UNBUCKLE_INTERACTION, M) & SIGNAL_RAISE_BUCKLE_INTERACTION_HANDLED) return if(!buckle_allowed || (buckle_flags & BUCKLING_NO_DEFAULT_RESIST)) return FALSE @@ -410,14 +410,14 @@ */ /mob/proc/buckled(atom/movable/AM, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) - SEND_SIGNAL(src, COMSIG_MOB_BUCKLED, AM, flags, user, semantic) + SEND_SIGNAL(src, COMSIG_MOB_BUCKLED_TO, AM, flags, user, semantic) /** * called when we're unbuckled from something */ /mob/proc/unbuckled(atom/movable/AM, flags, mob/user, semantic) SHOULD_CALL_PARENT(TRUE) - SEND_SIGNAL(src, COMSIG_MOB_UNBUCKLED, AM, flags, user, semantic) + SEND_SIGNAL(src, COMSIG_MOB_UNBUCKLED_FROM, AM, flags, user, semantic) /** * can we buckle to something? @@ -426,10 +426,10 @@ */ /mob/proc/can_buckle(atom/movable/AM, flags, mob/user, semantic, movable_opinion) SHOULD_CALL_PARENT(TRUE) - . = SEND_SIGNAL(src, COMSIG_MOB_CAN_BUCKLE, AM, flags, user, semantic, movable_opinion) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + . = SEND_SIGNAL(src, COMSIG_MOB_CAN_BUCKLE_TO, AM, flags, user, semantic, movable_opinion) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE - else if(. & COMPONENT_FORCE_BUCKLE_OPERATION) + else if(. & SIGNAL_RAISE_FORCE_BUCKLE_OPERATION) return TRUE return movable_opinion @@ -440,10 +440,10 @@ */ /mob/proc/can_unbuckle(atom/movable/AM, flags, mob/user, semantic, movable_opinion) SHOULD_CALL_PARENT(TRUE) - . = SEND_SIGNAL(src, COMSIG_MOB_CAN_UNBUCKLE, AM, flags, user, semantic, movable_opinion) - if(. & COMPONENT_BLOCK_BUCKLE_OPERATION) + . = SEND_SIGNAL(src, COMSIG_MOB_CAN_UNBUCKLE_FROM, AM, flags, user, semantic, movable_opinion) + if(. & SIGNAL_RAISE_BLOCK_BUCKLE_OPERATION) return FALSE - else if(. & COMPONENT_FORCE_BUCKLE_OPERATION) + else if(. & SIGNAL_RAISE_FORCE_BUCKLE_OPERATION) return TRUE return movable_opinion diff --git a/code/game/atoms/defense.dm b/code/game/atoms/defense.dm deleted file mode 100644 index 6fbf61c69c72..000000000000 --- a/code/game/atoms/defense.dm +++ /dev/null @@ -1,547 +0,0 @@ -//* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// - -//! Welcome to the atom damage module. -//! Enjoy the bitfield and #define vomit. - -// todo: everything needs comsigs comsigs comsigs - -//? Hooks / External - -/** - * todo: implement on most atoms/generic damage system - * todo: replace legacy_ex_act entirely with this - * - * React to being hit by an explosive shockwave - * - * ? Tip for overrides: . = ..() when you want signal to be sent, mdify power before if you need to; to ignore parent - * ? block power, just `return power` in your proc after . = ..(). - * - * @params - * - power - power our turf was hit with - * - direction - DIR_BIT bits; can bwe null if it wasn't a wave explosion!! - * - explosion - explosion automata datum; can be null - * - * @return power after falloff (e.g. hit with 30 power, return 20 to apply 10 falloff) - */ -/atom/proc/ex_act(power, dir, datum/automata/wave/explosion/E) - SHOULD_CALL_PARENT(TRUE) - SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, power, dir, E) - return power - -/** - * called on melee hit - * - * @params - * * user - person attacking - * * weapon - weapon used - * * target_zone - zone targeted - * * mult - damage multiplier - * - * @return clickchain flags to append - */ -/atom/proc/melee_act(mob/user, obj/item/weapon, target_zone, mult = 1) - return CLICKCHAIN_DO_NOT_ATTACK - -/** - * called on unarmed melee hit - * - * @params - * * user - person attacking - * * style - unarmed attack datum - * * target_zone - zone targeted - * * mult - damage multiplier - * - * @return clickchain flags to append - */ -/atom/proc/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, mult = 1) - return CLICKCHAIN_DO_NOT_ATTACK - -//? Damage API - -/** - * takes damage from a generic attack, taking into account armor but not shields. - * this does not handle playing sounds / anything, this is strictly generic damage handling - * usable by anything. - * - * @params - * * damage - raw damage - * * tier - (optional) penetration / attack tier - * * flag - (optional) armor flag as seen in [code/__DEFINES/combat/armor.dm]; leave out to not run armor. - * * mode - (optional) damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * gradual - loud effects like glass clanging should not happen. use for stuff like acid and fire. - * - * @return raw damage taken - */ -/atom/proc/inflict_atom_damage(damage, tier, flag, mode, attack_type, datum/weapon, gradual) - if(!integrity_enabled) - return 0 - if(integrity_flags & INTEGRITY_INDESTRUCTIBLE) - return 0 - if(flag) - var/list/returned = run_armor(arglist(args)) - damage = returned[1] - mode = returned[4] - if(!damage) - return - . = integrity - damage_integrity(damage) - . = . - integrity - -//? Hitsound API - -// todo: stuff like metal limbs punching walls making special sounds -// todo: this probably needs a rework - -/** - * gets hitsound override. return a value to be fed into playsound, or null for default. - * - * @params - * * damage_type - damage type like brute / burn / etc - * * damage_mode - damage mode for piercing / whatnot - * * attack_type - attack type enum like melee / projectile / thrown / unarmed / etc - * * weapon - attacking /obj/item for melee / thrown, /obj/projectile for ranged, /mob for unarmed - */ -/atom/proc/hitsound_override(damage_type, damage_mode, attack_type, datum/weapon) - return // default is null - -/atom/proc/hitsound_melee(obj/item/I) - . = I.attacksound_override(src, ATTACK_TYPE_MELEE) - if(!isnull(.)) - return - . = hitsound_override(I.damtype, I.damage_mode, ATTACK_TYPE_MELEE, I) - if(.) - return - . = (I.damtype == BURN? hit_sound_burn : hit_sound_brute) || I.attack_sound - if(.) - return - switch(I.damtype) - if(BRUTE) - return "swing_hit" - if(BURN) - return "'sound/items/welder.ogg" - else - return "swing_hit" - -/atom/proc/hitsound_projectile(obj/projectile/P) - //? todo: projectile gets final say - . = hitsound_override(P.damtype, P.damage_mode, ATTACK_TYPE_PROJECTILE, P) - if(.) - return - return islist(P.impact_sounds)? pick(P.impact_sounds) : P.impact_sounds - -/atom/proc/hitsound_throwhit(obj/item/I) - . = I.attacksound_override(src, ATTACK_TYPE_THROWN) - if(!isnull(.)) - return - . = hitsound_override(I.damtype, I.damage_mode, ATTACK_TYPE_THROWN, I) - if(.) - return - . = (I.damtype == BURN? hit_sound_burn : hit_sound_brute) || I.attack_sound - if(.) - return - switch(I.damtype) - if(BRUTE) - return "swing_hit" - if(BURN) - return 'sound/items/welder.ogg' - else - return "swing_hit" - -/atom/proc/hitsound_unarmed(mob/M, datum/unarmed_attack/style) - //? todo: style gets final say - . = hitsound_override(M, style.damage_mode, ATTACK_TYPE_UNARMED, style) - if(.) - return - // todo: way to override this from style side? we don't just want hitsound brute/burn. - . = (style.damage_type == BURN? hit_sound_burn : hit_sound_brute) || style.attack_sound - -//? Direct Integrity - -/** - * damages integrity directly, ignoring armor / shields - * - * @params - * * amount - how much - * * gradual - burst or gradual? if you want to play a sound or something, you usually want to check this. - * * do_not_break - skip calling atom_break - */ -/atom/proc/damage_integrity(amount, gradual, do_not_break) - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - var/was_working = integrity > integrity_failure - integrity = max(0, integrity - amount) - if(was_working && integrity <= integrity_failure && !do_not_break) - atom_break() - if(integrity <= 0) - atom_destruction() - -/** - * heals integrity directly - * - * @params - * * amount - how much - * * gradual - burst or gradual? if you want to play a sound or something, you usually want to check this. - * * do_not_fix - skip calling atom_fix - * - * @return amount healed - */ -/atom/proc/heal_integrity(amount, gradual, do_not_fix) - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - var/was_failing = integrity <= integrity_failure - . = integrity - integrity = min(integrity_max, integrity + amount) - . = integrity - . - if(was_failing && integrity > integrity_failure && !do_not_fix) - atom_fix() - -/** - * directly sets integrity - ignores armor / sihelds - * - * will not call [damage_integrity] or [heal_integrity] - * will call [atom_break], [atom_fix], [atom_destruction] - * - * @params - * * amount - how much to set to? - */ -/atom/proc/set_integrity(amount) - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - var/was_failing = integrity <= integrity_failure - integrity = clamp(amount, 0, integrity_max) - if(!was_failing && integrity <= integrity_failure) - atom_break() - else if(was_failing && integrity > integrity_failure) - atom_fix() - if(integrity <= 0) - atom_destruction() - -/** - * sets max integrity - will automatically reduce integrity if it's above max. - * - * will not call [damage_integrity] - * will call [atom_break], [atom_fix], [atom_destruction] - * - * @params - * * amount - how much to set to - */ -/atom/proc/set_max_integrity(amount) - integrity_max = max(amount, 0) - if(integrity < integrity_max) - return - var/was_broken = integrity <= integrity_failure - integrity = integrity_max - if(!was_broken && (integrity <= integrity_failure)) - atom_break() - if(integrity <= 0) - atom_destruction() - -/** - * sets integrity and max integrity - will automatically reduce integrity if it's above max. - * - * will not call [damage_integrity] - * will call [atom_break], [atom_fix], [atom_destruction] - * - * @params - * * integrity - how much to set integrity to - * * integrity_max - how much to set integrity_max to - */ -/atom/proc/set_full_integrity(integrity, integrity_max) - src.integrity_max = max(integrity_max, 0) - var/was_broken = src.integrity <= integrity_failure - src.integrity = clamp(integrity, 0, integrity_max) - var/now_broken = integrity <= integrity_failure - if(!was_broken && now_broken) - atom_break() - else if(was_broken && !now_broken) - atom_fix() - if(integrity <= 0) - atom_destruction() - -/** - * Set integrity to a multiple of initial. - * - * And fully restore it if specified. - * Otherwise, will retain the last percentage. - */ -/atom/proc/set_multiplied_integrity(factor, restore) - var/was_broken = src.integrity <= integrity_failure - if(restore) - integrity = integrity_max = initial(integrity_max) * factor - if(was_broken && integrity > integrity_failure) - atom_fix() - return - var/ratio = integrity / integrity_max - integrity_max = initial(integrity_max) * factor - integrity = integrity_max * ratio - var/now_broken = integrity <= integrity_failure - if(!was_broken && now_broken) - atom_break() - else if(was_broken && !now_broken) - atom_fix() - if(integrity <= 0) - atom_destruction() - -/** - * adjusts integrity - routes directly to [damage_integrity] and [heal_integrity] - * - * will call [damage_integrity] - * will call [atom_break], [atom_fix], [atom_destruction] - * - * @params - * * amount - how much - * * gradual - burst or gradual? - * * no_checks - do not call fix/break - */ -/atom/proc/adjust_integrity(amount, gradual, no_checks) - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - if(amount > 0) - return heal_integrity(amount, gradual, no_checks) - else - return damage_integrity(amount, gradual, no_checks) - -/** - * adjusts max integrity - will automatically reduce integrity if it's above max. - * - * will call [damage_integrity] - * will call [atom_break], [atom_fix], [atom_destruction] - * - * @params - * * amount - how much to adjust by - * * gradual - burst or gradual? - */ -/atom/proc/adjust_max_integrity(amount, gradual) - // lazy route lmao - set_max_integrity(integrity_max + amount) - -/** - * percent integrity, rounded. - */ -/atom/proc/percent_integrity(round_to = 0.1) - return integrity_max? round(integrity / integrity_max, round_to) : 0 - -/atom/proc/is_integrity_broken() - return atom_flags & ATOM_BROKEN - -/atom/proc/is_integrity_damaged() - return integrity < integrity_max - -//? Thresholds & Events - -/** - * called when integrity reaches 0 from a non 0 value - */ -/atom/proc/atom_destruction() - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - if(!(integrity_flags & INTEGRITY_NO_DECONSTRUCT)) - deconstruct(ATOM_DECONSTRUCT_DESTROYED) - -/** - * called when integrity drops below or at integrity_failure - * - * if integrity_failure is 0, this is called before destruction. - */ -/atom/proc/atom_break() - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - if(integrity > integrity_failure) - damage_integrity(integrity - integrity_failure, do_not_break = TRUE) - atom_flags |= ATOM_BROKEN - -/** - * called when integrity rises above integrity_failure - * - * if integrity_failure is 0, this still works. - */ -/atom/proc/atom_fix() - SHOULD_CALL_PARENT(TRUE) - SHOULD_NOT_SLEEP(TRUE) - if(integrity < integrity_failure) - heal_integrity(integrity_failure - integrity + 1, do_not_fix = TRUE) - atom_flags &= ~ATOM_BROKEN - -//? Deconstruction - -/** - * called to semantically deconstruct an atom - * - * @params - * * method - how we were deconstructed - */ -/atom/proc/deconstruct(method = ATOM_DECONSTRUCT_DISASSEMBLED) - SHOULD_NOT_OVERRIDE(TRUE) - - // send signal - // todo: signal - // do da funny logic - deconstructed(method) - // drop things after so things that rely on having objects don't break - drop_products(method, drop_location()) - // goodbye, cruel world - break_apart(method) - -/** - * called to actually destroy ourselves - */ -/atom/proc/break_apart(method) - qdel(src) - -/** - * called when we are deconstructed - * - * **do not drop products in here** - * - * @params - * - method - how we were deconstructed - */ -/atom/proc/deconstructed(method) - return - -/** - * called to drop the products of deconstruction - * - * @params - * * method - how we were deconstructed - * * where - where to drop products; set in base if null to drop_location(). - */ -/atom/proc/drop_products(method, atom/where = drop_location()) - return - -/** - * called to move a product to a place - * - * @params - * * method - how we were deconstructed - * * dropping - movable in question - * * where - where to move to - */ -/atom/proc/drop_product(method, atom/movable/dropping, atom/where) - dropping.forceMove(where || drop_location()) - -//? Armor - -/** - * resets our armor to initial values - */ -/atom/proc/reset_armor() - set_armor(initial(armor_type)) - -/** - * sets our armor - * - * @params - * * what - list of armor values or a /datum/armor path - */ -/atom/proc/set_armor(what) - armor = fetch_armor_struct(what) - -/** - * gets our armor datum or otherwise make sure it exists - */ -/atom/proc/fetch_armor() - RETURN_TYPE(/datum/armor) - return armor || (armor = generate_armor()) - -/** - * get default armor datum - */ -/atom/proc/generate_armor() - return fetch_armor_struct(armor_type) - -/** - * calculates the resulting damage from an attack, taking into account our armor and soak - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * - * @return args as list. - */ -/atom/proc/check_armor(damage, tier, flag, mode, attack_type, datum/weapon) - damage = fetch_armor().resultant_damage(damage, tier, flag) - return args.Copy() - -/** - * runs armor against an incoming attack - * this proc can have side effects - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * - * @return args as list. - */ -/atom/proc/run_armor(damage, tier, flag, mode, attack_type, datum/weapon) - damage = fetch_armor().resultant_damage(damage, tier, flag) - return args.Copy() - -//? shieldcalls - -/** - * checks for shields - * not always accurate - * - * params are modified and then returned as a list. - * - * @params - * * damage - raw damage - * * damtype - damage type - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/dcs/signals/atoms/signals_atom_defense.dm] - * * retval - shieldcall flags passed through components. [code/__DEFINES/dcs/signals/atoms/signals_atom_defense.dm] - * - * @return modified args as list - */ -/atom/proc/atom_shieldcheck(damage, damtype, tier, flag, mode, attack_type, datum/weapon, list/additional = list(), retval = NONE) - retval |= SHIELDCALL_JUST_CHECKING - for(var/datum/shieldcall/calling as anything in shieldcalls) - calling.handle_shieldcall(src, args) - return args.Copy() - -/** - * runs an attack against shields - * side effects are allowed - * - * params are modified and then returned as a list - * - * @params - * * damage - raw damage - * * damtype - damage type - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * additional - a way to retrieve data out of the shieldcall, passed in by attacks. [code/__DEFINES/combat/shieldcall.dm] - * * retval - shieldcall flags passed through components. [code/__DEFINES/combat/shieldcall.dm] - * - * @return modified args as list - */ -/atom/proc/atom_shieldcall(damage, damtype, tier, flag, mode, attack_type, datum/weapon, list/additional = list(), retval = NONE) - for(var/datum/shieldcall/calling as anything in shieldcalls) - calling.handle_shieldcall(src, args) - return args.Copy() - -/atom/proc/register_shieldcall(datum/shieldcall/delegate) - LAZYINITLIST(shieldcalls) - BINARY_INSERT(delegate, shieldcalls, /datum/shieldcall, delegate, priority, COMPARE_KEY) - -/atom/proc/unregister_shieldcall(datum/shieldcall/delegate) - LAZYREMOVE(shieldcalls, delegate) diff --git a/code/game/atoms/defense_old.dm b/code/game/atoms/defense_old.dm index ab7d141eed0c..d162a947cbfd 100644 --- a/code/game/atoms/defense_old.dm +++ b/code/game/atoms/defense_old.dm @@ -12,10 +12,6 @@ // todo: SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ATOM_EMP_ACT, severity) -/atom/proc/bullet_act(obj/projectile/P, def_zone) - P.on_hit(src, 0, def_zone) - . = 0 - // Called when a blob expands onto the tile the atom occupies. /atom/proc/blob_act() return diff --git a/code/game/atoms/movable/movement.dm b/code/game/atoms/movable/movable-movement.dm similarity index 85% rename from code/game/atoms/movable/movement.dm rename to code/game/atoms/movable/movable-movement.dm index 64c2e193c2be..412d21532059 100644 --- a/code/game/atoms/movable/movement.dm +++ b/code/game/atoms/movable/movable-movement.dm @@ -208,77 +208,96 @@ if(glide_size_override) set_glide_size(glide_size_override, FALSE) - if(loc != newloc) - if (!(direct & (direct - 1))) //Cardinal move - . = ..() - else //Diagonal move, split it into cardinal moves - moving_diagonally = FIRST_DIAG_STEP - var/first_step_dir - // The `&& moving_diagonally` checks are so that a force_move taking - // place due to a Crossed, Bumped, etc. call will interrupt - // the second half of the diagonal movement, or the second attempt - // at a first half if step() fails because we hit something. - if (direct & NORTH) - if (direct & EAST) - if (step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if (moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if (direct & WEST) - if (step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if (moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if (direct & SOUTH) - if (direct & EAST) - if (step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if (moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - else if (direct & WEST) - if (step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if (moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - if(moving_diagonally == SECOND_DIAG_STEP) - if(!.) - setDir(first_step_dir) - else if (!inertia_moving) - inertia_next_move = world.time + inertia_move_delay - newtonian_move(direct) - moving_diagonally = NOT_IN_DIAG_STEP - --in_move - return - else // trying to move to the same place + var/time_since_last_move = world.time - last_move + last_move = world.time + + // trying to move to the same place + if(loc == newloc) if(direct) last_move_dir = direct setDir(direct) --in_move - return TRUE // not moving is technically success + ai_tracking?.track_movement(time_since_last_move, NONE) + // not moving is technically success + return TRUE + + //Cardinal move + if (!(direct & (direct - 1))) + . = ..() + //Diagonal move, split it into cardinal moves + else + moving_diagonally = FIRST_DIAG_STEP + var/first_step_dir + // The `&& moving_diagonally` checks are so that a force_move taking + // place due to a Crossed, Bumped, etc. call will interrupt + // the second half of the diagonal movement, or the second attempt + // at a first half if step() fails because we hit something. + if (direct & NORTH) + if (direct & EAST) + if (step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if (moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if (direct & WEST) + if (step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if (moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if (direct & SOUTH) + if (direct & EAST) + if (step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if (moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + else if (direct & WEST) + if (step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if (moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + if(moving_diagonally == SECOND_DIAG_STEP) + if(!.) + setDir(first_step_dir) + else if (!inertia_moving) + inertia_next_move = world.time + inertia_move_delay + newtonian_move(direct) + moving_diagonally = NOT_IN_DIAG_STEP + --in_move + // track movement if we're no longer in a move; this way this fires only once for diag steps + if(ai_tracking && !in_move) + ai_tracking.track_movement(time_since_last_move, . ? direct : (moving_diagonally == SECOND_DIAG_STEP ? first_step_dir : NONE)) + return if(!loc || (loc == oldloc && oldloc != newloc)) last_move_dir = NONE --in_move - return + // track movement if we're no longer in a move; this way this fires only once for diag steps + if(ai_tracking && !in_move) + ai_tracking.track_movement(time_since_last_move, NONE) + return FALSE - if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, glide_size_override)) //movement failed due to buckled mob(s) + //movement failed due to buckled mob(s) + if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, glide_size_override)) + last_move_dir = NONE --in_move + // track movement if we're no longer in a move; this way this fires only once for diag steps + if(ai_tracking && !in_move) + ai_tracking.track_movement(time_since_last_move, NONE) return FALSE if(.) @@ -311,6 +330,10 @@ --in_move + // track movement if we're no longer in a move; this way this fires only once for diag steps + if(ai_tracking && !in_move) + ai_tracking.track_movement(time_since_last_move, direct) + // legacy move_speed = world.time - l_move_time l_move_time = world.time @@ -438,13 +461,21 @@ * Make sure you know what you're doing if you override or call this. * * This *must* be a pure proc. You cannot act on the atom if you override this! Use Bump() for that. + * + * * **warning**: `newloc` is a ss13 construct. BYOND-native pixel movement doesn't have that. + * + * @params + * * AM - the thing trying to un-overlap us + * * newloc - (optional) where they're going */ /atom/movable/Uncross(atom/movable/AM, atom/newloc) . = TRUE if(SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSS, AM) & COMPONENT_MOVABLE_BLOCK_UNCROSS) return FALSE - if(isturf(newloc) && !CheckExit(AM, newloc)) - return FALSE + if(isturf(newloc)) + var/our_opinion = CheckExit(AM, newloc) + if(!our_opinion && (AM.generic_canpass || !AM.CanPassThrough(src, newloc, our_opinion))) + return FALSE /** * Called when something uncrosses us. @@ -514,7 +545,7 @@ var/list/old_grabbed if(allow_grabbed) old_grabbed = list() - for(var/mob/M in grabbing()) + for(var/mob/M in get_grabbing()) if(check_grab(M) < allow_grabbed) continue old_grabbed += M @@ -542,7 +573,7 @@ /mob/getLocationTransitForceMoveTargets(atom/destination, recurse_levels = 0, allow_buckled = TRUE, allow_pulled = TRUE, allow_grabbed = GRAB_PASSIVE) . = ..() if(allow_grabbed) - var/list/grabbing = grabbing() + var/list/grabbing = get_grabbing() for(var/mob/M in grabbing) if(check_grab(M) < allow_grabbed) continue @@ -571,6 +602,9 @@ /atom/movable/proc/doMove(atom/destination) . = FALSE + // completely reset ai tracking + ai_tracking?.reset_movement() + ++in_move var/atom/oldloc = loc diff --git a/code/game/atoms/movable/throwing.dm b/code/game/atoms/movable/movable-throw.dm similarity index 98% rename from code/game/atoms/movable/throwing.dm rename to code/game/atoms/movable/movable-throw.dm index b6b21f4ce20c..742afd66cd8f 100644 --- a/code/game/atoms/movable/throwing.dm +++ b/code/game/atoms/movable/movable-throw.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// +//* Copyright (c) 2024 silicons *// //! Welcome to unoptimized hell. Enjoy your comsigs. @@ -186,7 +186,7 @@ // user momentum var/user_speed = L.movement_delay() // 1 decisecond of margin - if(L.last_move_dir && (L.last_move_time >= (world.time - user_speed + 1))) + if(L.last_move_dir && (L.last_self_move >= (world.time - user_speed + 1))) user_speed = max(user_speed, world.tick_lag) // convert to tiles per **decisecond** user_speed = 1/user_speed diff --git a/code/game/atoms/movable/movable.dm b/code/game/atoms/movable/movable.dm index d2e4a5f95cf7..9be084a15cc5 100644 --- a/code/game/atoms/movable/movable.dm +++ b/code/game/atoms/movable/movable.dm @@ -25,6 +25,8 @@ //* AI Holders /// AI holder bound to us var/datum/ai_holder/ai_holder + /// AI tracking datum. Handled by procs in [code/modules/ai/ai_tracking.dm]. + var/datum/ai_tracking/ai_tracking //? Intrinsics /// movable flags - see [code/__DEFINES/_flags/atoms.dm] @@ -36,28 +38,39 @@ /// Set this to TRUE if we are not a [TILE_MOVER]! var/pixel_movement = FALSE /// Whatever we're pulling. + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/atom/movable/pulling /// Who's currently pulling us + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/atom/movable/pulledby /// If false makes [CanPass][/atom/proc/CanPass] call [CanPassThrough][/atom/movable/proc/CanPassThrough] on this type instead of using default behaviour + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/generic_canpass = TRUE /// Pass flags. var/pass_flags = NONE /// movement calls we're in + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/in_move = 0 /// a direction, or null + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/moving_diagonally = NOT_IN_DIAG_STEP /// attempt to resume grab after moving instead of before. This is what atom/movable is pulling us during move-from-pulling. + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/atom/movable/moving_from_pull /// Direction of our last move. + /// /// * this variable is not visible and should not be edited in the map editor. var/tmp/last_move_dir = NONE + /// world.time of our last move + /// + /// * this variable is not visible and should not be edited in the map editor. + var/tmp/last_move /// Our default glide_size. Null to use global default. var/default_glide_size /// Movement types, see [code/__DEFINES/flags/movement.dm] diff --git a/code/game/atoms/movable/special/overlay.dm b/code/game/atoms/movable/special/overlay.dm index 56889c02b356..be8a448f50dd 100644 --- a/code/game/atoms/movable/special/overlay.dm +++ b/code/game/atoms/movable/special/overlay.dm @@ -5,10 +5,8 @@ // todo: nuke this shit from orbit, this is awful. var/atom/master = null -/atom/movable/overlay/attackby(a, b) - if (src.master) - return src.master.attackby(a, b) +/atom/movable/overlay/attackby(obj/item/I, mob/user, list/params, clickchain_flags, damage_multiplier) + return master.attackby(arglist(args)) -/atom/movable/overlay/attack_hand(a, b, c) - if (src.master) - return src.master.attack_hand(a, b, c) +/atom/movable/overlay/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) + return master.attack_hand(arglist(args)) diff --git a/code/game/atoms/vv.dm b/code/game/atoms/vv.dm index 2373c129b6aa..f36655a135e7 100644 --- a/code/game/atoms/vv.dm +++ b/code/game/atoms/vv.dm @@ -78,7 +78,7 @@ else if(was_failing && !now_failing) atom_fix() -/atom/vv_get_var(var_name) +/atom/vv_get_var(var_name, resolve) switch(var_name) if(NAMEOF(src, base_layer)) if(isnull(base_layer)) diff --git a/code/game/click/context.dm b/code/game/click/context.dm index 41001e80a434..1a0686db1d69 100644 --- a/code/game/click/context.dm +++ b/code/game/click/context.dm @@ -25,10 +25,14 @@ return TRUE return FALSE +/** + * @params + * * e_args - the actor data or a single mob + */ /atom/proc/context_menu(datum/event_args/actor/e_args) set waitfor = FALSE // admin proccall support - WRAP_MOB_TO_ACTOR_EVENT_ARGS(e_args) + E_ARGS_WRAP_USER_TO_ACTOR(e_args) // todo: dynamically rebuild menu based on distance? var/client/receiving = e_args.initiator.client if(isnull(receiving)) diff --git a/code/game/click/items-item_attack_chain.dm b/code/game/click/items-item_attack_chain.dm new file mode 100644 index 000000000000..8812171abc2d --- /dev/null +++ b/code/game/click/items-item_attack_chain.dm @@ -0,0 +1,70 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +//* Code for interacting as an item. *// + +/** + * Called to initiate a usage as item. + * + * * Called before tool_attack_chain() + * * Called before melee_attack_chain() + * + * @params + * * target - thing we're being used on + * * e_args - the clickchain data, including who's doing the interaction + * * clickchain_flags - the clickchain flags given + * * reachability_check - a callback used for reachability checks. if none, defaults to mob.Reachability when in clickcode, can always reach otherwise. + * + * @return CLICKCHAIN_* flags. These are added / interpreted the caller. + */ +/obj/item/proc/item_attack_chain(atom/target, datum/event_args/actor/clickchain/e_args, clickchain_flags, datum/callback/reachability_check) + SHOULD_NOT_OVERRIDE(TRUE) + + . = clickchain_flags + + . |= SEND_SIGNAL(src, COMSIG_ITEM_USING_AS_ITEM, target, e_args, clickchain_flags, reachability_check) + if(. & CLICKCHAIN_DO_NOT_PROPAGATE) + return + . |= using_as_item(target, e_args, clickchain_flags, reachability_check) + if(. & CLICKCHAIN_DO_NOT_PROPAGATE) + return + + . |= SEND_SIGNAL(src, COMSIG_ATOM_USING_ITEM_ON, src, e_args, clickchain_flags, reachability_check) + if(. & CLICKCHAIN_DO_NOT_PROPAGATE) + return + . |= target.using_item_on(src, e_args, clickchain_flags, reachability_check) + if(. & CLICKCHAIN_DO_NOT_PROPAGATE) + return + +/** + * Called when being used as an item. + * + * * If handled, return CLICKCHAIN_DO_NOT_PROPAGATE + * + * @params + * * target - thing we're being used on + * * e_args - the clickchain data, including who's doing the interaction + * * clickchain_flags - the clickchain flags given + * * reachability_check - a callback used for reachability checks. if none, defaults to mob.Reachability when in clickcode, can always reach otherwise. + * + * @return CLICKCHAIN_* flags. These are added / interpreted by the caller. + */ +/obj/item/proc/using_as_item(atom/target, datum/event_args/actor/clickchain/e_args, clickchain_flags, datum/callback/reachability_check) + return NONE + +/** + * Called when an item is being used on us as an item. + * + * * If handled, return CLICKCHAIN_DO_NOT_PROPAGATE + * * item's `item_attack_chain`, and then `used_as_item` are called first. + * + * @params + * * using - the item + * * e_args - the clickchain data, including who's doing the interaction + * * clickchain_flags - the clickchain flags given + * * reachability_check - a callback used for reachability checks. if none, defaults to mob.Reachability when in clickcode, can always reach otherwise. + * + * @return CLICKCHAIN_* flags. These are added / interpreted by the caller. + */ +/atom/proc/using_item_on(obj/item/using, datum/event_args/actor/clickchain/e_args, clickchain_flags, datum/callback/reachability_check) + return NONE diff --git a/code/game/click/items.dm b/code/game/click/items.dm index 368585390948..2e3111c6c0e3 100644 --- a/code/game/click/items.dm +++ b/code/game/click/items.dm @@ -10,6 +10,9 @@ /** * Called when trying to click something that the user can Reachability() to. * + * todo: this should allow passing in a clickchain datum instead. + * todo: lazy_melee_attack() for when you don't want to. + * * @params * - target - thing hitting * - user - user using us @@ -24,6 +27,11 @@ // todo: inject something here for 'used as item' much like /tg/, to get rid of attackby pattern + var/datum/event_args/actor/clickchain/e_args = new(user) + + if((. |= item_attack_chain(target, e_args, ., params)) & CLICKCHAIN_DO_NOT_PROPAGATE) + return + if((. |= tool_attack_chain(target, user, ., params)) & CLICKCHAIN_DO_NOT_PROPAGATE) return @@ -35,6 +43,7 @@ // - item use & receive item use (item_interaction() on /atom, definiteily) // - tool use & receive tool use (we already have tool_interaction() on /atom) // - melee attack & receive melee attack (melee_interaction() on /atom? not melee_act directly?) + // - melee attack shouldn't require attackby() to allow it to, it should be automatic on harm intent (?) // - the item should have final say but we need a way to allow click redirections so.. if(resolve_attackby(target, user, params, null, .)) return CLICKCHAIN_DO_NOT_PROPAGATE @@ -45,6 +54,9 @@ /** * Called when trying to click something that the user can't Reachability() to. * + * todo: this should allow passing in a clickchain datum instead. + * todo: lazy_ranged_attack() for when you don't want to. + * * @params * - target - thing hitting * - user - user using us @@ -312,6 +324,8 @@ * called when we're used to attack a non-mob * this doesn't actually need to be an obj. * + * todo: purge mult + * * @params * * target - atom being attacked * * clickchain - the /datum/event_args/actor/clickchain arguments included @@ -337,17 +351,21 @@ return CLICKCHAIN_DO_NOT_PROPAGATE //? legacy: decloak clickchain.performer.break_cloak() + // set mult + clickchain.damage_multiplier *= mult // click cooldown // todo: clickcd rework clickchain.performer.setClickCooldown(clickchain.performer.get_attack_speed(src)) // animation clickchain.performer.animate_swing_at_target(target) // perform the hit - . = melee_object_hit(target, clickchain, clickchain_flags, mult) + . = melee_object_hit(target, clickchain, clickchain_flags) /** * called at base of attack_object after standard melee attack misses * + * todo: purge mult + * * @return clickchain flags to append * * @params @@ -377,7 +395,7 @@ * * clickchain_flags - __DEFINES/procs/clickcode.dm flags * * mult - damage multiplier */ -/obj/item/proc/melee_object_hit(atom/target, datum/event_args/actor/clickchain/clickchain, clickchain_flags, mult = 1) +/obj/item/proc/melee_object_hit(atom/target, datum/event_args/actor/clickchain/clickchain, clickchain_flags) SHOULD_CALL_PARENT(TRUE) // harmless, just tap them and leave @@ -405,7 +423,7 @@ visible = SPAN_DANGER("[target] has been [islist(attack_verb)? pick(attack_verb) : attack_verb] with [src] by [clickchain.performer]!") ) // damage - target.melee_act(clickchain.performer, src, mult = mult) + target.melee_act(clickchain.performer, src, null, clickchain) // animate target.animate_hit_by_weapon(clickchain.performer, src) diff --git a/code/game/click/mobs.dm b/code/game/click/mobs.dm index 7ac0ad4c7373..3f0f13fb0bb3 100644 --- a/code/game/click/mobs.dm +++ b/code/game/click/mobs.dm @@ -1,6 +1,9 @@ /** * Called when trying to click on someone we can Reachability() to without an item in hand. * + * todo: this should allow passing in a clickchain datum instead. + * todo: lazy_melee_attack() for when you don't want to. + * * @params * - target - thing we're clicking * - clickchain_flags - see [code/__DEFINES/procs/clickcode.dm] @@ -98,7 +101,7 @@ log_attack(key_name(src), ismob(target)? key_name(target) : "[target] ([ref(target)])", "attacked with [style.attack_name] newhp ~[newhp || "unknown"]") /mob/proc/melee_attack_hit(atom/target, datum/event_args/actor/clickchain/clickchain, datum/unarmed_attack/style, clickchain_flags, target_zone, mult) - . = target.unarmed_act(src, style, target_zone, mult) + . = target.unarmed_act(src, style, target_zone, clickchain) if(. & CLICKCHAIN_ATTACK_MISSED) return . | melee_attack_miss(target, clickchain, style, clickchain_flags, target_zone, mult) // todo: the rest of this proc not qdel-safe diff --git a/code/game/click/other_mobs.dm b/code/game/click/other_mobs.dm index 8cbc452f270a..55a50d5cb69d 100644 --- a/code/game/click/other_mobs.dm +++ b/code/game/click/other_mobs.dm @@ -25,12 +25,16 @@ if(istype(G) && G.Touch(A,1)) return - A.attack_hand(src) + A.attack_hand(src, new /datum/event_args/actor/clickchain(src)) /// Return TRUE to cancel other attack hand effects that respect it. -// todo: /datum/event_args/actor/clickchain -/atom/proc/attack_hand(mob/user, list/params) - var/datum/event_args/actor/clickchain/e_args = new(user, target = src, intent = user.a_intent, params = params) +// todo: better desc +// todo: e_args is not specified all the time, yet. +/atom/proc/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) + // todo: remove + if(isnull(e_args)) + e_args = new(user) + // end if(on_attack_hand(e_args)) return TRUE if(user.a_intent == INTENT_HARM) diff --git a/code/game/click/reachability.dm b/code/game/click/reachability.dm index 2604d39f2207..4fec1fe4b952 100644 --- a/code/game/click/reachability.dm +++ b/code/game/click/reachability.dm @@ -187,7 +187,8 @@ * checks what we can directly reach */ /atom/movable/proc/DirectAccess() - return list(src, loc) + . = list(src, loc) + SEND_SIGNAL(src, COMSIG_ATOM_REACHABILITY_DIRECTACCESS, .) /mob/DirectAccess() return ..() + get_equipped_items() diff --git a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/contraband.dm b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/contraband.dm index 18172ed42cab..c5dfcecbd017 100644 --- a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/contraband.dm +++ b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/contraband.dm @@ -96,7 +96,7 @@ name = "sapper's kit" /obj/item/storage/box/cargo_null_entry_kit/sapper/legacy_spawn_contents() - new /obj/item/melee/energy/sword/ionic_rapier(src) + new /obj/item/melee/transforming/energy/sword/ionic_rapier(src) new /obj/item/storage/box/syndie_kit/space(src) new /obj/item/storage/box/syndie_kit/demolitions(src) new /obj/item/multitool/ai_detector(src) diff --git a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/munitions.dm b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/munitions.dm index d3fc78511e77..23757c6fe2cf 100644 --- a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/munitions.dm +++ b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/munitions.dm @@ -32,7 +32,7 @@ name = "Weapons - Experimental weapons crate" contains = list( /obj/item/gun/energy/xray = 2, - /obj/item/shield/energy = 2, + /obj/item/shield/transforming/energy = 2, ) container_type = /obj/structure/closet/crate/secure/corporate/nanotrasen container_name = "Experimental weapons crate" diff --git a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/security.dm b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/security.dm index bb09c938ed6b..fd0841c892d1 100644 --- a/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/security.dm +++ b/code/game/content/factions/corporations/nanotrasen/nanotrasen-supply/security.dm @@ -466,7 +466,7 @@ /obj/item/clothing/accessory/badge/holo/hos, /obj/item/clothing/accessory/holster/waist, /obj/item/melee/telebaton, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/clothing/head/beret/sec/corporate/hos, /obj/item/flashlight/maglight, ) diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index 3f2835b7d3dd..7fd8de7c9530 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -300,7 +300,7 @@ src.add_hiddenprint(user) nano_ui_interact(user) -/obj/machinery/computer/scan_consolenew/attack_hand(mob/user, list/params) +/obj/machinery/computer/scan_consolenew/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!..()) nano_ui_interact(user) diff --git a/code/game/gamemodes/changeling/powers/armblade.dm b/code/game/gamemodes/changeling/powers/armblade.dm index db77320eed6c..bc89f0efdeff 100644 --- a/code/game/gamemodes/changeling/powers/armblade.dm +++ b/code/game/gamemodes/changeling/powers/armblade.dm @@ -47,6 +47,14 @@ return 1 return 0 +// todo: full rework of all of this; changeling weapons are balanced by a numbskull holy shit fuck bay +// - block chances are way, way too high +// - insufficient armor penetration (ironically) for citrp combat balancing directives +// - need to rethink changeling defensives in general, they shouldn't be reliant on parrying + +/datum/parry_frame/passive_block/armblade + parry_sfx = 'sound/weapons/slash.ogg' + /obj/item/melee/changeling name = "arm weapon" desc = "A grotesque weapon made out of bone and flesh that cleaves through people as a hot knife through butter." @@ -62,8 +70,11 @@ var/weapType = "weapon" var/weapLocation = "arm" - defend_chance = 40 // The base chance for the weapon to parry. - projectile_parry_chance = 15 // The base chance for a projectile to be deflected. + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 40; + parry_chance_projectile = 15; + parry_frame = /datum/parry_frame/passive_block/armblade; + } /obj/item/melee/changeling/Initialize(mapload) . = ..() @@ -106,28 +117,6 @@ host.embedded -= src qdel(src) -/obj/item/melee/changeling/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(defend_chance)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/slash.ogg', 50, 1) - return 1 - if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/slash.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/melee/changeling/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - if(user.incapacitated() || !istype(damage_source, /obj/projectile)) - return 0 - - var/bad_arc = global.reverse_dir[user.dir] - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - /obj/item/melee/changeling/arm_blade name = "arm blade" desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter." @@ -138,15 +127,23 @@ edge = 1 pry = 1 attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - defend_chance = 60 - projectile_parry_chance = 25 + + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 60; + parry_chance_projectile = 25; + parry_frame = /datum/parry_frame/passive_block/armblade; + } /obj/item/melee/changeling/arm_blade/greater name = "arm greatblade" desc = "A grotesque blade made out of bone and flesh that cleaves through people and armor as a hot knife through butter." armor_penetration = 30 - defend_chance = 70 - projectile_parry_chance = 35 + + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 70; + parry_chance_projectile = 35; + parry_frame = /datum/parry_frame/passive_block/armblade; + } /obj/item/melee/changeling/claw name = "hand claw" @@ -156,13 +153,21 @@ sharp = 1 edge = 1 attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - defend_chance = 50 - projectile_parry_chance = 15 + + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 50; + parry_chance_projectile = 15; + parry_frame = /datum/parry_frame/passive_block/armblade; + } /obj/item/melee/changeling/claw/greater name = "hand greatclaw" damage_force = 20 armor_penetration = 20 pry = 1 - defend_chance = 60 - projectile_parry_chance = 25 + + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 60; + parry_chance_projectile = 25; + parry_frame = /datum/parry_frame/passive_block/armblade; + } diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 21fc3fd4d9c5..4c10d5b86868 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -37,7 +37,7 @@ . = ..() START_PROCESSING(SSobj, src) -/obj/structure/cult/pylon/attack_hand(mob/user, list/params) +/obj/structure/cult/pylon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) attackpylon(user, 5) /obj/structure/cult/pylon/attack_generic(var/mob/user, var/damage) @@ -46,12 +46,13 @@ /obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob) attackpylon(user, W.damage_force) -/obj/structure/cult/pylon/inflict_atom_damage(damage, tier, flag, mode, attack_type, datum/weapon, gradual) +/obj/structure/cult/pylon/inflict_atom_damage(damage, damage_type, damage_tier, damage_flag, damage_mode, hit_zone, attack_type, datum/weapon) pylonhit(damage) return damage -/obj/structure/cult/pylon/bullet_act(var/obj/projectile/Proj) - pylonhit(Proj.get_structure_damage()) +/obj/structure/cult/pylon/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + pylonhit(proj.get_structure_damage()) /obj/structure/cult/pylon/proc/pylonhit(var/damage) if(!isbroken) diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index e2e982ddfe11..5f9c08ac0772 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -101,7 +101,7 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa"," return -/obj/effect/rune/attack_hand(mob/user, list/params) +/obj/effect/rune/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!ishuman(user)) return var/mob/living/carbon/human/H = user diff --git a/code/game/gamemodes/events/black_hole.dm b/code/game/gamemodes/events/black_hole.dm index 1bc471d0be17..38e926507bd4 100644 --- a/code/game/gamemodes/events/black_hole.dm +++ b/code/game/gamemodes/events/black_hole.dm @@ -11,7 +11,7 @@ /obj/effect/bhole/Initialize(mapload) . = ..() - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) /obj/effect/bhole/process() if(!isturf(loc)) diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 3e5e2ef17498..7d7e72d4aa4e 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -162,7 +162,7 @@ if(T) if(istype(T, /turf/simulated/wall)) var/turf/simulated/wall/W = T - W.inflict_atom_damage(wall_power, flag = ARMOR_BOMB) // Stronger walls can halt asteroids. + W.inflict_atom_damage(wall_power, damage_flag = ARMOR_BOMB) // Stronger walls can halt asteroids. /obj/effect/meteor/proc/get_shield_damage() return max(((max(hits, 2)) * (heavy + 1) * rand(6, 12)) / hitpwr , 0) diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 9ac85720d743..9ecdb22d753d 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -139,7 +139,7 @@ datum/hSB continue if(istype(O, /obj/item/dummy)) continue - if(istype(O, /obj/item/melee/energy/sword)) + if(istype(O, /obj/item/melee/transforming/energy/sword)) continue if(istype(O, /obj/structure)) continue diff --git a/code/game/gamemodes/technomancer/devices/shield_armor.dm b/code/game/gamemodes/technomancer/devices/shield_armor.dm index 6bee50e73dd6..73892bfbcc7b 100644 --- a/code/game/gamemodes/technomancer/devices/shield_armor.dm +++ b/code/game/gamemodes/technomancer/devices/shield_armor.dm @@ -33,7 +33,25 @@ qdel(spark_system) return ..() -/obj/item/clothing/suit/armor/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/clothing/suit/armor/shield/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/clothing/suit/armor/shield/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/clothing/suit/armor/shield/proc/shieldcall(mob/user, list/shieldcall_args, fake_attack) + var/damage = shieldcall_args[SHIELDCALL_ARG_DAMAGE] + var/damage_source = shieldcall_args[SHIELDCALL_ARG_WEAPON] + //Since this is a pierce of armor that is passive, we do not need to check if the user is incapacitated. if(!active) return 0 @@ -66,12 +84,11 @@ P.agony -= agony_blocked P.damage = P.damage - damage_blocked - user.visible_message("\The [user]'s [src] absorbs [attack_text]!") + user.visible_message("\The [user]'s [src] absorbs the attack!") to_chat(user, "Your shield has absorbed most of \the [damage_source].") spark_system.start() playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 0 // This shield does not block all damage, so returning 0 is needed to tell the game to apply the new damage. /obj/item/clothing/suit/armor/shield/attack_self(mob/user) . = ..() diff --git a/code/game/gamemodes/technomancer/devices/tesla_armor.dm b/code/game/gamemodes/technomancer/devices/tesla_armor.dm index d731b5b37af2..c3fe7f29bc91 100644 --- a/code/game/gamemodes/technomancer/devices/tesla_armor.dm +++ b/code/game/gamemodes/technomancer/devices/tesla_armor.dm @@ -21,7 +21,27 @@ var/normal_icon_state = "tesla_armor_0" var/cooldown_to_charge = 15 SECONDS -/obj/item/clothing/suit/armor/tesla/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/clothing/suit/armor/tesla/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/clothing/suit/armor/tesla/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/clothing/suit/armor/tesla/proc/shieldcall(mob/user, list/shieldcall_args, fake_attack) + var/damage_source = shieldcall_args[SHIELDCALL_ARG_WEAPON] + + var/datum/event_args/actor/clickchain/clickchain = shieldcall_args[SHIELDCALL_ARG_CLICKCHAIN] + var/mob/attacker = clickchain?.performer + //First, some retaliation. if(active) if(istype(damage_source, /obj/projectile)) @@ -47,7 +67,7 @@ ready = 1 update_icon() to_chat(user, "\The [src] is ready to protect you once more.") - visible_message("\The [user]'s [src.name] blocks [attack_text]!") + visible_message("\The [user]'s [src.name] blocks the attack!") update_icon() return 1 return 0 diff --git a/code/game/gamemodes/technomancer/spells/energy_siphon.dm b/code/game/gamemodes/technomancer/spells/energy_siphon.dm index cc51d809667d..b05bc7fb2670 100644 --- a/code/game/gamemodes/technomancer/spells/energy_siphon.dm +++ b/code/game/gamemodes/technomancer/spells/energy_siphon.dm @@ -172,30 +172,22 @@ icon_state = "lightning" range = WORLD_ICON_SIZE * 6 power = 5 // This fires really fast, so this may add up if someone keeps standing in the beam. - penetrating = 5 + legacy_penetrating = 5 -/obj/projectile/beam/lightning/energy_siphon/Bump(atom/A as mob|obj|turf|area, forced=0) - if(A == firer) // For this, you CAN shoot yourself. - on_impact(A) - - density = 0 - invisibility = 101 - - qdel(src) - return 1 - ..() - -/obj/projectile/beam/lightning/energy_siphon/projectile_attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0) - if(target_mob == firer) // This shouldn't actually occur due to Bump(), but just in-case. - return 1 +/obj/projectile/beam/lightning/energy_siphon/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + var/mob/living/target_mob = target + if(!isliving(target_mob)) + return if(ishuman(target_mob)) // Otherwise someone else stood in the beam and is going to pay for it. var/mob/living/carbon/human/H = target_mob var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO)) H.electrocute_act(power, src, H.get_siemens_coefficient_organ(affected), affected, 0) else target_mob.electrocute_act(power, src, 0.75, BP_TORSO) - return 0 // Since this is a continous beam, it needs to keep flying until it hits the Technomancer. - + return PROJECTILE_IMPACT_PIERCE #undef SIPHON_CELL_TO_ENERGY #undef SIPHON_FBP_TO_ENERGY diff --git a/code/game/gamemodes/technomancer/spells/projectile/chain_lightning.dm b/code/game/gamemodes/technomancer/spells/projectile/chain_lightning.dm index 8d4254e3f33d..75160bb02526 100644 --- a/code/game/gamemodes/technomancer/spells/projectile/chain_lightning.dm +++ b/code/game/gamemodes/technomancer/spells/projectile/chain_lightning.dm @@ -34,7 +34,15 @@ var/list/hit_mobs = list() //Mobs which were already hit. var/power = 35 //How hard it will hit for with electrocute_act(), decreases with each bounce. -/obj/projectile/beam/chain_lightning/projectile_attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0) +// todo: rework this shit :/ + +/obj/projectile/beam/chain_lightning/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & (PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT | PROJECTILE_IMPACT_BLOCKED)) + return + var/mob/living/target_mob = target + if(!isliving(target_mob)) + return //First we shock the guy we just hit. if(ishuman(target_mob)) var/mob/living/carbon/human/H = target_mob @@ -70,11 +78,9 @@ if(new_target) var/turf/curloc = get_turf(target_mob) curloc.visible_message("\The [src] bounces to \the [new_target]!") - redirect(new_target.x, new_target.y, curloc, firer) + legacy_redirect(new_target.x, new_target.y, curloc, firer) bounces-- - - return 0 - return 1 + return PROJECTILE_IMPACT_PIERCE diff --git a/code/game/gamemodes/technomancer/spells/projectile/lightning.dm b/code/game/gamemodes/technomancer/spells/projectile/lightning.dm index 4ba595480de3..58e3bd19292d 100644 --- a/code/game/gamemodes/technomancer/spells/projectile/lightning.dm +++ b/code/game/gamemodes/technomancer/spells/projectile/lightning.dm @@ -32,7 +32,13 @@ var/power = 60 //How hard it will hit for with electrocute_act(). -/obj/projectile/beam/lightning/projectile_attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0) +/obj/projectile/beam/lightning/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & (PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT | PROJECTILE_IMPACT_BLOCKED)) + return + var/mob/living/target_mob = target + if(!isliving(target_mob)) + return if(ishuman(target_mob)) var/mob/living/carbon/human/H = target_mob var/obj/item/organ/external/affected = H.get_organ(check_zone(BP_TORSO)) diff --git a/code/game/gamemodes/technomancer/spells/reflect.dm b/code/game/gamemodes/technomancer/spells/reflect.dm index b1bd2526260f..293f5de0827d 100644 --- a/code/game/gamemodes/technomancer/spells/reflect.dm +++ b/code/game/gamemodes/technomancer/spells/reflect.dm @@ -30,64 +30,79 @@ spark_system = null return ..() -/obj/item/spell/reflect/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/spell/reflect/pickup(mob/user, flags, atom/oldLoc) + . = ..() + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/spell/reflect/dropped(mob/user, flags, atom/newLoc) + . = ..() + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/spell/reflect/proc/shieldcall(datum/source, list/shieldcall_args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_TERMINATE) + return + var/mob/user = source if(user.incapacitated()) - return 0 + return - var/damage_to_energy_cost = (damage_to_energy_multiplier * damage) + var/mob/attacker + var/damage_to_energy_cost = (damage_to_energy_multiplier * shieldcall_args[SHIELDCALL_ARG_DAMAGE]) + var/damage_source = shieldcall_args[SHIELDCALL_ARG_WEAPON] if(!pay_energy(damage_to_energy_cost)) to_chat(owner, "Your shield fades due to lack of energy!") qdel(src) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) + return - if(istype(damage_source, /obj/projectile)) - var/obj/projectile/P = damage_source + if(istype(damage_source, /obj/projectile)) + var/obj/projectile/P = damage_source + attacker = P.firer - if(P.starting && !P.reflected) - visible_message("\The [user]'s [src.name] reflects [attack_text]!") + if(P.starting && !P.reflected) + visible_message("\The [user]'s [src.name] reflects [P]!") - var/turf/curloc = get_turf(user) + var/turf/curloc = get_turf(user) - // redirect the projectile - P.redirect(P.starting.x, P.starting.y, curloc, user) - P.reflected = 1 - if(check_for_scepter()) - P.damage = P.damage * 1.5 + // redirect the projectile + P.legacy_redirect(P.starting.x, P.starting.y, curloc, user) + P.reflected = 1 + if(check_for_scepter()) + P.damage = P.damage * 1.5 - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - // now send a log so that admins don't think they're shooting themselves on purpose. - log_and_message_admins("[user] reflected [attacker]'s attack back at them.") - - if(!reflecting) - reflecting = 1 - spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected. - to_chat(owner, "Your shield fades due being used up!") - qdel(src) - - return PROJECTILE_CONTINUE // complete projectile permutation - - else if(istype(damage_source, /obj/item)) - var/obj/item/W = damage_source + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + // now send a log so that admins don't think they're shooting themselves on purpose. if(attacker) - W.melee_interaction_chain(attacker) - to_chat(attacker, "Your [damage_source.name] goes through \the [src] in one location, comes out \ - on the same side, and hits you!") - - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - log_and_message_admins("[user] reflected [attacker]'s attack back at them.") - if(!reflecting) - reflecting = 1 - spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected. - to_chat(owner, "Your shield fades due being used up!") - qdel(src) - return 1 - return 0 + if(!reflecting) + reflecting = 1 + spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected. + to_chat(owner, "Your shield fades due being used up!") + qdel(src) + + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAG_ATTACK_PASSTHROUGH | SHIELDCALL_FLAG_ATTACK_REDIRECT | SHIELDCALL_FLAG_ATTACK_BLOCKED | SHIELDCALL_FLAG_TERMINATE + + else if(istype(damage_source, /obj/item)) + var/obj/item/W = damage_source + var/datum/event_args/actor/clickchain/clickchain = shieldcall_args[SHIELDCALL_ARG_CLICKCHAIN] + attacker = clickchain.performer + if(attacker) + W.melee_interaction_chain(attacker, attacker) + to_chat(attacker, "Your [damage_source] goes through \the [src] in one location, comes out \ + on the same side, and hits you!") + + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + + log_and_message_admins("[user] reflected [attacker]'s attack back at them.") + + if(!reflecting) + reflecting = 1 + spawn(2 SECONDS) //To ensure that most or all of a burst fire cycle is reflected. + to_chat(owner, "Your shield fades due being used up!") + qdel(src) + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAG_ATTACK_REDIRECT | SHIELDCALL_FLAG_ATTACK_BLOCKED | SHIELDCALL_FLAG_TERMINATE diff --git a/code/game/gamemodes/technomancer/spells/shield.dm b/code/game/gamemodes/technomancer/spells/shield.dm index ad005ef6572b..cd487b6917e3 100644 --- a/code/game/gamemodes/technomancer/spells/shield.dm +++ b/code/game/gamemodes/technomancer/spells/shield.dm @@ -28,7 +28,24 @@ spark_system = null return ..() -/obj/item/spell/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/spell/shield/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/spell/shield/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/spell/shield/proc/shieldcall(mob/user, list/shieldcall_args, fake_attack) + var/damage = shieldcall_args[SHIELDCALL_ARG_DAMAGE] + if(user.incapacitated()) return 0 @@ -50,12 +67,8 @@ qdel(src) return 0 - //block as long as they are not directly behind us - var/bad_arc = global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - user.visible_message("\The [user]'s [src] blocks [attack_text]!") - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - adjust_instability(2) - return 1 - return 0 + + user.visible_message("\The [user]'s [src] blocks the attack!") + spark_system.start() + playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) + adjust_instability(2) diff --git a/code/game/machinery/CableLayer.dm b/code/game/machinery/CableLayer.dm index 0156cb84f94c..c70604f40cd3 100644 --- a/code/game/machinery/CableLayer.dm +++ b/code/game/machinery/CableLayer.dm @@ -17,7 +17,7 @@ . = ..() layCable(new_turf,M_Dir) -/obj/machinery/cablelayer/attack_hand(mob/user, list/params) +/obj/machinery/cablelayer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!cable&&!on) to_chat(user, SPAN_WARNING("\The [src] doesn't have any cable loaded.")) return diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index dea856fe44ae..839932093016 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -41,7 +41,7 @@ if(prob(25)) density = 0 -/obj/machinery/optable/attack_hand(mob/user, list/params) +/obj/machinery/optable/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(MUTATION_HULK in usr.mutations) visible_message(SPAN_DANGER("\The [usr] destroys \the [src]!")) density = FALSE diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index c894c4e47efd..88b51467ac46 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -37,7 +37,7 @@ /obj/machinery/sleep_console/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/sleep_console/attack_hand(mob/user, list/params) +/obj/machinery/sleep_console/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return 1 diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index dfb3d955096c..999f424386a8 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -156,7 +156,7 @@ ///Volume of interface sounds. var/clickvol = 40 var/obj/item/circuitboard/circuit = null - ///If false, SSmachines. If true, SSfastprocess. + ///If false, SSmachines. If true, SSprocess_5fps. var/speed_process = FALSE var/interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_SET_MACHINE @@ -176,7 +176,7 @@ if(!speed_process) START_MACHINE_PROCESSING(src) else - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) if(!mapload) // area handles this power_change() @@ -186,7 +186,7 @@ if(!speed_process) STOP_MACHINE_PROCESSING(src) else - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) if(component_parts) for(var/atom/A in component_parts) if(A.loc == src) // If the components are inside the machine, delete them. @@ -236,22 +236,6 @@ panel_open = panel_opened update_appearance() -/obj/machinery/legacy_ex_act(severity) - switch(severity) - if(1.0) - qdel(src) - return - if(2.0) - if(prob(50)) - qdel(src) - return - if(3.0) - if(prob(25)) - qdel(src) - return - else - return - /obj/machinery/vv_edit_var(var_name, new_value) if(var_name == NAMEOF(src, use_power)) update_use_power(new_value) @@ -304,7 +288,7 @@ return attack_hand(user) // todo: refactor -/obj/machinery/attack_hand(mob/user, list/params) +/obj/machinery/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() if(IsAdminGhost(user)) diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index c694e19bbdd0..f5780aa48c7d 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -240,7 +240,7 @@ . = ..() return attack_hand(user) -/obj/machinery/body_scanconsole/attack_hand(mob/user, list/params) +/obj/machinery/body_scanconsole/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index 477852c0229c..d7b7a582f8dc 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -57,7 +57,7 @@ /obj/machinery/ai_slipper/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/ai_slipper/attack_hand(mob/user, list/params) +/obj/machinery/ai_slipper/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return if((get_dist(src, user) > 1)) diff --git a/code/game/machinery/atm_ret_field.dm b/code/game/machinery/atm_ret_field.dm index 296cb80b75b1..c49273e59647 100644 --- a/code/game/machinery/atm_ret_field.dm +++ b/code/game/machinery/atm_ret_field.dm @@ -221,11 +221,11 @@ update_nearby_tiles() //Force ZAS update . = ..() -/obj/structure/atmospheric_retention_field/attack_hand(mob/user, list/params) +/obj/structure/atmospheric_retention_field/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(density) - visible_message("You touch the retention field, and it crackles faintly. Tingly!") + to_chat(user, "You touch the retention field, and it crackles faintly. Tingly!") else - visible_message("You try to touch the retention field, but pass through it like it isn't even there.") + to_chat(user, "You try to touch the retention field, but pass through it like it isn't even there.") /obj/structure/atmospheric_retention_field/legacy_ex_act() return diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm index 44618bf10b65..5e548e8bb2ea 100644 --- a/code/game/machinery/atmo_control.dm +++ b/code/game/machinery/atmo_control.dm @@ -86,7 +86,7 @@ radio_controller.remove_object(src, frequency) ..() -/obj/machinery/computer/general_air_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/general_air_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..(user)) return diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm index 2b89e6e6b2e8..b9a8ce78f739 100644 --- a/code/game/machinery/bioprinter.dm +++ b/code/game/machinery/bioprinter.dm @@ -125,7 +125,7 @@ . = ..() -/obj/machinery/organ_printer/attack_hand(mob/user, list/params) +/obj/machinery/organ_printer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/bomb_tester.dm b/code/game/machinery/bomb_tester.dm index 070d81a1fff5..935abaed349e 100644 --- a/code/game/machinery/bomb_tester.dm +++ b/code/game/machinery/bomb_tester.dm @@ -107,7 +107,7 @@ return ..() -/obj/machinery/bomb_tester/attack_hand(mob/user, list/params) +/obj/machinery/bomb_tester/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) ui_interact(user) diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index 2918472e4a30..9f7585241cac 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -18,7 +18,7 @@ /obj/machinery/button/attackby(obj/item/W, mob/user) return attack_hand(user) -/obj/machinery/button/attack_hand(obj/item/W, mob/user) +/obj/machinery/button/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE playsound(src, 'sound/machines/button.ogg', 100, TRUE) @@ -30,7 +30,7 @@ desc = "A remote control switch for polarized windows." var/range = 7 -/obj/machinery/button/windowtint/attack_hand(mob/user, list/params) +/obj/machinery/button/windowtint/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (..()) return TRUE else diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 26fa71139dcf..673b3de996f2 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -110,7 +110,7 @@ CREATE_WALL_MOUNTING_TYPES(/obj/machinery/camera) src.view_range = num GLOB.cameranet.updateVisibility(src, 0) -/obj/machinery/camera/attack_hand(mob/user, list/params) +/obj/machinery/camera/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/carbon/human/L = user if(!istype(L)) return diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index a851040377c9..2da823206971 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -145,7 +145,7 @@ else icon_state = "cameracase" -/obj/item/camera_assembly/attack_hand(mob/user, list/params) +/obj/item/camera_assembly/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!anchored) ..() diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index 813b3c83f07b..0beca8c9766c 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -81,7 +81,7 @@ else if(default_part_replacement(user, W)) return -/obj/machinery/cell_charger/attack_hand(mob/user, list/params) +/obj/machinery/cell_charger/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(charging) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index f099b8ffa241..3f04bf3cb143 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -60,7 +60,7 @@ add_hiddenprint(user) return attack_hand(user) -/obj/machinery/clonepod/attack_hand(mob/user, list/params) +/obj/machinery/clonepod/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((isnull(occupant)) || (machine_stat & NOPOWER)) return if((!isnull(occupant)) && (occupant.stat != 2)) diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index e73cb842326b..853f74f96f03 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -48,7 +48,7 @@ return ui_interact(user) -/obj/machinery/computer/operating/attack_hand(mob/user, list/params) +/obj/machinery/computer/operating/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return ui_interact(user) diff --git a/code/game/machinery/computer/RCON_Console.dm b/code/game/machinery/computer/RCON_Console.dm index 6dae0a2772ec..68d751075a29 100644 --- a/code/game/machinery/computer/RCON_Console.dm +++ b/code/game/machinery/computer/RCON_Console.dm @@ -26,7 +26,7 @@ // Proc: attack_hand() // Parameters: 1 (user - Person which clicked this computer) // Description: Opens UI of this machine. -/obj/machinery/computer/rcon/attack_hand(mob/user, list/params) +/obj/machinery/computer/rcon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() ui_interact(user) diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index ff694a8bba40..43b425dfcfa8 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -48,7 +48,7 @@ to_chat(user, SPAN_NOTICE("There is no AI loaded onto this computer, and no AI loaded onto [I]. What exactly are you trying to do here?")) return ..() -/obj/machinery/computer/aifixer/attack_hand(mob/user, list/params) +/obj/machinery/computer/aifixer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return ui_interact(user) diff --git a/code/game/machinery/computer/arcade/amputation.dm b/code/game/machinery/computer/arcade/amputation.dm index 3725192b5169..a978a40bdf79 100644 --- a/code/game/machinery/computer/arcade/amputation.dm +++ b/code/game/machinery/computer/arcade/amputation.dm @@ -7,7 +7,7 @@ icon_screen = null circuit = /obj/item/circuitboard/arcade/amputation -/obj/machinery/computer/arcade/amputation/attack_hand(mob/user, list/modifiers) +/obj/machinery/computer/arcade/amputation/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!iscarbon(user)) return var/mob/living/carbon/c_user = user diff --git a/code/game/machinery/computer/arcade/claw_machine.dm b/code/game/machinery/computer/arcade/claw_machine.dm index 9d0e3bc741ff..30b76f6a3f66 100644 --- a/code/game/machinery/computer/arcade/claw_machine.dm +++ b/code/game/machinery/computer/arcade/claw_machine.dm @@ -160,7 +160,7 @@ /obj/machinery/computer/arcade/clawmachine/Initialize(mapload) . = ..() -/obj/machinery/computer/arcade/clawmachine/attack_hand(mob/user, list/params) +/obj/machinery/computer/arcade/clawmachine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return ui_interact(user) diff --git a/code/game/machinery/computer/arcade/orion.dm b/code/game/machinery/computer/arcade/orion.dm index c1c365ded1b6..77d2a6c5f98a 100644 --- a/code/game/machinery/computer/arcade/orion.dm +++ b/code/game/machinery/computer/arcade/orion.dm @@ -487,7 +487,6 @@ GLOBAL_LIST_INIT(orion_events, generate_orion_events()) /mob/living/simple_mob/hostile/humanoid/orion name = "spaceport security" desc = "Premier corporate security forces for all spaceports found along the Orion Trail." - faction = "orion" loot_list = list() //del_on_death = TRUE diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index d22cfb98a8c2..d0b1000e0724 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -19,7 +19,7 @@ var/global/list/minor_air_alarms = list() atmosphere_alarm.unregister_alarm(src) return ..() -/obj/machinery/computer/atmos_alert/attack_hand(mob/user, list/params) +/obj/machinery/computer/atmos_alert/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/computer/atmos_alert/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm index 4bda92467aba..edf5ecf561a9 100644 --- a/code/game/machinery/computer/atmos_control.dm +++ b/code/game/machinery/computer/atmos_control.dm @@ -33,7 +33,7 @@ /obj/machinery/computer/atmoscontrol/attack_ai(mob/user) ui_interact(user) -/obj/machinery/computer/atmoscontrol/attack_hand(mob/user, list/params) +/obj/machinery/computer/atmoscontrol/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 3748b3970ce3..b7630fc39d64 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -28,7 +28,7 @@ /obj/machinery/computer/security/ui_interact(mob/user, datum/tgui/ui = null) camera.ui_interact(user, ui) -/obj/machinery/computer/security/attack_hand(mob/user, list/params) +/obj/machinery/computer/security/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index cc7b57599f99..906da036bf0f 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -94,7 +94,7 @@ /obj/machinery/computer/cloning/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/computer/cloning/attack_hand(mob/user, list/params) +/obj/machinery/computer/cloning/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) add_fingerprint(user) diff --git a/code/game/machinery/computer/command/card.dm b/code/game/machinery/computer/command/card.dm index 2428138350ee..d306d3bd22d5 100644 --- a/code/game/machinery/computer/command/card.dm +++ b/code/game/machinery/computer/command/card.dm @@ -97,7 +97,7 @@ /obj/machinery/computer/card/attack_ai(var/mob/user as mob) return attack_hand(user) -/obj/machinery/computer/card/attack_hand(mob/user, list/params) +/obj/machinery/computer/card/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(machine_stat & (NOPOWER|BROKEN)) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 401779e66468..afe0b69ef650 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -296,7 +296,7 @@ /obj/machinery/computer/communications/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/communications/attack_hand(mob/user, list/params) +/obj/machinery/computer/communications/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if ((LEGACY_MAP_DATUM) && !(src.z in (LEGACY_MAP_DATUM).contact_levels)) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 72bdbcf6831e..8c1265ef08e6 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -23,7 +23,7 @@ /obj/machinery/computer/crew/attack_ai(mob/user) attack_hand(user) -/obj/machinery/computer/crew/attack_hand(mob/user, list/params) +/obj/machinery/computer/crew/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index f6d34ea319fd..63601b4ab0e3 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -35,7 +35,7 @@ ..() -/obj/machinery/computer/aiupload/attack_hand(mob/user, list/params) +/obj/machinery/computer/aiupload/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & NOPOWER) to_chat(user, "The upload computer has no power!") return @@ -71,7 +71,7 @@ return ..() -/obj/machinery/computer/borgupload/attack_hand(mob/user, list/params) +/obj/machinery/computer/borgupload/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat& NOPOWER) to_chat(user, "The upload computer has no power!") return diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 40f65b2fb923..5705c2a9dd3a 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -101,7 +101,7 @@ /obj/machinery/computer/med_data/attack_ai(user as mob) return attack_hand(user) -/obj/machinery/computer/med_data/attack_hand(mob/user, list/params) +/obj/machinery/computer/med_data/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index d237215ad181..b14ff4229d32 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -78,7 +78,7 @@ linkedServer = message_servers[1] return ..() -/obj/machinery/computer/message_monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/message_monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return if(!istype(user)) diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm index ebeb9a383006..ee3abe342b34 100644 --- a/code/game/machinery/computer/pod.dm +++ b/code/game/machinery/computer/pod.dm @@ -46,7 +46,7 @@ /obj/machinery/computer/pod/attack_ai(var/mob/user as mob) return attack_hand(user) -/obj/machinery/computer/pod/attack_hand(mob/user, list/params) +/obj/machinery/computer/pod/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return @@ -142,7 +142,7 @@ title = "External Airlock Controls" req_access = list(ACCESS_FACTION_SYNDICATE) -/obj/machinery/computer/pod/old/syndicate/attack_hand(mob/user, list/params) +/obj/machinery/computer/pod/old/syndicate/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!allowed(user)) to_chat(user, "Access Denied") return diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm index 9171a6cf6115..e1acca8293c5 100644 --- a/code/game/machinery/computer/prisoner.dm +++ b/code/game/machinery/computer/prisoner.dm @@ -16,7 +16,7 @@ /obj/machinery/computer/prisoner/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/prisoner/attack_hand(mob/user, list/params) +/obj/machinery/computer/prisoner/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return ui_interact(user) diff --git a/code/game/machinery/computer/prisonshuttle.dm b/code/game/machinery/computer/prisonshuttle.dm index 398b877bec3e..04726d584174 100644 --- a/code/game/machinery/computer/prisonshuttle.dm +++ b/code/game/machinery/computer/prisonshuttle.dm @@ -27,7 +27,7 @@ var/prison_shuttle_timeleft = 0 /obj/machinery/computer/prison_shuttle/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/prison_shuttle/attack_hand(mob/user, list/params) +/obj/machinery/computer/prison_shuttle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!src.allowed(user) && (!hacked)) to_chat(user, "Access Denied.") return diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 477ab3ec77a0..db382db147eb 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -11,7 +11,7 @@ /obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) ui_interact(user) -/obj/machinery/computer/robotics/attack_hand(mob/user, list/params) +/obj/machinery/computer/robotics/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(machine_stat & (NOPOWER|BROKEN)) diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 981fc2fdfd99..70c3c201074d 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -87,7 +87,7 @@ /obj/machinery/computer/secure_data/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/computer/secure_data/attack_hand(mob/user, list/params) +/obj/machinery/computer/secure_data/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return add_fingerprint(user) diff --git a/code/game/machinery/computer/shutoff_monitor.dm b/code/game/machinery/computer/shutoff_monitor.dm index aca1c4988522..d362c764390e 100644 --- a/code/game/machinery/computer/shutoff_monitor.dm +++ b/code/game/machinery/computer/shutoff_monitor.dm @@ -15,7 +15,7 @@ QDEL_NULL(monitor) ..() -/obj/machinery/computer/shutoff_monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/shutoff_monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() monitor.ui_interact(user) diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm index e026411a300e..de865888aaab 100644 --- a/code/game/machinery/computer/skills.dm +++ b/code/game/machinery/computer/skills.dm @@ -65,7 +65,7 @@ return attack_hand(user) //Someone needs to break down the dat += into chunks instead of long ass lines. -/obj/machinery/computer/skills/attack_hand(mob/user, list/params) +/obj/machinery/computer/skills/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if ((LEGACY_MAP_DATUM) && !(src.z in (LEGACY_MAP_DATUM).contact_levels)) diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm index 4b565b2ae2e2..5814dbec0544 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -254,7 +254,7 @@ var/specops_shuttle_timeleft = 0 /obj/machinery/computer/specops_shuttle/emag_act(var/remaining_charges, var/mob/user) to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") -/obj/machinery/computer/specops_shuttle/attack_hand(mob/user, list/params) +/obj/machinery/computer/specops_shuttle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!allowed(user)) to_chat(user, "Access Denied.") return diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index 35b4b3187a4e..4f0ad22ba130 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -34,7 +34,7 @@ ui_interact(user) return -/obj/machinery/computer/station_alert/attack_hand(mob/user, list/params) +/obj/machinery/computer/station_alert/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/computer/supermatter_monitor_tgui.dm b/code/game/machinery/computer/supermatter_monitor_tgui.dm index c033af582418..6aaf789af6bc 100644 --- a/code/game/machinery/computer/supermatter_monitor_tgui.dm +++ b/code/game/machinery/computer/supermatter_monitor_tgui.dm @@ -17,7 +17,7 @@ /obj/machinery/computer/sm_monitor/attack_ai(mob/user) ui_interact(user) -/obj/machinery/computer/sm_monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/sm_monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/game/machinery/computer/supply.dm b/code/game/machinery/computer/supply.dm index 00e2d24145e2..840549abf934 100644 --- a/code/game/machinery/computer/supply.dm +++ b/code/game/machinery/computer/supply.dm @@ -35,7 +35,7 @@ /obj/machinery/computer/supplycomp/attack_ai(var/mob/user as mob) return attack_hand(user) -/obj/machinery/computer/supplycomp/attack_hand(mob/user, list/params) +/obj/machinery/computer/supplycomp/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(!allowed(user)) diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm index 1e681eb33c87..306a44b3afff 100644 --- a/code/game/machinery/computer/syndicate_specops_shuttle.dm +++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm @@ -191,7 +191,7 @@ var/syndicate_elite_shuttle_timeleft = 0 /obj/machinery/computer/syndicate_elite_shuttle/emag_act(var/remaining_charges, var/mob/user) to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") -/obj/machinery/computer/syndicate_elite_shuttle/attack_hand(mob/user, list/params) +/obj/machinery/computer/syndicate_elite_shuttle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!allowed(user)) to_chat(user, "Access Denied.") return diff --git a/code/game/machinery/computer/timeclock_vr.dm b/code/game/machinery/computer/timeclock_vr.dm index 79bc9bbfb951..55f5bf30f726 100644 --- a/code/game/machinery/computer/timeclock_vr.dm +++ b/code/game/machinery/computer/timeclock_vr.dm @@ -65,7 +65,7 @@ return . = ..() -/obj/machinery/computer/timeclock/attack_hand(mob/user, list/params) +/obj/machinery/computer/timeclock/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return user.set_machine(src) diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 237dd9408a77..6f4ef4ab70b5 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -82,7 +82,7 @@ if(occupant == user && !user.stat) go_out() -/obj/machinery/atmospherics/component/unary/cryo_cell/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/unary/cryo_cell/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) /** diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index afed66b803fb..4b803e871166 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -99,7 +99,7 @@ /obj/machinery/computer/cryopod/attack_ai() attack_hand() -/obj/machinery/computer/cryopod/attack_hand(mob/user = usr) +/obj/machinery/computer/cryopod/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return ..() diff --git a/code/game/machinery/deployable_vr.dm b/code/game/machinery/deployable_vr.dm index 55b62ce1844e..3b50fc26d7c7 100644 --- a/code/game/machinery/deployable_vr.dm +++ b/code/game/machinery/deployable_vr.dm @@ -63,7 +63,7 @@ . = ..() untopple() -/obj/structure/barricade/cutout/attack_hand(mob/user, list/params) +/obj/structure/barricade/cutout/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index 648181e273e4..7280a965dcc8 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -34,7 +34,7 @@ playsound(src.loc, /datum/soundbyte/grouped/sparks, 100, TRUE) return 1 -/obj/machinery/button/remote/attack_hand(mob/user, list/params) +/obj/machinery/button/remote/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/game/machinery/doorbell_vr.dm b/code/game/machinery/doorbell_vr.dm index 1379236cf3d4..c2bd4e3c45ed 100644 --- a/code/game/machinery/doorbell_vr.dm +++ b/code/game/machinery/doorbell_vr.dm @@ -111,7 +111,7 @@ else icon_state = "doorbell-standby" -/obj/machinery/button/doorbell/attack_hand(mob/user, list/params) +/obj/machinery/button/doorbell/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(..()) return diff --git a/code/game/machinery/doors/airlock/airlock.dm b/code/game/machinery/doors/airlock/airlock.dm index 819a31bca723..7fd4d8f73dfe 100644 --- a/code/game/machinery/doors/airlock/airlock.dm +++ b/code/game/machinery/doors/airlock/airlock.dm @@ -646,7 +646,7 @@ About the new airlock wires panel: last_spark = world.time return ..() -/obj/machinery/door/airlock/attack_hand(mob/user, list/params) +/obj/machinery/door/airlock/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(usr, /mob/living/silicon)) if(src.isElectrified()) if(src.shock(user, 100)) @@ -909,7 +909,7 @@ About the new airlock wires panel: for(var/turf/turf in locs) for(var/atom/movable/AM in turf) if(AM.airlock_crush(DOOR_CRUSH_DAMAGE)) - inflict_atom_damage(DOOR_CRUSH_DAMAGE, flag = ARMOR_MELEE) + inflict_atom_damage(DOOR_CRUSH_DAMAGE, damage_flag = ARMOR_MELEE) use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people has_beeped = 0 diff --git a/code/game/machinery/doors/airlock/airlock_control.dm b/code/game/machinery/doors/airlock/airlock_control.dm index c71c236d47e5..9fffbae73405 100644 --- a/code/game/machinery/doors/airlock/airlock_control.dm +++ b/code/game/machinery/doors/airlock/airlock_control.dm @@ -177,7 +177,7 @@ else icon_state = "airlock_sensor_off" -/obj/machinery/airlock_sensor/attack_hand(mob/user, list/params) +/obj/machinery/airlock_sensor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/datum/signal/signal = new signal.transmission_method = TRANSMISSION_RADIO //radio signal signal.data["tag"] = master_tag @@ -281,7 +281,7 @@ return ..() -/obj/machinery/access_button/attack_hand(mob/user, list/params) +/obj/machinery/access_button/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(!allowed(user)) to_chat(user, "Access Denied") diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index b9041727b7d7..0e7fa26bc677 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -116,7 +116,7 @@ //Proc: attack_hand //Description: Attacked with empty hand. Only to allow special attack_bys. -/obj/machinery/door/blast/attack_hand(mob/user, list/params) +/obj/machinery/door/blast/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user, /mob/living/carbon/human)) var/mob/living/carbon/human/X = user if(istype(X.species, /datum/species/xenos)) diff --git a/code/game/machinery/doors/defense.dm b/code/game/machinery/doors/defense.dm index eb2c3b434913..850c13bc2a3f 100644 --- a/code/game/machinery/doors/defense.dm +++ b/code/game/machinery/doors/defense.dm @@ -3,5 +3,5 @@ if(exposed_temperature > maxtemperature) var/burndamage = log(RAND_F(0.9, 1.1) * (exposed_temperature - maxtemperature)) - inflict_atom_damage(burndamage, flag = ARMOR_FIRE, gradual = TRUE) + inflict_atom_damage(burndamage, damage_flag = ARMOR_FIRE, damage_mode = DAMAGE_MODE_GRADUAL) return ..() diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 9dab9b833db9..0042f37be31e 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -155,7 +155,7 @@ /obj/machinery/door/attack_ai(mob/user as mob) return src.attack_hand(user) -/obj/machinery/door/attack_hand(mob/user, list/params) +/obj/machinery/door/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() return src.attackby(user, user) diff --git a/code/game/machinery/doors/door_timer.dm b/code/game/machinery/doors/door_timer.dm index e45245f39a8a..cd5690419e7b 100644 --- a/code/game/machinery/doors/door_timer.dm +++ b/code/game/machinery/doors/door_timer.dm @@ -160,7 +160,7 @@ /obj/machinery/door_timer/attack_ai(mob/user) return src.attack_hand(user) -/obj/machinery/door_timer/attack_hand(mob/user, list/params) +/obj/machinery/door_timer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 0000e5c416a8..ebe353bb3e0d 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -170,7 +170,7 @@ GLOBAL_LIST_INIT(firelock_align_types, typecacheof(list( attack_hand(M) return 0 -/obj/machinery/door/firedoor/attack_hand(mob/user, list/params) +/obj/machinery/door/firedoor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(operating) return//Already doing something. diff --git a/code/game/machinery/doors/unpowered.dm b/code/game/machinery/doors/unpowered.dm index 6236374a1055..1b6b5fb30232 100644 --- a/code/game/machinery/doors/unpowered.dm +++ b/code/game/machinery/doors/unpowered.dm @@ -9,7 +9,7 @@ return /obj/machinery/door/unpowered/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I, /obj/item/melee/energy/blade)) return + if(istype(I, /obj/item/melee/ninja_energy_blade)) return if(src.locked) return ..() return diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 9bdb1357afc6..bf8007ae0194 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -145,7 +145,7 @@ /obj/machinery/door/window/attack_ai(mob/user as mob) return src.attack_hand(user) -/obj/machinery/door/window/attack_hand(mob/user, list/params) +/obj/machinery/door/window/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() src.add_fingerprint(user) @@ -194,7 +194,7 @@ return //Emags and ninja swords? You may pass. - if (istype(I, /obj/item/melee/energy/blade)) + if (istype(I, /obj/item/melee/ninja_energy_blade)) if(emag_act(10, user)) var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, src.loc) diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 5cc1846c54aa..137e78cdbbbf 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -48,7 +48,7 @@ /obj/machinery/embedded_controller/attack_ai(mob/user as mob) ui_interact(user) -/obj/machinery/embedded_controller/attack_hand(mob/user, list/params) +/obj/machinery/embedded_controller/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!user.IsAdvancedToolUser()) return 0 diff --git a/code/game/machinery/event.dm b/code/game/machinery/event.dm index aabe77a9a12f..8fc316d2498f 100644 --- a/code/game/machinery/event.dm +++ b/code/game/machinery/event.dm @@ -65,7 +65,7 @@ The goal here is to create esoteric or niche, specialized machines that follow t else return -/obj/machinery/magma_pump/attack_hand(mob/user, list/params) +/obj/machinery/magma_pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/machinery/magma_pump/interact(mob/user) @@ -211,7 +211,7 @@ The goal here is to create esoteric or niche, specialized machines that follow t else return -/obj/machinery/magma_reservoir/attack_hand(mob/user, list/params) +/obj/machinery/magma_reservoir/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/machinery/magma_reservoir/interact(mob/user) diff --git a/code/game/machinery/exonet_node.dm b/code/game/machinery/exonet_node.dm index fb6c5c49b8a2..ac1e50634956 100644 --- a/code/game/machinery/exonet_node.dm +++ b/code/game/machinery/exonet_node.dm @@ -103,7 +103,7 @@ GLOBAL_LIST_EMPTY(exonet_nodes) // Proc: attack_hand() // Parameters: 1 (user - the person clicking on the machine) // Description: Opens the TGUI interface with ui_interact() -/obj/machinery/exonet_node/attack_hand(mob/user, list/params) +/obj/machinery/exonet_node/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) // Proc: ui_interact() diff --git a/code/game/machinery/fire_alarm.dm b/code/game/machinery/fire_alarm.dm index 1ee973b36652..edce90384ca9 100644 --- a/code/game/machinery/fire_alarm.dm +++ b/code/game/machinery/fire_alarm.dm @@ -121,8 +121,9 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/fire_alarm/alarms_hidden, 21) /obj/machinery/fire_alarm/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/fire_alarm/bullet_act() - return alarm() +/obj/machinery/fire_alarm/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + alarm() /obj/machinery/fire_alarm/emp_act(severity) if(prob(50 / severity)) @@ -177,7 +178,7 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/fire_alarm/alarms_hidden, 21) spawn(rand(0,15)) update_icon() -/obj/machinery/fire_alarm/attack_hand(mob/user, list/params) +/obj/machinery/fire_alarm/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.stat || machine_stat & (NOPOWER | BROKEN)) return diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 0dc55edd94e7..fd2df07fd4a6 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -130,7 +130,7 @@ name = "flasher button" desc = "A remote control switch for a mounted flasher." -/obj/machinery/button/flasher/attack_hand(mob/user, list/params) +/obj/machinery/button/flasher/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm index fd2ba4b08a02..8548bc99021d 100644 --- a/code/game/machinery/floodlight.dm +++ b/code/game/machinery/floodlight.dm @@ -67,7 +67,7 @@ if(!turn_on(TRUE)) to_chat(user, "You try to turn on \the [src] but it does not work.") -/obj/machinery/floodlight/attack_hand(mob/user, list/params) +/obj/machinery/floodlight/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(open && cell) user.grab_item_from_interacted_with(cell, src) diff --git a/code/game/machinery/floor_inflatables.dm b/code/game/machinery/floor_inflatables.dm index 7e87f68451a8..2a79c18e4d31 100644 --- a/code/game/machinery/floor_inflatables.dm +++ b/code/game/machinery/floor_inflatables.dm @@ -47,7 +47,7 @@ else to_chat(user, "[C] is to large for [src]") -/obj/machinery/floor_inflatables/attack_hand(mob/user, list/params) +/obj/machinery/floor_inflatables/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() var/confirm = input(user, "Do you want to trigger [src]'s deployment?","Trigger Floormount") as null|anything in list("Yes","No") if(confirm && confirm == "Yes") diff --git a/code/game/machinery/floor_light.dm b/code/game/machinery/floor_light.dm index 510b838e4900..6975c0a4f182 100644 --- a/code/game/machinery/floor_light.dm +++ b/code/game/machinery/floor_light.dm @@ -62,7 +62,7 @@ var/list/floor_light_cache = list() attack_hand(user) return -/obj/machinery/floor_light/attack_hand(mob/user, list/params) +/obj/machinery/floor_light/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM && !issmall(user)) if(!isnull(damaged) && !(machine_stat & BROKEN)) diff --git a/code/game/machinery/floorlayer.dm b/code/game/machinery/floorlayer.dm index f2cb0f8e2c78..c52162902c1c 100644 --- a/code/game/machinery/floorlayer.dm +++ b/code/game/machinery/floorlayer.dm @@ -32,7 +32,7 @@ old_turf = isturf(loc)? loc : null -/obj/machinery/floorlayer/attack_hand(mob/user, list/params) +/obj/machinery/floorlayer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) on=!on user.visible_message( \ SPAN_NOTICE("[user] has [!on?"de":""]activated \the [src]."), \ diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm index 4208e4973304..7b28e9b6766d 100644 --- a/code/game/machinery/holosign.dm +++ b/code/game/machinery/holosign.dm @@ -59,7 +59,7 @@ icon = 'icons/obj/power.dmi' icon_state = "crema_switch" -/obj/machinery/button/holosign/attack_hand(mob/user, list/params) +/obj/machinery/button/holosign/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index a4d038ddb312..21b86283ba57 100644 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -13,7 +13,7 @@ /obj/machinery/igniter/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/igniter/attack_hand(mob/user, list/params) +/obj/machinery/igniter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return @@ -121,7 +121,7 @@ name = "ignition switch" desc = "A remote control switch for a mounted igniter." -/obj/machinery/button/ignition/attack_hand(mob/user, list/params) +/obj/machinery/button/ignition/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index 77f4f9f052ba..c75979412d70 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -166,7 +166,7 @@ else return ..() -/obj/machinery/iv_drip/attack_hand(mob/user, list/params) +/obj/machinery/iv_drip/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(reagent_container) reagent_container.loc = get_turf(src) reagent_container = null @@ -240,7 +240,7 @@ if(!speed_process) START_MACHINE_PROCESSING(src) else - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) update_appearance() //! Plumbing Signal diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index 25758ac5be7d..0d687a25a999 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -273,7 +273,7 @@ /obj/machinery/media/jukebox/attack_ai(mob/user) return src.attack_hand(user) -/obj/machinery/media/jukebox/attack_hand(mob/user, list/params) +/obj/machinery/media/jukebox/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/machinery/media/jukebox/proc/explode() diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index 21a0dddf6323..4a44849248b9 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -52,7 +52,7 @@ /obj/machinery/light_switch/examine(mob/user, dist) . += SPAN_NOTICE("A light switch. It is [on? "on" : "off"].") -/obj/machinery/light_switch/attack_hand(mob/user, list/params) +/obj/machinery/light_switch/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) on = !on diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index 00c9426b463c..a9f4e837fa54 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -248,7 +248,7 @@ /obj/machinery/magnetic_controller/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/magnetic_controller/attack_hand(mob/user, list/params) +/obj/machinery/magnetic_controller/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return user.set_machine(src) diff --git a/code/game/machinery/misc/bioscan_antenna.dm b/code/game/machinery/misc/bioscan_antenna.dm index 15dab56fba6a..22da7d056c67 100644 --- a/code/game/machinery/misc/bioscan_antenna.dm +++ b/code/game/machinery/misc/bioscan_antenna.dm @@ -58,7 +58,7 @@ GLOBAL_LIST_EMPTY(bioscan_antenna_list) .[TOOL_MULTITOOL] = "change network" return merge_double_lazy_assoc_list(., ..()) -/obj/machinery/bioscan_antenna/attack_hand(mob/user, list/params) +/obj/machinery/bioscan_antenna/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // todo: better xenomorphs if(ishuman(user)) var/mob/living/carbon/human/H = user diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index 2f46990e8c84..5bc31cd85761 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -92,7 +92,7 @@ var/global/list/navbeacons = list() // no I don't like putting this in, but it w /obj/machinery/navbeacon/attack_ai(var/mob/user) interact(user, 1) -/obj/machinery/navbeacon/attack_hand(mob/user, list/params) +/obj/machinery/navbeacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!user.IsAdvancedToolUser()) return FALSE diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 9846a08a5260..45445346e415 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -250,7 +250,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co /obj/machinery/newscaster/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/newscaster/attack_hand(mob/user, list/params) //########### THE MAIN BEEF IS HERE! And in the proc below this...############ +/obj/machinery/newscaster/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!ispowered || isbroken) return diff --git a/code/game/machinery/nuclear_bomb.dm b/code/game/machinery/nuclear_bomb.dm index d7f7a571d1d5..836768f4b54a 100644 --- a/code/game/machinery/nuclear_bomb.dm +++ b/code/game/machinery/nuclear_bomb.dm @@ -162,7 +162,7 @@ var/bomb_set return ..() -/obj/machinery/nuclearbomb/attack_hand(mob/user, list/params) +/obj/machinery/nuclearbomb/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(extended) if(!ishuman(user)) to_chat(user, "You don't have the dexterity to do this!") diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index 05cdf8d22a0d..6c7cd305216f 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -51,7 +51,7 @@ attach_mask(target) src.add_fingerprint(usr) -/obj/machinery/oxygen_pump/attack_hand(mob/user, list/params) +/obj/machinery/oxygen_pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((machine_stat & MAINT) && tank) user.visible_message( \ SPAN_NOTICE("\The [user] removes \the [tank] from \the [src]."), \ diff --git a/code/game/machinery/pda_multicaster.dm b/code/game/machinery/pda_multicaster.dm index 324fc042db0d..7273710bbf41 100644 --- a/code/game/machinery/pda_multicaster.dm +++ b/code/game/machinery/pda_multicaster.dm @@ -46,7 +46,7 @@ /obj/machinery/pda_multicaster/attack_ai(mob/user) attack_hand(user) -/obj/machinery/pda_multicaster/attack_hand(mob/user, list/params) +/obj/machinery/pda_multicaster/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) toggle_power(user) /obj/machinery/pda_multicaster/proc/toggle_power(mob/user) diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm index 57c717216504..d20efd83cccc 100644 --- a/code/game/machinery/pipe/pipe_dispenser.dm +++ b/code/game/machinery/pipe/pipe_dispenser.dm @@ -12,7 +12,7 @@ var/disposals = FALSE -/obj/machinery/pipedispenser/attack_hand(mob/user, list/params) +/obj/machinery/pipedispenser/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return ui_interact(user) diff --git a/code/game/machinery/pointdefense.dm b/code/game/machinery/pointdefense.dm index 4d71af4a6196..7be2b8d14300 100644 --- a/code/game/machinery/pointdefense.dm +++ b/code/game/machinery/pointdefense.dm @@ -39,7 +39,7 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) ui = new(user, src, "PointDefenseControl") // 400, 600 ui.open() -/obj/machinery/pointdefense_control/attack_hand(mob/user, list/params) +/obj/machinery/pointdefense_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) @@ -96,7 +96,7 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) // Check for duplicate controllers with this ID for(var/thing in GLOB.pointdefense_controllers) var/obj/machinery/pointdefense_control/PC = thing - if(PC != src && PC.id_tag == id_tag) + if(PC != src && PC.id_tag == new_ident) to_chat(user, "The [new_ident] network already has a controller.") return to_chat(user, "You register [src] with the [new_ident] network.") @@ -208,11 +208,6 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) if(W?.is_multitool()) var/new_ident = input(user, "Enter a new ident tag.", "[src]", id_tag) as null|text if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, GLOB.physical_state)) - // Check for duplicate controllers with this ID - for(var/obj/machinery/pointdefense_control/PC as anything in GLOB.pointdefense_controllers) - if(PC != src && PC.id_tag == id_tag) - to_chat(user, SPAN_WARNING("\The [new_ident] network already has a controller!")) - return to_chat(user, SPAN_NOTICE("You register [src] with \the [new_ident] network.")) id_tag = new_ident return diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index b016f4d0a5c3..5bddf8fc4db8 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -101,7 +101,7 @@ else if(default_part_replacement(user, G)) return -/obj/machinery/recharger/attack_hand(mob/user, list/params) +/obj/machinery/recharger/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user,/mob/living/silicon)) return diff --git a/code/game/machinery/recipe_lookup.dm b/code/game/machinery/recipe_lookup.dm index 3116a8e8ea5c..8fe6954bd83a 100644 --- a/code/game/machinery/recipe_lookup.dm +++ b/code/game/machinery/recipe_lookup.dm @@ -9,7 +9,7 @@ /obj/machinery/recipe_lookup/proc/display_options() return tgui_input_list(usr, "Pick a [result_type_name] to view its recipe.", "Select [result_type_name]", recipe_list) -/obj/machinery/recipe_lookup/attack_hand(mob/user) +/obj/machinery/recipe_lookup/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // regular use checks if (!isliving(user)) return FALSE diff --git a/code/game/machinery/records_scanner.dm b/code/game/machinery/records_scanner.dm index 5ba7d9db0a76..6c1ac78a9fe3 100644 --- a/code/game/machinery/records_scanner.dm +++ b/code/game/machinery/records_scanner.dm @@ -36,7 +36,7 @@ obj/machinery/scanner else icon_state = "scanner_idle" -obj/machinery/scanner/attack_hand(mob/user, list/params) +obj/machinery/scanner/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(stat & NOPOWER) return if(!ishuman(user) || lastuser == user.real_name) diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 3c24ce2293e7..3f7b01c32662 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -119,7 +119,7 @@ var/list/obj/machinery/requests_console/allConsoles = list() req_console_information -= department return ..() -/obj/machinery/requests_console/attack_hand(mob/user, list/params) +/obj/machinery/requests_console/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..(user)) return nano_ui_interact(user) diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index 6dad1ded03ec..4f2fd5f44d85 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -74,7 +74,7 @@ ..() return -/obj/machinery/space_heater/attack_hand(mob/user, list/params) +/obj/machinery/space_heater/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/machinery/space_heater/interact(mob/user as mob) @@ -232,7 +232,7 @@ return ..() -/obj/machinery/power/thermoregulator/attack_hand(mob/user, list/params) +/obj/machinery/power/thermoregulator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) interact(user) diff --git a/code/game/machinery/suit_storage/suit_cycler.dm b/code/game/machinery/suit_storage/suit_cycler.dm index dbdc44a1ea6a..e17e8357b43f 100644 --- a/code/game/machinery/suit_storage/suit_cycler.dm +++ b/code/game/machinery/suit_storage/suit_cycler.dm @@ -260,7 +260,7 @@ updateUsrDialog() return 1 -/obj/machinery/suit_cycler/attack_hand(mob/user, list/params) +/obj/machinery/suit_cycler/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) diff --git a/code/game/machinery/suit_storage/suit_storage_massive.dm b/code/game/machinery/suit_storage/suit_storage_massive.dm index dd5dc3cf21c6..773e6ab6e280 100644 --- a/code/game/machinery/suit_storage/suit_storage_massive.dm +++ b/code/game/machinery/suit_storage/suit_storage_massive.dm @@ -65,7 +65,7 @@ dump_everything() qdel(src) -/obj/machinery/suit_storage_closet/attack_hand(mob/user, list/params) +/obj/machinery/suit_storage_closet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(machine_stat & NOPOWER) diff --git a/code/game/machinery/suit_storage/suit_storage_unit.dm b/code/game/machinery/suit_storage/suit_storage_unit.dm index a1b8ff57cca6..cb80c6ff01f4 100644 --- a/code/game/machinery/suit_storage/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage/suit_storage_unit.dm @@ -78,7 +78,7 @@ dump_everything() qdel(src) -/obj/machinery/suit_storage_unit/attack_hand(mob/user, list/params) +/obj/machinery/suit_storage_unit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(machine_stat & NOPOWER) diff --git a/code/game/machinery/supplybeacon.dm b/code/game/machinery/supplybeacon.dm index f3337336291c..7b7c5bbf476e 100644 --- a/code/game/machinery/supplybeacon.dm +++ b/code/game/machinery/supplybeacon.dm @@ -57,7 +57,7 @@ return return ..() -/obj/machinery/power/supply_beacon/attack_hand(mob/user, list/params) +/obj/machinery/power/supply_beacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(expended) update_use_power(USE_POWER_OFF) diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm index 6148d58e0681..481d8d39008f 100644 --- a/code/game/machinery/syndicatebeacon.dm +++ b/code/game/machinery/syndicatebeacon.dm @@ -15,7 +15,7 @@ var/selfdestructing = FALSE var/charges = 1 -/obj/machinery/syndicate_beacon/attack_hand(mob/user, list/params) +/obj/machinery/syndicate_beacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) var/dat = "Scanning [pick("retina pattern", "voice print", "fingerprints", "dna sequence")]...
Identity confirmed,
" if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon/ai)) @@ -108,7 +108,7 @@ /obj/machinery/power/singularity_beacon/attack_ai(mob/user) return -/obj/machinery/power/singularity_beacon/attack_hand(mob/user, list/params) +/obj/machinery/power/singularity_beacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(anchored) return active ? Deactivate(user) : Activate(user) else @@ -159,7 +159,7 @@ //! ## VR FILE MERGE ## !// // Virgo modified syndie beacon, does not give objectives -/obj/machinery/syndicate_beacon/virgo/attack_hand(mob/user, list/params) +/obj/machinery/syndicate_beacon/virgo/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) var/dat = "Scanning [pick("retina pattern", "voice print", "fingerprints", "dna sequence")]...
Identity confirmed,
" if(istype(user, /mob/living/carbon/human) || istype(user, /mob/living/silicon/ai)) diff --git a/code/game/machinery/telecomms/_telecomms.dm b/code/game/machinery/telecomms/_telecomms.dm index 7c9f141ca39e..65a6994ea967 100644 --- a/code/game/machinery/telecomms/_telecomms.dm +++ b/code/game/machinery/telecomms/_telecomms.dm @@ -363,7 +363,7 @@ return UI_CLOSE . = ..() -/obj/machinery/telecomms/attack_hand(mob/user, list/params) +/obj/machinery/telecomms/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/telecomms/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/game/machinery/telecomms/logbrowser.dm b/code/game/machinery/telecomms/logbrowser.dm index 94977779139f..a6673e2ed6de 100644 --- a/code/game/machinery/telecomms/logbrowser.dm +++ b/code/game/machinery/telecomms/logbrowser.dm @@ -62,7 +62,7 @@ return data -/obj/machinery/computer/telecomms/server/attack_hand(mob/user, list/params) +/obj/machinery/computer/telecomms/server/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return ui_interact(user) diff --git a/code/game/machinery/telecomms/message_server.dm b/code/game/machinery/telecomms/message_server.dm index 56b0aec8744a..3f5bcda9869d 100644 --- a/code/game/machinery/telecomms/message_server.dm +++ b/code/game/machinery/telecomms/message_server.dm @@ -140,7 +140,7 @@ var/global/list/obj/machinery/message_server/message_servers = list() Console.set_light(2) -/obj/machinery/message_server/attack_hand(mob/user, list/params) +/obj/machinery/message_server/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // to_chat(user, "There seem to be some parts missing from this server. They should arrive on the station in a few days, give or take a few CentCom delays.") to_chat(user, "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") active = !active diff --git a/code/game/machinery/telecomms/telemonitor.dm b/code/game/machinery/telecomms/telemonitor.dm index eff16361e954..ddf0bc204dd3 100644 --- a/code/game/machinery/telecomms/telemonitor.dm +++ b/code/game/machinery/telecomms/telemonitor.dm @@ -50,7 +50,7 @@ return data -/obj/machinery/computer/telecomms/monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/telecomms/monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return ui_interact(user) diff --git a/code/game/machinery/teleporter/console.dm b/code/game/machinery/teleporter/console.dm index d2c7551f9240..71aa5f335f4c 100644 --- a/code/game/machinery/teleporter/console.dm +++ b/code/game/machinery/teleporter/console.dm @@ -86,7 +86,7 @@ return -/obj/machinery/computer/teleporter/attack_hand(mob/user, list/params) +/obj/machinery/computer/teleporter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/computer/teleporter/ui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) diff --git a/code/game/machinery/teleporter/projector.dm b/code/game/machinery/teleporter/projector.dm index d304f36e6d99..5d2922237cc6 100644 --- a/code/game/machinery/teleporter/projector.dm +++ b/code/game/machinery/teleporter/projector.dm @@ -182,7 +182,7 @@ /obj/machinery/tele_projector/attack_ai() attack_hand() -/obj/machinery/tele_projector/attack_hand(mob/user, list/params) +/obj/machinery/tele_projector/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(engaged) disengage() else diff --git a/code/game/machinery/turnstile.dm b/code/game/machinery/turnstile.dm index 36e45c60134c..e694c18177b7 100644 --- a/code/game/machinery/turnstile.dm +++ b/code/game/machinery/turnstile.dm @@ -18,11 +18,6 @@ /obj/machinery/turnstile/CanAtmosPass(turf/T) return TRUE -/* -/obj/machinery/turnstile/bullet_act(obj/item/projectile/P, def_zone) - return -1 //Pass through! -*/ - /obj/machinery/turnstile/proc/allowed_access(var/mob/B) if(B.pulledby && ismob(B.pulledby)) return allowed(B.pulledby) | allowed(B) diff --git a/code/game/machinery/turrets/subtypes/misc.dm b/code/game/machinery/turrets/subtypes/misc.dm index 83e75bc795d2..e7ed365d0ea0 100644 --- a/code/game/machinery/turrets/subtypes/misc.dm +++ b/code/game/machinery/turrets/subtypes/misc.dm @@ -75,7 +75,7 @@ integrity_max = 200 turret_type = "industrial" -/obj/machinery/porta_turret/industrial/bullet_act(obj/projectile/Proj) +/obj/machinery/porta_turret/industrial/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() if(enabled) if(!attacked && !emagged) diff --git a/code/game/machinery/turrets/turret-ai_holder.dm b/code/game/machinery/turrets/turret-ai_holder.dm index 330b4d8a4247..01590029a9e9 100644 --- a/code/game/machinery/turrets/turret-ai_holder.dm +++ b/code/game/machinery/turrets/turret-ai_holder.dm @@ -35,6 +35,11 @@ set_ticking(idle_retarget_pulse_time) /datum/ai_holder/turret/tick(cycles) + var/obj/machinery/porta_turret/turret = agent + // check if we should do anything + if(turret.disabled || !turret.enabled) + idle() + return // first, evaluate var/found_in_wake_range = continuous_evaluation() // then, @@ -156,7 +161,8 @@ */ /datum/ai_holder/turret/proc/trace_trajectory(atom/target, angle) var/obj/projectile/trace/trace = new(agent.loc) - trace.prepare_trace(target, null, TRUE) + trace.only_opacity = TRUE + trace.prepare_trace(target) trace.fire(angle) return trace.could_hit_target diff --git a/code/game/machinery/turrets/turret.dm b/code/game/machinery/turrets/turret.dm index 4a8590311c1d..a22964f65fd1 100644 --- a/code/game/machinery/turrets/turret.dm +++ b/code/game/machinery/turrets/turret.dm @@ -288,7 +288,7 @@ nano_ui_interact(user) -/obj/machinery/porta_turret/attack_hand(mob/user, list/params) +/obj/machinery/porta_turret/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(isLocked(user)) return @@ -441,7 +441,8 @@ else to_chat(user, "Access denied.") - ..() + else + return ..() /obj/machinery/porta_turret/emag_act(remaining_charges, mob/user) if(!emagged) @@ -462,16 +463,18 @@ if(!gradual && prob(45) && amount > 5) spark_system.start() -/obj/machinery/porta_turret/bullet_act(obj/projectile/P, def_zone) +/obj/machinery/porta_turret/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() aggro_for(6 SECONDS) - if(P.firer) + if(proj.firer) // todo: proper AI provoke API. var/datum/ai_holder/turret/snowflake_ai_holder = ai_holder - snowflake_ai_holder.retaliate(P.firer) - return ..() + snowflake_ai_holder.retaliate(proj.firer) -/obj/machinery/porta_turret/melee_act(mob/user, obj/item/weapon, target_zone, mult) +/obj/machinery/porta_turret/melee_act(mob/user, obj/item/weapon, target_zone, datum/event_args/actor/clickchain/clickchain) . = ..() + if(. & CLICKCHAIN_FLAGS_ATTACK_ABORT) + return if(. > 0) aggro_for(6 SECONDS, user) // todo: proper AI provoke API. @@ -540,7 +543,7 @@ if(L.invisibility >= INVISIBILITY_LEVEL_ONE) // Cannot see him. see_invisible is a mob-var return TURRET_NOT_TARGET - if(faction && L.faction == faction) + if(faction && L.has_iff_faction(faction)) return TURRET_NOT_TARGET if(!emagged && issilicon(L) && check_all == FALSE) // Don't target silica, unless told to neutralize everything. @@ -574,7 +577,7 @@ if(assess_perp(L) < 4) return TURRET_NOT_TARGET //if threat level < 4, keep going - if(L.stat != CONSCIOUS) //if the perp is lying down, it's still a target but a less-important target + if(L.stat != CONSCIOUS && (lethal || emagged)) //if the perp is lying down, it's still a target but a less-important target return check_down ? TURRET_SECONDARY_TARGET : TURRET_NOT_TARGET return TURRET_PRIORITY_TARGET //if the perp has passed all previous tests, congrats, it is now a "shoot-me!" nominee @@ -648,7 +651,7 @@ * @return TRUE on success */ /obj/machinery/porta_turret/proc/try_fire_at(atom/target, angle) - if(disabled || is_integrity_broken()) + if(disabled || !enabled || is_integrity_broken()) return FALSE if(is_on_cooldown()) return FALSE diff --git a/code/game/machinery/turrets/turret_control.dm b/code/game/machinery/turrets/turret_control.dm index e01425d540a2..b163c6a66656 100644 --- a/code/game/machinery/turrets/turret_control.dm +++ b/code/game/machinery/turrets/turret_control.dm @@ -118,7 +118,7 @@ nano_ui_interact(user) -/obj/machinery/turretid/attack_hand(mob/user, list/params) +/obj/machinery/turretid/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(isLocked(user)) return diff --git a/code/game/machinery/turrets/turret_frame.dm b/code/game/machinery/turrets/turret_frame.dm index 220d30456661..8a3c97076fcb 100644 --- a/code/game/machinery/turrets/turret_frame.dm +++ b/code/game/machinery/turrets/turret_frame.dm @@ -103,7 +103,7 @@ to_chat(user, "You add the prox sensor to the turret.") return - //attack_hand() removes the gun + //attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(5) if(I.is_screwdriver()) @@ -112,7 +112,7 @@ to_chat(user, "You close the internal access hatch.") return - //attack_hand() removes the prox sensor + //attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(6) if(I.is_material_stack_of(/datum/material/steel)) @@ -173,7 +173,7 @@ ..() -/obj/machinery/porta_turret_construct/attack_hand(mob/user, list/params) +/obj/machinery/porta_turret_construct/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) switch(build_step) if(4) if(!installation) diff --git a/code/game/machinery/vending/vending.dm b/code/game/machinery/vending/vending.dm index a47f2cf8da65..999a8526eb4d 100644 --- a/code/game/machinery/vending/vending.dm +++ b/code/game/machinery/vending/vending.dm @@ -283,7 +283,7 @@ /obj/machinery/vending/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/vending/attack_hand(mob/user, list/params) +/obj/machinery/vending/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index db2d8c8b4d29..7c0232aa5b14 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -120,7 +120,7 @@ ..() update_icon() -/obj/machinery/washing_machine/attack_hand(mob/user, list/params) +/obj/machinery/washing_machine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) switch(state) if(1) state = 2 diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm index cd2ffb6a64d4..c233bd51cdf3 100644 --- a/code/game/machinery/wishgranter.dm +++ b/code/game/machinery/wishgranter.dm @@ -9,7 +9,7 @@ var/charges = 1 var/insisting = 0 -/obj/machinery/wish_granter/attack_hand(mob/user, list/params) +/obj/machinery/wish_granter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) if(charges <= 0) diff --git a/code/game/objects/defense.dm b/code/game/objects/defense.dm deleted file mode 100644 index 29786cc77d47..000000000000 --- a/code/game/objects/defense.dm +++ /dev/null @@ -1,82 +0,0 @@ -//* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2023 Citadel Station developers. *// - -/obj/ex_act(power, dir, datum/automata/wave/explosion/E) - . = ..() - // todo: wave explosions - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - inflict_atom_damage(power * (1 / 2.5), flag = ARMOR_BOMB) - -/obj/legacy_ex_act(severity, target) - . = ..() - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - inflict_atom_damage(global._legacy_ex_atom_damage[severity], flag = ARMOR_BOMB) - -/obj/melee_act(mob/user, obj/item/weapon, target_zone, mult) - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - inflict_atom_damage(weapon.damage_force, weapon.damage_tier, weapon.damage_flag, weapon.damage_mode, ATTACK_TYPE_MELEE, weapon) - return NONE - -/obj/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, mult) - // todo: this should just be style.attack(attacker, src) - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - inflict_atom_damage(style.get_unarmed_damage(attacker, src), style.damage_tier, style.damage_flag, style.damage_mode, ATTACK_TYPE_UNARMED, attacker) - return NONE - -/obj/bullet_act(obj/projectile/P, def_zone) - . = ..() - // todo: should this really be here? - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - inflict_atom_damage(P.get_structure_damage(), P.damage_tier, P.damage_flag, P.damage_mode, ATTACK_TYPE_PROJECTILE, P) - -/obj/throw_impacted(atom/movable/AM, datum/thrownthing/TT) - . = ..() - // todo: /atom/movable/proc/throw_impact_attack(atom/target) - if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) - return - if(isitem(AM)) - var/obj/item/I = AM - inflict_atom_damage(I.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), I.damage_flag, I.damage_mode, ATTACK_TYPE_THROWN, AM) - else - inflict_atom_damage(AM.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), ARMOR_MELEE, null, ATTACK_TYPE_THROWN, AM) - // if we got destroyed - if(QDELETED(src) && (obj_flags & OBJ_ALLOW_THROW_THROUGH)) - . |= COMPONENT_THROW_HIT_PIERCE - -/obj/blob_act(obj/structure/blob/blob) - . = ..() - inflict_atom_damage(100, flag = ARMOR_MELEE, attack_type = ATTACK_TYPE_MELEE) - -/obj/drop_products(method, atom/where) - . = ..() - if(obj_storage?.drop_on_deconstruction_methods & method) - obj_storage.drop_everything_at(where) - -/obj/hitsound_melee(obj/item/I) - if(!isnull(material_primary)) - var/datum/material/primary = get_primary_material() - . = I.damtype == BURN? primary.sound_melee_burn : primary.sound_melee_brute - if(!isnull(.)) - return - return ..() - -/obj/hitsound_throwhit(obj/item/I) - if(!isnull(material_primary)) - var/datum/material/primary = get_primary_material() - . = I.damtype == BURN? primary.sound_melee_burn : primary.sound_melee_brute - if(!isnull(.)) - return - return ..() - -/obj/hitsound_unarmed(mob/M, datum/unarmed_attack/style) - if(!isnull(material_primary)) - var/datum/material/primary = get_primary_material() - . = style.damage_type == BURN? primary.sound_melee_burn : primary.sound_melee_brute - if(!isnull(.)) - return - return ..() diff --git a/code/game/objects/effects/_effect.dm b/code/game/objects/effects/_effect.dm index f2f3ff7e6f1b..14ae0396a50e 100644 --- a/code/game/objects/effects/_effect.dm +++ b/code/game/objects/effects/_effect.dm @@ -10,6 +10,7 @@ * however, at a certain point, do consider using /structure or /machinery instead. */ /obj/effect + anchored = TRUE integrity_enabled = FALSE density = FALSE opacity = FALSE diff --git a/code/game/objects/effects/debris/cleanable/blood.dm b/code/game/objects/effects/debris/cleanable/blood.dm index 095f46374693..220680d47bd7 100644 --- a/code/game/objects/effects/debris/cleanable/blood.dm +++ b/code/game/objects/effects/debris/cleanable/blood.dm @@ -132,7 +132,7 @@ var/global/list/image/splatter_cache=list() add_atom_colour(newcolor, FIXED_COLOUR_PRIORITY) amount = 0 -/obj/effect/debris/cleanable/blood/attack_hand(mob/user, list/params) +/obj/effect/debris/cleanable/blood/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/game/objects/effects/debris/cleanable/misc.dm b/code/game/objects/effects/debris/cleanable/misc.dm index 8a9ead843099..41866828989a 100644 --- a/code/game/objects/effects/debris/cleanable/misc.dm +++ b/code/game/objects/effects/debris/cleanable/misc.dm @@ -15,7 +15,7 @@ icon_state = "ash" anchored = 1 -/obj/effect/debris/cleanable/ash/attack_hand(mob/user, list/params) +/obj/effect/debris/cleanable/ash/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, "[src] sifts through your fingers.") var/turf/simulated/floor/F = get_turf(src) if (istype(F)) diff --git a/code/game/objects/effects/decals/remains.dm b/code/game/objects/effects/decals/remains.dm index b8e7644be70c..95ea5bd8b49b 100644 --- a/code/game/objects/effects/decals/remains.dm +++ b/code/game/objects/effects/decals/remains.dm @@ -55,7 +55,7 @@ desc = "They look like human remains. They've been here a long time." icon_state = "mummified2" -/obj/effect/decal/remains/attack_hand(mob/user, list/params) +/obj/effect/decal/remains/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, "[src] sinks together into a pile of ash.") var/turf/simulated/floor/F = get_turf(src) if (istype(F)) diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index bc62c5021dc1..5e949319b074 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -406,7 +406,7 @@ steam.start() -- spawns the effect return if(!ismovable(holder)) return - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) on = TRUE /datum/effect_system/ion_trail_follow/process(wait) @@ -425,7 +425,7 @@ steam.start() -- spawns the effect return oldposition = null on = FALSE - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) ///////////////////////////////////////////// //////// Attach a steam trail to an object (eg. a reacting beaker) that will follow it diff --git a/code/game/objects/effects/item_pickup_ghost.dm b/code/game/objects/effects/item_pickup_ghost.dm index 0944e2753eea..58675c8ff70a 100644 --- a/code/game/objects/effects/item_pickup_ghost.dm +++ b/code/game/objects/effects/item_pickup_ghost.dm @@ -1,11 +1,17 @@ /obj/effect/temporary_effect/item_pickup_ghost - anchored = 1 plane = MOB_PLANE layer = ABOVE_MOB_LAYER - mouse_opacity = 0//just in case something dumb happens + mouse_opacity = MOUSE_OPACITY_TRANSPARENT time_to_die = 0.5 SECONDS var/lifetime = 0.25 SECONDS //so it doesn't die before animation ends +/obj/effect/temporary_effect/item_pickup_ghost/Initialize(mapload, obj/item/from_item, atom/towards_target) + . = ..() + if(from_item) + assumeform(from_item) + if(towards_target) + animate_towards(towards_target) + /obj/effect/temporary_effect/item_pickup_ghost/proc/assumeform(var/obj/item/picked_up) icon = picked_up.icon icon_state = picked_up.icon_state diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index 88e0f9904874..8338325c57e6 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -18,6 +18,8 @@ wires = new(src) /obj/effect/mine/proc/explode(var/mob/living/M) + if(QDELETED(src)) + return var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread() triggered = 1 s.set_up(3, 1, src) @@ -27,8 +29,9 @@ qdel(s) qdel(src) -/obj/effect/mine/bullet_act() - if(prob(50)) +/obj/effect/mine/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + spawn(0) explode() /obj/effect/mine/legacy_ex_act(severity) @@ -167,7 +170,7 @@ var/turf/O = get_turf(src) if(!O) return - src.fragmentate(O, 20, 7, list(/obj/projectile/bullet/pellet/fragment)) //only 20 weak fragments because you're stepping directly on it + shrapnel_explosion(20, 7, /obj/projectile/bullet/pellet/fragment) visible_message("\The [src.name] detonates!") spawn(0) qdel(s) diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index 1ce7e665ac46..d03e8a0d3c43 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -116,25 +116,5 @@ icon_state = "begin" anchored = TRUE -/obj/effect/list_container - name = "list container" - -/obj/effect/list_container/mobl - name = "mobl" - var/master = null - - var/list/container = list( ) - -/obj/effect/stop - icon_state = "empty" - name = "Geas" - desc = "You can't resist." - var/atom/movable/victim - -/obj/effect/stop/Uncross(atom/movable/AM) - . = ..() - if(AM == victim) - return FALSE - /obj/effect/spawner name = "object spawner" diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index b6e81d12bebe..4216510e7dda 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -20,7 +20,7 @@ GLOBAL_LIST_BOILERPLATE(all_portals, /obj/effect/portal) . = ..() teleport(AM) -/obj/effect/portal/attack_hand(mob/user, list/params) +/obj/effect/portal/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user) && !(istype(user,/mob/living))) return //do not send ghosts, zshadows, ai eyes, etc spawn(0) diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 557cd497a544..a0b957a07837 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -15,10 +15,10 @@ if(exposed_temperature > 300 + T0C) damage_integrity(5) -/obj/effect/spider/melee_act(mob/user, obj/item/weapon, target_zone, mult) - if(weapon.damtype == BURN) - mult *= 2 - return ..() +/obj/effect/spider/process_damage_instance(list/shieldcall_args, filter_zone) + . = ..() + if(shieldcall_args[SHIELDCALL_ARG_DAMAGE_TYPE]) + shieldcall_args[SHIELDCALL_ARG_DAMAGE] *= 2 /obj/effect/spider/stickyweb icon_state = "stickyweb1" diff --git a/code/game/objects/effects/traps.dm b/code/game/objects/effects/traps.dm index 01a3f46511bd..42855a8026c5 100644 --- a/code/game/objects/effects/traps.dm +++ b/code/game/objects/effects/traps.dm @@ -554,7 +554,7 @@ Add those other swinging traps you mentioned above! /* This is all per-tick processing stuff. It isn't working the way I want, so I'm reverting it. if (istype(AM, /mob/living)) - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) var/mob/living/M = AM M.visible_message("[M] is slashed by the spinning blades!", \ "You are slashed by the spinning blades!") @@ -565,7 +565,7 @@ if (istype(AM, /mob/living)) M.apply_damage(damage, BRUTE) /obj/effect/trap/pop_up/pillar/Destroy() - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) return ..() */ diff --git a/code/game/objects/items-carry_weight.dm b/code/game/objects/items-carry_weight.dm new file mode 100644 index 000000000000..f26b7114f7f3 --- /dev/null +++ b/code/game/objects/items-carry_weight.dm @@ -0,0 +1,74 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Carry Weight *// +//* The carry weight system is a modular system used to discourage *// +//* carrying too much, through both weight (recursive weight) and *// +//* encumbrance (only on the stuff worn, and potentially also in hand) *// + +/obj/item/proc/get_weight() + return weight + obj_storage?.get_containing_weight() + +/obj/item/proc/get_encumbrance() + return encumbrance + +/obj/item/proc/get_flat_encumbrance() + return flat_encumbrance + +/obj/item/proc/update_weight() + if(isnull(weight_registered)) + return null + . = get_weight() + if(. == weight_registered) + return 0 + . -= weight_registered + weight_registered += . + var/mob/living/wearer = worn_mob() + if(istype(wearer)) + wearer.adjust_current_carry_weight(.) + +/obj/item/proc/update_encumbrance() + if(isnull(encumbrance_registered)) + return null + . = get_encumbrance() + if(. == encumbrance_registered) + return 0 + . -= encumbrance_registered + encumbrance_registered += . + var/mob/living/wearer = worn_mob() + if(istype(wearer)) + wearer.adjust_current_carry_encumbrance(.) + +/obj/item/proc/update_flat_encumbrance() + var/mob/living/wearer = worn_mob() + if(istype(wearer)) + wearer.recalculate_carry() + +/obj/item/proc/set_weight(amount) + if(amount == weight) + return + var/old = weight + weight = amount + update_weight() + propagate_weight(old, weight) + +/obj/item/proc/set_encumbrance(amount) + if(amount == encumbrance) + return + encumbrance = amount + update_encumbrance() + +/obj/item/proc/set_flat_encumbrance(amount) + if(amount == flat_encumbrance) + return + flat_encumbrance = amount + update_flat_encumbrance() + +/obj/item/proc/set_slowdown(amount) + if(amount == slowdown) + return + slowdown = amount + worn_mob()?.update_item_slowdown() + +/obj/item/proc/propagate_weight(old_weight, new_weight) + loc?.on_contents_weight_change(src, old_weight, new_weight) diff --git a/code/game/objects/items-defense.dm b/code/game/objects/items-defense.dm new file mode 100644 index 000000000000..4ad604fcbcd6 --- /dev/null +++ b/code/game/objects/items-defense.dm @@ -0,0 +1,12 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Armor *// + +/** + * Called during a mob armor call cycle + */ +/obj/item/proc/mob_armorcall(mob/defending, list/shieldcall_args, fake_attack) + // use our own armor + var/datum/armor/our_armor = fetch_armor() + our_armor.handle_shieldcall(shieldcall_args, fake_attack) diff --git a/code/game/objects/items-interaction.dm b/code/game/objects/items-interaction.dm index 8e557d574c81..a1b37b7c17c2 100644 --- a/code/game/objects/items-interaction.dm +++ b/code/game/objects/items-interaction.dm @@ -1,6 +1,165 @@ //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2024 silicons *// +//* Attack Hand *// + +/obj/item/on_attack_hand(datum/event_args/actor/clickchain/e_args) + . = ..() + if(.) + return + + if(e_args.performer.is_in_inventory(src)) + if(e_args.performer.is_holding(src)) + if(obj_storage?.allow_open_via_offhand_click && obj_storage.auto_handle_interacted_open(e_args)) + return TRUE + else + if(obj_storage?.allow_open_via_equipped_click && obj_storage.auto_handle_interacted_open(e_args)) + return TRUE + if(!e_args.performer.is_holding(src)) + if(should_attempt_pickup(e_args) && attempt_pickup(e_args.performer)) + return TRUE + +/obj/item/proc/should_attempt_pickup(datum/event_args/actor/actor) + return TRUE + +/** + * @params + * * actor - (optional) person doing it + * * silent - suppress feedback + */ +/obj/item/proc/should_allow_pickup(datum/event_args/actor/actor, silent) + if(anchored) + if(!silent) + actor?.chat_feedback( + SPAN_NOTICE("\The [src] won't budge, you can't pick it up!"), + target = src, + ) + return FALSE + return TRUE + +/obj/item/proc/attempt_pickup(mob/user) + . = TRUE + if (!user) + return + + if(!should_allow_pickup(new /datum/event_args/actor(user))) + return FALSE + + if(!CHECK_MOBILITY(user, MOBILITY_CAN_PICKUP)) + user.action_feedback(SPAN_WARNING("You can't do that right now."), src) + return + + // todo: rewrite this part iin hand rewrite + if (hasorgans(user)) + var/mob/living/carbon/human/H = user + var/obj/item/organ/external/temp = H.organs_by_name[H.hand? "l_hand" : "r_hand"] + if(!temp) + to_chat(user, "You try to use your hand, but realize it is no longer attached!") + return + if(!temp.is_usable()) + to_chat(user, "You try to move your [temp.name], but cannot!") + return + + var/old_loc = src.loc + var/obj/item/actually_picked_up = src + var/has_to_drop_to_ground_on_fail = FALSE + + if(isturf(old_loc)) + // if picking up from floor + throwing?.terminate() + else if(item_flags & ITEM_IN_STORAGE) + // trying to take out of backpack + var/datum/object_system/storage/resolved + if(istype(loc, /atom/movable/storage_indirection)) + var/atom/movable/storage_indirection/holder = loc + resolved = holder.parent + else if(isobj(loc)) + var/obj/obj_loc = loc + resolved = obj_loc.obj_storage + if(isnull(resolved)) + item_flags &= ~ITEM_IN_STORAGE + CRASH("in storage at [loc] ([REF(loc)]) ([loc?.type || "NULL"]) but cannot resolve storage system") + actually_picked_up = resolved.try_remove(src, user, new /datum/event_args/actor(user)) + // they're in user, but not equipped now. this is so it doesn't touch the ground first. + has_to_drop_to_ground_on_fail = TRUE + + if(isnull(actually_picked_up)) + to_chat(user, SPAN_WARNING("[src] somehow slips through your grasp. What just happened?")) + return + if(!user.put_in_hands(actually_picked_up)) + if(has_to_drop_to_ground_on_fail) + actually_picked_up.forceMove(user.drop_location()) + return + // success + if(isturf(old_loc)) + new /obj/effect/temporary_effect/item_pickup_ghost(old_loc, actually_picked_up, user) + +//* Drag / Drop *// + +/obj/item/OnMouseDrop(atom/over, mob/user, proximity, params) + . = ..() + if(. & CLICKCHAIN_DO_NOT_PROPAGATE) + return + if(anchored) // Don't. + return + if(user.restrained()) + return // don't. + // todo: restraint levels, e.g. handcuffs vs straightjacket + if(!user.is_in_inventory(src)) + // not being held + if(!isturf(loc)) // yea nah + return ..() + if(user.Adjacent(src)) + // check for equip + if(istype(over, /atom/movable/screen/inventory/hand)) + var/atom/movable/screen/inventory/hand/H = over + user.put_in_hand(src, H.index) + return CLICKCHAIN_DO_NOT_PROPAGATE + else if(istype(over, /atom/movable/screen/inventory/slot)) + var/atom/movable/screen/inventory/slot/S = over + user.equip_to_slot_if_possible(src, S.slot_id) + return CLICKCHAIN_DO_NOT_PROPAGATE + // check for slide + if(Adjacent(over) && user.CanSlideItem(src, over) && (istype(over, /obj/structure/table/rack) || istype(over, /obj/structure/table) || istype(over, /turf))) + var/turf/old = get_turf(src) + if(over == old) // same tile don't bother + return CLICKCHAIN_DO_NOT_PROPAGATE + if(!Move(get_turf(over))) + return CLICKCHAIN_DO_NOT_PROPAGATE + //! todo: i want to strangle the mofo who did planes instead of invisibility, which makes it computationally infeasible to check ghost invisibility in get hearers in view + //! :) FUCK YOU. + //! this if check is all for you. FUCK YOU. + if(!isobserver(user)) + user.visible_message(SPAN_NOTICE("[user] slides [src] over."), SPAN_NOTICE("You slide [src] over."), range = MESSAGE_RANGE_COMBAT_SUBTLE) + log_inventory("[user] slid [src] from [COORD(old)] to [COORD(over)]") + return CLICKCHAIN_DO_NOT_PROPAGATE + else + // being held, check for attempt unequip + if(istype(over, /atom/movable/screen/inventory/hand)) + var/atom/movable/screen/inventory/hand/H = over + user.put_in_hand(src, H.index) + return CLICKCHAIN_DO_NOT_PROPAGATE + else if(istype(over, /atom/movable/screen/inventory/slot)) + var/atom/movable/screen/inventory/slot/S = over + user.equip_to_slot_if_possible(src, S.slot_id) + return CLICKCHAIN_DO_NOT_PROPAGATE + else if(istype(over, /turf)) + user.drop_item_to_ground(src) + return CLICKCHAIN_DO_NOT_PROPAGATE + +// funny! +// todo: move to mob files +/mob/proc/CanSlideItem(obj/item/I, turf/over) + return FALSE + +// todo: move to mob files +/mob/living/CanSlideItem(obj/item/I, turf/over) + return Adjacent(I) && !incapacitated() && !stat && !restrained() + +// todo: move to mob files +/mob/observer/dead/CanSlideItem(obj/item/I, turf/over) + return is_spooky + //* Inhand Triggers *// /** diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index bd331dd7d3a3..e61d13a64890 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -27,7 +27,18 @@ /// the action will check for var/item_action_mobility_flags = MOBILITY_CAN_HOLD | MOBILITY_CAN_USE - //? Flags + //* Combat *// + /// passive parry data / frame + /// + /// * anonymous typepath is allowed + /// * typepath is allowed + /// * instance is allowed + /// + /// note that the component will not be modified while held; + /// if this is changed, the component needs to be remade. + var/passive_parry + + //* Flags *// /// Item flags. /// These flags are listed in [code/__DEFINES/inventory/item_flags.dm]. var/item_flags = ITEM_ENCUMBERS_WHILE_HELD @@ -423,14 +434,6 @@ /obj/item/ui_action_click(datum/action/action, datum/event_args/actor/actor) attack_self(usr) -//RETURN VALUES -//handle_shield should return a positive value to indicate that the attack is blocked and should be prevented. -//If a negative value is returned, it should be treated as a special return value for bullet_act() and handled appropriately. -//For non-projectile attacks this usually means the attack is blocked. -//Otherwise should return 0 to indicate that the attack is not affected in any way. -/obj/item/proc/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - return 0 - /obj/item/proc/get_loc_turf() var/atom/L = loc while(L && !istype(L, /turf/)) @@ -526,7 +529,7 @@ if (!..()) return 0 - if(istype(src, /obj/item/melee/energy)) + if(istype(src, /obj/item/melee/transforming/energy)) return //if we haven't made our blood_overlay already @@ -759,27 +762,6 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. var/datum/action/action = item_actions action.revoke(user.inventory.actions) -//* Armor *// - -/** - * called to be checked for mob armor - * - * @returns copy of args with modified values - */ -/obj/item/proc/checking_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - damage = fetch_armor().resultant_damage(damage, tier, flag) - return args.Copy() - -/** - * called to be used as mob armor - * side effects are allowed - * - * @returns copy of args with modified values - */ -/obj/item/proc/running_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - damage = fetch_armor().resultant_damage(damage, tier, flag) - return args.Copy() - //* Attack *// /** @@ -830,7 +812,20 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. /obj/item/proc/is_shred(strict) return (damage_mode & DAMAGE_MODE_SHRED) -//* Interaction *// +//* Combat *// + +/obj/item/proc/load_passive_parry() + if(!passive_parry) + return + passive_parry = resolve_passive_parry_data(passive_parry) + var/datum/component/passive_parry/loaded = GetComponent(/datum/component/passive_parry) + if(loaded) + loaded.parry_data = passive_parry + +/obj/item/proc/reload_passive_parry() + load_passive_parry() + +//* Interactions *// /obj/item/attackby(obj/item/I, mob/user, list/params, clickchain_flags, damage_multiplier) if(isturf(loc) && I.obj_storage?.allow_mass_gather && I.obj_storage.allow_mass_gather_via_click) @@ -859,237 +854,8 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. /obj/item/proc/attacksound_override(atom/target, attack_type) return -//* Carry Weight *// - -/obj/item/proc/get_weight() - return weight + obj_storage?.get_containing_weight() - -/obj/item/proc/get_encumbrance() - return encumbrance - -/obj/item/proc/get_flat_encumbrance() - return flat_encumbrance - -/obj/item/proc/update_weight() - if(isnull(weight_registered)) - return null - . = get_weight() - if(. == weight_registered) - return 0 - . -= weight_registered - weight_registered += . - var/mob/living/wearer = worn_mob() - if(istype(wearer)) - wearer.adjust_current_carry_weight(.) - -/obj/item/proc/update_encumbrance() - if(isnull(encumbrance_registered)) - return null - . = get_encumbrance() - if(. == encumbrance_registered) - return 0 - . -= encumbrance_registered - encumbrance_registered += . - var/mob/living/wearer = worn_mob() - if(istype(wearer)) - wearer.adjust_current_carry_encumbrance(.) - -/obj/item/proc/update_flat_encumbrance() - var/mob/living/wearer = worn_mob() - if(istype(wearer)) - wearer.recalculate_carry() - -/obj/item/proc/set_weight(amount) - if(amount == weight) - return - var/old = weight - weight = amount - update_weight() - propagate_weight(old, weight) - -/obj/item/proc/set_encumbrance(amount) - if(amount == encumbrance) - return - encumbrance = amount - update_encumbrance() - -/obj/item/proc/set_flat_encumbrance(amount) - if(amount == flat_encumbrance) - return - flat_encumbrance = amount - update_flat_encumbrance() - -/obj/item/proc/set_slowdown(amount) - if(amount == slowdown) - return - slowdown = amount - worn_mob()?.update_item_slowdown() - -/obj/item/proc/propagate_weight(old_weight, new_weight) - loc?.on_contents_weight_change(src, old_weight, new_weight) - -//* Interactions *// - -/obj/item/on_attack_hand(datum/event_args/actor/clickchain/e_args) - . = ..() - if(.) - return - - if(e_args.performer.is_in_inventory(src)) - if(e_args.performer.is_holding(src)) - if(obj_storage?.allow_open_via_offhand_click && obj_storage.auto_handle_interacted_open(e_args)) - return TRUE - else - if(obj_storage?.allow_open_via_equipped_click && obj_storage.auto_handle_interacted_open(e_args)) - return TRUE - if(!e_args.performer.is_holding(src)) - if(should_attempt_pickup(e_args) && attempt_pickup(e_args.performer)) - return TRUE - -/obj/item/OnMouseDrop(atom/over, mob/user, proximity, params) - . = ..() - if(. & CLICKCHAIN_DO_NOT_PROPAGATE) - return - if(anchored) // Don't. - return - if(user.restrained()) - return // don't. - // todo: restraint levels, e.g. handcuffs vs straightjacket - if(!user.is_in_inventory(src)) - // not being held - if(!isturf(loc)) // yea nah - return ..() - if(user.Adjacent(src)) - // check for equip - if(istype(over, /atom/movable/screen/inventory/hand)) - var/atom/movable/screen/inventory/hand/H = over - user.put_in_hand(src, H.index) - return CLICKCHAIN_DO_NOT_PROPAGATE - else if(istype(over, /atom/movable/screen/inventory/slot)) - var/atom/movable/screen/inventory/slot/S = over - user.equip_to_slot_if_possible(src, S.slot_id) - return CLICKCHAIN_DO_NOT_PROPAGATE - // check for slide - if(Adjacent(over) && user.CanSlideItem(src, over) && (istype(over, /obj/structure/table/rack) || istype(over, /obj/structure/table) || istype(over, /turf))) - var/turf/old = get_turf(src) - if(over == old) // same tile don't bother - return CLICKCHAIN_DO_NOT_PROPAGATE - if(!Move(get_turf(over))) - return CLICKCHAIN_DO_NOT_PROPAGATE - //! todo: i want to strangle the mofo who did planes instead of invisibility, which makes it computationally infeasible to check ghost invisibility in get hearers in view - //! :) FUCK YOU. - //! this if check is all for you. FUCK YOU. - if(!isobserver(user)) - user.visible_message(SPAN_NOTICE("[user] slides [src] over."), SPAN_NOTICE("You slide [src] over."), range = MESSAGE_RANGE_COMBAT_SUBTLE) - log_inventory("[user] slid [src] from [COORD(old)] to [COORD(over)]") - return CLICKCHAIN_DO_NOT_PROPAGATE - else - // being held, check for attempt unequip - if(istype(over, /atom/movable/screen/inventory/hand)) - var/atom/movable/screen/inventory/hand/H = over - user.put_in_hand(src, H.index) - return CLICKCHAIN_DO_NOT_PROPAGATE - else if(istype(over, /atom/movable/screen/inventory/slot)) - var/atom/movable/screen/inventory/slot/S = over - user.equip_to_slot_if_possible(src, S.slot_id) - return CLICKCHAIN_DO_NOT_PROPAGATE - else if(istype(over, /turf)) - user.drop_item_to_ground(src) - return CLICKCHAIN_DO_NOT_PROPAGATE - -// funny! -// todo: move to mob files -/mob/proc/CanSlideItem(obj/item/I, turf/over) - return FALSE - -// todo: move to mob files -/mob/living/CanSlideItem(obj/item/I, turf/over) - return Adjacent(I) && !incapacitated() && !stat && !restrained() - -// todo: move to mob files -/mob/observer/dead/CanSlideItem(obj/item/I, turf/over) - return is_spooky - //* Inventory *// -/obj/item/proc/should_attempt_pickup(datum/event_args/actor/actor) - return TRUE - -/** - * @params - * * actor - (optional) person doing it - * * silent - suppress feedback - */ -/obj/item/proc/should_allow_pickup(datum/event_args/actor/actor, silent) - if(anchored) - if(!silent) - actor?.chat_feedback( - SPAN_NOTICE("\The [src] won't budge, you can't pick it up!"), - target = src, - ) - return FALSE - return TRUE - -/obj/item/proc/attempt_pickup(mob/user) - . = TRUE - if (!user) - return - - if(!should_allow_pickup(new /datum/event_args/actor(user))) - return FALSE - - if(!CHECK_MOBILITY(user, MOBILITY_CAN_PICKUP)) - user.action_feedback(SPAN_WARNING("You can't do that right now."), src) - return - - if (hasorgans(user)) - var/mob/living/carbon/human/H = user - var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] - if (H.hand) - temp = H.organs_by_name["l_hand"] - if(temp && !temp.is_usable()) - to_chat(user, "You try to move your [temp.name], but cannot!") - return - if(!temp) - to_chat(user, "You try to use your hand, but realize it is no longer attached!") - return - - var/old_loc = src.loc - var/obj/item/actually_picked_up = src - var/has_to_drop_to_ground_on_fail = FALSE - - if(isturf(old_loc)) - // if picking up from floor - throwing?.terminate() - else if(item_flags & ITEM_IN_STORAGE) - // trying to take out of backpack - var/datum/object_system/storage/resolved - if(istype(loc, /atom/movable/storage_indirection)) - var/atom/movable/storage_indirection/holder = loc - resolved = holder.parent - else if(isobj(loc)) - var/obj/obj_loc = loc - resolved = obj_loc.obj_storage - if(isnull(resolved)) - item_flags &= ~ITEM_IN_STORAGE - CRASH("in storage at [loc] ([REF(loc)]) ([loc?.type || "NULL"]) but cannot resolve storage system") - actually_picked_up = resolved.try_remove(src, user, new /datum/event_args/actor(user)) - // they're in user, but not equipped now. this is so it doesn't touch the ground first. - has_to_drop_to_ground_on_fail = TRUE - - if(isnull(actually_picked_up)) - to_chat(user, SPAN_WARNING("[src] somehow slips through your grasp. What just happened?")) - return - if(!user.put_in_hands(actually_picked_up)) - if(has_to_drop_to_ground_on_fail) - actually_picked_up.forceMove(user.drop_location()) - return - // success - if(isturf(old_loc)) - var/obj/effect/temporary_effect/item_pickup_ghost/ghost = new(old_loc) - ghost.assumeform(actually_picked_up) - ghost.animate_towards(user) - /** * Called when someone clisk us on a storage, before the storage handler's * 'put item in' runs. Return FALSE to deny. @@ -1172,8 +938,18 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. //* VV *// +/obj/item/vv_get_var(var_name, resolve) + switch(var_name) + if(NAMEOF(src, passive_parry)) + if(resolve) + load_passive_parry() + return ..() + /obj/item/vv_edit_var(var_name, var_value, mass_edit, raw_edit) switch(var_name) + if(NAMEOF(src, passive_parry)) + . = ..() + reload_passive_parry() if(NAMEOF(src, item_flags)) var/requires_update = (item_flags & (ITEM_ENCUMBERS_WHILE_HELD | ITEM_ENCUMBERS_ONLY_HELD)) != (var_value & (ITEM_ENCUMBERS_WHILE_HELD | ITEM_ENCUMBERS_ONLY_HELD)) . = ..() @@ -1193,7 +969,7 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. L.update_carry_slowdown() if(NAMEOF(src, slowdown)) . = ..() - if(. ) + if(.) var/mob/living/L = worn_mob() // check, as worn_mob() returns /mob, not /living if(istype(L)) diff --git a/code/game/objects/items/bells.dm b/code/game/objects/items/bells.dm index 3eeb418f2f73..39ebae855cbc 100644 --- a/code/game/objects/items/bells.dm +++ b/code/game/objects/items/bells.dm @@ -18,7 +18,7 @@ if(broken) . += "It looks damaged, the ringer is stuck firmly inside." -/obj/item/deskbell/attack_hand(mob/user, list/params) +/obj/item/deskbell/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) //This defines the radials and what call we're assiging to them. var/list/options = list() diff --git a/code/game/objects/items/contraband.dm b/code/game/objects/items/contraband.dm index a54ee11a41e7..e8e86f7fc15e 100644 --- a/code/game/objects/items/contraband.dm +++ b/code/game/objects/items/contraband.dm @@ -109,12 +109,12 @@ /obj/item/grenade/flashbang/clusterbang, /obj/item/grenade/flashbang/clusterbang, /obj/item/grenade/spawnergrenade/spesscarp, - /obj/item/melee/energy/sword/ionic_rapier, + /obj/item/melee/transforming/energy/sword/ionic_rapier, /obj/item/clothing/shoes/syndigaloshes, /obj/item/storage/backpack/dufflebag/syndie, /obj/item/binoculars, /obj/item/storage/firstaid/combat, - /obj/item/melee/energy/sword, + /obj/item/melee/transforming/energy/sword, /obj/item/melee/telebaton, /obj/item/pen/reagent/paralysis, /obj/item/pickaxe/diamonddrill, @@ -124,7 +124,7 @@ /obj/item/reagent_containers/food/snacks/xenomeat, /obj/item/reagent_containers/glass/beaker/neurotoxin, /obj/item/hardsuit/combat, - /obj/item/shield/energy, + /obj/item/shield/transforming/energy, /obj/item/stamp/centcom, /obj/item/stamp/oricon, /obj/item/storage/fancy/cigar/havana, @@ -217,13 +217,13 @@ /obj/item/grenade/flashbang/clusterbang, /obj/item/grenade/flashbang/clusterbang, /obj/item/grenade/spawnergrenade/spesscarp, - /obj/item/melee/energy/sword, + /obj/item/melee/transforming/energy/sword, /obj/item/melee/telebaton, /obj/item/pen/reagent/paralysis, /obj/item/pickaxe/diamonddrill, /obj/item/reagent_containers/glass/beaker/neurotoxin, /obj/item/hardsuit/combat, - /obj/item/shield/energy, + /obj/item/shield/transforming/energy, /obj/item/stamp/centcom, /obj/item/stamp/oricon, /obj/item/storage/fancy/cigar/havana, diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index 40a96b4cd4f0..7b0616c46b66 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -110,7 +110,7 @@ to_chat(M, "Your chameleon-projector deactivates.") master.disrupt() -/obj/effect/dummy/chameleon/attack_hand(mob/user, list/params) +/obj/effect/dummy/chameleon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) for(var/mob/M in src) to_chat(M, "Your chameleon-projector deactivates.") master.disrupt() @@ -120,10 +120,10 @@ to_chat(M, "Your chameleon-projector deactivates.") master.disrupt() -/obj/effect/dummy/chameleon/bullet_act() +/obj/effect/dummy/chameleon/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() for(var/mob/M in src) to_chat(M, "Your chameleon-projector deactivates.") - ..() master.disrupt() /obj/effect/dummy/chameleon/relaymove(var/mob/user, direction) diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm index 18afd34b01ff..b657b547ca10 100644 --- a/code/game/objects/items/devices/defib.dm +++ b/code/game/objects/items/devices/defib.dm @@ -67,7 +67,7 @@ /obj/item/defib_kit/ui_action_click(datum/action/action, datum/event_args/actor/actor) toggle_paddles() -/obj/item/defib_kit/attack_hand(mob/user, list/params) +/obj/item/defib_kit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(loc == user) toggle_paddles() else diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 0c5736fac96a..0e34f154c131 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -189,7 +189,7 @@ return CLICKCHAIN_DO_NOT_PROPAGATE return ..() -/obj/item/flashlight/attack_hand(mob/user, list/params) +/obj/item/flashlight/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(cell) cell.update_appearance() diff --git a/code/game/objects/items/devices/gps.dm b/code/game/objects/items/devices/gps.dm index a2712ccf6d4f..009374244b8b 100644 --- a/code/game/objects/items/devices/gps.dm +++ b/code/game/objects/items/devices/gps.dm @@ -212,7 +212,7 @@ hud_bound?.add_screen(hud_arrow) hud_arrow.set_disabled(FALSE) update_tracking() - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) return TRUE /** @@ -225,7 +225,7 @@ tracking = null // just kick it out hud_arrow?.set_disabled(TRUE) - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) return TRUE /obj/item/gps/process(delta_time) diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index d4d8b82e28b2..d2d971bd8b08 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -70,7 +70,7 @@ /obj/item/powersink/attack_ai() return -/obj/item/powersink/attack_hand(mob/user, list/params) +/obj/item/powersink/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) switch(mode) if(0) ..() diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index a4a142c7017a..f835513866fb 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -15,7 +15,7 @@ var/code = 2 -/obj/item/radio/electropack/attack_hand(mob/user, list/params) +/obj/item/radio/electropack/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(src == user.item_by_slot_id(SLOT_ID_BACK)) to_chat(user, "You need help taking this off!") return diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 4c96ed809894..198d2981da3b 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -151,7 +151,7 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/item/radio/intercom, 28) spawn (0) attack_self(user) -/obj/item/radio/intercom/attack_hand(mob/user, list/params) +/obj/item/radio/intercom/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) src.add_fingerprint(user) spawn (0) attack_self(user) diff --git a/code/game/objects/items/devices/radio/jammer.dm b/code/game/objects/items/devices/radio/jammer.dm index 69ef1cf538f8..80f9f6431053 100644 --- a/code/game/objects/items/devices/radio/jammer.dm +++ b/code/game/objects/items/devices/radio/jammer.dm @@ -69,7 +69,7 @@ var/global/list/active_radio_jammers = list() update_icon() -/obj/item/radio_jammer/attack_hand(mob/user, list/params) +/obj/item/radio_jammer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && power_source) to_chat(user,"You eject \the [power_source] from \the [src].") user.put_in_hands(power_source) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 6bedeea1a533..ef0cea675388 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -813,7 +813,7 @@ GLOBAL_DATUM_INIT(virtual_announcer_ai, /mob/living/silicon/ai/announcer, new(nu /obj/item/bluespace_radio/ui_action_click(datum/action/action, datum/event_args/actor/actor) toggle_handset() -/obj/item/bluespace_radio/attack_hand(mob/user, list/params) +/obj/item/bluespace_radio/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(loc == user) toggle_handset() else diff --git a/code/game/objects/items/devices/spy_bug.dm b/code/game/objects/items/devices/spy_bug.dm index 8165600c4cb9..fd10b1265039 100644 --- a/code/game/objects/items/devices/spy_bug.dm +++ b/code/game/objects/items/devices/spy_bug.dm @@ -109,14 +109,15 @@ qdel(src) ..() -/obj/item/camerabug/bullet_act() +/obj/item/camerabug/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return visible_message("The [src] lens shatters!") new brokentype(get_turf(src)) if(linkedmonitor) linkedmonitor.unpair(src) linkedmonitor = null - spawn(0) - qdel(src) /obj/item/camerabug/Destroy() if(linkedmonitor) diff --git a/code/game/objects/items/devices/tape_recorder/tape_recorder.dm b/code/game/objects/items/devices/tape_recorder/tape_recorder.dm index 076367dc8a25..7dfd403748f8 100644 --- a/code/game/objects/items/devices/tape_recorder/tape_recorder.dm +++ b/code/game/objects/items/devices/tape_recorder/tape_recorder.dm @@ -62,7 +62,7 @@ tape.ruin() //Fires destroy the tape return ..() -/obj/item/tape_recorder/attack_hand(mob/user, list/params) +/obj/item/tape_recorder/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(tape) eject() diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 3a38f6b4f221..a35e5ec5b34c 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -57,7 +57,7 @@ return ..() -/obj/item/tape_recorder/attack_hand(mob/user, list/params) +/obj/item/tape_recorder/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(mytape) eject() diff --git a/code/game/objects/items/devices/text_to_speech.dm b/code/game/objects/items/devices/text_to_speech.dm index 71c444de8c53..07aa8e7721a6 100644 --- a/code/game/objects/items/devices/text_to_speech.dm +++ b/code/game/objects/items/devices/text_to_speech.dm @@ -51,8 +51,20 @@ if(activated) var/message = message_args["message"] var/whispering = message_args["whispering"] + var/message_mode = message_args["message_mode"] + var/speech_verb = whispering ? "quietly states" : "states" message_args["cancelled"] = TRUE - audible_message("[icon2html(thing = src, target = world)] \The [name] [whispering ? "quietly states" : "states"], \"[message]\"", null, whispering ? 2 : world.view) + audible_message("[icon2html(thing = src, target = world)] \The [name] [speech_verb], \"[message]\"", null, whispering ? 2 : world.view) if(!whispering) linked_user.say_overhead(message, FALSE, MESSAGE_RANGE_COMBAT_LOUD) + + if(ishuman(source) && message_mode != null) + var/mob/living/carbon/human/H = source + var/obj/item/radio/headset/left_radio = H.l_ear + var/obj/item/radio/headset/right_radio = H.r_ear + if(istype(left_radio)) + left_radio.talk_into(source, message, message_mode, speech_verb, null) + if(istype(right_radio)) + right_radio.talk_into(source, message, message_mode, speech_verb, null) + playsound(src, 'sound/items/tts/stopped_type.ogg', 25, TRUE) diff --git a/code/game/objects/items/id_cards/station_ids.dm b/code/game/objects/items/id_cards/station_ids.dm index f14e2d1b912f..ccbca7e089f1 100644 --- a/code/game/objects/items/id_cards/station_ids.dm +++ b/code/game/objects/items/id_cards/station_ids.dm @@ -71,7 +71,6 @@ . += "" if(Adjacent(user)) . += SPAN_NOTICE("Alt-click to [extra_info_visible ? "close" : "open"] the confidential information flap.") - return . /obj/item/card/id/get_description_info() . = ..() diff --git a/code/game/objects/items/latexballoon.dm b/code/game/objects/items/latexballoon.dm index 8c5ecb1731b0..5b469f669e58 100644 --- a/code/game/objects/items/latexballoon.dm +++ b/code/game/objects/items/latexballoon.dm @@ -30,16 +30,8 @@ item_state = "lgloves" loc.assume_air(air_contents) -/obj/item/latexballon/legacy_ex_act(severity) - burst() - switch(severity) - if (1) - qdel(src) - if (2) - if (prob(50)) - qdel(src) - -/obj/item/latexballon/bullet_act() +/obj/item/latexballon/inflict_atom_damage(damage, damage_type, damage_tier, damage_flag, damage_mode, hit_zone, attack_type, datum/weapon) + . = ..() burst() /obj/item/latexballon/fire_act(datum/gas_mixture/air, temperature, volume) diff --git a/code/game/objects/items/melee/melee.dm b/code/game/objects/items/melee/melee.dm new file mode 100644 index 000000000000..ec0b78656760 --- /dev/null +++ b/code/game/objects/items/melee/melee.dm @@ -0,0 +1,22 @@ +/datum/passive_parry/melee + parry_arc = 155 + parry_arc_round_down = TRUE + +/** + * this is like /device and /weapon but a little less dumb + * + * this has some simple wrappers for default defense stuff, so 'common'ly melee weapons + * like knives, armblades, etc, can easily use them. + * + * * Certain things may or may not 'graduate' to /obj/item level later. + */ +/obj/item/melee + icon = 'icons/obj/weapons.dmi' + attack_sound = "swing_hit" + item_icons = list( + SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', + SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', + ) + passive_parry = /datum/passive_parry{ + parry_chance_melee = 5; + } diff --git a/code/game/objects/items/weapons/melee/misc.dm b/code/game/objects/items/melee/types/misc.dm similarity index 94% rename from code/game/objects/items/weapons/melee/misc.dm rename to code/game/objects/items/melee/types/misc.dm index 41774bbc0db3..ec2cf5250f4c 100644 --- a/code/game/objects/items/weapons/melee/misc.dm +++ b/code/game/objects/items/melee/types/misc.dm @@ -68,13 +68,10 @@ can_speak = 1 var/list/voice_mobs = list() //The curse of the sword is that it has someone trapped inside. - -/obj/item/melee/cursedblade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 + passive_parry = /datum/passive_parry/melee{ + parry_chance_projectile = 15; + parry_chance_default = 50; + } /obj/item/melee/cursedblade/proc/ghost_inhabit(var/mob/candidate) if(!isobserver(candidate)) @@ -329,19 +326,14 @@ var/wieldsound = null var/unwieldsound = null -//Allow a small chance of parrying melee attacks when wielded - maybe generalize this to other weapons someday -/obj/item/melee/twohanded/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(wielded && default_parry_check(user, attacker, damage_source) && prob(15)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 + passive_parry = /datum/passive_parry/melee{ + parry_chance_melee = 15; + } /obj/item/melee/twohanded/attack_self(mob/user) . = ..() if(.) return - . = ..() if(!wielded) wielded = 1 else if(wielded) diff --git a/code/game/objects/items/melee/types/ninja_energy_blade.dm b/code/game/objects/items/melee/types/ninja_energy_blade.dm new file mode 100644 index 000000000000..c7d110bc483e --- /dev/null +++ b/code/game/objects/items/melee/types/ninja_energy_blade.dm @@ -0,0 +1,66 @@ +/obj/item/melee/ninja_energy_blade + name = "energy blade" + desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal." + icon_state = "ninja_energy_blade" + item_state = "ninja_energy_blade" + damage_force = 40 //Normal attacks deal very high damage - about the same as wielded fire axe + armor_penetration = 100 + sharp = 1 + edge = 1 + anchored = 1 // Never spawned outside of inventory, should be fine. + throw_force = 1 //Throwing or dropping the item deletes it. + throw_speed = 1 + throw_range = 1 + w_class = WEIGHT_CLASS_BULKY//So you can't hide it in your pocket or some such. + atom_flags = NOBLOODY + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + var/mob/living/creator + var/datum/effect_system/spark_spread/spark_system + + var/lrange = 2 + var/lpower = 2 + var/lcolor = "#00FF00" + + passive_parry = /datum/passive_parry{ + parry_chance_default = 60; + parry_chance_projectile = 60; + } + +/obj/item/melee/ninja_energy_blade/Initialize(mapload) + . = ..() + spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + START_PROCESSING(SSobj, src) + set_light(lrange, lpower, lcolor) + +/obj/item/melee/ninja_energy_blade/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/melee/ninja_energy_blade/attack_self(mob/user) + . = ..() + if(.) + return + qdel(src) + +/obj/item/melee/ninja_energy_blade/dropped(mob/user, atom_flags, atom/newLoc) + . = ..() + qdel(src) + +/obj/item/melee/ninja_energy_blade/process(delta_time) + if(!creator || loc != creator || !creator.is_holding(src)) + // Tidy up a bit. + if(istype(loc,/mob/living)) + var/mob/living/carbon/human/host = loc + if(istype(host)) + for(var/obj/item/organ/external/organ in host.organs) + for(var/obj/item/O in organ.implants) + if(O == src) + organ.implants -= src + host.pinned -= src + host.embedded -= src + host._handle_inventory_hud_remove(src) + qdel(src) diff --git a/code/game/objects/items/melee/types/transforming.dm b/code/game/objects/items/melee/types/transforming.dm new file mode 100644 index 000000000000..fe7f4160f939 --- /dev/null +++ b/code/game/objects/items/melee/types/transforming.dm @@ -0,0 +1,177 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/** + * single-toggle weapons + */ +/obj/item/melee/transforming + icon = 'icons/items/melee/transforming.dmi' + damage_mode = NONE + item_icons = null + + /// are we active? + var/active = FALSE + /// when active, do we use an overlay instead of an icon state? + var/active_via_overlay = FALSE + + /// activation sound; also deactivation if it's not specified + var/activation_sound = 'sound/weapons/empty.ogg' + var/deactivation_sound + /// sound volume + var/toggle_sound_volume = 50 + + /// do not allow passive parry while off + var/no_block_while_off = TRUE + + //* active / inactive damage *// + + var/active_damage_force + var/inactive_damage_force + + var/active_damage_mode + var/inactive_damage_mode + + var/active_damage_tier + var/inactive_damage_tier + + var/active_damage_type + var/inactive_damage_type + + //* active / inactive effects *// + + var/list/active_attack_verb + var/list/inactive_attack_verb + + //* active / inactive inventory costs *// + + var/active_weight_class + var/inactive_weight_class + + var/active_weight_volume + var/inactive_weight_volume + + //* active / inactive throwing *// + + var/active_throw_force + var/inactive_throw_force + + var/active_throw_resist + var/inactive_throw_resist + + var/active_throw_range + var/inactive_throw_range + + var/active_throw_speed + var/inactive_throw_speed + +/obj/item/melee/transforming/Initialize(mapload) + . = ..() + if(islist(active_attack_verb)) + active_attack_verb = typelist(NAMEOF(src, active_attack_verb), active_attack_verb) + if(islist(inactive_attack_verb)) + inactive_attack_verb = typelist(NAMEOF(src, inactive_attack_verb), inactive_attack_verb) + +/obj/item/melee/transforming/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + if(!active && no_block_while_off) + return // cancel + return ..() + +/obj/item/melee/transforming/update_icon_state() + icon_state = "[base_icon_state || initial(icon_state)][active && !active_via_overlay ? "-active" : ""]" + return ..() + +/obj/item/melee/transforming/update_overlays() + . = ..() + if(!active || !active_via_overlay) + return + . += build_active_overlay() + +/obj/item/melee/transforming/proc/build_active_overlay() + RETURN_TYPE(/image) + var/image/creating = image(icon, "[base_icon_state || icon_state]-active") + return creating + +/obj/item/melee/transforming/proc/build_active_worn_overlay(worn_state) + RETURN_TYPE(/image) + var/image/creating = image(icon, "[worn_state]-active") + return creating + +/obj/item/melee/transforming/render_apply_overlays(mutable_appearance/MA, bodytype, inhands, datum/inventory_slot/slot_meta, icon_used) + . = ..() + if(active_via_overlay && active) + MA.overlays += build_active_worn_overlay(MA.icon_state) + +/obj/item/melee/transforming/on_attack_self(datum/event_args/actor/e_args) + . = ..() + if(.) + return + add_fingerprint(e_args.performer) + toggle(e_args) + return CLICKCHAIN_DO_NOT_PROPAGATE + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/melee/transforming/proc/toggle(datum/event_args/actor/actor, silent) + set_activation(!active, actor, silent) + +/obj/item/melee/transforming/proc/set_activation(state, datum/event_args/actor/actor, silent) + active = state + if(active) + on_activate(actor, silent) + else + on_deactivate(actor, silent) + update_icon() + update_worn_icon() + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/melee/transforming/proc/on_activate(datum/event_args/actor/actor, silent) + E_ARGS_WRAP_USER_TO_ACTOR(actor) + + damage_force = VALUE_OR_DEFAULT(active_damage_force, initial(damage_force)) + damage_tier = VALUE_OR_DEFAULT(active_damage_tier, initial(damage_tier)) + damage_mode = VALUE_OR_DEFAULT(active_damage_mode, initial(damage_mode)) + damtype = VALUE_OR_DEFAULT(active_damage_type, initial(damtype)) + + throw_force = VALUE_OR_DEFAULT(active_throw_force, initial(throw_force)) + throw_resist = VALUE_OR_DEFAULT(active_throw_resist, initial(throw_resist)) + throw_range = VALUE_OR_DEFAULT(active_throw_range, initial(throw_range)) + throw_speed = VALUE_OR_DEFAULT(active_throw_speed, initial(throw_speed)) + + set_weight_class(VALUE_OR_DEFAULT(active_weight_class, initial(w_class))) + set_weight_volume(VALUE_OR_DEFAULT(active_weight_volume, initial(weight_volume))) + + attack_verb = active_attack_verb + + if(!silent && activation_sound) + playsound(src, activation_sound, toggle_sound_volume, TRUE) + + // todo: logging + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/melee/transforming/proc/on_deactivate(datum/event_args/actor/actor, silent) + E_ARGS_WRAP_USER_TO_ACTOR(actor) + + damage_force = VALUE_OR_DEFAULT(inactive_damage_force, initial(damage_force)) + damage_tier = VALUE_OR_DEFAULT(inactive_damage_tier, initial(damage_tier)) + damage_mode = VALUE_OR_DEFAULT(inactive_damage_mode, initial(damage_mode)) + damtype = VALUE_OR_DEFAULT(inactive_damage_type, initial(damtype)) + + throw_force = VALUE_OR_DEFAULT(inactive_throw_force, initial(throw_force)) + throw_resist = VALUE_OR_DEFAULT(inactive_throw_resist, initial(throw_resist)) + throw_range = VALUE_OR_DEFAULT(inactive_throw_range, initial(throw_range)) + throw_speed = VALUE_OR_DEFAULT(inactive_throw_speed, initial(throw_speed)) + + set_weight_class(VALUE_OR_DEFAULT(inactive_weight_class, initial(w_class))) + set_weight_volume(VALUE_OR_DEFAULT(inactive_weight_volume, initial(weight_volume))) + + attack_verb = inactive_attack_verb + + if(!silent && (activation_sound || deactivation_sound)) + playsound(src, deactivation_sound || activation_sound, toggle_sound_volume, TRUE) + + // todo: logging diff --git a/code/game/objects/items/melee/types/transforming/energy.dm b/code/game/objects/items/melee/types/transforming/energy.dm new file mode 100644 index 000000000000..f011dec3091b --- /dev/null +++ b/code/game/objects/items/melee/types/transforming/energy.dm @@ -0,0 +1,176 @@ +/datum/passive_parry/melee/energy + parry_frame = /datum/parry_frame/passive_block/energy + +/datum/parry_frame/passive_block/energy + parry_sfx = 'sound/weapons/blade1.ogg' + +/obj/item/melee/transforming/energy + armor_penetration = 50 + atom_flags = NOCONDUCT | NOBLOODY + active_via_overlay = TRUE + + var/lrange = 2 + var/lpower = 2 + var/lcolor = "#0099FF" + var/colorable = FALSE + var/rainbow = FALSE + // If it uses energy. + var/use_cell = FALSE + var/hitcost = 120 + var/obj/item/cell/bcell = null + var/cell_type = /obj/item/cell/device + + passive_parry = /datum/passive_parry/melee/energy + + activation_sound = 'sound/weapons/saberon.ogg' + deactivation_sound = 'sound/weapons/saberoff.ogg' + +/obj/item/melee/transforming/energy/examine(mob/user, dist) + . = ..() + if(colorable) + . += SPAN_NOTICE("Alt-click to recolor it.") + +/obj/item/melee/transforming/energy/on_activate(datum/event_args/actor/actor, silent) + . = ..() + set_light(lrange, lpower, lcolor) + +/obj/item/melee/transforming/energy/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + set_light(0) + +/obj/item/melee/transforming/energy/proc/use_charge(var/cost) + if(active) + if(bcell) + if(bcell.checked_use(cost)) + return 1 + else + return 0 + return null + +/obj/item/melee/transforming/energy/examine(mob/user, dist) + . = ..() + if(use_cell) + if(bcell) + . += "The blade is [round(bcell.percent())]% charged." + if(!bcell) + . += "The blade does not have a power source installed." + +/obj/item/melee/transforming/energy/toggle(datum/event_args/actor/actor, silent) + if(use_cell) + if((!bcell || bcell.charge < hitcost) && !active) + if(!silent) + actor.chat_feedback( + "\The [src] does not seem to have power.", + target = src, + ) + return FALSE + return ..() + +/obj/item/melee/transforming/energy/attack_mob(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) + . = ..() + if(active && use_cell) + if(!use_charge(hitcost)) + set_activation(FALSE) + visible_message("\The [src]'s blade flickers, before deactivating.") + +/obj/item/melee/transforming/energy/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/multitool) && colorable && !active) + if(!rainbow) + rainbow = TRUE + else + rainbow = FALSE + to_chat(user, "You manipulate the color controller in [src].") + update_icon() + if(use_cell) + if(istype(W, cell_type)) + if(!bcell) + if(!user.attempt_insert_item_for_installation(W, src)) + return + bcell = W + to_chat(user, "You install a cell in [src].") + update_icon() + else + to_chat(user, "[src] already has a cell.") + else if(W.is_screwdriver() && bcell) + bcell.update_icon() + bcell.forceMove(get_turf(loc)) + bcell = null + to_chat(user, "You remove the cell from \the [src].") + set_activation(FALSE) + update_icon() + return + return ..() + +/obj/item/melee/transforming/energy/get_cell(inducer) + return bcell + +/obj/item/melee/transforming/energy/build_active_overlay() + var/image/creating = ..() + if(rainbow) + creating.icon_state += "-rainbow" + else + creating.color = lcolor + return creating + +/obj/item/melee/transforming/energy/build_active_worn_overlay() + var/image/creating = ..() + if(rainbow) + creating.icon_state += "-rainbow" + else + creating.color = lcolor + return creating + +/obj/item/melee/transforming/energy/AltClick(mob/living/user) + if(!colorable) //checks if is not colorable + return + if(!in_range(src, user)) //Basic checks to prevent abuse + return + if(user.incapacitated() || !istype(user)) + to_chat(user, "You can't do that right now!") + return + + if(alert("Are you sure you want to recolor your blade?", "Confirm Recolor", "Yes", "No") == "Yes") + var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null + if(energy_color_input) + lcolor = "#[sanitize_hexcolor(energy_color_input)]" + color = lcolor + update_icon() + return ..() + +// todo: no inhand! +// /obj/item/melee/transforming/energy/spear +// name = "energy spear" +// desc = "Concentrated energy forming a sharp tip at the end of a long rod." +// icon_state = "espear" +// armor_penetration = 75 +// sharp = 1 +// edge = 1 +// damage_force = 5 +// throw_force = 10 +// throw_speed = 7 +// throw_range = 11 +// reach = 2 +// w_class = WEIGHT_CLASS_BULKY +// active_damage_force = 25 +// active_throw_force = 30 +// active_weight_class = WEIGHT_CLASS_HUGE +// colorable = TRUE +// lcolor = "#800080" + +// passive_parry = /datum/passive_parry/melee/energy{ +// parry_chance_default = 50 +// } + +// /obj/item/melee/transforming/energy/spear/activate(mob/living/user) +// if(!active) +// to_chat(user, "\The [src] is now energised.") +// ..() +// attack_verb = list("jabbed", "stabbed", "impaled") +// AddComponent(/datum/component/jousting) + +// /obj/item/melee/transforming/energy/spear/deactivate(mob/living/user) +// if(active) +// to_chat(user, "\The [src] deactivates!") +// ..() +// attack_verb = list("whacked", "beat", "slapped", "thonked") +// DelComponent(/datum/component/jousting) diff --git a/code/game/objects/items/melee/types/transforming/energy/axe.dm b/code/game/objects/items/melee/types/transforming/energy/axe.dm new file mode 100644 index 000000000000..42d6e7be1c9a --- /dev/null +++ b/code/game/objects/items/melee/types/transforming/energy/axe.dm @@ -0,0 +1,49 @@ + +/obj/item/melee/transforming/energy/axe + name = "energy axe" + desc = "An energised battle axe." + icon_state = "energy_axe" + base_icon_state = "energy_axe" + damage_force = 20 + throw_force = 10 + throw_speed = 1 + throw_range = 5 + w_class = WEIGHT_CLASS_NORMAL + origin_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 4) + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + sharp = 1 + edge = 1 + can_cleave = TRUE + + active_damage_force = 60 + active_throw_force = 35 + active_weight_class = WEIGHT_CLASS_HUGE + active_damage_type = SEARING + +/obj/item/melee/transforming/energy/axe/on_activate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/energy/axe/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You de-energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/energy/axe/charge + name = "charge axe" + desc = "An energised axe." + active_damage_force = 30 + active_throw_force = 20 + armor_penetration = 25 + damage_force = 15 + use_cell = TRUE + hitcost = 120 + +/obj/item/melee/transforming/energy/axe/charge/loaded/Initialize(mapload) + . = ..() + bcell = new/obj/item/cell/device/weapon(src) diff --git a/code/game/objects/items/melee/types/transforming/energy/ionic_rapier.dm b/code/game/objects/items/melee/types/transforming/energy/ionic_rapier.dm new file mode 100644 index 000000000000..16c15a514bbe --- /dev/null +++ b/code/game/objects/items/melee/types/transforming/energy/ionic_rapier.dm @@ -0,0 +1,56 @@ +/obj/item/melee/transforming/energy/sword/ionic_rapier + name = "ionic rapier" + desc = "Designed specifically for disrupting electronics at close range, it is extremely deadly against synthetics, but almost harmless to pure organic targets." + description_info = "This is a dangerous melee weapon that will deliver a moderately powerful electromagnetic pulse to whatever it strikes. \ + Striking a lesser robotic entity will compel it to attack you, as well. It also does extra burn damage to robotic entities, but it does \ + very little damage to purely organic targets." + icon_state = "ionrapier" + item_state = "ionrapier" + active_damage_force = 10 + active_throw_force = 3 + sharp = 1 + edge = 1 + armor_penetration = 0 + atom_flags = NOBLOODY + lrange = 2 + lpower = 2 + lcolor = "#0000FF" + + passive_parry = /datum/passive_parry{ + parry_chance_default = 60; + parry_chance_projectile = 30; + } + +/obj/item/melee/transforming/energy/sword/ionic_rapier/afterattack(atom/target, mob/user, clickchain_flags, list/params) + if(istype(target, /obj) && (clickchain_flags & CLICKCHAIN_HAS_PROXIMITY) && active) + // EMP stuff. + var/obj/O = target + O.emp_act(3) // A weaker severity is used because this has infinite uses. + playsound(get_turf(O), 'sound/effects/EMPulse.ogg', 100, 1) + user.setClickCooldown(user.get_attack_speed(src)) // A lot of objects don't set click delay. + return ..() + +/obj/item/melee/transforming/energy/sword/ionic_rapier/melee_mob_hit(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) + . = ..() + var/mob/living/L = target + if(!istype(L)) + return + if(L.isSynthetic() && active) + // Do some extra damage. Not a whole lot more since emp_act() is pretty nasty on FBPs already. + L.emp_act(3) // A weaker severity is used because this has infinite uses. + playsound(get_turf(L), 'sound/effects/EMPulse.ogg', 100, 1) + L.adjustFireLoss(damage_force * 3) // 15 Burn, for 20 total. + playsound(get_turf(L), 'sound/weapons/blade1.ogg', 100, 1) + + // Make lesser robots really mad at us. + if(L.mob_class & MOB_CLASS_SYNTHETIC) + if(L.has_polaris_AI()) + L.taunt(user) + L.adjustFireLoss(damage_force * 6) // 30 Burn, for 50 total. + +/obj/item/melee/transforming/energy/sword/ionic_rapier/lance + name = "zero-point lance" + desc = "Designed specifically for disrupting electronics at relatively close range, however it is still capable of dealing some damage to living beings." + active_damage_force = 20 + armor_penetration = 15 + reach = 2 diff --git a/code/game/objects/items/melee/types/transforming/energy/saber.dm b/code/game/objects/items/melee/types/transforming/energy/saber.dm new file mode 100644 index 000000000000..48e1eb491400 --- /dev/null +++ b/code/game/objects/items/melee/types/transforming/energy/saber.dm @@ -0,0 +1,141 @@ +/obj/item/melee/transforming/energy/sword + name = "energy saber" + desc = "May the fourth be within you." + icon_state = "saber" + base_icon_state = "saber" + damage_force = 3 + throw_force = 5 + throw_speed = 1 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + atom_flags = NOBLOODY + origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) + colorable = TRUE + drop_sound = 'sound/items/drop/sword.ogg' + pickup_sound = 'sound/items/pickup/sword.ogg' + + active_damage_force = 30 + active_throw_force = 20 + active_weight_class = WEIGHT_CLASS_BULKY + active_damage_mode = DAMAGE_MODE_SHARP | DAMAGE_MODE_EDGE + active_attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + passive_parry = /datum/passive_parry{ + parry_chance_default = 60; + parry_chance_projectile = 65; + } + +/obj/item/melee/transforming/energy/sword/on_activate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/energy/sword/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You de-energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/energy/sword/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + . = ..() + if(!.) + return + + var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(5, 0, defending.loc) + spark_system.start() + +/obj/item/melee/transforming/energy/sword/attackby(obj/item/W, mob/living/user, params) + if(istype(W, /obj/item/melee/transforming/energy/sword)) + if(HAS_TRAIT(W, TRAIT_ITEM_NODROP) || HAS_TRAIT(src, TRAIT_ITEM_NODROP)) + to_chat(user, "\the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? src : W] is stuck to your hand, you can't attach it to \the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? W : src]!") + return + if(istype(W, /obj/item/melee/transforming/energy/sword/charge)) + to_chat(user,"These blades are incompatible, you can't attach them to each other!") + return + else + to_chat(user, "You combine the two energy swords, making a single supermassive blade! You're cool.") + new /obj/item/melee/transforming/energy/sword/dual(user.drop_location()) + qdel(W) + qdel(src) + else + return ..() + +/obj/item/melee/transforming/energy/sword/cutlass + name = "energy cutlass" + desc = "Arrrr matey." + icon_state = "cutlass" + base_icon_state = "cutlass" + colorable = TRUE + +/obj/item/melee/transforming/energy/sword/dual + name = "double-bladed energy sword" + desc = "Handle with care." + icon_state = "saber-dual" + base_icon_state = "saber-dual" + damage_force = 3 + active_damage_force = 60 + throw_force = 5 + throw_speed = 3 + armor_penetration = 35 + colorable = TRUE + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + passive_parry = /datum/passive_parry{ + parry_chance_default = 60; + parry_chance_projectile = 85; + } + +/obj/item/melee/transforming/energy/sword/charge + name = "charge sword" + desc = "A small, handheld device which emits a high-energy 'blade'." + origin_tech = list(TECH_COMBAT = 5, TECH_MAGNET = 3, TECH_ILLEGAL = 4) + active_damage_force = 25 + armor_penetration = 25 + colorable = TRUE + use_cell = TRUE + hitcost = 75 + +/obj/item/melee/transforming/energy/sword/charge/loaded/Initialize(mapload) + . = ..() + bcell = new/obj/item/cell/device/weapon(src) + +/obj/item/melee/transforming/energy/sword/charge/attackby(obj/item/W, mob/living/user, params) + if(istype(W, /obj/item/melee/transforming/energy/sword/charge)) + if(HAS_TRAIT(W, TRAIT_ITEM_NODROP) || HAS_TRAIT(src, TRAIT_ITEM_NODROP)) + to_chat(user, "\the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? src : W] is stuck to your hand, you can't attach it to \the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? W : src]!") + return + else + to_chat(user, "You combine the two charge swords, making a single supermassive blade! You're cool.") + new /obj/item/melee/transforming/energy/sword/charge/dualsaber(user.drop_location()) + qdel(W) + qdel(src) + else + return ..() + +/obj/item/melee/transforming/energy/sword/charge/dualsaber + name = "double-bladed charge sword" + desc = "Make sure you bought batteries." + icon_state = "saber-dual" + base_icon_state = "saber-dual" + damage_force = 3 + active_damage_force = 50 + throw_force = 5 + throw_speed = 3 + armor_penetration = 30 + colorable = TRUE + hitcost = 150 + + passive_parry = /datum/passive_parry{ + parry_chance_default = 60; + parry_chance_projectile = 65; + } + +/obj/item/melee/transforming/energy/sword/imperial + name = "imperial sword" + desc = "What the hell is this?" + icon_state = "imperial_sword" + base_icon_state = "imperial_sword" diff --git a/code/game/objects/items/melee/types/transforming/hfmachete.dm b/code/game/objects/items/melee/types/transforming/hfmachete.dm new file mode 100644 index 000000000000..5b5653a84f74 --- /dev/null +++ b/code/game/objects/items/melee/types/transforming/hfmachete.dm @@ -0,0 +1,50 @@ +/obj/item/melee/transforming/hfmachete + name = "high-frequency machete" + desc = "A high-frequency broad blade used either as an implement or in combat like a short sword." + icon_state = "hfmachete" + base_icon_state = "hfmachete" + damage_mode = DAMAGE_MODE_SHARP | DAMAGE_MODE_EDGE + damage_force = 20 // You can be crueler than that, Jack. + throw_force = 40 + throw_speed = 8 + throw_range = 8 + w_class = WEIGHT_CLASS_NORMAL + siemens_coefficient = 1 + origin_tech = list(TECH_COMBAT = 3, TECH_ILLEGAL = 3) + attack_verb = list("attacked", "diced", "cleaved", "torn", "cut", "slashed") + armor_penetration = 50 + var/base_state = "hfmachete" + attack_sound = "machete_hit_sound" // dont mind the meaty hit sounds if you hit something that isnt meaty + can_cleave = TRUE + embed_chance = 0 // let's not + + active_damage_force = 40 + + activation_sound = 'sound/weapons/hf_machete/hfmachete1.ogg' + deactivation_sound = 'sound/weapons/hf_machete/hfmachete0.ogg' + + active_weight_class = WEIGHT_CLASS_BULKY + inactive_weight_class = WEIGHT_CLASS_NORMAL + +/obj/item/melee/transforming/hfmachete/on_activate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/hfmachete/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + actor.chat_feedback( + SPAN_WARNING("You de-energize \the [src]."), + target = src, + ) + +/obj/item/melee/transforming/hfmachete/afterattack(atom/target, mob/user, clickchain_flags, list/params) + if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) + return + ..() + if(target) + if(istype(target,/obj/effect/plant)) + var/obj/effect/plant/P = target + P.die_off() diff --git a/code/game/objects/items/shield/shield.dm b/code/game/objects/items/shield/shield.dm new file mode 100644 index 000000000000..c55b07df8598 --- /dev/null +++ b/code/game/objects/items/shield/shield.dm @@ -0,0 +1,17 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/datum/passive_parry/shield + parry_arc = 155 + parry_arc_round_down = TRUE + parry_frame = /datum/parry_frame/passive_block/shield + +/datum/parry_frame/passive_block/shield + parry_sfx = /datum/soundbyte/grouped/block_metal_with_metal + block_verb = "blocks" // full block for now + +/obj/item/shield + name = "shield" + passive_parry = /datum/passive_parry/shield{ + parry_chance_default = 50; + } diff --git a/code/game/objects/items/shield/types/shields_legacy.dm b/code/game/objects/items/shield/types/shields_legacy.dm new file mode 100644 index 000000000000..1b73e20897f7 --- /dev/null +++ b/code/game/objects/items/shield/types/shields_legacy.dm @@ -0,0 +1,204 @@ +/obj/item/shield/riot + name = "riot shield" + desc = "A shield adept for close quarters engagement. It's also capable of protecting from less powerful projectiles." + icon = 'icons/items/shields/basic.dmi' + icon_state = "riot" + slot_flags = SLOT_BACK + damage_force = 5 + throw_force = 5 + throw_speed = 1 + throw_range = 4 + w_class = WEIGHT_CLASS_BULKY + origin_tech = list(TECH_MATERIAL = 2) + materials_base = list(MAT_GLASS = 7500, MAT_STEEL = 1000) + attack_verb = list("shoved", "bashed") + worth_intrinsic = 300 + var/cooldown = 0 //shield bash cooldown. based on world.time + +/obj/item/shield/riot/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + if(istype(weapon, /obj/projectile)) + var/obj/projectile/proj = weapon + if(((is_sharp(proj) && proj.armor_penetration >= 10) || proj.damage_tier >= ARMOR_TIER_HIGH || istype(proj, /obj/projectile/beam)) && prob(50)) + //If we're at this point, the bullet/beam is going to go through the shield, however it will hit for less damage. + //Bullets get slowed down, while beams are diffused as they hit the shield, so these shields are not /completely/ + //useless. Extremely penetrating projectiles will go through the shield without less damage. + defending.visible_message("\The [defending]'s [src.name] is pierced by [proj]!") + proj.dampen_on_pierce_experimental(src, 20, ARMOR_TIER_HIGH) + playsound(src, /datum/soundbyte/grouped/block_metal_with_metal, 65, TRUE) + return null + return ..() + +/obj/item/shield/riot/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/melee/baton)) + if(cooldown < world.time - 25) + user.visible_message("[user] bashes [src] with [W]!") + playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1) + cooldown = world.time + else + ..() + +/obj/item/shield/riot/flash + name = "strobe shield" + desc = "A shield with a built in, high intensity light capable of blinding and disorienting suspects. Takes regular handheld flashes as bulbs." + icon_state = "riot-flash" + item_state = "riot-flash" + var/obj/item/flash/embedded_flash + var/flashfail = 0 + +/obj/item/shield/riot/flash/Initialize(mapload) + . = ..() + embedded_flash = new(src) + +/obj/item/shield/riot/flash/attack_mob(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) + if(user.a_intent == INTENT_HARM) + return ..() + return embedded_flash.attack_mob(arglist(args)) + +/obj/item/shield/riot/flash/attack_self(mob/user) + . = ..() + if(.) + return + . = embedded_flash.attack_self(user) + update_icon() + +/obj/item/shield/riot/flash/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + . = ..() + if(!.) + return + if(embedded_flash.broken) + return + if(!(attack_type & (ATTACK_TYPE_MELEE | ATTACK_TYPE_UNARMED))) + return + // var/datum/event_args/actor/clickchain/clickchain = shieldcall_args[SHIELDCALL_ARG_CLICKCHAIN] + // var/mob/attacker = clickchain?.performer + // if(attacker) + // log_attack(key_name(attacker), key_name(defending), "flash shield auto-invoke") + // embedded_flash.melee_interaction_chain(attacker, defending) + // else + log_attack(key_name(defending), "none (AoE)", "flash shield auto-invoke") + embedded_flash.attack_self(defending) + update_icon() + +/obj/item/shield/riot/flash/attackby(obj/item/W, mob/user) + if(istype(W, /obj/item/flash)) + var/obj/item/flash/flash = W + if(flashfail) + to_chat(user, "No sense replacing it with a broken bulb!") + return + else + to_chat(user, "You begin to replace the bulb...") + if(do_after(user, 20, target = user)) + if(flashfail || !flash || QDELETED(flash)) + return + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + qdel(embedded_flash) + embedded_flash = flash + flash.forceMove(src) + update_icon() + return + ..() + +/obj/item/shield/riot/flash/emp_act(severity) + . = ..() + embedded_flash.emp_act(severity) + update_icon() + +/obj/item/shield/riot/flash/update_icon_state() + . = ..() + if(!embedded_flash || embedded_flash.broken) + icon_state = "riot" + item_state = "riot" + else + icon_state = "flashshield" + item_state = "flashshield" + +/obj/item/shield/riot/flash/examine(mob/user, dist) + . = ..() + if (embedded_flash?.broken) + . += "The mounted bulb has burnt out. You can try replacing it with a new one." + +/obj/item/shield/makeshift + name = "metal shield" + desc = "A large shield made of wired and welded sheets of metal. The handle is made of cloth and leather, making it unwieldy." + icon = 'icons/obj/weapons.dmi' + icon_state = "makeshift" + inhand_state = "metal" + slot_flags = null + damage_force = 10 + throw_force = 7 + +/obj/item/shield/riot/tower + name = "tower shield" + desc = "An immense tower shield. Designed to ensure maximum protection to the user, at the expense of mobility." + item_state = "metal" + damage_force = 16 + encumbrance = ITEM_ENCUMBRANCE_SHIELD_TOWER + throw_force = 15 //Massive piece of metal + w_class = WEIGHT_CLASS_HUGE + +/obj/item/shield/riot/tower/swat + name = "swat shield" + +/obj/item/shield/riot/energy_proof + name = "energy resistant shield" + desc = "An ablative shield designed to absorb and disperse energy attacks. This comes at significant cost to its ability to withstand ballistics and kinetics, breaking apart easily." + icon_state = "riot-laser" + +/obj/item/shield/riot/kinetic_proof + name = "kinetic resistant shield" + desc = "A polymer and ceramic shield designed to absorb ballistic projectiles and kinetic force. It doesn't do very well into energy attacks, especially from weapons that inflict burns." + icon_state = "riot-bullet" + +//Exotics/Costume Shields +/obj/item/shield/riot/roman + name = "scutum" + desc = "A replica shield for close quarters engagement. Its modern materials are also capable of protecting from less powerful projectiles." + icon = 'icons/obj/weapons.dmi' + icon_state = "roman" + slot_flags = SLOT_BACK + materials_base = list(MAT_WOOD = 7500, MAT_STEEL = 1000) + +/obj/item/shield/fluff/roman + name = "replica scutum" + desc = "A replica shield for close quarters engagement. It looks sturdy enough to withstand foam weapons, and nothing more." + icon = 'icons/obj/weapons.dmi' + icon_state = "roman" + slot_flags = SLOT_BACK + damage_force = 5.0 + throw_force = 5.0 + throw_speed = 2 + throw_range = 6 + +/obj/item/shield/riot/buckler + name = "buckler" + desc = "A wrist mounted round shield for close quarters engagement. Its modern materials are also capable of protecting from less powerful projectiles." + icon = 'icons/obj/weapons.dmi' + icon_state = "buckler" + slot_flags = SLOT_BACK | SLOT_BELT + materials_base = list(MAT_WOOD = 7500, MAT_STEEL = 1000) + +/obj/item/shield/riot/foam + name = "foam riot shield" + desc = "A shield for close quarters engagement. It looks sturdy enough to withstand foam weapons, and nothing more." + icon = 'icons/obj/weapons.dmi' + icon_state = "foam" + slot_flags = SLOT_BACK + damage_force = 0 + throw_force = 0 + throw_speed = 2 + throw_range = 6 + materials_base = list(MAT_PLASTIC = 7500, "foam" = 1000) + +/obj/item/shield/riot/foam/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + var/allowed = FALSE + if(isobj(weapon)) + var/obj/casted_object = weapon + if(istype(casted_object, /obj/projectile)) + var/obj/projectile/casted_projectile = casted_object + if(istype(casted_projectile, /obj/projectile/bullet/reusable/foam)) + allowed = TRUE + else if(casted_object.get_primary_material_id() == /datum/material/toy_foam::id) + allowed = TRUE + if(!allowed) + return + return ..() diff --git a/code/game/objects/items/shield/types/shields_legacy_vr.dm b/code/game/objects/items/shield/types/shields_legacy_vr.dm new file mode 100644 index 000000000000..d0d00bba9d9a --- /dev/null +++ b/code/game/objects/items/shield/types/shields_legacy_vr.dm @@ -0,0 +1,14 @@ + +/obj/item/shield/fluff/wolfgirlshield + name = "Autumn Shield" + desc = "A shiny silvery shield with a large red leaf symbol in the center." + icon = 'icons/obj/weapons_vr.dmi' + icon_state = "wolfgirlshield" + slot_flags = SLOT_BACK | SLOT_OCLOTHING + damage_force = 5.0 + throw_force = 5.0 + throw_speed = 2 + throw_range = 6 + item_icons = list(SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', SLOT_ID_BACK = 'icons/vore/custom_items_vr.dmi', SLOT_ID_SUIT = 'icons/vore/custom_items_vr.dmi') + attack_verb = list("shoved", "bashed") + allowed = list(/obj/item/melee/fluffstuff/wolfgirlsword) diff --git a/code/game/objects/items/shield/types/transforming.dm b/code/game/objects/items/shield/types/transforming.dm new file mode 100644 index 000000000000..d60a18805403 --- /dev/null +++ b/code/game/objects/items/shield/types/transforming.dm @@ -0,0 +1,110 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/** + * toggleable shields, like energy combat shields and telescoping shields + */ +/obj/item/shield/transforming + /// are we active? + var/active = FALSE + /// when active, do we use an overlay instead of an icon state? + /// + /// * applies to regular overlay + /// * applies to worn overlay as well + var/active_via_overlay = FALSE + + var/active_weight_class = WEIGHT_CLASS_BULKY + var/inactive_weight_class + var/active_weight_volume + var/inactive_weight_volume + + var/active_damage_force + var/inactive_damage_force + + /// activation sound; also deactivation if it's not specified + var/activation_sound = 'sound/weapons/empty.ogg' + var/deactivation_sound + /// sound volume + var/toggle_sound_volume = 50 + +/obj/item/shield/transforming/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + if(!active) + return // cancel + return ..() + +/obj/item/shield/transforming/update_icon_state() + icon_state = "[initial(icon_state)][active && !active_via_overlay ? "-active" : ""]" + return ..() + +/obj/item/shield/transforming/update_overlays() + . = ..() + if(!active || !active_via_overlay) + return + . += build_active_overlay() + +/obj/item/shield/transforming/proc/build_active_overlay() + RETURN_TYPE(/image) + var/image/creating = image(icon, "[base_icon_state || icon_state]-active") + return creating + +/obj/item/shield/transforming/proc/build_active_worn_overlay(worn_state) + RETURN_TYPE(/image) + var/image/creating = image(icon, "[worn_state]-active") + return creating + +/obj/item/shield/transforming/render_apply_overlays(mutable_appearance/MA, bodytype, inhands, datum/inventory_slot/slot_meta, icon_used) + . = ..() + if(active_via_overlay && active) + MA.overlays += build_active_worn_overlay(MA.icon_state) + +/obj/item/shield/transforming/on_attack_self(datum/event_args/actor/e_args) + . = ..() + if(.) + return + add_fingerprint(e_args.performer) + toggle(e_args) + return CLICKCHAIN_DO_NOT_PROPAGATE + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/shield/transforming/proc/toggle(datum/event_args/actor/actor, silent) + active = !active + if(active) + on_activate(actor, silent) + else + on_deactivate(actor, silent) + update_icon() + update_worn_icon() + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/shield/transforming/proc/on_activate(datum/event_args/actor/actor, silent) + E_ARGS_WRAP_USER_TO_ACTOR(actor) + + damage_force = VALUE_OR_DEFAULT(active_damage_force, initial(damage_force)) + + set_weight_class(VALUE_OR_DEFAULT(active_weight_class, initial(w_class))) + set_weight_volume(VALUE_OR_DEFAULT(active_weight_volume, initial(weight_volume))) + + if(!silent && activation_sound) + playsound(src, activation_sound, toggle_sound_volume, TRUE) + + // todo: logging + +/** + * actor can be /datum/event_args/actor or a single mob. + */ +/obj/item/shield/transforming/proc/on_deactivate(datum/event_args/actor/actor, silent) + E_ARGS_WRAP_USER_TO_ACTOR(actor) + + damage_force = VALUE_OR_DEFAULT(inactive_damage_force, initial(damage_force)) + + set_weight_class(VALUE_OR_DEFAULT(inactive_weight_class, initial(w_class))) + set_weight_volume(VALUE_OR_DEFAULT(inactive_weight_volume, initial(weight_volume))) + + if(!silent && (activation_sound || deactivation_sound)) + playsound(src, deactivation_sound || activation_sound, toggle_sound_volume, TRUE) + + // todo: logging diff --git a/code/game/objects/items/shield/types/transforming/energy.dm b/code/game/objects/items/shield/types/transforming/energy.dm new file mode 100644 index 000000000000..143665070c65 --- /dev/null +++ b/code/game/objects/items/shield/types/transforming/energy.dm @@ -0,0 +1,111 @@ +/obj/item/shield/transforming/energy + name = "energy combat shield" + desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere." + icon = 'icons/items/shields/transforming.dmi' + icon_state = "energy" + base_icon_state = "energy" + slot_flags = SLOT_EARS + atom_flags = NOCONDUCT + + damage_force = 3 + active_damage_force = 10 + + w_class = WEIGHT_CLASS_TINY + active_weight_class = WEIGHT_CLASS_BULKY + + throw_force = 5.0 + throw_speed = 1 + throw_range = 4 + + passive_parry = /datum/passive_parry/shield{ + parry_chance_default = 50; + parry_frame = /datum/parry_frame/passive_block/energy; + } + + active_via_overlay = TRUE + + var/lrange = 1.5 + var/lpower = 1.5 + var/lcolor = "#006AFF" + + origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 4) + attack_verb = list("shoved", "bashed") + + /// drop projectiles sometimes? + var/legacy_projectile_damage_drop = TRUE + /// divisor to projectile damage before we drop the hit + var/legacy_projectile_damage_drop_divisor = 1.6 + + activation_sound = 'sound/weapons/saberon.ogg' + deactivation_sound = 'sound/weapons/saberoff.ogg' + +/obj/item/shield/transforming/energy/passive_parry_intercept(mob/defending, attack_type, datum/weapon, datum/passive_parry/parry_data) + if(istype(weapon, /obj/projectile)) + var/obj/projectile/casted_projectile = weapon + if(legacy_projectile_damage_drop) + if((is_sharp(casted_projectile) && casted_projectile.damage > 10) || (casted_projectile.projectile_type & PROJECTILE_TYPE_BEAM)) + if(prob(casted_projectile.damage / legacy_projectile_damage_drop_divisor)) + return // drop the shield + . = ..() + if(!.) + return + var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(3, 0, defending.loc) + spark_system.start() + +/obj/item/shield/transforming/energy/on_activate(datum/event_args/actor/actor, silent) + . = ..() + slot_flags = NONE + if(!silent) + actor.chat_feedback( + SPAN_WARNING("You activate \the [src]."), + target = src, + ) + set_light(lrange, lpower, lcolor) + +/obj/item/shield/transforming/energy/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + slot_flags = SLOT_EARS + if(!silent) + actor.chat_feedback( + SPAN_WARNING("You collapse \the [src]."), + target = src, + ) + set_light(0) + +/obj/item/shield/transforming/energy/build_active_overlay() + var/image/built = ..() + if(lcolor) + built.color = lcolor + return built + +/obj/item/shield/transforming/energy/build_active_worn_overlay() + var/image/built = ..() + if(lcolor) + built.color = lcolor + return built + +// todo: legacy below + +/obj/item/shield/transforming/energy/AltClick(mob/living/user) + if(!in_range(src, user)) //Basic checks to prevent abuse + return + if(user.incapacitated() || !istype(user)) + to_chat(user, "You can't do that right now!") + return + if(alert("Are you sure you want to recolor your shield?", "Confirm Recolor", "Yes", "No") == "Yes") + var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null + if(energy_color_input) + lcolor = sanitize_hexcolor(energy_color_input, desired_format=6, include_crunch=1) + update_icon() + +/obj/item/shield/transforming/energy/examine(mob/user, dist) + . = ..() + . += "Alt-click to recolor it." + +/obj/item/shield/transforming/energy/imperial + name = "imperial shield" + desc = "What the hell is this?" + desc = "It's really easy to mispronounce the name of this shield if you've only read it in books." + icon_state = "imperial_shield" + base_icon_state = "imperial_shield" diff --git a/code/game/objects/items/shield/types/transforming/telescopic.dm b/code/game/objects/items/shield/types/transforming/telescopic.dm new file mode 100644 index 000000000000..76e96d790f6f --- /dev/null +++ b/code/game/objects/items/shield/types/transforming/telescopic.dm @@ -0,0 +1,37 @@ +/obj/item/shield/transforming/telescopic + name = "telescopic shield" + desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." + icon = 'icons/items/shields/transforming.dmi' + icon_state = "telescopic" + base_icon_state = "telescopic" + slot_flags = NONE + + damage_force = 5 + active_damage_force = 10 + inactive_damage_force = 5 + + w_class = WEIGHT_CLASS_NORMAL + active_weight_class = WEIGHT_CLASS_BULKY + inactive_weight_class = WEIGHT_CLASS_NORMAL + + activation_sound = 'sound/weapons/empty.ogg' + + throw_force = 5 + throw_speed = 3 + throw_range = 5 + +/obj/item/shield/transforming/telescopic/on_activate(datum/event_args/actor/actor, silent) + . = ..() + slot_flags = SLOT_BACK + actor.chat_feedback( + SPAN_WARNING("You extend \the [src]."), + target = src, + ) + +/obj/item/shield/transforming/telescopic/on_deactivate(datum/event_args/actor/actor, silent) + . = ..() + slot_flags = NONE + actor.chat_feedback( + SPAN_WARNING("You collapse \the [src]."), + target = src, + ) diff --git a/code/game/objects/items/shield_projector/shield_matrix.dm b/code/game/objects/items/shield_projector/shield_matrix.dm new file mode 100644 index 000000000000..eee6c5f280c2 --- /dev/null +++ b/code/game/objects/items/shield_projector/shield_matrix.dm @@ -0,0 +1,79 @@ +// todo: /obj/effect/shield_matrix +// This is the actual shield. The projector is a different item. +/obj/effect/directional_shield + name = "directional combat shield" + desc = "A wide shield, which has the property to block incoming projectiles but allow outgoing projectiles to pass it. \ + Slower moving objects are not blocked, so people can walk in and out of the barrier, and things can be thrown into and out \ + of it." + icon = 'icons/effects/effects.dmi' + icon_state = "directional_shield" + density = FALSE // People can move pass these shields. + opacity = FALSE + anchored = TRUE + integrity_flags = INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF + layer = MOB_LAYER + 0.1 + mouse_opacity = FALSE + var/obj/item/shield_projector/projector = null // The thing creating the shield. + var/x_offset = 0 // Offset from the 'center' of where the projector is, so that if it moves, the shield can recalc its position. + var/y_offset = 0 // Ditto. + +/obj/effect/directional_shield/New(var/newloc, var/new_projector) + if(new_projector) + projector = new_projector + var/turf/us = get_turf(src) + var/turf/them = get_turf(projector) + if(them) + x_offset = us.x - them.x + y_offset = us.y - them.y + else + update_color() + ..(newloc) + +/obj/effect/directional_shield/proc/relocate() + if(!projector) + return // Nothing to follow. + var/turf/T = get_turf(projector) + if(!T) + return + var/turf/new_pos = locate(T.x + x_offset, T.y + y_offset, T.z) + if(new_pos) + forceMove(new_pos) + else + qdel(src) + +/obj/effect/directional_shield/proc/update_color(var/new_color) + if(!projector) + color = "#0099FF" + else + animate(src, color = new_color, 5) +// color = new_color + +/obj/effect/directional_shield/Destroy() + if(projector) + projector.active_shields -= src + projector = null + return ..() + +/obj/effect/directional_shield/CanPass(atom/movable/mover, turf/target) + . = ..() + if(istype(mover, /obj/projectile)) + var/obj/projectile/P = mover + if(P.projectile_type & PROJECTILE_TYPE_TRACE) + return TRUE + if(check_defensive_arc_tile(src, P, 90, null, dir)) + return FALSE + return TRUE + +/obj/effect/directional_shield/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + impact_flags &= ~PROJECTILE_IMPACT_FLAGS_SHOULD_NOT_HIT + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + adjust_health(-proj.get_structure_damage()) + playsound(src, 'sound/effects/EMPulse.ogg', 75, 1) + +// All the shields tied to their projector are one 'unit', and don't have individualized health values like most other shields. +/obj/effect/directional_shield/proc/adjust_health(amount) + if(projector) + projector.adjust_health(amount) // Projector will kill the shield if needed. + // If the shield lacks a projector, then it was probably spawned in by an admin for bus, so it's indestructable. diff --git a/code/modules/shieldgen/directional_shield.dm b/code/game/objects/items/shield_projector/shield_projector.dm similarity index 81% rename from code/modules/shieldgen/directional_shield.dm rename to code/game/objects/items/shield_projector/shield_projector.dm index fd1bc9df0f6d..e9b3547cf28e 100644 --- a/code/modules/shieldgen/directional_shield.dm +++ b/code/game/objects/items/shield_projector/shield_projector.dm @@ -1,81 +1,3 @@ -// This is the actual shield. The projector is a different item. -/obj/effect/directional_shield - name = "directional combat shield" - desc = "A wide shield, which has the property to block incoming projectiles but allow outgoing projectiles to pass it. \ - Slower moving objects are not blocked, so people can walk in and out of the barrier, and things can be thrown into and out \ - of it." - icon = 'icons/effects/effects.dmi' - icon_state = "directional_shield" - density = FALSE // People can move pass these shields. - opacity = FALSE - anchored = TRUE - integrity_flags = INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF - layer = MOB_LAYER + 0.1 - mouse_opacity = FALSE - var/obj/item/shield_projector/projector = null // The thing creating the shield. - var/x_offset = 0 // Offset from the 'center' of where the projector is, so that if it moves, the shield can recalc its position. - var/y_offset = 0 // Ditto. - -/obj/effect/directional_shield/New(var/newloc, var/new_projector) - if(new_projector) - projector = new_projector - var/turf/us = get_turf(src) - var/turf/them = get_turf(projector) - if(them) - x_offset = us.x - them.x - y_offset = us.y - them.y - else - update_color() - ..(newloc) - -/obj/effect/directional_shield/proc/relocate() - if(!projector) - return // Nothing to follow. - var/turf/T = get_turf(projector) - if(!T) - return - var/turf/new_pos = locate(T.x + x_offset, T.y + y_offset, T.z) - if(new_pos) - forceMove(new_pos) - else - qdel(src) - -/obj/effect/directional_shield/proc/update_color(var/new_color) - if(!projector) - color = "#0099FF" - else - animate(src, color = new_color, 5) -// color = new_color - -/obj/effect/directional_shield/Destroy() - if(projector) - projector.active_shields -= src - projector = null - return ..() - -/obj/effect/directional_shield/CanPass(atom/movable/mover, turf/target) - . = ..() - if(istype(mover, /obj/projectile)) - var/obj/projectile/P = mover - if(istype(P, /obj/projectile/test)) // Turrets need to try to kill the shield and so their test bullet needs to penetrate. - return TRUE - - var/bad_arc = global.reverse_dir[dir] // Arc of directions from which we cannot block. - if(check_shield_arc(src, bad_arc, P)) // This is actually for mobs but it will work for our purposes as well. - return FALSE - return TRUE - -/obj/effect/directional_shield/bullet_act(var/obj/projectile/P) - adjust_health(-P.get_structure_damage()) - P.on_hit() - playsound(src, 'sound/effects/EMPulse.ogg', 75, 1) - -// All the shields tied to their projector are one 'unit', and don't have individualized health values like most other shields. -/obj/effect/directional_shield/proc/adjust_health(amount) - if(projector) - projector.adjust_health(amount) // Projector will kill the shield if needed. - // If the shield lacks a projector, then it was probably spawned in by an admin for bus, so it's indestructable. - // This actually creates the shields. It's an item so that it can be carried, but it could also be placed inside a stationary object if desired. // It should work inside the contents of any mob. diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm index 0d19a6ecb53b..8925f463c055 100644 --- a/code/game/objects/items/shooting_range.dm +++ b/code/game/objects/items/shooting_range.dm @@ -29,7 +29,7 @@ return ..() -/obj/item/target/attack_hand(mob/user, list/params) +/obj/item/target/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // taking pinned targets off! var/obj/structure/target_stake/stake for(var/obj/structure/target_stake/T in view(3,src)) @@ -75,12 +75,14 @@ desc = "A shooting target that looks vaguely human shaped but not enough to cause controversy." hp = 1800 // alium onest too kinda -/obj/item/target/bullet_act(var/obj/projectile/Proj) - var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!" - var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1) +/obj/item/target/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + + var/p_x = proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset proj.p_x!" + var/p_y = proj.p_y + pick(0,0,0,0,0,-1,1) var/decaltype = 1 // 1 - scorch, 2 - bullet - if(istype(/obj/projectile/bullet, Proj)) + if(istype(/obj/projectile/bullet, proj)) decaltype = 2 @@ -88,7 +90,7 @@ if( virtualIcon.GetPixel(p_x, p_y) ) // if the located pixel isn't blank (null) - hp -= Proj.damage + hp -= proj.damage if(hp <= 0) for(var/mob/O in oviewers()) if ((O.client && !( O.has_status_effect(/datum/status_effect/sight/blindness) ))) @@ -109,7 +111,7 @@ bmark.pixel_x-- bmark.pixel_y-- - if(Proj.damage >= 20 || istype(Proj, /obj/projectile/beam/practice)) + if(proj.damage >= 20 || istype(proj, /obj/projectile/beam/practice)) bmark.icon_state = "scorch" bmark.setDir(pick(NORTH,SOUTH,EAST,WEST)) // random scorch design @@ -121,12 +123,12 @@ // Bullets are hard. They make dents! bmark.icon_state = "dent" - if(Proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes + if(proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes if(decaltype == 2) // bullet - if(prob(Proj.damage+30)) // bullets make holes more commonly! + if(prob(proj.damage+30)) // bullets make holes more commonly! new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole else // Lasers! - if(prob(Proj.damage-10)) // lasers make holes less commonly + if(prob(proj.damage-10)) // lasers make holes less commonly new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole // draw bullet holes @@ -141,8 +143,7 @@ return - return PROJECTILE_CONTINUE // the bullet/projectile goes through the target! - + return . | PROJECTILE_IMPACT_PASSTHROUGH // Small memory holder entity for transparent bullet holes /datum/bullethole diff --git a/code/game/objects/items/stacks/marker_beacons.dm b/code/game/objects/items/stacks/marker_beacons.dm index 9be024a7645a..9c923e7c75ce 100644 --- a/code/game/objects/items/stacks/marker_beacons.dm +++ b/code/game/objects/items/stacks/marker_beacons.dm @@ -106,7 +106,7 @@ var/list/marker_beacon_colors = list( icon_state = "[initial(icon_state)][lowertext(picked_color)]-on" set_light(light_range, light_power, marker_beacon_colors[picked_color]) -/obj/structure/marker_beacon/attack_hand(mob/user, list/params) +/obj/structure/marker_beacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, "You start picking [src] up...") if(do_after(user, remove_speed, target = src)) var/obj/item/stack/marker_beacon/M = new(loc) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index bde4383c2036..65a4985587b5 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -304,7 +304,7 @@ if(!amount) break -/obj/item/stack/attack_hand(mob/user, list/params) +/obj/item/stack/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) change_stack(user, 1) else diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index aecfe62bf7f6..2e9fea684955 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -262,8 +262,8 @@ /obj/item/cell/device/weapon, /obj/item/material/butterfly, /obj/item/material/knife, - /obj/item/melee/energy/sword, - /obj/item/shield/energy, + /obj/item/melee/transforming/energy/sword, + /obj/item/shield/transforming/energy, /obj/item/ammo_casing/, /obj/item/ammo_magazine/, /obj/item/storage/box/beanbags, diff --git a/code/game/objects/items/storage/lockbox.dm b/code/game/objects/items/storage/lockbox.dm index 558830cbd480..98673ed4e5c1 100644 --- a/code/game/objects/items/storage/lockbox.dm +++ b/code/game/objects/items/storage/lockbox.dm @@ -36,7 +36,7 @@ return else to_chat(user, "Access Denied") - else if(istype(W, /obj/item/melee/energy/blade)) + else if(istype(W, /obj/item/melee/ninja_energy_blade)) if(emag_act(INFINITY, user, W, "The locker has been sliced open by [user] with an energy blade!", "You hear metal being sliced and sparks flying.")) var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, src.loc) diff --git a/code/game/objects/items/storage/quickdraw.dm b/code/game/objects/items/storage/quickdraw.dm index 11575db69b74..3c64736e68ca 100644 --- a/code/game/objects/items/storage/quickdraw.dm +++ b/code/game/objects/items/storage/quickdraw.dm @@ -17,7 +17,7 @@ //When set to 1, a click while it is equipped will instead move the first item inside it to your hand // var/quickmode = 0 -// /obj/item/storage/quickdraw/attack_hand(mob/user, list/params) +// /obj/item/storage/quickdraw/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // if(src.loc == user) //If they aren't holding us, we do nothing special // if(ishuman(user)) // var/mob/living/carbon/human/H = user diff --git a/code/game/objects/items/storage/secure.dm b/code/game/objects/items/storage/secure.dm index ee732cef7faf..9d249922cbf5 100644 --- a/code/game/objects/items/storage/secure.dm +++ b/code/game/objects/items/storage/secure.dm @@ -33,7 +33,7 @@ /obj/item/storage/secure/attackby(obj/item/W as obj, mob/user as mob) if(locked) - if (istype(W, /obj/item/melee/energy/blade) && emag_act(INFINITY, user, "You slice through the lock of \the [src]")) + if (istype(W, /obj/item/melee/ninja_energy_blade) && emag_act(INFINITY, user, "You slice through the lock of \the [src]")) var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, src.loc) spark_system.start() @@ -198,5 +198,5 @@ /obj/item/pen ) -/obj/item/storage/secure/safe/attack_hand(mob/user, list/params) +/obj/item/storage/secure/safe/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return attack_self(user) diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 710acf38d926..03498f3fb1e0 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -28,7 +28,7 @@ new /obj/item/plastique(src) if("murder") - new /obj/item/melee/energy/sword(src) + new /obj/item/melee/transforming/energy/sword(src) new /obj/item/clothing/glasses/thermal/syndi(src) new /obj/item/card/emag(src) new /obj/item/clothing/shoes/syndigaloshes(src) diff --git a/code/game/objects/items/stream_projector/medichine.dm b/code/game/objects/items/stream_projector/medichine.dm index fb87b5393695..5ab66a2b6ad3 100644 --- a/code/game/objects/items/stream_projector/medichine.dm +++ b/code/game/objects/items/stream_projector/medichine.dm @@ -58,7 +58,7 @@ GLOBAL_LIST_EMPTY(medichine_cell_datums) /obj/item/stream_projector/medichine/valid_target(atom/entity) return isliving(entity) -/obj/item/stream_projector/medichine/attack_hand(mob/user, list/params) +/obj/item/stream_projector/medichine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.is_holding_inactive(src)) if(isnull(inserted_cartridge)) user.action_feedback(SPAN_WARNING("[src] has no vial loaded."), src) diff --git a/code/game/objects/items/tools/switchtool.dm b/code/game/objects/items/tools/switchtool.dm index c6d9b2bd7545..ae87558fd9e3 100644 --- a/code/game/objects/items/tools/switchtool.dm +++ b/code/game/objects/items/tools/switchtool.dm @@ -169,11 +169,6 @@ /obj/item/switchtool/proc/get_switchtool_enum(obj/item/I) return tools[I] -/obj/item/switchtool/handle_shield(mob/user) - if(get_switchtool_enum(deployed) == SWITCHTOOL_SHIELD) - return TRUE - return FALSE - /obj/item/switchtool/update_overlays() . = ..() if(!deployed) @@ -344,8 +339,8 @@ /obj/item/multitool/holoswitch = SWITCHTOOL_MULTITOOL, /obj/item/flashlight/holoswitch = SWITCHTOOL_LIGHT, /obj/item/soap/holoswitch = SWITCHTOOL_SOAP, - /obj/item/melee/energy/sword/holoswitch = SWITCHTOOL_SWORD, - /obj/item/shield/holoswitch = SWITCHTOOL_SHIELD + /obj/item/melee/transforming/energy/sword/holoswitch = SWITCHTOOL_SWORD, + // /obj/item/shield/holoswitch = SWITCHTOOL_SHIELD ) tool_functions = list( TOOL_SCALPEL, @@ -484,7 +479,7 @@ desc = "This should not exist." tool_speed = 0.9 -/obj/item/melee/energy/sword/holoswitch +/obj/item/melee/transforming/energy/sword/holoswitch name = "hardlight blade" desc = "This should not exist." diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index b564a95919b9..9811dbc5c85c 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -651,7 +651,7 @@ update_icon() return 0 -/obj/item/weldingtool/electric/attack_hand(mob/user, list/params) +/obj/item/weldingtool/electric/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(power_supply) power_supply.update_icon() diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 83ebd8e9c1b2..4ef067b847a6 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -518,7 +518,7 @@ playsound(user, 'sound/mecha/mechstep.ogg', 20, 1) cooldown = world.time -/obj/item/toy/prize/attack_hand(mob/user, list/params) +/obj/item/toy/prize/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(loc == user) if(cooldown < world.time - 8) to_chat(user, "You play with [src].") @@ -926,7 +926,7 @@ density = 1 var/phrase = "I don't want to exist anymore!" -/obj/structure/plushie/attack_hand(mob/user, list/params) +/obj/structure/plushie/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(user.a_intent == INTENT_HELP) user.visible_message("\The [user] hugs [src]!","You hug [src]!") diff --git a/code/game/objects/items/uav.dm b/code/game/objects/items/uav.dm index 852a1a8dac98..27ef0ae3ee6f 100644 --- a/code/game/objects/items/uav.dm +++ b/code/game/objects/items/uav.dm @@ -64,7 +64,7 @@ STOP_PROCESSING(SSobj, src) return ..() -/obj/item/uav/attack_hand(mob/user, list/params) +/obj/item/uav/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) //Has to be on the ground to work with it properly if(!isturf(loc)) return ..() diff --git a/code/game/objects/items/weapons/barrier_tape.dm b/code/game/objects/items/weapons/barrier_tape.dm index 8eb40321c06d..c82c16b019ca 100644 --- a/code/game/objects/items/weapons/barrier_tape.dm +++ b/code/game/objects/items/weapons/barrier_tape.dm @@ -133,7 +133,7 @@ var/list/tape_roll_applications = list() update_icon() return ..() -/obj/item/barrier_tape_roll/attack_hand(mob/user, list/params) +/obj/item/barrier_tape_roll/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) update_icon() return ..() @@ -334,7 +334,7 @@ var/list/tape_roll_applications = list() /obj/item/barrier_tape_segment/attackby(obj/item/W as obj, mob/user as mob) breaktape(user) -/obj/item/barrier_tape_segment/attack_hand(mob/user, list/params) +/obj/item/barrier_tape_segment/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (user.a_intent == INTENT_HELP && src.allowed(user)) user.show_viewers("\The [user] lifts \the [src], allowing passage.") for(var/obj/item/barrier_tape_segment/T in gettapeline()) diff --git a/code/game/objects/items/weapons/cigs_lighters.dm b/code/game/objects/items/weapons/cigs_lighters.dm index 630b93d5e7c4..58db3effd425 100644 --- a/code/game/objects/items/weapons/cigs_lighters.dm +++ b/code/game/objects/items/weapons/cigs_lighters.dm @@ -289,8 +289,8 @@ CIGARETTE PACKETS ARE IN FANCY.DM /obj/item/clothing/mask/smokable/cigarette/attackby(obj/item/W as obj, mob/user as mob) ..() - if(istype(W, /obj/item/melee/energy/sword)) - var/obj/item/melee/energy/sword/S = W + if(istype(W, /obj/item/melee/transforming/energy/sword)) + var/obj/item/melee/transforming/energy/sword/S = W if(S.active) light("[user] swings their [W], barely missing their nose. They light their [name] in the process.") @@ -464,7 +464,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM quench() /obj/item/clothing/mask/smokable/pipe/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/melee/energy/sword)) + if(istype(W, /obj/item/melee/transforming/energy/sword)) return ..() diff --git a/code/game/objects/items/weapons/duct_tape.dm b/code/game/objects/items/weapons/duct_tape.dm index 5fbb2b0d3199..6e05e576feae 100644 --- a/code/game/objects/items/weapons/duct_tape.dm +++ b/code/game/objects/items/weapons/duct_tape.dm @@ -202,7 +202,7 @@ qdel(I) to_chat(user, "You place \the [I] back into \the [src].") -/obj/item/duct_tape_piece/attack_hand(mob/user, list/params) +/obj/item/duct_tape_piece/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) anchored = FALSE return ..() // Pick it up now that it's unanchored. diff --git a/code/game/objects/items/weapons/game_kit.dm b/code/game/objects/items/weapons/game_kit.dm index 190f0364ad28..fafb6e8281ab 100644 --- a/code/game/objects/items/weapons/game_kit.dm +++ b/code/game/objects/items/weapons/game_kit.dm @@ -57,7 +57,7 @@ THAT STUPID GAME KIT /obj/item/game_kit/attack_ai(mob/user as mob, unused, flag) return src.attack_hand(user, unused, flag) -/obj/item/game_kit/attack_hand(mob/user as mob, unused, flag) +/obj/item/game_kit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (flag) return ..() diff --git a/code/game/objects/items/weapons/grenades/concussion.dm b/code/game/objects/items/weapons/grenades/concussion.dm index b8d5da08343b..c3ea4db24d4a 100644 --- a/code/game/objects/items/weapons/grenades/concussion.dm +++ b/code/game/objects/items/weapons/grenades/concussion.dm @@ -84,5 +84,5 @@ var/turf/O = get_turf(src) if(!O) return - src.fragmentate(O, num_fragments, spread_range, fragment_types) + shrapnel_explosion(num_fragments, spread_range, fragment_types) ..() diff --git a/code/game/objects/items/weapons/grenades/explosive.dm b/code/game/objects/items/weapons/grenades/explosive.dm index 0eae29d84cca..c750cac61625 100644 --- a/code/game/objects/items/weapons/grenades/explosive.dm +++ b/code/game/objects/items/weapons/grenades/explosive.dm @@ -21,7 +21,7 @@ if(explosion_size) on_explosion(O) - src.fragmentate(O, num_fragments, spread_range, fragment_types) + shrapnel_explosion(num_fragments, spread_range, fragment_types) qdel(src) /obj/item/grenade/explosive/proc/on_explosion(var/turf/O) @@ -38,34 +38,6 @@ fragment_types = list(/obj/projectile/bullet/pellet/fragment) num_fragments = 200 //total number of fragments produced by the grenade - - -/obj/proc/fragmentate(var/turf/T=get_turf(src), var/fragment_number = 30, var/spreading_range = 5, var/list/fragtypes=list(/obj/projectile/bullet/pellet/fragment/)) - set waitfor = 0 - var/list/target_turfs = getcircle(T, spreading_range) - var/fragments_per_projectile = round(fragment_number/target_turfs.len) - - for(var/turf/O in target_turfs) - sleep(0) - var/fragment_type = pickweight(fragtypes) - var/obj/projectile/bullet/pellet/fragment/P = new fragment_type(T) - P.pellets = fragments_per_projectile - P.shot_from = name - - P.old_style_target(O) - P.fire() - - //Make sure to hit any mobs in the source turf - for(var/mob/living/M in T) - //lying on a frag grenade while the grenade is on the ground causes you to absorb most of the shrapnel. - //you will most likely be dead, but others nearby will be spared the fragments that hit you instead. - if(M.lying && isturf(src.loc)) - P.projectile_attack_mob(M, 0, 5) - else if(!M.lying && src.loc != get_turf(src)) //if it's not on the turf, it must be in the mob! - P.projectile_attack_mob(M, 0, 25) //you're holding a grenade, dude! - else - P.projectile_attack_mob(M, 0, 100) //otherwise, allow a decent amount of fragments to pass - /obj/item/grenade/explosive/mini name = "mini fragmentation grenade" desc = "A miniaturized fragmentation grenade, this one poses relatively little threat on its own." diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm index 7018d5f8d96f..1a447da35f2b 100644 --- a/code/game/objects/items/weapons/grenades/flashbang.dm +++ b/code/game/objects/items/weapons/grenades/flashbang.dm @@ -110,7 +110,7 @@ var/turf/O = get_turf(src) if(!O) return - src.fragmentate(O, num_fragments, spread_range, fragment_types) + shrapnel_explosion(num_fragments, spread_range, fragment_types) ..() /obj/item/grenade/flashbang/stingbang/shredbang diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 795f743c060b..a2bf2996ee9f 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -111,7 +111,7 @@ ..() return -/obj/item/grenade/attack_hand(mob/user, list/params) +/obj/item/grenade/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) walk(src, null, null) ..() return diff --git a/code/game/objects/items/weapons/grenades/projectile.dm b/code/game/objects/items/weapons/grenades/projectile.dm index 3b781ef46118..2621d12150bf 100644 --- a/code/game/objects/items/weapons/grenades/projectile.dm +++ b/code/game/objects/items/weapons/grenades/projectile.dm @@ -7,6 +7,7 @@ //The radius of the circle used to launch projectiles. Lower values mean less projectiles are used but if set too low gaps may appear in the spread pattern var/spread_range = 7 + var/total_pellets = 1 // default value of 1 just forces one per turf as we round up loadable = FALSE @@ -17,12 +18,9 @@ if(!O) return - src.launch_many_projectiles(O, spread_range, projectile_types) - + shrapnel_explosion(total_pellets, spread_range, projectile_types) qdel(src) - - /obj/item/grenade/shooter/rubber name = "rubber pellet grenade" desc = "An anti-riot grenade that fires a cloud of rubber projectiles upon detonation." @@ -47,30 +45,3 @@ /obj/item/grenade/shooter/energy/tesla name = "tesla grenade" projectile_types = list(/obj/projectile/beam/chain_lightning/lesser) - - -// This is just fragmentate, but less specific. Don't know how to make either of them less awful, at the moment -/obj/proc/launch_many_projectiles(var/turf/T=get_turf(src), var/spreading_range = 5, var/list/projectiletypes=list(/obj/projectile/bullet/pistol/rubber)) - set waitfor = 0 - var/list/target_turfs = getcircle(T, spreading_range) - - for(var/turf/O in target_turfs) - sleep(0) - var/shot_type = pick(projectiletypes) - - var/obj/projectile/P = new shot_type(T) - P.shot_from = src.name - - P.old_style_target(O) - P.fire() - - //Make sure to hit any mobs in the source turf - for(var/mob/living/M in T) - //lying on a frag grenade while the grenade is on the ground causes you to absorb most of the shrapnel. - //you will most likely be dead, but others nearby will be spared the fragments that hit you instead. - if(M.lying && isturf(src.loc)) - P.projectile_attack_mob(M, 0, 5) - else if(!M.lying && src.loc != get_turf(src)) //if it's not on the turf, it must be in the mob! - P.projectile_attack_mob(M, 0, 25) //you're holding a grenade, dude! - else - P.projectile_attack_mob(M, 0, 100) //otherwise, allow a decent amount of fragments to pass diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm index 0bfe624f50f9..92769892f314 100644 --- a/code/game/objects/items/weapons/implants/implant.dm +++ b/code/game/objects/items/weapons/implants/implant.dm @@ -92,6 +92,10 @@ else ..() +/obj/item/implant/surgically_remove(mob/living/carbon/human/target, obj/item/organ/external/chest/removing_from) + . = ..() + imp_in = null + implanted = 0 ////////////////////////////// // Tracking Implant ////////////////////////////// diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm index fe80c61ab876..e997a571d9be 100644 --- a/code/game/objects/items/weapons/implants/implantchair.dm +++ b/code/game/objects/items/weapons/implants/implantchair.dm @@ -24,7 +24,7 @@ ..() add_implants() -/obj/machinery/implantchair/attack_hand(mob/user, list/params) +/obj/machinery/implantchair/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) var/health_text = "" if(src.occupant) diff --git a/code/game/objects/items/weapons/implants/implantpad.dm b/code/game/objects/items/weapons/implants/implantpad.dm index cec946b04872..5a1a4076d171 100644 --- a/code/game/objects/items/weapons/implants/implantpad.dm +++ b/code/game/objects/items/weapons/implants/implantpad.dm @@ -21,7 +21,7 @@ return -/obj/item/implantpad/attack_hand(mob/user, list/params) +/obj/item/implantpad/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if ((src.case && user.is_holding(src))) user.put_in_active_hand(case) diff --git a/code/game/objects/items/weapons/material/knives.dm b/code/game/objects/items/weapons/material/knives.dm index 2c7c409f1a73..a7fa5162c084 100644 --- a/code/game/objects/items/weapons/material/knives.dm +++ b/code/game/objects/items/weapons/material/knives.dm @@ -171,12 +171,10 @@ item_state = "armblade" slot_flags = NONE -/obj/item/material/knife/machete/armblade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(33)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return TRUE - return FALSE + passive_parry = /datum/passive_parry{ + parry_chance_projectile = 0; + parry_chance_default = 33; + } /obj/item/material/knife/machete/armblade/hardsuit var/obj/item/hardsuit_module/armblade/storing_module diff --git a/code/game/objects/items/weapons/material/material.dm b/code/game/objects/items/weapons/material/material.dm index f1ebf5f3b956..c3394a87aea4 100644 --- a/code/game/objects/items/weapons/material/material.dm +++ b/code/game/objects/items/weapons/material/material.dm @@ -36,6 +36,7 @@ // var/dulled_divisor = 0.1 //Just drops the damage to a tenth // var/drops_debris = 1 + /obj/item/material/Initialize(mapload, material) if(!isnull(material)) material_parts = material diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm index 0b4cdc519d42..b29f86dd53b5 100644 --- a/code/game/objects/items/weapons/material/swords.dm +++ b/code/game/objects/items/weapons/material/swords.dm @@ -11,19 +11,17 @@ pickup_sound = 'sound/items/pickup/sword.ogg' force_multiplier = 1.5 + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 50; + parry_chance_projectile = 10; + } + /obj/item/material/sword/plasteel material_parts = /datum/material/plasteel /obj/item/material/sword/durasteel material_parts = /datum/material/durasteel -/obj/item/material/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(unique_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - /obj/item/material/sword/suicide_act(mob/user) var/datum/gender/TU = GLOB.gender_datums[user.get_visible_gender()] viewers(user) << "[user] is falling on the [src.name]! It looks like [TU.he] [TU.is] trying to commit suicide." @@ -63,31 +61,13 @@ SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', ) -/obj/item/material/sword/sabre/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(60)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - playsound(user.loc, 'sound/items/drop/knife.ogg', 50, 1) - return 1 - if(unique_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - - playsound(user.loc, 'sound/weapons/plasma_cutter.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/material/sword/sabre/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - if(user.incapacitated() || !istype(damage_source, /obj/projectile/)) - return 0 - - var/bad_arc = global.reverse_dir[user.dir] - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 + passive_parry = /datum/passive_parry/melee{ + parry_chance_default = 50; + } -//meant to play when unsheathing the blade from the sabre sheath. +// meant to play when unsheathing the blade from the sabre sheath. +// todo: -_- this should be on the sheath +// todo: we need a better way to do unsheath sounds for weapons and storage... /obj/item/material/sword/sabre/on_enter_storage(datum/object_system/storage/storage) . = ..() playsound(loc, 'sound/effects/holster/sheathin.ogg', 50, 1) diff --git a/code/game/objects/items/weapons/material/twohanded.dm b/code/game/objects/items/weapons/material/twohanded.dm index 4e615154fed7..16f3fb71cd2b 100644 --- a/code/game/objects/items/weapons/material/twohanded.dm +++ b/code/game/objects/items/weapons/material/twohanded.dm @@ -28,6 +28,10 @@ drop_sound = 'sound/items/drop/sword.ogg' pickup_sound = 'sound/items/pickup/sword.ogg' + passive_parry = /datum/passive_parry/melee{ + parry_chance_melee = 15; + } + /obj/item/material/twohanded/update_held_icon() var/mob/living/M = loc if(istype(M) && M.can_wield_item(src) && is_held_twohanded(M)) @@ -50,14 +54,6 @@ . = ..() update_icon() -//Allow a small chance of parrying melee attacks when wielded - maybe generalize this to other weapons someday -/obj/item/material/twohanded/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(wielded && default_parry_check(user, attacker, damage_source) && prob(15)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - /obj/item/material/twohanded/update_icon() icon_state = "[base_icon][wielded]" item_state = icon_state diff --git a/code/game/objects/items/weapons/melee/deflect.dm b/code/game/objects/items/weapons/melee/deflect.dm deleted file mode 100644 index e951f4d5c5a1..000000000000 --- a/code/game/objects/items/weapons/melee/deflect.dm +++ /dev/null @@ -1,31 +0,0 @@ -/* - * The home of basic deflect / defense code. - */ - -/obj/item/melee - var/defend_chance = 5 // The base chance for the weapon to parry. - var/projectile_parry_chance = 0 // The base chance for a projectile to be deflected. - -/obj/item/melee/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(.) - return . - if(default_parry_check(user, attacker, damage_source) && prob(defend_chance)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - return 1 - if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - return 1 - - return 0 - -/obj/item/melee/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - if(.) - return . - if(user.incapacitated() || !istype(damage_source, /obj/projectile)) - return 0 - - var/bad_arc = global.reverse_dir[user.dir] - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm deleted file mode 100644 index 467de03f3614..000000000000 --- a/code/game/objects/items/weapons/melee/energy.dm +++ /dev/null @@ -1,729 +0,0 @@ -/obj/item/melee/energy - var/active = 0 - var/active_force - var/active_throwforce - var/active_w_class - var/active_embed_chance = 0 //In the off chance one of these is supposed to embed, you can just tweak this var - icon = 'icons/obj/weapons.dmi' - sharp = 0 - edge = 0 - armor_penetration = 50 - atom_flags = NOCONDUCT | NOBLOODY - var/lrange = 2 - var/lpower = 2 - var/lcolor = "#0099FF" - var/colorable = FALSE - var/rainbow = FALSE - // If it uses energy. - var/use_cell = FALSE - var/hitcost = 120 - var/obj/item/cell/bcell = null - var/cell_type = /obj/item/cell/device - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/melee/energy/proc/activate(mob/living/user) - if(active) - return - active = 1 - if(rainbow) - item_state = "[icon_state]_blade_rainbow" - else - item_state = "[icon_state]_blade" - embed_chance = active_embed_chance - damage_force = active_force - throw_force = active_throwforce - sharp = 1 - edge = 1 - set_weight_class(active_w_class) - playsound(user, 'sound/weapons/saberon.ogg', 50, 1) - update_icon() - set_light(lrange, lpower, lcolor) - to_chat(user, "Alt-click to recolor it.") - -/obj/item/melee/energy/proc/deactivate(mob/living/user) - if(!active) - return - playsound(user, 'sound/weapons/saberoff.ogg', 50, 1) - item_state = "[icon_state]" - active = 0 - embed_chance = initial(embed_chance) - damage_force = initial(damage_force) - throw_force = initial(throw_force) - sharp = initial(sharp) - edge = initial(edge) - set_weight_class(initial(w_class)) - update_icon() - set_light(0,0) - -/obj/item/melee/energy/proc/use_charge(var/cost) - if(active) - if(bcell) - if(bcell.checked_use(cost)) - return 1 - else - return 0 - return null - -/obj/item/melee/energy/examine(mob/user, dist) - . = ..() - if(use_cell) - if(bcell) - . += "The blade is [round(bcell.percent())]% charged." - if(!bcell) - . += "The blade does not have a power source installed." - -/obj/item/melee/energy/attack_self(mob/user) - . = ..() - if(.) - return - if(use_cell) - if((!bcell || bcell.charge < hitcost) && !active) - to_chat(user, "\The [src] does not seem to have power.") - return - - var/datum/gender/TU = GLOB.gender_datums[user.get_visible_gender()] - if (active) - if ((MUTATION_CLUMSY in user.mutations) && prob(50)) - user.visible_message("\The [user] accidentally cuts [TU.himself] with \the [src].",\ - "You accidentally cut yourself with \the [src].") - var/mob/living/carbon/human/H = ishuman(user)? user : null - H.take_random_targeted_damage(brute = 5, burn = 5) - deactivate(user) - else - activate(user) - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return - -/obj/item/melee/energy/suicide_act(mob/user) - var/datum/gender/TU = GLOB.gender_datums[user.get_visible_gender()] - if(active) - user.visible_message(pick("\The [user] is slitting [TU.his] stomach open with \the [src]! It looks like [TU.he] [TU.is] trying to commit seppuku.",\ - "\The [user] is falling on \the [src]! It looks like [TU.he] [TU.is] trying to commit suicide.")) - return (BRUTELOSS|FIRELOSS) - -/obj/item/melee/energy/attack_mob(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) - . = ..() - if(active && use_cell) - if(!use_charge(hitcost)) - deactivate(user) - visible_message("\The [src]'s blade flickers, before deactivating.") - -/obj/item/melee/energy/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/multitool) && colorable && !active) - if(!rainbow) - rainbow = TRUE - else - rainbow = FALSE - to_chat(user, "You manipulate the color controller in [src].") - update_icon() - if(use_cell) - if(istype(W, cell_type)) - if(!bcell) - if(!user.attempt_insert_item_for_installation(W, src)) - return - bcell = W - to_chat(user, "You install a cell in [src].") - update_icon() - else - to_chat(user, "[src] already has a cell.") - else if(W.is_screwdriver() && bcell) - bcell.update_icon() - bcell.forceMove(get_turf(loc)) - bcell = null - to_chat(user, "You remove the cell from \the [src].") - deactivate() - update_icon() - return - return ..() - -/obj/item/melee/energy/get_cell(inducer) - return bcell - -/obj/item/melee/energy/update_icon() - . = ..() - var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") - blade_overlay.color = lcolor - color = lcolor - if(rainbow) - blade_overlay = mutable_appearance(icon, "[icon_state]_blade_rainbow") - blade_overlay.color = "FFFFFF" - color = "FFFFFF" - cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other - if(active) - add_overlay(blade_overlay) - if(istype(usr,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = usr - H.update_inv_l_hand() - H.update_inv_r_hand() - -/obj/item/melee/energy/AltClick(mob/living/user) - if(!colorable) //checks if is not colorable - return - if(!in_range(src, user)) //Basic checks to prevent abuse - return - if(user.incapacitated() || !istype(user)) - to_chat(user, "You can't do that right now!") - return - - if(alert("Are you sure you want to recolor your blade?", "Confirm Recolor", "Yes", "No") == "Yes") - var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null - if(energy_color_input) - lcolor = "#[sanitize_hexcolor(energy_color_input)]" - color = lcolor - deactivate() - update_icon() - . = ..() - - -/* - * Energy Axe - */ -/obj/item/melee/energy/axe - name = "energy axe" - desc = "An energised battle axe." - icon_state = "eaxe" - item_state = "eaxe" - //active_force = 150 //holy... - active_force = 60 - active_throwforce = 35 - active_w_class = WEIGHT_CLASS_HUGE - //damage_force = 40 - //throw_force = 25 - damage_force = 20 - throw_force = 10 - throw_speed = 1 - throw_range = 5 - w_class = WEIGHT_CLASS_NORMAL - origin_tech = list(TECH_MAGNET = 3, TECH_COMBAT = 4) - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - sharp = 1 - edge = 1 - can_cleave = TRUE - -/obj/item/melee/energy/axe/activate(mob/living/user) - ..() - damtype = SEARING - to_chat(user, "\The [src] is now energised.") - -/obj/item/melee/energy/axe/deactivate(mob/living/user) - ..() - damtype = BRUTE - to_chat(user, "\The [src] is de-energised. It's just a regular axe now.") - -/obj/item/melee/energy/axe/suicide_act(mob/user) - var/datum/gender/TU = GLOB.gender_datums[user.get_visible_gender()] - visible_message("\The [user] swings \the [src] towards [TU.his] head! It looks like [TU.he] [TU.is] trying to commit suicide.") - return (BRUTELOSS|FIRELOSS) - -/obj/item/melee/energy/axe/charge - name = "charge axe" - desc = "An energised axe." - active_force = 35 - active_throwforce = 20 - damage_force = 15 - use_cell = TRUE - hitcost = 120 - -/obj/item/melee/energy/axe/charge/loaded/Initialize(mapload) - . = ..() - bcell = new/obj/item/cell/device/weapon(src) - -/* - * Energy Sword - */ -/obj/item/melee/energy/sword - color - name = "energy sword" - desc = "May the damage_force be within you." - icon_state = "esword" - item_state = "esword" - active_force = 30 - active_throwforce = 20 - active_w_class = WEIGHT_CLASS_BULKY - damage_force = 3 - throw_force = 5 - throw_speed = 1 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - atom_flags = NOBLOODY - origin_tech = list(TECH_MAGNET = 3, TECH_ILLEGAL = 4) - sharp = 1 - edge = 1 - colorable = TRUE - drop_sound = 'sound/items/drop/sword.ogg' - pickup_sound = 'sound/items/pickup/sword.ogg' - projectile_parry_chance = 65 - -/obj/item/melee/energy/sword/dropped(mob/user, atom_flags, atom/newLoc) - . = ..() - if(!istype(loc,/mob)) - deactivate(user) - -/obj/item/melee/energy/sword/activate(mob/living/user) - if(!active) - to_chat(user, "\The [src] is now energised.") - - ..() - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - -/obj/item/melee/energy/sword/deactivate(mob/living/user) - if(active) - to_chat(user, "\The [src] deactivates!") - ..() - attack_verb = list() - -/obj/item/melee/energy/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(60)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - if(active && unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/melee/energy/sword/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - if(user.incapacitated() || !istype(damage_source, /obj/projectile/)) - return 0 - - var/bad_arc = global.reverse_dir[user.dir] - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -/obj/item/melee/energy/sword/attackby(obj/item/W, mob/living/user, params) - if(istype(W, /obj/item/melee/energy/sword)) - if(HAS_TRAIT(W, TRAIT_ITEM_NODROP) || HAS_TRAIT(src, TRAIT_ITEM_NODROP)) - to_chat(user, "\the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? src : W] is stuck to your hand, you can't attach it to \the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? W : src]!") - return - if(istype(W, /obj/item/melee/energy/sword/charge)) - to_chat(user,"These blades are incompatible, you can't attach them to each other!") - return - else - to_chat(user, "You combine the two energy swords, making a single supermassive blade! You're cool.") - new /obj/item/melee/energy/sword/dualsaber(user.drop_location()) - qdel(W) - qdel(src) - else - return ..() - -/obj/item/melee/energy/sword/pirate - name = "energy cutlass" - desc = "Arrrr matey." - icon_state = "cutlass" - item_state = "cutlass" - colorable = TRUE - -//Return of the King -/obj/item/melee/energy/sword/dualsaber - name = "double-bladed energy sword" - desc = "Handle with care." - icon_state = "dualsaber" - item_state = "dualsaber" - damage_force = 3 - active_force = 60 - throw_force = 5 - throw_speed = 3 - armor_penetration = 35 - colorable = TRUE - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - projectile_parry_chance = 85 - -/* - *Ionic Rapier - */ - -/obj/item/melee/energy/sword/ionic_rapier - name = "ionic rapier" - desc = "Designed specifically for disrupting electronics at close range, it is extremely deadly against synthetics, but almost harmless to pure organic targets." - description_info = "This is a dangerous melee weapon that will deliver a moderately powerful electromagnetic pulse to whatever it strikes. \ - Striking a lesser robotic entity will compel it to attack you, as well. It also does extra burn damage to robotic entities, but it does \ - very little damage to purely organic targets." - icon_state = "ionrapier" - item_state = "ionrapier" - active_force = 5 - active_throwforce = 3 - active_embed_chance = 0 - sharp = 1 - edge = 1 - armor_penetration = 0 - atom_flags = NOBLOODY - lrange = 2 - lpower = 2 - lcolor = "#0000FF" - projectile_parry_chance = 30 // It's not specifically designed for cutting and slashing, but it can still, maybe, save your life. - -/obj/item/melee/energy/sword/ionic_rapier/afterattack(atom/target, mob/user, clickchain_flags, list/params) - if(istype(target, /obj) && (clickchain_flags & CLICKCHAIN_HAS_PROXIMITY) && active) - // EMP stuff. - var/obj/O = target - O.emp_act(3) // A weaker severity is used because this has infinite uses. - playsound(get_turf(O), 'sound/effects/EMPulse.ogg', 100, 1) - user.setClickCooldown(user.get_attack_speed(src)) // A lot of objects don't set click delay. - return ..() - -/obj/item/melee/energy/sword/ionic_rapier/melee_mob_hit(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) - . = ..() - var/mob/living/L = target - if(!istype(L)) - return - if(L.isSynthetic() && active) - // Do some extra damage. Not a whole lot more since emp_act() is pretty nasty on FBPs already. - L.emp_act(3) // A weaker severity is used because this has infinite uses. - playsound(get_turf(L), 'sound/effects/EMPulse.ogg', 100, 1) - L.adjustFireLoss(damage_force * 3) // 15 Burn, for 20 total. - playsound(get_turf(L), 'sound/weapons/blade1.ogg', 100, 1) - - // Make lesser robots really mad at us. - if(L.mob_class & MOB_CLASS_SYNTHETIC) - if(L.has_polaris_AI()) - L.taunt(user) - L.adjustFireLoss(damage_force * 6) // 30 Burn, for 50 total. - -/obj/item/melee/energy/sword/ionic_rapier/lance - name = "zero-point lance" - desc = "Designed specifically for disrupting electronics at relatively close range, however it is still capable of dealing some damage to living beings." - active_force = 20 - armor_penetration = 15 - reach = 2 - -/* - * Charge blade. Uses a cell, and costs energy per strike. - */ - -/obj/item/melee/energy/sword/charge - name = "charge sword" - desc = "A small, handheld device which emits a high-energy 'blade'." - origin_tech = list(TECH_COMBAT = 5, TECH_MAGNET = 3, TECH_ILLEGAL = 4) - active_force = 25 - armor_penetration = 25 - projectile_parry_chance = 40 - colorable = TRUE - use_cell = TRUE - hitcost = 75 - -/obj/item/melee/energy/sword/charge/loaded/Initialize(mapload) - . = ..() - bcell = new/obj/item/cell/device/weapon(src) - -/obj/item/melee/energy/sword/charge/attackby(obj/item/W, mob/living/user, params) - if(istype(W, /obj/item/melee/energy/sword/charge)) - if(HAS_TRAIT(W, TRAIT_ITEM_NODROP) || HAS_TRAIT(src, TRAIT_ITEM_NODROP)) - to_chat(user, "\the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? src : W] is stuck to your hand, you can't attach it to \the [HAS_TRAIT(src, TRAIT_ITEM_NODROP) ? W : src]!") - return - else - to_chat(user, "You combine the two charge swords, making a single supermassive blade! You're cool.") - new /obj/item/melee/energy/sword/charge/dualsaber(user.drop_location()) - qdel(W) - qdel(src) - else - return ..() - -//Charge Type Double Esword -/obj/item/melee/energy/sword/charge/dualsaber - name = "double-bladed charge sword" - desc = "Make sure you bought batteries." - icon_state = "dualsaber" - item_state = "dualsaber" - damage_force = 3 - active_force = 50 - throw_force = 5 - throw_speed = 3 - armor_penetration = 30 - colorable = TRUE - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - projectile_parry_chance = 65 - hitcost = 150 - -//Energy Blade (ninja uses this) - -//Can't be activated or deactivated, so no reason to be a subtype of energy -/obj/item/melee/energy/blade - name = "energy blade" - desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal." - icon_state = "blade" - item_state = "blade" - damage_force = 40 //Normal attacks deal very high damage - about the same as wielded fire axe - armor_penetration = 100 - sharp = 1 - edge = 1 - anchored = 1 // Never spawned outside of inventory, should be fine. - throw_force = 1 //Throwing or dropping the item deletes it. - throw_speed = 1 - throw_range = 1 - w_class = WEIGHT_CLASS_BULKY//So you can't hide it in your pocket or some such. - atom_flags = NOBLOODY - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - var/mob/living/creator - var/datum/effect_system/spark_spread/spark_system - projectile_parry_chance = 60 - lcolor = "#00FF00" - -/obj/item/melee/energy/blade/Initialize(mapload) - . = ..() - spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - START_PROCESSING(SSobj, src) - set_light(lrange, lpower, lcolor) - -/obj/item/melee/energy/blade/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/melee/energy/blade/attack_self(mob/user) - . = ..() - if(.) - return - qdel(src) - -/obj/item/melee/energy/blade/dropped(mob/user, atom_flags, atom/newLoc) - . = ..() - qdel(src) - -/obj/item/melee/energy/blade/process(delta_time) - if(!creator || loc != creator || !creator.is_holding(src)) - // Tidy up a bit. - if(istype(loc,/mob/living)) - var/mob/living/carbon/human/host = loc - if(istype(host)) - for(var/obj/item/organ/external/organ in host.organs) - for(var/obj/item/O in organ.implants) - if(O == src) - organ.implants -= src - host.pinned -= src - host.embedded -= src - host._handle_inventory_hud_remove(src) - qdel(src) - -/obj/item/melee/energy/blade/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(60)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - if(unique_parry_check(user, attacker, damage_source) && prob(projectile_parry_chance)) - user.visible_message("\The [user] deflects [attack_text] with \the [src]!") - - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - - return 0 - -/obj/item/melee/energy/blade/unique_parry_check(mob/user, mob/attacker, atom/damage_source) - - if(user.incapacitated() || !istype(damage_source, /obj/projectile/)) - return 0 - - var/bad_arc = global.reverse_dir[user.dir] - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -//Energy Spear - -/obj/item/melee/energy/spear - name = "energy spear" - desc = "Concentrated energy forming a sharp tip at the end of a long rod." - icon_state = "espear" - armor_penetration = 75 - sharp = 1 - edge = 1 - damage_force = 5 - throw_force = 10 - throw_speed = 7 - throw_range = 11 - reach = 2 - w_class = WEIGHT_CLASS_BULKY - active_force = 25 - active_throwforce = 30 - active_w_class = WEIGHT_CLASS_HUGE - colorable = TRUE - lcolor = "#800080" - -/obj/item/melee/energy/spear/activate(mob/living/user) - if(!active) - to_chat(user, "\The [src] is now energised.") - ..() - attack_verb = list("jabbed", "stabbed", "impaled") - AddComponent(/datum/component/jousting) - - -/obj/item/melee/energy/spear/deactivate(mob/living/user) - if(active) - to_chat(user, "\The [src] deactivates!") - ..() - attack_verb = list("whacked", "beat", "slapped", "thonked") - DelComponent(/datum/component/jousting) - -/obj/item/melee/energy/spear/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return 1 - return 0 - -/obj/item/melee/energy/hfmachete // ported from /vg/station - vgstation-coders/vgstation13#13913, fucked up by hatterhat - name = "high-frequency machete" - desc = "A high-frequency broad blade used either as an implement or in combat like a short sword." - icon_state = "hfmachete0" - sharp = TRUE - edge = TRUE - damage_force = 20 // You can be crueler than that, Jack. - throw_force = 40 - throw_speed = 8 - throw_range = 8 - w_class = WEIGHT_CLASS_NORMAL - siemens_coefficient = 1 - origin_tech = list(TECH_COMBAT = 3, TECH_ILLEGAL = 3) - attack_verb = list("attacked", "diced", "cleaved", "torn", "cut", "slashed") - armor_penetration = 50 - var/base_state = "hfmachete" - attack_sound = "machete_hit_sound" // dont mind the meaty hit sounds if you hit something that isnt meaty - can_cleave = TRUE - embed_chance = 0 // let's not - -/obj/item/melee/energy/hfmachete/update_icon() - icon_state = "[base_state][active]" - -/obj/item/melee/energy/hfmachete/attack_self(mob/user) - toggleActive(user) - add_fingerprint(user) - -/obj/item/melee/energy/hfmachete/proc/toggleActive(mob/user, var/togglestate = "") - switch(togglestate) - if("on") - active = 1 - if("off") - active = 0 - else - active = !active - if(active) - damage_force = 40 - throw_force = 20 - throw_speed = 3 - // sharpness = 1.7 - // sharpness_flags += HOT_EDGE | CUT_WALL | CUT_AIRLOCK - if only there a good sharpness system - armor_penetration = 100 - to_chat(user, " [src] starts vibrating.") - playsound(user, 'sound/weapons/hf_machete/hfmachete1.ogg', 40, 0) - set_weight_class(WEIGHT_CLASS_BULKY) - // user.lazy_register_event(/lazy_event/on_moved, src, PROC_REF(mob_moved)) - else - damage_force = initial(damage_force) - throw_force = initial(throw_force) - throw_speed = initial(throw_speed) - // sharpness = initial(sharpness) - // sharpness_flags = initial(sharpness_flags) - if only there was a good sharpness system - armor_penetration = initial(armor_penetration) - to_chat(user, " [src] stops vibrating.") - playsound(user, 'sound/weapons/hf_machete/hfmachete0.ogg', 40, 0) - set_weight_class(WEIGHT_CLASS_NORMAL) - // user.lazy_unregister_event(/lazy_event/on_moved, src, PROC_REF(mob_moved)) - update_icon() - -/obj/item/melee/energy/hfmachete/afterattack(atom/target, mob/user, clickchain_flags, list/params) - if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) - return - ..() - if(target) - if(istype(target,/obj/effect/plant)) - var/obj/effect/plant/P = target - P.die_off() - -/* -/obj/item/melee/energy/hfmachete/dropped(mob/user, atom_flags, atom/newLoc) - user.lazy_unregister_event(/lazy_event/on_moved, src, PROC_REF(mob_moved)) - -/obj/item/melee/energy/hfmachete/throw_at_old(atom/target, range, speed, thrower) // todo: get silicons to interpret this because >sleeps - if(!usr) - return ..() - spawn() - playsound(src, get_sfx("machete_throw"),30, 0) - animate(src, transform = turn(matrix(), -30), time = 1, loop = -1) - animate(transform = turn(matrix(), -60), time = 1) - animate(transform = turn(matrix(), -90), time = 1) - animate(transform = turn(matrix(), -120), time = 1) - animate(transform = turn(matrix(), -150), time = 1) - animate(transform = null, time = 1) - while(throwing) - sleep(5) - animate(src) - ..(target, range, speed = 3, thrower) -*/ - -// none of these are working properly in testing which is something you absolutely hate to see -/* -/obj/item/melee/energy/hfmachete/throw_at_old(atom/target, range, speed, thrower) - playsound(src, get_sfx("machete_throw"), 30, 0) - . = ..() - -/obj/item/melee/energy/hfmachete/throw_impact(atom/hit_atom, speed) - if(isturf(hit_atom)) - for(var/mob/M in hit_atom) - playsound(M, get_sfx("machete_throw_hit"), 60, 0) - ..() - -/obj/item/melee/energy/hfmachete/attack(mob/M, mob/living/user) - playsound(M, get_sfx("machete_hit"), 50, 0) - ..() -*/ -/* -/obj/item/melee/energy/hfmachete/proc/mob_moved(atom/movable/mover) - if(iscarbon(mover) && active) - for(var/obj/effect/plantsegment/P in range(mover,0)) - qdel(P) - -/obj/item/melee/energy/hfmachete/attackby(obj/item/W, mob/living/user) - ..() - if(istype(W, /obj/item/melee/energy/hfmachete)) - to_chat(user, "You combine the two [W] together, making a single scissor-bladed weapon! You feel fucking invincible!") - qdel(W) - W = null - qdel(src) - var/B = new /obj/item/bloodlust(user.loc) - user.put_in_hands(B) - // blust one day lads. -*/ - -/obj/item/melee/energy/sword/imperial - name = "energy gladius" - desc = "A broad, short energy blade. You'll be glad to have this in a fight." - icon_state = "sword0" - icon = 'icons/obj/weapons_vr.dmi' - item_icons = list(SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi') - -/obj/item/melee/energy/sword/imperial/activate(mob/living/user) - ..() - icon_state = "sword1" diff --git a/code/game/objects/items/weapons/melee/melee.dm b/code/game/objects/items/weapons/melee/melee.dm deleted file mode 100644 index 0856d665d166..000000000000 --- a/code/game/objects/items/weapons/melee/melee.dm +++ /dev/null @@ -1,7 +0,0 @@ -/obj/item/melee - icon = 'icons/obj/weapons.dmi' - attack_sound = "swing_hit" - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) diff --git a/code/game/objects/items/weapons/nullrod.dm b/code/game/objects/items/weapons/nullrod.dm index b70acf30e7b3..ce93b1ee66b2 100644 --- a/code/game/objects/items/weapons/nullrod.dm +++ b/code/game/objects/items/weapons/nullrod.dm @@ -359,7 +359,7 @@ if(used_blessing) else if(user.mind && (user.mind.isholy)) to_chat(user, "You are blessed by Carp-Sie. Wild space carp will no longer attack you.") - user.faction |= "carp" + user.add_iff_faction(MOB_IFF_FACTION_CARP) used_blessing = TRUE /obj/item/nullrod/claymore/bostaff //May as well make it a "claymore" and inherit the blocking diff --git a/code/game/objects/items/weapons/other.dm b/code/game/objects/items/weapons/other.dm index 9b5c907b0eb2..9cd042fb58f6 100644 --- a/code/game/objects/items/weapons/other.dm +++ b/code/game/objects/items/weapons/other.dm @@ -427,11 +427,11 @@ . = CLICKCHAIN_DO_NOT_PROPAGATE if(!target.mind) return - if(target.faction == user.faction) + if(target.shares_iff_faction(user)) to_chat(target, "You are graced by the familiar gaze of the Mother for a brief moment.") to_chat(user, "You smear the Mark of the Mother on [target]'s forehead using the [src].") to_chat(target, "You sense an unfamiliar presence looming over you. It encases you in a gentle, all-encompassing warmth.") - target.faction = user.faction + target.copy_iff_factions(user) playsound(src, pick(use_sound), 25) qdel(src) diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm deleted file mode 100644 index b036965c9baf..000000000000 --- a/code/game/objects/items/weapons/shields.dm +++ /dev/null @@ -1,494 +0,0 @@ -//** Shield Helpers -//These are shared by various items that have shield-like behaviour - -//bad_arc is the ABSOLUTE arc of directions from which we cannot block. If you want to fix it to e.g. the user's facing you will need to rotate the dirs yourself. -/proc/check_shield_arc(mob/user, var/bad_arc, atom/damage_source = null, mob/attacker = null) - //check attack direction - var/attack_dir = 0 //direction from the user to the source of the attack - if(istype(damage_source, /obj/projectile)) - var/obj/projectile/P = damage_source - attack_dir = get_dir(get_turf(user), P.starting) - else if(attacker) - attack_dir = get_dir(get_turf(user), get_turf(attacker)) - else if(damage_source) - attack_dir = get_dir(get_turf(user), get_turf(damage_source)) - - if(!(attack_dir && (attack_dir & bad_arc))) - return 1 - return 0 - -/proc/default_parry_check(mob/user, mob/attacker, atom/damage_source) - //parry only melee attacks - if(istype(damage_source, /obj/projectile) || (attacker && get_dist(user, attacker) > 1) || user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(!check_shield_arc(user, bad_arc, damage_source, attacker)) - return 0 - - return 1 - -/obj/item/proc/unique_parry_check(mob/user, mob/attacker, atom/damage_source) // An overrideable version of the above proc. - return default_parry_check(user, attacker, damage_source) - -/obj/item/shield - name = "shield" - var/base_block_chance = 50 - preserve_item = 1 - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - if(prob(get_block_chance(user, damage, damage_source, attacker))) - user.visible_message("\The [user] blocks [attack_text] with \the [src]!") - return 1 - return 0 - -/obj/item/shield/proc/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - return base_block_chance - -/obj/item/shield/riot - name = "riot shield" - desc = "A shield adept for close quarters engagement. It's also capable of protecting from less powerful projectiles." - icon = 'icons/obj/weapons.dmi' - icon_state = "riot" - slot_flags = SLOT_BACK - damage_force = 5.0 - throw_force = 5.0 - throw_speed = 1 - throw_range = 4 - w_class = WEIGHT_CLASS_BULKY - origin_tech = list(TECH_MATERIAL = 2) - materials_base = list(MAT_GLASS = 7500, MAT_STEEL = 1000) - attack_verb = list("shoved", "bashed") - worth_intrinsic = 300 - var/cooldown = 0 //shield bash cooldown. based on world.time - -/obj/item/shield/riot/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(user.incapacitated()) - return 0 - - //block as long as they are not directly behind us - var/bad_arc = global.reverse_dir[user.dir] //arc of directions from which we cannot block - if(check_shield_arc(user, bad_arc, damage_source, attacker)) - if(prob(get_block_chance(user, damage, damage_source, attacker))) - //At this point, we succeeded in our roll for a block attempt, however these kinds of shields struggle to stand up - //to strong bullets and lasers. They still do fine to pistol rounds of all kinds, however. - if(istype(damage_source, /obj/projectile)) - var/obj/projectile/P = damage_source - if((is_sharp(P) && P.armor_penetration >= 10) || istype(P, /obj/projectile/beam)) - //If we're at this point, the bullet/beam is going to go through the shield, however it will hit for less damage. - //Bullets get slowed down, while beams are diffused as they hit the shield, so these shields are not /completely/ - //useless. Extremely penetrating projectiles will go through the shield without less damage. - user.visible_message("\The [user]'s [src.name] is pierced by [attack_text]!") - if(P.armor_penetration < 30) //PTR bullets and x-rays will bypass this entirely. - P.damage = P.damage / 2 - return 0 - //Otherwise, if we're here, we're gonna stop the attack entirely. - user.visible_message("\The [user] blocks [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/Genhit.ogg', 50, 1) - return 1 - return 0 - -/obj/item/shield/riot/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/melee/baton)) - if(cooldown < world.time - 25) - user.visible_message("[user] bashes [src] with [W]!") - playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1) - cooldown = world.time - else - ..() - -/obj/item/shield/riot/flash - name = "strobe shield" - desc = "A shield with a built in, high intensity light capable of blinding and disorienting suspects. Takes regular handheld flashes as bulbs." - icon_state = "flashshield" - item_state = "flashshield" - var/obj/item/flash/embedded_flash - var/flashfail = 0 - -/obj/item/shield/riot/flash/Initialize(mapload) - . = ..() - embedded_flash = new(src) - -/obj/item/shield/riot/flash/attack_mob(mob/target, mob/user, clickchain_flags, list/params, mult, target_zone, intent) - if(user.a_intent == INTENT_HARM) - return ..() - return embedded_flash.attack_mob(arglist(args)) - -/obj/item/shield/riot/flash/attack_self(mob/user) - . = ..() - if(.) - return - . = embedded_flash.attack_self(user) - update_icon() - -/obj/item/shield/riot/flash/handle_shield(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) - . = ..() - if (. && damage && !embedded_flash.broken) - embedded_flash.melee_interaction_chain() - update_icon() - -/obj/item/shield/riot/flash/attackby(obj/item/W, mob/user) - if(istype(W, /obj/item/flash)) - var/obj/item/flash/flash = W - if(flashfail) - to_chat(user, "No sense replacing it with a broken bulb!") - return - else - to_chat(user, "You begin to replace the bulb...") - if(do_after(user, 20, target = user)) - if(flashfail || !flash || QDELETED(flash)) - return - playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) - qdel(embedded_flash) - embedded_flash = flash - flash.forceMove(src) - update_icon() - return - ..() - -/obj/item/shield/riot/flash/emp_act(severity) - . = ..() - embedded_flash.emp_act(severity) - update_icon() - -/obj/item/shield/riot/flash/update_icon_state() - . = ..() - if(!embedded_flash || embedded_flash.broken) - icon_state = "riot" - item_state = "riot" - else - icon_state = "flashshield" - item_state = "flashshield" - -/obj/item/shield/riot/flash/examine(mob/user, dist) - . = ..() - if (embedded_flash?.broken) - . += "The mounted bulb has burnt out. You can try replacing it with a new one." - -/obj/item/shield/makeshift - name = "metal shield" - desc = "A large shield made of wired and welded sheets of metal. The handle is made of cloth and leather, making it unwieldy." - icon = 'icons/obj/weapons.dmi' - icon_state = "makeshift_shield" - item_state = "metal" - slot_flags = null - damage_force = 10 - throw_force = 7 - -/obj/item/shield/riot/tower - name = "tower shield" - desc = "An immense tower shield. Designed to ensure maximum protection to the user, at the expense of mobility." - item_state = "metal" - icon_state = "metal" - damage_force = 16 - encumbrance = ITEM_ENCUMBRANCE_SHIELD_TOWER - throw_force = 15 //Massive piece of metal - w_class = WEIGHT_CLASS_HUGE - -/obj/item/shield/riot/tower/swat - name = "swat shield" - -/* I don't know if I really want this in the game. I DO want the code though. -/obj/item/shield/riot/implant - name = "hardlight shield implant" - desc = "A hardlight plane of force projected from the implant. While it is capable of withstanding immense amounts of abuse, it will eventually overload from sustained impacts, especially against energy attacks. Recharges while retracted." - item_state = "holoshield" - icon_state = "holoshield" - slowdown = 1 - shield_flags = SHIELD_FLAGS_DEFAULT - integrity_max = 100 - obj_integrity = 100 - can_shatter = FALSE - clothing_flags = ITEM_CAN_BLOCK - shield_flags = SHIELD_FLAGS_DEFAULT | SHIELD_KINETIC_STRONG | SHIELD_DISABLER_DISRUPTED - var/recharge_timerid - var/recharge_delay = 15 SECONDS - -/// Entirely overriden take_damage. This shouldn't exist outside of an implant (other than maybe christmas). -/obj/item/shield/riot/implant/take_damage_legacy(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, armour_penetration = 0) - obj_integrity -= damage_amount - if(obj_integrity < 0) - obj_integrity = 0 - if(obj_integrity == 0) - if(ismob(loc)) - var/mob/living/L = loc - playsound(src, /datum/soundbyte/grouped/sparks, 100, TRUE) - L.visible_message("[src] overloads from the damage sustained!") - L.dropItemToGround(src) //implant component catch hook will grab it. - -/obj/item/shield/riot/implant/Moved() - . = ..() - if(istype(loc, /obj/item/organ/cyberimp/arm/shield)) - recharge_timerid = addtimer(CALLBACK(src, PROC_REF(recharge)), recharge_delay, flags = TIMER_STOPPABLE) - else //extending - if(recharge_timerid) - deltimer(recharge_timerid) - recharge_timerid = null - -/obj/item/shield/riot/implant/proc/recharge() - if(obj_integrity == integrity_max) - return - obj_integrity = integrity_max - if(ismob(loc.loc)) //cyberimplant.user - to_chat(loc, "[src] has recharged its reinforcement matrix and is ready for use!") -*/ - -/obj/item/shield/riot/energy_proof - name = "energy resistant shield" - desc = "An ablative shield designed to absorb and disperse energy attacks. This comes at significant cost to its ability to withstand ballistics and kinetics, breaking apart easily." - icon_state = "riot_laser" - -/obj/item/shield/riot/kinetic_proof - name = "kinetic resistant shield" - desc = "A polymer and ceramic shield designed to absorb ballistic projectiles and kinetic force. It doesn't do very well into energy attacks, especially from weapons that inflict burns." - icon_state = "riot_bullet" - -//Exotics/Costume Shields -/obj/item/shield/riot/roman - name = "scutum" - desc = "A replica shield for close quarters engagement. Its modern materials are also capable of protecting from less powerful projectiles." - icon = 'icons/obj/weapons.dmi' - icon_state = "roman_shield" - slot_flags = SLOT_BACK - materials_base = list(MAT_WOOD = 7500, MAT_STEEL = 1000) - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - -/obj/item/shield/riot/buckler - name = "buckler" - desc = "A wrist mounted round shield for close quarters engagement. Its modern materials are also capable of protecting from less powerful projectiles." - icon = 'icons/obj/weapons.dmi' - icon_state = "buckler" - slot_flags = SLOT_BACK | SLOT_BELT - materials_base = list(MAT_WOOD = 7500, MAT_STEEL = 1000) - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - -/* - * Energy Shield - */ - -/obj/item/shield/energy - name = "energy combat shield" - desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere." - icon = 'icons/obj/weapons.dmi' - icon_state = "eshield" - item_state = "eshield" - slot_flags = SLOT_EARS - atom_flags = NOCONDUCT - damage_force = 3.0 - throw_force = 5.0 - throw_speed = 1 - throw_range = 4 - w_class = WEIGHT_CLASS_SMALL - var/lrange = 1.5 - var/lpower = 1.5 - var/lcolor = "#006AFF" - origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 4) - attack_verb = list("shoved", "bashed") - var/active = 0 - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - worth_intrinsic = 500 // op as balls - -/obj/item/shield/energy/handle_shield(mob/user) - if(!active) - return 0 //turn it on first! - . = ..() - - if(.) - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - -/obj/item/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null) - if(istype(damage_source, /obj/projectile)) - var/obj/projectile/P = damage_source - if((is_sharp(P) && damage > 10) || istype(P, /obj/projectile/beam)) - return (base_block_chance - round(damage / 3)) //block bullets and beams using the old block chance - return base_block_chance - -/obj/item/shield/energy/attack_self(mob/user) - . = ..() - if(.) - return - if ((MUTATION_CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You beat yourself in the head with [src].") - var/mob/living/carbon/human/H = ishuman(user)? user : null - H?.take_random_targeted_damage(brute = 5) - active = !active - if (active) - damage_force = 10 - update_icon() - set_weight_class(WEIGHT_CLASS_BULKY) - slot_flags = null - playsound(user, 'sound/weapons/saberon.ogg', 50, 1) - to_chat(user, "\The [src] is now active.") - - else - damage_force = 3 - update_icon() - set_weight_class(WEIGHT_CLASS_TINY) - slot_flags = SLOT_EARS - playsound(user, 'sound/weapons/saberoff.ogg', 50, 1) - to_chat(user, "\The [src] can now be concealed.") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return - -/obj/item/shield/energy/update_icon() - var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade") - if(lcolor) - blade_overlay.color = lcolor - cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other - if(active) - add_overlay(blade_overlay) - item_state = "[icon_state]_blade" - set_light(lrange, lpower, lcolor) - else - set_light(0) - item_state = "[icon_state]" - -/obj/item/shield/energy/AltClick(mob/living/user) - if(!in_range(src, user)) //Basic checks to prevent abuse - return - if(user.incapacitated() || !istype(user)) - to_chat(user, "You can't do that right now!") - return - if(alert("Are you sure you want to recolor your shield?", "Confirm Recolor", "Yes", "No") == "Yes") - var/energy_color_input = input(usr,"","Choose Energy Color",lcolor) as color|null - if(energy_color_input) - lcolor = sanitize_hexcolor(energy_color_input, desired_format=6, include_crunch=1) - update_icon() - -/obj/item/shield/energy/examine(mob/user, dist) - . = ..() - . += "Alt-click to recolor it." - -/obj/item/shield/riot/tele - name = "telescopic shield" - desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." - icon = 'icons/obj/weapons.dmi' - icon_state = "teleriot0" - slot_flags = null - damage_force = 3 - throw_force = 3 - throw_speed = 3 - throw_range = 4 - w_class = WEIGHT_CLASS_NORMAL - var/active = 0 -/* -/obj/item/shield/energy/IsShield() - if(active) - return 1 - else - return 0 -*/ -/obj/item/shield/riot/tele/attack_self(mob/user) - . = ..() - if(.) - return - active = !active - icon_state = "teleriot[active]" - playsound(src.loc, 'sound/weapons/empty.ogg', 50, 1) - - if(active) - damage_force = 8 - throw_force = 5 - throw_speed = 2 - set_weight_class(WEIGHT_CLASS_BULKY) - slot_flags = SLOT_BACK - to_chat(user, "You extend \the [src].") - else - damage_force = 3 - throw_force = 3 - throw_speed = 3 - set_weight_class(WEIGHT_CLASS_NORMAL) - slot_flags = null - to_chat(user, "[src] can now be concealed.") - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - return - -/obj/item/shield/energy/imperial - name = "energy scutum" - desc = "It's really easy to mispronounce the name of this shield if you've only read it in books." - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "eshield0" // eshield1 for expanded - item_icons = list(SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi') - -/obj/item/shield/fluff/wolfgirlshield - name = "Autumn Shield" - desc = "A shiny silvery shield with a large red leaf symbol in the center." - icon = 'icons/obj/weapons_vr.dmi' - icon_state = "wolfgirlshield" - slot_flags = SLOT_BACK | SLOT_OCLOTHING - damage_force = 5.0 - throw_force = 5.0 - throw_speed = 2 - throw_range = 6 - item_icons = list(SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', SLOT_ID_BACK = 'icons/vore/custom_items_vr.dmi', SLOT_ID_SUIT = 'icons/vore/custom_items_vr.dmi') - attack_verb = list("shoved", "bashed") - var/cooldown = 0 //shield bash cooldown. based on world.time - allowed = list(/obj/item/melee/fluffstuff/wolfgirlsword) - -/obj/item/shield/fluff/roman - name = "replica scutum" - desc = "A replica shield for close quarters engagement. It looks sturdy enough to withstand foam weapons, and nothing more." - icon = 'icons/obj/weapons.dmi' - icon_state = "roman_shield" - slot_flags = SLOT_BACK - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) - damage_force = 5.0 - throw_force = 5.0 - throw_speed = 2 - throw_range = 6 - -//Foam Shield -/obj/item/shield/riot/foam - name = "foam riot shield" - desc = "A shield for close quarters engagement. It looks sturdy enough to withstand foam weapons, and nothing more." - icon = 'icons/obj/weapons.dmi' - icon_state = "foamriot" - slot_flags = SLOT_BACK - base_block_chance = 5 - damage_force = 0 - throw_force = 0 - throw_speed = 2 - throw_range = 6 - materials_base = list(MAT_PLASTIC = 7500, "foam" = 1000) - item_icons = list( - SLOT_ID_LEFT_HAND = 'icons/mob/items/lefthand_melee.dmi', - SLOT_ID_RIGHT_HAND = 'icons/mob/items/righthand_melee.dmi', - ) diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index ac7800a0ec4c..add625fde68d 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -100,7 +100,7 @@ else to_chat(user, "This cell is not fitted for [src].") -/obj/item/melee/baton/attack_hand(mob/user, list/params) +/obj/item/melee/baton/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(bcell && !integrated_cell) bcell.update_icon() diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index dd4676495514..b5c7a599daa3 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -38,7 +38,9 @@ icon_state = "tonfa" item_state = "tonfa" atom_flags = NOBLOODY - defend_chance = 15 + passive_parry = /datum/passive_parry{ + parry_chance_melee = 15; + } //Telescopic baton /obj/item/melee/telebaton @@ -145,12 +147,15 @@ ) item_state = "armblade" damage_force = 15 // same damage_force as a drill - defend_chance = 20 // did you know melee weapons have a default 5% chance to block frontal melee? sharp = TRUE edge = TRUE var/SA_bonus_damage = 35 // 50 total against animals and aberrations. var/SA_vulnerability = MOB_CLASS_ANIMAL | MOB_CLASS_ABERRATION + passive_parry = /datum/passive_parry{ + parry_chance_melee = 20; + } + /obj/item/melee/disruptor/afterattack(atom/target, mob/user, clickchain_flags, list/params) . = ..() if(isliving(target)) diff --git a/code/game/objects/items/weapons/tanks/tank.dm b/code/game/objects/items/weapons/tanks/tank.dm index 4a3291e1b993..24bb915b4fa1 100644 --- a/code/game/objects/items/weapons/tanks/tank.dm +++ b/code/game/objects/items/weapons/tanks/tank.dm @@ -455,7 +455,19 @@ var/list/global/tank_gauge_cache = list() var/num_fragments = round(rand(8,10) * sqrt(strength * mult)) - src.fragmentate(T, num_fragments, rand(5) + 7, list(/obj/projectile/bullet/pellet/fragment/tank/small = 7,/obj/projectile/bullet/pellet/fragment/tank = 2,/obj/projectile/bullet/pellet/fragment/strong = 1)) + shrapnel_explosion( + num_fragments, + rand(5, 7), + list( + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank, + /obj/projectile/bullet/pellet/fragment/tank, + /obj/projectile/bullet/pellet/fragment/strong = 1, + ), + ) if(istype(loc, /obj/item/transfer_valve)) var/obj/item/transfer_valve/TTV = loc @@ -482,13 +494,25 @@ var/list/global/tank_gauge_cache = list() visible_message("[icon2html(thing = src, target = world)] \The [src] flies apart!", "You hear a bang!") T.hotspot_expose(air_contents.temperature, 70, 1) - var/strength = 1+((pressure-TANK_LEAK_PRESSURE)/TANK_FRAGMENT_SCALE) var/mult = (air_contents.total_moles**2/3)/((29*0.64) **2/3) //tanks appear to be experiencing a reduction on scale of about 0.64 total moles var/num_fragments = round(rand(6,8) * sqrt(strength * mult)) //Less chunks, but bigger - src.fragmentate(T, num_fragments, 7, list(/obj/projectile/bullet/pellet/fragment/tank/small = 1,/obj/projectile/bullet/pellet/fragment/tank = 5,/obj/projectile/bullet/pellet/fragment/strong = 4)) + + shrapnel_explosion( + num_fragments, + rand(5, 7), + list( + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank/small, + /obj/projectile/bullet/pellet/fragment/tank, + /obj/projectile/bullet/pellet/fragment/tank, + /obj/projectile/bullet/pellet/fragment/strong = 1, + ), + ) if(istype(loc, /obj/item/transfer_valve)) var/obj/item/transfer_valve/TTV = loc diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index d447853fecb3..100a837531d8 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -85,7 +85,7 @@ if(!has_buckled_mobs()) anchored = FALSE -/obj/item/beartrap/attack_hand(mob/user, list/params) +/obj/item/beartrap/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // check unbuckle first if(click_unbuckle_interaction(user)) return CLICKCHAIN_DO_NOT_PROPAGATE diff --git a/code/game/objects/items/weapons/weldbackpack.dm b/code/game/objects/items/weapons/weldbackpack.dm index 8c1a5be5dc90..38a495269b72 100644 --- a/code/game/objects/items/weapons/weldbackpack.dm +++ b/code/game/objects/items/weapons/weldbackpack.dm @@ -84,7 +84,7 @@ to_chat(user,"The tank scoffs at your insolence. It only provides services to welders.") return -/obj/item/weldpack/attack_hand(mob/user, list/params) +/obj/item/weldpack/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user, /mob/living/carbon/human)) var/mob/living/carbon/human/wearer = user if(wearer.back == src) diff --git a/code/game/objects/misc.dm b/code/game/objects/misc.dm index 0a4d51c5a0fa..04856ddf13ac 100644 --- a/code/game/objects/misc.dm +++ b/code/game/objects/misc.dm @@ -7,7 +7,7 @@ /obj/structure/signpost/attackby(obj/item/W, mob/user) return attack_hand(user) -/obj/structure/signpost/attack_hand(mob/user, list/params) +/obj/structure/signpost/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) switch(tgui_alert(user, "Do you want to go to SS13?", "Travel", list("Yes", "No"))) if("Yes") user.forceMove(SSjob.get_latejoin_spawnpoint(faction = JOB_FACTION_STATION)) diff --git a/code/game/objects/mob_spawner.dm b/code/game/objects/mob_spawner.dm index 17005e489806..a7750f0b37de 100644 --- a/code/game/objects/mob_spawner.dm +++ b/code/game/objects/mob_spawner.dm @@ -64,7 +64,7 @@ if(total_spawns > 0) total_spawns-- if(mob_faction) - L.faction = mob_faction + L.set_iff_factions(mob_faction) return L /obj/structure/mob_spawner/proc/get_death_report(var/mob/living/L) @@ -102,7 +102,7 @@ It also makes it so a ghost wont know where all the goodies/mobs are. if(world.time > last_spawn + spawn_delay) var/turf/mainloc = get_turf(src) for(var/mob/living/A in range(range,mainloc)) - if ((A.faction != mob_faction) && (A.move_speed < 12)) + if (((!mob_faction || !A.has_iff_faction(mob_faction))) && (A.move_speed < 12)) var/chosen_mob = choose_spawn() if(chosen_mob) do_spawn(chosen_mob) @@ -279,7 +279,7 @@ It also makes it so a ghost wont know where all the goodies/mobs are. my_mob.low_priority = TRUE if(faction) - my_mob.faction = faction + my_mob.set_iff_factions(faction) if(atmos_comp) var/turf/T = get_turf(src) diff --git a/code/game/objects/obj-construction.dm b/code/game/objects/obj-construction.dm new file mode 100644 index 000000000000..6b1ec1001e85 --- /dev/null +++ b/code/game/objects/obj-construction.dm @@ -0,0 +1,9 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Deconstruction *// + +/obj/drop_products(method, atom/where) + . = ..() + if(obj_storage?.drop_on_deconstruction_methods & method) + obj_storage.drop_everything_at(where) diff --git a/code/game/objects/obj-defense.dm b/code/game/objects/obj-defense.dm new file mode 100644 index 000000000000..4bbfcff3a177 --- /dev/null +++ b/code/game/objects/obj-defense.dm @@ -0,0 +1,118 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/obj/ex_act(power, dir, datum/automata/wave/explosion/E) + . = ..() + // todo: wave explosions + // no named arguments for speed reasons + run_damage_instance(power * (1 / 2.5) * (0.01 * rand(80, 120)), BRUTE, null, ARMOR_BOMB) + +/obj/legacy_ex_act(severity, target) + . = ..() + // todo: wave explosions + // no named arguments for speed reasons + run_damage_instance(global._legacy_ex_atom_damage[severity] * (0.01 * rand(80, 120)), BRUTE, null, ARMOR_BOMB) + +/obj/melee_act(mob/user, obj/item/weapon, target_zone, datum/event_args/actor/clickchain/clickchain) + var/shieldcall_returns = atom_shieldcall_handle_item_melee(weapon, clickchain, FALSE, NONE) + if(shieldcall_returns & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return CLICKCHAIN_FULL_BLOCKED + // todo: maybe the item side should handle this? + run_damage_instance( + weapon.damage_force * (clickchain ? clickchain.damage_multiplier : 1), + weapon.damtype, + weapon.damage_tier, + weapon.damage_flag, + weapon.damage_mode, + ATTACK_TYPE_MELEE, + weapon, + NONE, + target_zone, + null, + null, + ) + return NONE + +/obj/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, datum/event_args/actor/clickchain/clickchain) + var/shieldcall_returns = atom_shieldcall_handle_unarmed_melee(style, clickchain, FALSE, NONE) + if(shieldcall_returns & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return CLICKCHAIN_FULL_BLOCKED + // todo: maybe the unarmed_style side should handle this? + run_damage_instance( + style.get_unarmed_damage(attacker, src) * (clickchain ? clickchain.damage_multiplier : 1), + style.damage_type, + style.damage_tier, + style.damage_flag, + style.damage_mode, + ATTACK_TYPE_UNARMED, + style, + NONE, + target_zone, + null, + null, + ) + return NONE + +/obj/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(!(impact_flags & (PROJECTILE_IMPACT_BLOCKED | PROJECTILE_IMPACT_SKIP_STANDARD_DAMAGE))) + // todo: maybe the projectile side should handle this? + run_damage_instance( + proj.get_structure_damage() * bullet_act_args[BULLET_ACT_ARG_EFFICIENCY], + proj.damage_type, + proj.damage_tier, + proj.damage_flag, + proj.damage_mode, + ATTACK_TYPE_PROJECTILE, + proj, + NONE, + bullet_act_args[BULLET_ACT_ARG_ZONE], + null, + null, + ) + if(QDELETED(src)) + impact_flags |= PROJECTILE_IMPACT_TARGET_DELETED + return ..() + +/obj/throw_impacted(atom/movable/AM, datum/thrownthing/TT) + . = ..() + if(TT.throw_flags & THROW_AT_IS_GENTLE) + return + // todo: /atom/movable/proc/throw_impact_attack(atom/target) + if(temporary_legacy_dont_auto_handle_obj_damage_for_mechs) + return + if(isitem(AM)) + var/obj/item/I = AM + inflict_atom_damage(I.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), I.damage_flag, I.damage_mode, ATTACK_TYPE_THROWN, AM) + else + inflict_atom_damage(AM.throw_force * TT.get_damage_multiplier(src), TT.get_damage_tier(src), ARMOR_MELEE, null, ATTACK_TYPE_THROWN, AM) + // if we got destroyed + if(QDELETED(src) && (obj_flags & OBJ_ALLOW_THROW_THROUGH)) + . |= COMPONENT_THROW_HIT_PIERCE + +/obj/blob_act(obj/structure/blob/blob) + . = ..() + inflict_atom_damage(100, damage_flag = ARMOR_MELEE, attack_type = ATTACK_TYPE_MELEE) + +/obj/hitsound_melee(obj/item/I) + if(!isnull(material_primary)) + var/datum/material/primary = get_primary_material() + . = I.damtype == BURN? primary.sound_melee_burn : primary.sound_melee_brute + if(!isnull(.)) + return + return ..() + +/obj/hitsound_throwhit(obj/item/I) + if(!isnull(material_primary)) + var/datum/material/primary = get_primary_material() + . = I.damtype == BURN? primary.sound_melee_burn : primary.sound_melee_brute + if(!isnull(.)) + return + return ..() + +/obj/hitsound_unarmed(mob/M, datum/unarmed_attack/style) + if(!isnull(material_primary)) + var/datum/material/primary = get_primary_material() + . = style.damage_type == BURN? primary.sound_melee_burn : primary.sound_melee_brute + if(!isnull(.)) + return + return ..() diff --git a/code/game/objects/objs.dm b/code/game/objects/obj.dm similarity index 99% rename from code/game/objects/objs.dm rename to code/game/objects/obj.dm index 828bbda90e8a..f58e08f536e6 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/obj.dm @@ -529,7 +529,7 @@ /obj/proc/do_climb_target(mob/living/climber) return get_turf(src) -/obj/attack_hand(mob/user, list/params) +/obj/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() . = ..() diff --git a/code/game/objects/random/mapping.dm b/code/game/objects/random/mapping.dm index d6e34c541f6a..dfda3c019e94 100644 --- a/code/game/objects/random/mapping.dm +++ b/code/game/objects/random/mapping.dm @@ -405,11 +405,11 @@ // /obj/item/archaeological_find //), prob(1);list( - /obj/item/melee/energy/sword, - /obj/item/melee/energy/sword, - /obj/item/melee/energy/sword, - /obj/item/shield/energy, - /obj/item/shield/energy, + /obj/item/melee/transforming/energy/sword, + /obj/item/melee/transforming/energy/sword, + /obj/item/melee/transforming/energy/sword, + /obj/item/shield/transforming/energy, + /obj/item/shield/transforming/energy, /obj/structure/closet/crate/science ), prob(1);list( diff --git a/code/game/objects/random/misc.dm b/code/game/objects/random/misc.dm index 6a874a3abaed..a7ec6ec77a90 100644 --- a/code/game/objects/random/misc.dm +++ b/code/game/objects/random/misc.dm @@ -811,7 +811,7 @@ prob(8);/obj/item/gun/energy/gun/eluger, prob(8);/obj/item/gun/energy/xray, prob(8);/obj/item/gun/ballistic/automatic/c20r, - prob(8);/obj/item/melee/energy/sword, + prob(8);/obj/item/melee/transforming/energy/sword, prob(8);/obj/item/gun/ballistic/derringer, prob(8);/obj/item/gun/ballistic/konigin, prob(8);/obj/item/gun/ballistic/revolver/lemat, @@ -835,7 +835,7 @@ prob(4);/obj/item/gun/ballistic/deagle, prob(4);/obj/item/gun/ballistic/deagle/taj, prob(4);/obj/item/material/knife/tacknife/combatknife, - prob(4);/obj/item/melee/energy/sword, + prob(4);/obj/item/melee/transforming/energy/sword, prob(2);/obj/item/gun/ballistic/automatic/mini_uzi, prob(2);/obj/item/gun/ballistic/automatic/mini_uzi/taj, prob(4);/obj/item/gun/ballistic/automatic/wt274, diff --git a/code/game/objects/random/mob.dm b/code/game/objects/random/mob.dm index fce0aa645816..006dfdbeb277 100644 --- a/code/game/objects/random/mob.dm +++ b/code/game/objects/random/mob.dm @@ -227,7 +227,7 @@ . = ..() if(istype(., /mob/living/simple_mob)) var/mob/living/simple_mob/this_mob = . - this_mob.faction = src.faction + this_mob.copy_iff_factions(src) if (this_mob.minbodytemp > 200) // Temporary hotfix. Eventually I'll add code to change all mob vars to fit the environment they are spawned in. this_mob.minbodytemp = 200 //wander the mobs around so they aren't always in the same spots diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 87a0afecc2d1..08af691efc11 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -31,7 +31,7 @@ return ..() -/obj/structure/attack_hand(mob/user, list/params) +/obj/structure/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(breakable) if(MUTATION_HULK in user.mutations) user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 4adaffbe1e29..ad8aa2d2cd0e 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -55,7 +55,7 @@ T.thermal_conductivity = initial(T.thermal_conductivity) ..() -/obj/structure/alien/resin/attack_hand(mob/user, list/params) +/obj/structure/alien/resin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(iscarbon(user)) var/mob/living/carbon/C = user if(locate(/obj/item/organ/internal/xenos/hivenode) in C.internal_organs) @@ -344,7 +344,7 @@ Alien plants should do something if theres a lot of poison if((status == GROWING) && (BURST == 0)) Grow() -/obj/structure/alien/egg/attack_hand(mob/user, list/params) +/obj/structure/alien/egg/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/carbon/M = user if(!istype(M) || !(locate(/obj/item/organ/internal/xenos/hivenode) in M.internal_organs)) diff --git a/code/game/objects/structures/ashlander.dm b/code/game/objects/structures/ashlander.dm index f84c552bd6ad..4c9a0d39b7da 100644 --- a/code/game/objects/structures/ashlander.dm +++ b/code/game/objects/structures/ashlander.dm @@ -299,7 +299,7 @@ src.updateUsrDialog() return 0 -/obj/structure/ashlander/calcinator/attack_hand(mob/user, list/params) +/obj/structure/ashlander/calcinator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/structure/ashlander/calcinator/AltClick(mob/user) @@ -387,7 +387,7 @@ . = ..() set_light(3, 2, "#9463bb") -/obj/structure/ashlander/statue/attack_hand(mob/user, list/params) +/obj/structure/ashlander/statue/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/choice = tgui_alert(user, "Do you wish to pray to the statue?", "Interact With the Statue", list("Yes", "No")) if(choice != "Yes") return @@ -397,10 +397,10 @@ /obj/structure/ashlander/statue/proc/Bless(mob/user) var/mob/living/carbon/human/H = usr - if(!H.faction == "lavaland") - to_chat(user, "You feel as if an eye briefly regards you, and then turns away.") - else - H.add_modifier(/datum/modifier/ashlander_blessing, 15 MINUTES) + // if(!H.faction == "lavaland") + // to_chat(user, "You feel as if an eye briefly regards you, and then turns away.") + // else + H.add_modifier(/datum/modifier/ashlander_blessing, 15 MINUTES) /datum/modifier/ashlander_blessing name = "The Mother's Blessing" diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index 23bb25c13752..554be30604dd 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -216,7 +216,7 @@ LINEN BINS hidden = I to_chat(user, "You hide [I] among the sheets.") -/obj/structure/bedsheetbin/attack_hand(mob/user, list/params) +/obj/structure/bedsheetbin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(amount >= 1) amount-- diff --git a/code/game/objects/structures/bonfire.dm b/code/game/objects/structures/bonfire.dm index a4d652be4679..f43c1aebf1ba 100644 --- a/code/game/objects/structures/bonfire.dm +++ b/code/game/objects/structures/bonfire.dm @@ -81,7 +81,7 @@ else return ..() -/obj/structure/bonfire/attack_hand(mob/user, list/params) +/obj/structure/bonfire/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(has_buckled_mobs()) return ..() @@ -288,7 +288,7 @@ else return ..() -/obj/structure/fireplace/attack_hand(mob/user, list/params) +/obj/structure/fireplace/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(get_fuel_amount()) remove_fuel(user) diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index e5cfe97fa403..4203261cf627 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -135,7 +135,7 @@ . = ..() activate() -/obj/effect/catwalk_plated/attack_hand(mob/user, list/params) +/obj/effect/catwalk_plated/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) attack_generic() /obj/effect/catwalk_plated/attack_ghost() @@ -194,7 +194,7 @@ if(6 to 50) inflict_atom_damage( rand(10, 20), - flag = ARMOR_MELEE, + damage_flag = ARMOR_MELEE, ) visible_message("The planks creak and groan as they're crossed.") if(51 to 100) diff --git a/code/game/objects/structures/charge_pylon.dm b/code/game/objects/structures/charge_pylon.dm index 6a86d5b85f8e..0225508a2b7e 100644 --- a/code/game/objects/structures/charge_pylon.dm +++ b/code/game/objects/structures/charge_pylon.dm @@ -12,7 +12,7 @@ if(Adjacent(user)) attack_hand(user) -/obj/structure/adherent_pylon/attack_hand(mob/user, list/params) +/obj/structure/adherent_pylon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) charge_user(user) /obj/structure/adherent_pylon/proc/charge_user(var/mob/living/user) diff --git a/code/game/objects/structures/cliff.dm b/code/game/objects/structures/cliff.dm index 8dae834f31c5..f649caacdfc3 100644 --- a/code/game/objects/structures/cliff.dm +++ b/code/game/objects/structures/cliff.dm @@ -141,13 +141,14 @@ two tiles on initialization, and which way a cliff is facing may change during m return ..() // Projectiles and objects flying 'upward' have a chance to hit the cliff instead, wasting the shot. - else if(istype(mover, /obj)) - var/obj/O = mover - if(check_shield_arc(src, dir, O)) // This is actually for mobs but it will work for our purposes as well. + else if(istype(mover, /obj/projectile) || mover.throwing) + if(get_dir(mover, src) & dir) if(prob(uphill_penalty / (1 + is_double_cliff) )) // Firing upwards facing NORTH means it will likely have to pass through two cliffs, so the chance is halved. return FALSE return TRUE + return ..() + /obj/structure/cliff/Bumped(atom/A) if(isliving(A)) var/mob/living/L = A diff --git a/code/game/objects/structures/coathanger.dm b/code/game/objects/structures/coathanger.dm index 0b2e643c2f98..3c7e21c9f3a4 100644 --- a/code/game/objects/structures/coathanger.dm +++ b/code/game/objects/structures/coathanger.dm @@ -6,7 +6,7 @@ var/obj/item/clothing/suit/coat var/list/allowed = list(/obj/item/clothing/suit/storage/toggle/labcoat, /obj/item/clothing/suit/storage/det_trench) -/obj/structure/coatrack/attack_hand(mob/user, list/params) +/obj/structure/coatrack/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.visible_message("[user] takes [coat] off \the [src].", "You take [coat] off the \the [src]") if(!user.put_in_active_hand(coat)) coat.loc = get_turf(user) diff --git a/code/game/objects/structures/crates_lockers/__closet.dm b/code/game/objects/structures/crates_lockers/__closet.dm index 979cae2af26c..d1c3e2fcb82b 100644 --- a/code/game/objects/structures/crates_lockers/__closet.dm +++ b/code/game/objects/structures/crates_lockers/__closet.dm @@ -314,7 +314,7 @@ return if(!user.attempt_insert_item_for_installation(I, opened? loc : src)) return - else if(istype(I, /obj/item/melee/energy/blade)) + else if(istype(I, /obj/item/melee/ninja_energy_blade)) if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [I]!", "You hear metal being sliced and sparks flying.")) var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, loc) @@ -412,7 +412,7 @@ if(!open()) to_chat(user, "It won't budge!") -/obj/structure/closet/attack_hand(mob/user, list/params) +/obj/structure/closet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(locked && secure) togglelock(user) diff --git a/code/game/objects/structures/crates_lockers/closets/coffin.dm b/code/game/objects/structures/crates_lockers/closets/coffin.dm index 4c1e44438b8c..44e4f8f46529 100644 --- a/code/game/objects/structures/crates_lockers/closets/coffin.dm +++ b/code/game/objects/structures/crates_lockers/closets/coffin.dm @@ -32,8 +32,9 @@ opened = 1 color = "#c2b29f" use_old_icon_update = TRUE + obj_flags = OBJ_MELEE_TARGETABLE -/obj/structure/closet/grave/attack_hand(mob/user, list/params) +/obj/structure/closet/grave/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(opened) visible_message("[user] starts to climb into \the [src.name].", \ "You start to lower yourself into \the [src.name].") @@ -146,9 +147,6 @@ .=..() alpha = 255 // Needed because of grave hiding -/obj/structure/closet/grave/bullet_act(var/obj/projectile/P) - return PROJECTILE_CONTINUE // It's a hole in the ground, doesn't usually stop or even care about bullets - /obj/structure/closet/grave/return_air_for_internal_lifeform(var/mob/living/L) var/gasid = GAS_ID_CARBON_DIOXIDE if(ishuman(L)) diff --git a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm index e7e320a32367..b58927665393 100644 --- a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm +++ b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm @@ -109,7 +109,7 @@ update_icon() -/obj/structure/closet/fireaxecabinet/attack_hand(mob/user, list/params) +/obj/structure/closet/fireaxecabinet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/hasaxe = 0 if(fireaxe) hasaxe = 1 diff --git a/code/game/objects/structures/crates_lockers/closets/gimmick.dm b/code/game/objects/structures/crates_lockers/closets/gimmick.dm index ed1c5f42e035..3f3901f4a740 100644 --- a/code/game/objects/structures/crates_lockers/closets/gimmick.dm +++ b/code/game/objects/structures/crates_lockers/closets/gimmick.dm @@ -57,7 +57,7 @@ starts_with = list( /obj/item/clothing/suit/armor/tdome/red = 3, - /obj/item/melee/energy/sword = 3, + /obj/item/melee/transforming/energy/sword = 3, /obj/item/gun/energy/laser = 3, /obj/item/melee/baton = 3, /obj/item/storage/box/flashbangs = 3, @@ -71,7 +71,7 @@ starts_with = list( /obj/item/clothing/suit/armor/tdome/green = 3, - /obj/item/melee/energy/sword = 3, + /obj/item/melee/transforming/energy/sword = 3, /obj/item/gun/energy/laser = 3, /obj/item/melee/baton = 3, /obj/item/storage/box/flashbangs = 3, diff --git a/code/game/objects/structures/crates_lockers/closets/secure/genpop.dm b/code/game/objects/structures/crates_lockers/closets/secure/genpop.dm index d3410971f156..6ce0bf5f5e37 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/genpop.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/genpop.dm @@ -95,7 +95,7 @@ locked = TRUE return ..() -/obj/structure/closet/secure_closet/genpop/attack_hand(mob/user) +/obj/structure/closet/secure_closet/genpop/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.lying && get_dist(src, user) > 0) return diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm index 24ea0ce03cfe..ad445635d1b4 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm @@ -50,7 +50,7 @@ update_icon() else to_chat(user, "Access Denied") - else if(istype(W, /obj/item/melee/energy/blade)) + else if(istype(W, /obj/item/melee/ninja_energy_blade)) if(emag_act(INFINITY, user, "The locker has been sliced open by [user] with \an [W]!", "You hear metal being sliced and sparks flying.")) var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() spark_system.set_up(5, 0, loc) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index dfe4064a21ea..1b53f9f27386 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -97,7 +97,7 @@ /obj/item/clothing/glasses/sunglasses/sechud, /obj/item/barrier_tape_roll/police, /obj/item/shield/riot, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/storage/box/holobadge/hos, /obj/item/storage/box/firingpins, /obj/item/clothing/accessory/badge/holo/hos, @@ -173,7 +173,7 @@ /obj/item/radio/headset/heads/hos/alt, /obj/item/clothing/accessory/armor/helmetcamera/security, /obj/item/clothing/accessory/armor/helmetcamera/security/body, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/storage/box/holobadge/hos, /obj/item/clothing/accessory/badge/holo/hos, /obj/item/reagent_containers/spray/pepper, @@ -500,7 +500,7 @@ GLOBAL_LIST_BOILERPLATE(all_brig_closets, /obj/structure/closet/secure_closet/br /obj/item/clothing/glasses/sunglasses/sechud, /obj/item/barrier_tape_roll/police, /obj/item/shield/riot, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/storage/box/holobadge/hos, /obj/item/clothing/accessory/badge/holo/hos, /obj/item/reagent_containers/spray/pepper, diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm index 76fde00e4861..6de122885a82 100644 --- a/code/game/objects/structures/crates_lockers/closets/statue.dm +++ b/code/game/objects/structures/crates_lockers/closets/statue.dm @@ -90,7 +90,7 @@ /obj/structure/closet/statue/relaymove() return -/obj/structure/closet/statue/attack_hand(mob/user, list/params) +/obj/structure/closet/statue/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return /obj/structure/closet/statue/verb_toggleopen() diff --git a/code/game/objects/structures/crates_lockers/closets/syndicate.dm b/code/game/objects/structures/crates_lockers/closets/syndicate.dm index e0b788d4508b..513aeb888ce4 100644 --- a/code/game/objects/structures/crates_lockers/closets/syndicate.dm +++ b/code/game/objects/structures/crates_lockers/closets/syndicate.dm @@ -16,7 +16,7 @@ /obj/item/cell/high, /obj/item/card/id/syndicate, /obj/item/multitool, - /obj/item/shield/energy, + /obj/item/shield/transforming/energy, /obj/item/clothing/shoes/magboots) diff --git a/code/game/objects/structures/crates_lockers/closets/walllocker.dm b/code/game/objects/structures/crates_lockers/closets/walllocker.dm index 2554fcc6496a..3c3b8db1ba14 100644 --- a/code/game/objects/structures/crates_lockers/closets/walllocker.dm +++ b/code/game/objects/structures/crates_lockers/closets/walllocker.dm @@ -28,7 +28,7 @@ /obj/structure/closet/walllocker/emerglocker/attackby(obj/item/W as obj, mob/user as mob) return -/obj/structure/closet/walllocker/emerglocker/attack_hand(mob/user, list/params) +/obj/structure/closet/walllocker/emerglocker/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (istype(user, /mob/living/silicon/ai)) //Added by Strumpetplaya - AI shouldn't be able to return //activate emergency lockers. This fixes that. (Does this make sense, the AI can't call attack_hand, can it? --Mloc) if(!amount) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 5f5d7baf0312..33e09af3771c 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -201,7 +201,7 @@ else to_chat(usr, "This mob type can't use this verb.") -/obj/structure/closet/crate/secure/attack_hand(mob/user, list/params) +/obj/structure/closet/crate/secure/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) src.add_fingerprint(user) if(locked) src.togglelock(user) @@ -211,7 +211,7 @@ /obj/structure/closet/crate/secure/attackby(obj/item/W as obj, mob/user as mob) if(is_type_in_list(W, list(/obj/item/packageWrap, /obj/item/stack/cable_coil, /obj/item/radio/electropack, /obj/item/tool/wirecutters))) return ..() - if(istype(W, /obj/item/melee/energy/blade)) + if(istype(W, /obj/item/melee/ninja_energy_blade)) emag_act(INFINITY, user) if(!opened) src.togglelock(user) @@ -261,17 +261,17 @@ req_access += pick(get_all_station_access()) ..() -/obj/structure/closet/crate/secure/bullet_act(var/obj/projectile/Proj) - if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - return +/obj/structure/closet/crate/secure/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(!(proj.damage_type == BRUTE || proj.damage_type == BURN)) + return ..() - if(locked && tamper_proof && integrity <= Proj.damage) + if(locked && tamper_proof && integrity <= proj.damage) if(tamper_proof == 2) // Mainly used for events to prevent any chance of opening the box improperly. visible_message("The anti-tamper mechanism of [src] triggers an explosion!") var/turf/T = get_turf(src.loc) explosion(T, 0, 0, 0, 1) // Non-damaging, but it'll alert security. qdel(src) - return + return impact_flags var/open_chance = rand(1,5) switch(open_chance) if(1) @@ -287,12 +287,9 @@ qdel(src) if(5) visible_message("The anti-tamper mechanism of [src] fails!") - return - - ..() - - return + return impact_flags + return ..() /obj/structure/closet/crate/plastic name = "plastic crate" diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index 0a77584a74a1..47eb371602f8 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -62,7 +62,7 @@ */ -/obj/structure/largecrate/attack_hand(mob/user, list/params) +/obj/structure/largecrate/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, "You need a crowbar to pry this open!") return diff --git a/code/game/objects/structures/crates_lockers/vehiclecage.dm b/code/game/objects/structures/crates_lockers/vehiclecage.dm index c4987c4f0600..f255d50a6274 100644 --- a/code/game/objects/structures/crates_lockers/vehiclecage.dm +++ b/code/game/objects/structures/crates_lockers/vehiclecage.dm @@ -23,7 +23,7 @@ load_vehicle(I) update_icon() -/obj/structure/vehiclecage/attack_hand(mob/user, list/params) +/obj/structure/vehiclecage/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, "You need a wrench to take this apart!") return diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index 8dfecee5121e..96379a2ea4d0 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -7,6 +7,10 @@ opacity = 1 density = 0 anchored = TRUE + integrity = 40 + integrity_failure = 30 + integrity_max = 40 + var/obj/item/stack/mat = /obj/item/stack/material/plastic /obj/structure/curtain/open @@ -15,14 +19,7 @@ layer = 3.3 //3.3 so its above windows, not the same as them. anything below 3.3 puts the curtain beneath the window sprite in current build opacity = 0 -/obj/structure/curtain/bullet_act(obj/projectile/P, def_zone) - if(!P.nodamage) - visible_message("[P] tears [src] down!") - qdel(src) - else - ..(P, def_zone) - -/obj/structure/curtain/attack_hand(mob/user, list/params) +/obj/structure/curtain/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(get_turf(loc), "rustle", 15, 1, -5) toggle() ..() diff --git a/code/game/objects/structures/decorations.dm b/code/game/objects/structures/decorations.dm index f7c5260e74e9..bcbf68b08f0f 100644 --- a/code/game/objects/structures/decorations.dm +++ b/code/game/objects/structures/decorations.dm @@ -3,15 +3,10 @@ desc = "Ornately twisted rope holding up a religious seal." icon = 'icons/obj/decals.dmi' icon_state = "shrine_seal" - layer = 3.3 //3.3 so its above windows, not the same as them. anything below 3.3 puts the curtain beneath the window sprite in current build - opacity = 0 - -/obj/structure/shrine_seal/bullet_act(obj/projectile/P, def_zone) - if(!P.nodamage) - visible_message("[P] tears [src] down!") - qdel(src) - else - ..(P, def_zone) + layer = ABOVE_WINDOW_LAYER + integrity = 40 + integrity_max = 40 + integrity_failure = 30 /obj/structure/shrine_seal/attackby(obj/item/P, mob/user) if(P.is_wirecutter()) diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index 0ef8330e5c20..21b2b9d7ea3f 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -23,7 +23,7 @@ else src.icon_state = "glassbox[src.occupied]" -/obj/structure/displaycase/attack_hand(mob/user, list/params) +/obj/structure/displaycase/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((atom_flags & ATOM_BROKEN) && occupied) new /obj/item/gun/energy/captain( src.loc ) to_chat(user, "You deactivate the hover field built into the case.") diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index 147f5d25ff76..87795c2998e8 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -324,7 +324,7 @@ // Airlock frames are indestructable, so bullets hitting them would always be stopped. // To fix this, airlock assemblies will sometimes let bullets pass through, since generally the sprite shows them partially open. -/obj/structure/door_assembly/bullet_act(var/obj/projectile/P) - if(prob(40)) // Chance for the frame to let the bullet keep going. - return PROJECTILE_CONTINUE +/obj/structure/door_assembly/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(prob(40)) + return PROJECTILE_IMPACT_PASSTHROUGH return ..() diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm index c1897ea9bca8..67e93490a3a9 100644 --- a/code/game/objects/structures/extinguisher.dm +++ b/code/game/objects/structures/extinguisher.dm @@ -45,7 +45,7 @@ update_icon() -/obj/structure/extinguisher_cabinet/attack_hand(mob/user, list/params) +/obj/structure/extinguisher_cabinet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(isrobot(user)) return if (ishuman(user)) diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm index 82c954d2d326..067a553e9243 100644 --- a/code/game/objects/structures/fence.dm +++ b/code/game/objects/structures/fence.dm @@ -144,7 +144,7 @@ desc = "It looks like it has a strong padlock attached." locked = TRUE -/obj/structure/fence/door/attack_hand(mob/user, list/params) +/obj/structure/fence/door/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(can_open(user)) toggle(user) else diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm index 5b21c9d390de..994b2e183976 100644 --- a/code/game/objects/structures/fireaxe.dm +++ b/code/game/objects/structures/fireaxe.dm @@ -92,7 +92,7 @@ toggle_close_open() -/obj/structure/fireaxecabinet/attack_hand(mob/user, list/params) +/obj/structure/fireaxecabinet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) //var/hasaxe = 0 //Fuck this. Fuck everything about this. Who wrote this. Why. //if(fireaxe) // hasaxe = 1 diff --git a/code/game/objects/structures/fitness.dm b/code/game/objects/structures/fitness.dm index 355d2f9f1db6..ea926dc26e1c 100644 --- a/code/game/objects/structures/fitness.dm +++ b/code/game/objects/structures/fitness.dm @@ -10,7 +10,7 @@ density = 1 var/list/hit_message = list("hit", "punch", "kick", "robust") -/obj/structure/fitness/punchingbag/attack_hand(mob/user, list/params) +/obj/structure/fitness/punchingbag/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user)) ..() return @@ -38,7 +38,7 @@ weight = ((weight) % qualifiers.len) + 1 to_chat(user, "You set the machine's weight level to [weight].") -/obj/structure/fitness/weightlifter/attack_hand(mob/user, list/params) +/obj/structure/fitness/weightlifter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user)) return if(user.loc != src.loc) diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 81d9f24f58aa..9b166f6170d6 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -397,7 +397,7 @@ /obj/structure/flora/sif icon = 'icons/obj/flora/sifflora.dmi' -/obj/structure/flora/sif/attack_hand(mob/user, list/params) +/obj/structure/flora/sif/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (user.a_intent == INTENT_HARM) if(do_after(user, 5 SECONDS)) user.visible_message("\The [user] digs up \the [src.name].", "You dig up \the [src.name].") @@ -518,7 +518,7 @@ var/gift_type = /obj/item/b_gift var/list/ckeys_that_took = list() -/obj/structure/flora/pumpkin/pumpkin_patch/presents/attack_hand(mob/user, list/params) +/obj/structure/flora/pumpkin/pumpkin_patch/presents/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/game/objects/structures/flora/rocks.dm b/code/game/objects/structures/flora/rocks.dm index 74528dfd6a46..63ec2bbd4ed1 100644 --- a/code/game/objects/structures/flora/rocks.dm +++ b/code/game/objects/structures/flora/rocks.dm @@ -38,11 +38,12 @@ return . = ..() -/obj/structure/flora/rock/bullet_act(obj/projectile/P, def_zone) - if(P.damage_flag == ARMOR_BOMB) //Intended for kinetic accelerators/daggers to just get rid of this stuff quickly. They're rocks. - GetDrilled() - return +/obj/structure/flora/rock/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_TARGET_ABORT) + return + if(proj.damage_flag == ARMOR_BOMB) //Intended for kinetic accelerators/daggers to just get rid of this stuff quickly. They're rocks. + GetDrilled() /obj/structure/flora/rock/proc/GetDrilled() new outcropdrop(get_turf(src),rand(mindrop,upperdrop)) diff --git a/code/game/objects/structures/flora/trees.dm b/code/game/objects/structures/flora/trees.dm index 3b6f73af5e2b..fe7ac42a99f0 100644 --- a/code/game/objects/structures/flora/trees.dm +++ b/code/game/objects/structures/flora/trees.dm @@ -65,10 +65,10 @@ animate(src, transform=turn(M, shake_animation_degrees * shake_dir), pixel_x=init_px + 2*shake_dir, time=1) animate(transform=M, pixel_x=init_px, time=6, easing=ELASTIC_EASING) -/obj/structure/flora/tree/inflict_atom_damage(damage, tier, flag, mode, attack_type, datum/weapon, gradual) +/obj/structure/flora/tree/inflict_atom_damage(damage, damage_type, damage_tier, damage_flag, damage_mode, hit_zone, attack_type, datum/weapon) . = ..() // ruins some of the wood if you use high power modes or types - if(. > 5 && ((mode & (DAMAGE_MODE_ABLATING | DAMAGE_MODE_PIERCE | DAMAGE_MODE_SHRED)) || (flag == ARMOR_BOMB))) + if(. > 5 && ((damage_mode & (DAMAGE_MODE_ABLATING | DAMAGE_MODE_PIERCE | DAMAGE_MODE_SHRED)) || (damage_flag == ARMOR_BOMB))) product_amount -= round((. * 0.5) / integrity_max * initial(product_amount)) /obj/structure/flora/tree/atom_break() @@ -133,7 +133,7 @@ /obj/structure/flora/tree/pine/xmas/presents/choose_icon_state() return "pinepresents" -/obj/structure/flora/tree/pine/xmas/presents/attack_hand(mob/user, list/params) +/obj/structure/flora/tree/pine/xmas/presents/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/game/objects/structures/ghost_pods/ghost_pods.dm b/code/game/objects/structures/ghost_pods/ghost_pods.dm index ebfc4daa2afc..c8ae98d84a06 100644 --- a/code/game/objects/structures/ghost_pods/ghost_pods.dm +++ b/code/game/objects/structures/ghost_pods/ghost_pods.dm @@ -43,7 +43,7 @@ /obj/structure/ghost_pod/manual var/confirm_before_open = FALSE // Recommended to be TRUE if the pod contains a surprise. -/obj/structure/ghost_pod/manual/attack_hand(mob/user, list/params) +/obj/structure/ghost_pod/manual/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!used) if(confirm_before_open) if(alert(user, "Are you sure you want to touch \the [src]?", "Confirm", "No", "Yes") == "No") diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 4d97e12a8f6a..58a1913f12f9 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -43,36 +43,14 @@ return TRUE return ..() -/obj/structure/grille/bullet_act(var/obj/projectile/Proj) - //Flimsy grilles aren't so great at stopping projectiles. However they can absorb some of the impact - var/damage = Proj.get_structure_damage() - var/passthrough = 0 - - if(!damage) return - - //20% chance that the grille provides a bit more cover than usual. Support structure for example might take up 20% of the grille's area. - //If they click on the grille itself then we assume they are aiming at the grille itself and the extra cover behaviour is always used. - switch(Proj.damage_type) - if(BRUTE) - //bullets - if(Proj.original == src || prob(20)) - Proj.damage *= clamp( Proj.damage/60, 0, 0.5) - if(prob(max((damage-10)/25, 0))*100) - passthrough = 1 - else - Proj.damage *= clamp( Proj.damage/60, 0, 1) - passthrough = 1 - if(BURN) - //beams and other projectiles are either blocked completely by grilles or stop half the damage. - if(!(Proj.original == src || prob(20))) - Proj.damage *= 0.5 - passthrough = 1 - - if(passthrough) - . = PROJECTILE_CONTINUE - damage = between(0, (damage - Proj.damage)*(Proj.damage_type == BRUTE? 0.4 : 1), 10) //if the bullet passes through then the grille avoids most of the damage - - inflict_atom_damage(damage, Proj.damage_tier, Proj.damage_flag, Proj.damage_mode, ATTACK_TYPE_PROJECTILE, Proj) +/obj/structure/grille/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(proj.original_target == src) + impact_flags |= PROJECTILE_IMPACT_TRIVIAL + else + impact_flags |= PROJECTILE_IMPACT_TRIVIAL | PROJECTILE_IMPACT_PIERCE + . = ..() + if(impact_flags & PROJECTILE_IMPACT_PIERCE) + proj.dampen_on_pierce_experimental(src, 10, ARMOR_TIER_ABOVE) /obj/structure/grille/attackby(obj/item/W as obj, mob/user as mob) if(!istype(W)) @@ -120,12 +98,12 @@ WD.update_appearance() return ..() -/obj/structure/grille/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, mult) +/obj/structure/grille/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, datum/event_args/actor/clickchain/clickchain) if(shock(attacker, 70)) return FALSE return ..() -/obj/structure/grille/melee_act(mob/user, obj/item/weapon, target_zone, mult) +/obj/structure/grille/melee_act(mob/user, obj/item/weapon, target_zone, datum/event_args/actor/clickchain/clickchain) if(shock(user, 70, weapon)) return FALSE return ..() @@ -174,7 +152,7 @@ /obj/structure/grille/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(!destroyed) if(exposed_temperature > T0C + 1500) - inflict_atom_damage(1, flag = ARMOR_FIRE) + inflict_atom_damage(1, damage_flag = ARMOR_FIRE) ..() /obj/structure/grille/proc/is_on_frame() diff --git a/code/game/objects/structures/holoplant.dm b/code/game/objects/structures/holoplant.dm index d239087db652..714980a4d26d 100644 --- a/code/game/objects/structures/holoplant.dm +++ b/code/game/objects/structures/holoplant.dm @@ -21,7 +21,7 @@ . = ..() activate() -/obj/machinery/holoplant/attack_hand(mob/user, list/params) +/obj/machinery/holoplant/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user) || interference) return diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm index 516693d6f557..3a40b5c873ef 100644 --- a/code/game/objects/structures/inflatable.dm +++ b/code/game/objects/structures/inflatable.dm @@ -92,7 +92,7 @@ if(get_dist(user,src) <= 1) //not remotely though return TryToSwitchState(user) -/obj/structure/inflatable/door/attack_hand(mob/user, list/params) +/obj/structure/inflatable/door/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return TryToSwitchState(user) /obj/structure/inflatable/door/CanAllowThrough(atom/movable/mover, turf/target) diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index a231a24ab6db..57e3f2541921 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -89,7 +89,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) mybag.attackby(I, user) -/obj/structure/janitorialcart/attack_hand(mob/user, list/params) +/obj/structure/janitorialcart/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) return @@ -177,6 +177,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) anchored = 1 density = 1 atom_flags = OPENCONTAINER + integrity_flags = INTEGRITY_INDESTRUCTIBLE //copypaste sorry var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite var/obj/item/storage/bag/trash/mybag = null @@ -210,7 +211,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) to_chat(user, "You hook the trashbag onto the [callme].") mybag = I -/obj/structure/bed/chair/janicart/attack_hand(mob/user, list/params) +/obj/structure/bed/chair/janicart/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(mybag) user.grab_item_from_interacted_with(mybag, src) mybag = null @@ -279,13 +280,10 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) L.pixel_x = -13 L.pixel_y = 7 -/obj/structure/bed/chair/janicart/bullet_act(var/obj/projectile/Proj) - if(has_buckled_mobs()) - if(prob(85)) - var/mob/living/L = pick(buckled_mobs) - return L.bullet_act(Proj) - visible_message("[Proj] ricochets off the [callme]!") - +/obj/structure/bed/chair/janicart/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(has_buckled_mobs() && prob(85)) + return proj.impact_redirect(pick(buckled_mobs), args) + return ..() /obj/item/key name = "key" diff --git a/code/game/objects/structures/kitchen_foodcart.dm b/code/game/objects/structures/kitchen_foodcart.dm index 968d990be4c5..963becea89b1 100644 --- a/code/game/objects/structures/kitchen_foodcart.dm +++ b/code/game/objects/structures/kitchen_foodcart.dm @@ -22,7 +22,7 @@ return return ..() -/obj/structure/foodcart/attack_hand(mob/user, list/params) +/obj/structure/foodcart/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(contents.len) var/obj/item/reagent_containers/food/choice = input("What would you like to grab from the cart?") as null|obj in contents if(choice) diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm index ecc3dffbb52f..eb239b4e9623 100644 --- a/code/game/objects/structures/kitchen_spike.dm +++ b/code/game/objects/structures/kitchen_spike.dm @@ -50,7 +50,7 @@ meat = 5 return 1 -/obj/structure/kitchenspike/attack_hand(mob/user, list/params) +/obj/structure/kitchenspike/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..() || !occupied) return meat-- diff --git a/code/game/objects/structures/loot_piles.dm b/code/game/objects/structures/loot_piles.dm index a18bd0046e62..25738b913faa 100644 --- a/code/game/objects/structures/loot_piles.dm +++ b/code/game/objects/structures/loot_piles.dm @@ -42,7 +42,7 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh var/list/rare_loot = list() // Rare is powerful, or somewhat unique items. var/list/very_rare_loot = list()// Very Rare really powerful, or at least unique items. -/obj/structure/loot_pile/attack_hand(mob/user, list/params) +/obj/structure/loot_pile/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) //Human mob if(isliving(user)) var/mob/living/L = user @@ -809,6 +809,9 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh /obj/structure/loot_pile/mecha/gygax/dark icon_state = "darkgygax-broken" +/obj/structure/loot_pile/mecha/mimir + icon_state = "mimir_wreck" + // Todo: Better loot. /obj/structure/loot_pile/mecha/gygax/dark/adv icon_state = "darkgygax_adv-broken" diff --git a/code/game/objects/structures/medical_stand_vr.dm b/code/game/objects/structures/medical_stand_vr.dm index 0eed1c210f1a..ee4ca93c4d39 100644 --- a/code/game/objects/structures/medical_stand_vr.dm +++ b/code/game/objects/structures/medical_stand_vr.dm @@ -164,7 +164,7 @@ update_icon() -/obj/structure/medical_stand/attack_hand(mob/user, list/params) +/obj/structure/medical_stand/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/list/available_options = list() if (tank) available_options += "Toggle valve" diff --git a/code/game/objects/structures/mineral_bath.dm b/code/game/objects/structures/mineral_bath.dm index bf572a66f100..f729e46cd33f 100644 --- a/code/game/objects/structures/mineral_bath.dm +++ b/code/game/objects/structures/mineral_bath.dm @@ -66,7 +66,7 @@ START_PROCESSING(SSobj, src) return TRUE -/obj/structure/adherent_bath/attack_hand(mob/user, list/params) +/obj/structure/adherent_bath/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) eject_occupant() /obj/structure/adherent_bath/proc/eject_occupant() diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index df8f872b1056..d120edc51470 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -18,7 +18,7 @@ pixel_x = (dir & 3)? 0 : (dir == 4 ? -28 : 28) pixel_y = (dir & 3)? (dir == 1 ? -30 : 30) : 0 -/obj/structure/mirror/attack_hand(mob/user, list/params) +/obj/structure/mirror/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!glass) return if(shattered) return @@ -39,14 +39,13 @@ desc = "Oh no, seven years of bad luck!" -/obj/structure/mirror/bullet_act(var/obj/projectile/Proj) - - if(prob(Proj.get_structure_damage() * 2)) +/obj/structure/mirror/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(prob(proj.get_structure_damage() * 2)) if(!shattered) shatter() else if(glass) playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, 1) - ..() /obj/structure/mirror/attackby(obj/item/I as obj, mob/user as mob) if(I.is_wrench()) diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 18b979d9bbd8..c73ca39802fa 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -82,7 +82,7 @@ if(Adjacent(user)) attack_hand(user) -/obj/structure/morgue/attack_hand(mob/user, list/params) +/obj/structure/morgue/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (src.connected) close() else @@ -173,7 +173,7 @@ if(Adjacent(user)) attack_hand(user) -/obj/structure/m_tray/attack_hand(mob/user, list/params) +/obj/structure/m_tray/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (src.connected) for(var/atom/movable/A as mob|obj in src.loc) if (!( A.anchored )) @@ -227,7 +227,7 @@ GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) src.icon_state = "crema1" return -/obj/structure/morgue/crematorium/attack_hand(mob/user, list/params) +/obj/structure/morgue/crematorium/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (cremating) to_chat(usr, "It's locked.") return @@ -350,7 +350,7 @@ GLOBAL_LIST_BOILERPLATE(all_crematoriums, /obj/structure/morgue/crematorium) req_access = list(ACCESS_GENERAL_CREMATOR) id = 1 -/obj/machinery/button/crematorium/attack_hand(mob/user, list/params) +/obj/machinery/button/crematorium/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(src.allowed(user)) diff --git a/code/game/objects/structures/noticeboard.dm b/code/game/objects/structures/noticeboard.dm index 9376814056a7..9a710c58a5f2 100644 --- a/code/game/objects/structures/noticeboard.dm +++ b/code/game/objects/structures/noticeboard.dm @@ -43,7 +43,7 @@ new /obj/item/frame/noticeboard( src.loc ) qdel(src) -/obj/structure/noticeboard/attack_hand(mob/user, list/params) +/obj/structure/noticeboard/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.do_examinate(src) // Since Topic() never seems to interact with usr on more than a superficial diff --git a/code/game/objects/structures/poster.dm b/code/game/objects/structures/poster.dm index fa5c8a1d0729..4c7978e4a6da 100644 --- a/code/game/objects/structures/poster.dm +++ b/code/game/objects/structures/poster.dm @@ -158,7 +158,7 @@ roll_and_drop(user.loc) return -/obj/structure/sign/poster/attack_hand(mob/user, list/params) +/obj/structure/sign/poster/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(ruined) return diff --git a/code/game/objects/structures/props/beam_prism.dm b/code/game/objects/structures/props/beam_prism.dm index fad0ecd0328f..11ca14327755 100644 --- a/code/game/objects/structures/props/beam_prism.dm +++ b/code/game/objects/structures/props/beam_prism.dm @@ -1,5 +1,6 @@ //A series(?) of prisms for PoIs. The base one only works for beams. +// todo: refactor to /obj/structure/reflector /obj/structure/prop/prism name = "prismatic turret" desc = "A raised, externally powered 'turret'. It seems to have a massive crystal ring around its base." @@ -12,6 +13,12 @@ layer = 3.1 //Layer over projectiles. plane = -10 //Layer over projectiles. + /// projectile_type's to reflect + /// all of these must be on a projectile + var/projectile_type = PROJECTILE_TYPE_BEAM | PROJECTILE_TYPE_PHOTONIC + /// can't reflect these + var/projectile_type_cant = NONE + var/rotation_lock = 0 // Can you rotate the prism at all? var/free_rotate = 1 // Does the prism rotate in any direction, or only in the eight standard compass directions? var/external_control_lock = 0 // Does the prism only rotate from the controls of an external switch? @@ -19,8 +26,6 @@ var/compass_directions = list("North" = 0, "South" = 180, "East" = 90, "West" = 270, "Northwest" = 315, "Northeast" = 45, "Southeast" = 135, "Southwest" = 225) var/interaction_sound = 'sound/mecha/mechmove04.ogg' - var/redirect_type = /obj/projectile/beam - var/dialID = null var/obj/structure/prop/prismcontrol/remote_dial = null @@ -43,7 +48,7 @@ var/degrees_to_rotate = -1 * degrees_from_north animate(src, transform = turn(src.transform, degrees_to_rotate), time = 2) -/obj/structure/prop/prism/attack_hand(mob/user, list/params) +/obj/structure/prop/prism/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(rotation_lock) @@ -119,19 +124,17 @@ else animate(src, transform = turn(src.transform, rotate_degrees), time = 6) -/obj/structure/prop/prism/bullet_act(var/obj/projectile/Proj) - if(istype(Proj, redirect_type)) - if(!silent) - visible_message("\The [src] redirects \the [Proj]!") - flick("[initial(icon_state)]+glow", src) - - var/new_x = (1 * round(10 * cos(degrees_from_north - 90))) + x //Vectors vectors vectors. - var/new_y = (-1 * round(10 * sin(degrees_from_north - 90))) + y - var/turf/curloc = get_turf(src) - - Proj.penetrating += 1 // Needed for the beam to get out of the turret. +/obj/structure/prop/prism/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(((proj.projectile_type & projectile_type) != projectile_type) || (proj.projectile_type & projectile_type_cant)) + return ..() + if(!silent) + visible_message("\The [src] redirects \the [proj]!") + flick("[initial(icon_state)]+glow", src) - Proj.redirect(new_x, new_y, curloc, null) + // todo: this is not the right way; visuals are weird and the raycast is :/ + proj.physics_kick_forwards(16) + proj.set_angle(degrees_from_north) + return PROJECTILE_IMPACT_REFLECT /obj/structure/prop/prism/incremental free_rotate = 0 @@ -157,7 +160,7 @@ var/list/my_turrets = list() var/dialID = null -/obj/structure/prop/prismcontrol/attack_hand(mob/user, list/params) +/obj/structure/prop/prismcontrol/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() var/confirm = input("Do you want to try to rotate \the [src]?", "[name]") in list("Yes", "No") diff --git a/code/game/objects/structures/props/nest.dm b/code/game/objects/structures/props/nest.dm index 2d968a94671d..190e7f78c182 100644 --- a/code/game/objects/structures/props/nest.dm +++ b/code/game/objects/structures/props/nest.dm @@ -34,7 +34,7 @@ STOP_PROCESSING(SSobj, src) ..() -/obj/structure/prop/nest/attack_hand(mob/user, list/params) // Used to tell the player that this isn't useful for anything. +/obj/structure/prop/nest/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(user && prob(disturbance_spawn_chance)) spawn_creature(get_turf(src)) @@ -53,7 +53,7 @@ var/spawn_choice = pick(creature_types) var/mob/living/L = new spawn_choice(spawnpoint) if(den_faction) - L.faction = den_faction + L.set_iff_factions(den_faction) visible_message("\The [L] crawls out of \the [src].") den_mobs += L tally++ diff --git a/code/game/objects/structures/props/projectile_lock.dm b/code/game/objects/structures/props/projectile_lock.dm index 1bd5eaad95da..110d54c74a58 100644 --- a/code/game/objects/structures/props/projectile_lock.dm +++ b/code/game/objects/structures/props/projectile_lock.dm @@ -28,9 +28,11 @@ else icon_state = "[initial(icon_state)]" +// todo: this is shitcode rework this /obj/structure/prop/lock/projectile name = "beam lock" desc = "An esoteric object that responds to high intensity light." + integrity_flags = INTEGRITY_INDESTRUCTIBLE var/projectile_key = /obj/projectile/beam var/timed = 0 @@ -39,11 +41,14 @@ interaction_message = "The object remains inert to your touch." -/obj/structure/prop/lock/projectile/bullet_act(var/obj/projectile/Proj) - if(!istype(Proj, projectile_key) || timing) - return +/obj/structure/prop/lock/projectile/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(!istype(proj, projectile_key)) + return ..() - if(istype(Proj, /obj/projectile/beam/heavylaser/cannon) || istype(Proj, /obj/projectile/beam/emitter) || (Proj.damage >= 80 && Proj.damtype == BURN)) + if(timing) + return PROJECTILE_IMPACT_DELETE + + if(istype(proj, /obj/projectile/beam/heavylaser/cannon) || istype(proj, /obj/projectile/beam/emitter) || (proj.damage >= 80 && proj.damtype == BURN)) toggle_lock() visible_message("\The [src] [enabled ? "disengages" : "engages"] its locking mechanism.") @@ -51,3 +56,5 @@ timing = 1 spawn(time_limit) toggle_lock() + + return PROJECTILE_IMPACT_DELETE diff --git a/code/game/objects/structures/props/prop.dm b/code/game/objects/structures/props/prop.dm index c82b4b4dcdef..8471e5fde491 100644 --- a/code/game/objects/structures/props/prop.dm +++ b/code/game/objects/structures/props/prop.dm @@ -9,7 +9,7 @@ anchored = TRUE var/interaction_message = null -/obj/structure/prop/attack_hand(mob/user, list/params) // Used to tell the player that this isn't useful for anything. +/obj/structure/prop/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user)) return FALSE if(!interaction_message) diff --git a/code/game/objects/structures/props/puzzledoor.dm b/code/game/objects/structures/props/puzzledoor.dm index 42416e90c3f2..d3e25d3acf8b 100644 --- a/code/game/objects/structures/props/puzzledoor.dm +++ b/code/game/objects/structures/props/puzzledoor.dm @@ -26,10 +26,9 @@ return 0 return 1 -/obj/machinery/door/blast/puzzle/bullet_act(var/obj/projectile/Proj) - if(!istype(Proj, /obj/projectile/test)) - visible_message("\The [src] is completely unaffected by \the [Proj].") - qdel(Proj) //No piercing. No. +/obj/machinery/door/blast/puzzle/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + impact_flags &= ~PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH + return ..() /obj/machinery/door/blast/puzzle/legacy_ex_act(severity) visible_message("\The [src] is completely unaffected by the blast.") @@ -53,7 +52,7 @@ locks -= L ..() -/obj/machinery/door/blast/puzzle/attack_hand(mob/user, list/params) +/obj/machinery/door/blast/puzzle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(check_locks()) force_toggle(1, user) else diff --git a/code/game/objects/structures/props/swarm.dm b/code/game/objects/structures/props/swarm.dm index 0941273d10bd..5f46aa887112 100644 --- a/code/game/objects/structures/props/swarm.dm +++ b/code/game/objects/structures/props/swarm.dm @@ -14,12 +14,14 @@ /obj/structure/cult/pylon/swarm/CanAllowThrough(atom/movable/mover, turf/target) if(istype(mover, /mob/living)) var/mob/living/L = mover - if(L.faction == "swarmer") + if(L.has_iff_faction(MOB_IFF_FACTION_SWARMER)) return TRUE else if(istype(mover, /obj/projectile)) var/obj/projectile/P = mover - if(istype(P.firer) && P.firer.faction == "swarmer") - return TRUE + if(isliving(P.firer)) + var/mob/living/L = P.firer + if(L.has_iff_faction(MOB_IFF_FACTION_SWARMER)) + return TRUE return ..() /obj/structure/cult/pylon/swarm/Initialize(mapload) diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index f171fb7bfba4..7f4641ea6de5 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -70,7 +70,7 @@ FLOOR SAFES icon_state = initial(icon_state) -/obj/structure/safe/attack_hand(mob/user, list/params) +/obj/structure/safe/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) var/dat = "
" dat += "[open ? "Close" : "Open"] [src] | - [dial * 5] +" diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm index 9797dfd7ef23..12f948c593c3 100644 --- a/code/game/objects/structures/simple_doors.dm +++ b/code/game/objects/structures/simple_doors.dm @@ -49,7 +49,7 @@ if(get_dist(user,src) <= 1) //not remotely though return TryToSwitchState(user) -/obj/structure/simple_door/attack_hand(mob/user, list/params) +/obj/structure/simple_door/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() return TryToSwitchState(user) diff --git a/code/game/objects/structures/snowman.dm b/code/game/objects/structures/snowman.dm index 702d870a15ad..d3e5539eda5e 100644 --- a/code/game/objects/structures/snowman.dm +++ b/code/game/objects/structures/snowman.dm @@ -5,7 +5,7 @@ desc = "A happy little snowman smiles back at you!" anchored = 1 -/obj/structure/snowman/attack_hand(mob/user, list/params) +/obj/structure/snowman/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) to_chat(user, "In one hit, [src] easily crumples into a pile of snow. You monster.") var/turf/simulated/floor/F = get_turf(src) diff --git a/code/game/objects/structures/stasis_cage.dm b/code/game/objects/structures/stasis_cage.dm index e61d37956543..a4e3b3988a8b 100644 --- a/code/game/objects/structures/stasis_cage.dm +++ b/code/game/objects/structures/stasis_cage.dm @@ -14,7 +14,7 @@ if(A) contain(A) -/obj/structure/stasis_cage/attack_hand(mob/user, list/params) +/obj/structure/stasis_cage/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) release() /obj/structure/stasis_cage/attack_robot(var/mob/user) diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm index 475ebfcbb9a2..30784dac7950 100644 --- a/code/game/objects/structures/statues.dm +++ b/code/game/objects/structures/statues.dm @@ -51,7 +51,7 @@ deconstruct(ATOM_DECONSTRUCT_DISASSEMBLED) return return ..() -/obj/structure/statue/attack_hand(mob/user, list/params) +/obj/structure/statue/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) user.visible_message("[user] rubs some dust off from the [name]'s surface.", \ "You rub some dust off from the [name]'s surface.") diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm index 26cece5c48f0..ebae37616894 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -51,7 +51,7 @@ qdel(src) return CLICKCHAIN_DO_NOT_PROPAGATE -/obj/structure/bed/chair/attack_hand(mob/user) +/obj/structure/bed/chair/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!stacked_size) return ..() var/obj/item/material/twohanded/folded_metal_chair/F = new(loc) diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm index bade56588a11..6f38a4dd187c 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm @@ -97,7 +97,7 @@ create_track() driving = 0 -/obj/structure/bed/chair/wheelchair/attack_hand(mob/user, list/params) +/obj/structure/bed/chair/wheelchair/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (pulling_along) MouseDrop(usr) return ..() diff --git a/code/game/objects/structures/tables/interactions.dm b/code/game/objects/structures/tables/interactions.dm index 4f20ef5225db..6d48e296228e 100644 --- a/code/game/objects/structures/tables/interactions.dm +++ b/code/game/objects/structures/tables/interactions.dm @@ -33,10 +33,10 @@ return 1 if (get_dist(P.starting, loc) <= 1) //Tables won't help you if people are THIS close return 1 - if (get_turf(P.original) == cover) + if (get_turf(P.original_target) == cover) var/chance = 20 - if (ismob(P.original)) - var/mob/M = P.original + if (ismob(P.original_target)) + var/mob/M = P.original_target if (M.lying) chance += 20 //Lying down lets you catch less bullets if(flipped==1) @@ -71,7 +71,7 @@ else playsound(loc, 'sound/weapons/tablehit1.ogg', 50, 1) var/turf/old_loc = loc - inflict_atom_damage(40, flag = ARMOR_MELEE) + inflict_atom_damage(40, damage_flag = ARMOR_MELEE) if(QDELETED(src)) // got broken visible_message(SPAN_DANGER("[src] shatters under the impact!")) diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index dc0ee1691ed6..4fadf95503c6 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -44,7 +44,7 @@ return attack_hand(user) ..() -/obj/structure/dispenser/attack_hand(mob/user, list/params) +/obj/structure/dispenser/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) var/dat = "[src]

" dat += "Oxygen tanks: [oxygentanks] - [oxygentanks ? "Dispense" : "empty"]
" diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index ab9122056f71..73521b73b830 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -29,7 +29,7 @@ else return ..() -/obj/structure/target_stake/attack_hand(mob/user, list/params) +/obj/structure/target_stake/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // taking pinned targets off! if(pinned_target) pinned_target.layer = OBJ_LAYER @@ -47,8 +47,7 @@ else return ..() -/obj/structure/target_stake/bullet_act(obj/projectile/P, def_zone) +/obj/structure/target_stake/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(pinned_target) - return pinned_target.bullet_act(P, def_zone) - else - return ..() + return proj.impact_redirect(pinned_target, args) + return ..() diff --git a/code/game/objects/structures/transit_tubes.dm b/code/game/objects/structures/transit_tubes.dm index 8c2052ee6146..3a85ad813067 100644 --- a/code/game/objects/structures/transit_tubes.dm +++ b/code/game/objects/structures/transit_tubes.dm @@ -102,7 +102,7 @@ return -/obj/structure/transit_tube/station/attack_hand(mob/user, list/params) +/obj/structure/transit_tube/station/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!pod_moving) for(var/obj/structure/transit_tube_pod/pod in loc) if(!pod.moving && (pod.dir in directions())) diff --git a/code/game/objects/structures/trash_pile.dm b/code/game/objects/structures/trash_pile.dm index 6fd09fa29026..ce0607b10237 100644 --- a/code/game/objects/structures/trash_pile.dm +++ b/code/game/objects/structures/trash_pile.dm @@ -79,7 +79,7 @@ else return ..() -/obj/structure/trash_pile/attack_hand(mob/user, list/params) +/obj/structure/trash_pile/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) //Human mob if(ishuman(user)) var/mob/living/carbon/human/H = user diff --git a/code/game/objects/structures/under_wardrobe.dm b/code/game/objects/structures/under_wardrobe.dm index 3436f3cda468..65fe0be1a86a 100644 --- a/code/game/objects/structures/under_wardrobe.dm +++ b/code/game/objects/structures/under_wardrobe.dm @@ -5,7 +5,7 @@ icon_state = "cabinet_closed" density = 1 -/obj/structure/undies_wardrobe/attack_hand(mob/user, list/params) +/obj/structure/undies_wardrobe/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!human_who_can_use_underwear(user)) to_chat(user, "Sadly there's nothing in here for you to wear.") return diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index a4939c7520a7..d94c0aa091ea 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -17,7 +17,7 @@ open = round(rand(0, 1)) update_icon() -/obj/structure/toilet/attack_hand(mob/user, list/params) +/obj/structure/toilet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(swirlie) usr.setClickCooldown(user.get_attack_speed()) usr.visible_message("[user] slams the toilet seat onto [swirlie.name]'s head!", "You slam the toilet seat onto [swirlie.name]'s head!", "You hear reverberating porcelain.") @@ -172,7 +172,7 @@ anchored = 1 mouse_opacity = 0 -/obj/machinery/shower/attack_hand(mob/user, list/params) +/obj/machinery/shower/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/M = user if(!istype(M)) return @@ -388,7 +388,7 @@ thing.reagents.clear_reagents() thing.update_icon() -/obj/structure/sink/attack_hand(mob/user, list/params) +/obj/structure/sink/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (ishuman(user)) var/mob/living/carbon/human/H = user var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] @@ -497,7 +497,7 @@ icon_state = "puddle" desc = "A small pool of some liquid, ostensibly water." -/obj/structure/sink/puddle/attack_hand(mob/user, list/params) +/obj/structure/sink/puddle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) icon_state = "puddle-splash" ..() icon_state = "puddle" @@ -521,7 +521,7 @@ reagents.add_reagent(dispensedreagent, 20) /* Okay, just straight up, I tried to code this like blood overlays, but I just do NOT understand the system. If someone wants to sort it, enable this too. -/obj/structure/sink/oil_well/attack_hand(mob/user, list/params) +/obj/structure/sink/oil_well/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) flick("puddle-oil-splash",src) reagents.reaction(M, 20) //Covers target in 20u of oil. to_chat(M, "You touch the pool of oil, only to get oil all over yourself. It would be wise to wash this off with water.") diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index b1f77bcbba06..efe8c3688804 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -207,7 +207,7 @@ user.visible_message(SPAN_NOTICE("Something knocks on [src].")) playsound(loc, 'sound/effects/Glasshit.ogg', 50, TRUE) -/obj/structure/window/attack_hand(mob/user, list/params) +/obj/structure/window/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() @@ -291,7 +291,7 @@ /obj/structure/window/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) . = ..() if (exposed_temperature > maximal_heat) - inflict_atom_damage(damage_per_fire_tick, flag = ARMOR_FIRE, gradual = TRUE) + inflict_atom_damage(damage_per_fire_tick, damage_flag = ARMOR_FIRE, damage_mode = DAMAGE_MODE_GRADUAL) /obj/structure/window/drop_products(method, atom/where) . = ..() diff --git a/code/game/objects/systems/storage.dm b/code/game/objects/systems/storage.dm index 913261e1214f..f323897778fd 100644 --- a/code/game/objects/systems/storage.dm +++ b/code/game/objects/systems/storage.dm @@ -456,7 +456,7 @@ target = parent, ) return TRUE - if(!actor.performer.Reachability(parent)) + if(!actor.performer.Reachability(indirection || parent)) return TRUE if(!try_insert(inserting, actor, silent, suppressed)) return TRUE diff --git a/code/game/rendering/legacy/radial.dm b/code/game/rendering/legacy/radial.dm index 31b2425da037..39da472582e4 100644 --- a/code/game/rendering/legacy/radial.dm +++ b/code/game/rendering/legacy/radial.dm @@ -256,7 +256,6 @@ GLOBAL_LIST_EMPTY(radial_menus) choices_icons[id] = I setup_menu(use_tooltips) - /datum/radial_menu/proc/extract_image(E) var/mutable_appearance/MA = new /mutable_appearance(E) if(MA) @@ -264,7 +263,6 @@ GLOBAL_LIST_EMPTY(radial_menus) MA.appearance_flags |= RESET_TRANSFORM return MA - /datum/radial_menu/proc/next_page() if(pages > 1) current_page = WRAP(current_page + 1,1,pages+1) diff --git a/code/game/rendering/parallax/parallax_object.dm b/code/game/rendering/parallax/parallax_object.dm index ac298821a082..b998484e9fee 100644 --- a/code/game/rendering/parallax/parallax_object.dm +++ b/code/game/rendering/parallax/parallax_object.dm @@ -84,6 +84,8 @@ clone.icon = icon clone.icon_state = icon_state clone.overlays = GetOverlays() + clone.layer = FLOAT_LAYER + clone.plane = FLOAT_PLANE // do NOT inherit our overlays! parallax layers should never have overlays, // because if it inherited us it'll result in exponentially increasing overlays // due to cut_overlays() above over there being a queue operation and not instant! diff --git a/code/game/rendering/screen.dm b/code/game/rendering/screen.dm index 480f07350067..b379d64f7323 100644 --- a/code/game/rendering/screen.dm +++ b/code/game/rendering/screen.dm @@ -36,7 +36,7 @@ G.s_click(src) return 1 -/atom/movable/screen/grab/attack_hand(mob/user, list/params) +/atom/movable/screen/grab/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return /atom/movable/screen/grab/attackby() @@ -422,9 +422,6 @@ var/darkness = round(1 - T.get_lumcount(),0.1) to_chat(usr,"Darkness: [darkness]") if("energy") - var/mob/living/simple_mob/shadekin/SK = usr - if(istype(SK)) - to_chat(usr,"Energy: [SK.energy] ([SK.dark_gains])") var/mob/living/carbon/human/H = usr if(istype(H) && istype(H.species, /datum/species/shadekin)) to_chat(usr,"Energy: [H.shadekin_get_energy(H)]") diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index 94367ea22ca8..aa5a0dbbe86b 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -43,6 +43,8 @@ GLOBAL_LIST_INIT(multiz_hole_baseturfs, typecacheof(list( /** * get what /turf/baseturf_bottom should be + * + * todo: using a proc is inefficient. is there a better way? like a define? */ /turf/proc/baseturf_core() // todo: this is shitcode, pull it out on maploader refactor. @@ -58,6 +60,8 @@ GLOBAL_LIST_INIT(multiz_hole_baseturfs, typecacheof(list( . = /turf/simulated/open /** * get baseturf on bottom + * + * todo: using a proc is inefficient. is there a better way? like a define? */ /turf/proc/baseturf_bottom() . = islist(baseturfs)? baseturfs[1] : baseturfs @@ -65,6 +69,8 @@ GLOBAL_LIST_INIT(multiz_hole_baseturfs, typecacheof(list( /** * get baseturf underneath + * + * todo: using a proc is inefficient. is there a better way? like a define? */ /turf/proc/baseturf_underneath() . = islist(baseturfs)? baseturfs[length(baseturfs)] : baseturfs @@ -82,6 +88,7 @@ GLOBAL_LIST_INIT(multiz_hole_baseturfs, typecacheof(list( // then we can skip all this bullshit and have proper space zmimic // as long as zm overhead isn't too high. //* THIS CANNOT CALL ANY PROCS UP UNTIL 'new path'! *// + // todo: this entire switch section is kind of ass switch(path) if(null) return diff --git a/code/game/turfs/defense.dm b/code/game/turfs/defense.dm deleted file mode 100644 index 34a90ac239ac..000000000000 --- a/code/game/turfs/defense.dm +++ /dev/null @@ -1,2 +0,0 @@ -/turf/break_apart(method) - ScrapeAway(1, CHANGETURF_INHERIT_AIR) diff --git a/code/game/turfs/simulated/floor/floor_types.dm b/code/game/turfs/simulated/floor/floor_types.dm index 4b555d2589fb..5bb299a9a46c 100644 --- a/code/game/turfs/simulated/floor/floor_types.dm +++ b/code/game/turfs/simulated/floor/floor_types.dm @@ -81,7 +81,9 @@ var/mutable_appearance/under_ma if(ispath(under)) // It's just a mapper-specified path - under_ma = new() + under_ma = new + under_ma.layer = FLOAT_LAYER + under_ma.plane = FLOAT_PLANE under_ma.icon = initial(under.icon) under_ma.icon_state = initial(under.icon_state) under_ma.color = initial(under.color) diff --git a/code/game/turfs/simulated/floor_types/snow.dm b/code/game/turfs/simulated/floor_types/snow.dm index 8d491ae4ceda..24df6a6e6f2d 100644 --- a/code/game/turfs/simulated/floor_types/snow.dm +++ b/code/game/turfs/simulated/floor_types/snow.dm @@ -41,7 +41,7 @@ CREATE_STANDARD_TURFS(/turf/simulated/floor/outdoors/snow) else ..() -/turf/simulated/floor/outdoors/snow/attack_hand(mob/user, list/params) +/turf/simulated/floor/outdoors/snow/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) visible_message("[user] starts scooping up some snow.", "You start scooping up some snow.") if(do_after(user, 1 SECOND)) user.put_in_hands_or_drop(new /obj/item/stack/material/snow) diff --git a/code/game/turfs/simulated/flooring/flooring_traps.dm b/code/game/turfs/simulated/flooring/flooring_traps.dm index 659166ce710f..944abc1e4cb7 100644 --- a/code/game/turfs/simulated/flooring/flooring_traps.dm +++ b/code/game/turfs/simulated/flooring/flooring_traps.dm @@ -31,7 +31,7 @@ else if (tripped) icon_state = "[initial(icon_state)]_tripped" -/turf/simulated/floor/trap/attack_hand(mob/user, list/params) +/turf/simulated/floor/trap/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(tripped) to_chat(usr, "You reset the triggered mechanism.") tripped = 0 diff --git a/code/game/turfs/simulated/wall/defense.dm b/code/game/turfs/simulated/wall/defense.dm deleted file mode 100644 index 8811811f9492..000000000000 --- a/code/game/turfs/simulated/wall/defense.dm +++ /dev/null @@ -1,48 +0,0 @@ -/turf/simulated/wall/throw_impacted(atom/movable/AM, datum/thrownthing/TT) - . = ..() - if(TT.throw_flags & THROW_AT_IS_GENTLE) - return - - // todo: /atom/movable/proc/throw_impact_attack(atom/target) - if(isitem(AM)) - var/obj/item/I = AM - inflict_atom_damage(I.throw_force * TT.get_damage_multiplier(src), I.damage_tier, I.damage_flag, I.damage_mode, ATTACK_TYPE_THROWN, AM) - else - inflict_atom_damage(AM.throw_force * TT.get_damage_multiplier(src), MELEE_TIER_LIGHT, ARMOR_MELEE, null, ATTACK_TYPE_THROWN, AM) - -/turf/simulated/wall/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, mult) - // todo: this should just be style.attack(attacker, src) - inflict_atom_damage(style.get_unarmed_damage(attacker, src), style.damage_tier, style.damage_flag, style.damage_mode, ATTACK_TYPE_UNARMED, attacker) - return NONE - -/turf/simulated/wall/melee_act(mob/user, obj/item/weapon, target_zone, mult) - inflict_atom_damage(weapon.damage_force, weapon.damage_tier, weapon.damage_flag, weapon.damage_mode, ATTACK_TYPE_MELEE, weapon) - return NONE - -/turf/simulated/wall/bullet_act(var/obj/projectile/Proj) - if(istype(Proj,/obj/projectile/beam)) - burn(2500) - else if(istype(Proj,/obj/projectile/ion)) - burn(500) - - if(Proj.damage_type == BURN && Proj.damage > 0) - if(thermite) - thermitemelt() - - if(Proj.ricochet_sounds && prob(15)) - playsound(src, pick(Proj.ricochet_sounds), 100, 1) - - inflict_atom_damage(Proj.get_structure_damage(), Proj.damage_tier, Proj.damage_flag, Proj.damage_mode, ATTACK_TYPE_PROJECTILE, Proj) - -/turf/simulated/wall/break_apart(method) - dismantle_wall() - -/turf/simulated/wall/damage_integrity(amount, gradual, do_not_break) - . = ..() - // todo: optimize - update_appearance() - -/turf/simulated/wall/heal_integrity(amount, gradual, do_not_fix) - . = ..() - // todo: optimize - update_appearance() diff --git a/code/game/turfs/simulated/wall/wall-construction.dm b/code/game/turfs/simulated/wall/wall-construction.dm new file mode 100644 index 000000000000..7633c8afaafb --- /dev/null +++ b/code/game/turfs/simulated/wall/wall-construction.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Deconstruction *// + +/turf/simulated/wall/break_apart(method) + dismantle_wall() // this handles deletion too diff --git a/code/game/turfs/simulated/wall/wall-damage.dm b/code/game/turfs/simulated/wall/wall-damage.dm new file mode 100644 index 000000000000..e46661a6902b --- /dev/null +++ b/code/game/turfs/simulated/wall/wall-damage.dm @@ -0,0 +1,14 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Integrity - Direct Manipulation *// + +/turf/simulated/wall/damage_integrity(amount, gradual, do_not_break) + . = ..() + // todo: optimize + update_appearance() + +/turf/simulated/wall/heal_integrity(amount, gradual, do_not_fix) + . = ..() + // todo: optimize + update_appearance() diff --git a/code/game/turfs/simulated/wall/wall-defense.dm b/code/game/turfs/simulated/wall/wall-defense.dm new file mode 100644 index 000000000000..eeed876c8ccc --- /dev/null +++ b/code/game/turfs/simulated/wall/wall-defense.dm @@ -0,0 +1,97 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +/turf/simulated/wall/throw_impacted(atom/movable/AM, datum/thrownthing/TT) + . = ..() + if(TT.throw_flags & THROW_AT_IS_GENTLE) + return + // todo: this method of detecting destruction is shitcode but turf refs don't change so qdeleted() won't work + var/old_type = type + // todo: /atom/movable/proc/throw_impact_attack(atom/target) + if(isitem(AM)) + var/obj/item/I = AM + inflict_atom_damage(I.throw_force * TT.get_damage_multiplier(src), I.damage_tier, I.damage_flag, I.damage_mode, ATTACK_TYPE_THROWN, AM) + else + inflict_atom_damage(AM.throw_force * TT.get_damage_multiplier(src), MELEE_TIER_LIGHT, ARMOR_MELEE, null, ATTACK_TYPE_THROWN, AM) + // turf refs don't change so while QDELETED() doesn't work this is a close approximate + // until we have a better system or we decide to pay some overhead to track with a number or something + if(old_type != type) + if(!density) + . |= COMPONENT_THROW_HIT_PIERCE // :trol: + return + +/turf/simulated/wall/unarmed_act(mob/attacker, datum/unarmed_attack/style, target_zone, datum/event_args/actor/clickchain/clickchain) + var/shieldcall_returns = atom_shieldcall_handle_unarmed_melee(style, clickchain, FALSE, NONE) + if(shieldcall_returns & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return CLICKCHAIN_FULL_BLOCKED + // todo: maybe the unarmed_style side should handle this? + run_damage_instance( + style.damage * (clickchain ? clickchain.damage_multiplier : 1), + style.damage_type, + style.damage_tier, + style.damage_flag, + style.damage_mode, + ATTACK_TYPE_UNARMED, + style, + NONE, + target_zone, + null, + null, + ) + return NONE + +/turf/simulated/wall/melee_act(mob/user, obj/item/weapon, target_zone, datum/event_args/actor/clickchain/clickchain) + var/shieldcall_returns = atom_shieldcall_handle_item_melee(weapon, clickchain, FALSE, NONE) + if(shieldcall_returns & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return CLICKCHAIN_FULL_BLOCKED + // todo: maybe the item side should handle this? + run_damage_instance( + weapon.damage_force * (clickchain ? clickchain.damage_multiplier : 1), + weapon.damtype, + weapon.damage_tier, + weapon.damage_flag, + weapon.damage_mode, + ATTACK_TYPE_MELEE, + weapon, + NONE, + target_zone, + null, + null, + ) + return NONE + +/turf/simulated/wall/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + // todo: this method of detecting destruction is shitcode but turf refs don't change so qdeleted() won't work + var/old_type = type + if(!(impact_flags & (PROJECTILE_IMPACT_BLOCKED | PROJECTILE_IMPACT_SKIP_STANDARD_DAMAGE))) + // todo: maybe the projectile side should handle this? + run_damage_instance( + proj.get_structure_damage() * bullet_act_args[BULLET_ACT_ARG_EFFICIENCY], + proj.damage_type, + proj.damage_tier, + proj.damage_flag, + proj.damage_mode, + ATTACK_TYPE_PROJECTILE, + proj, + NONE, + bullet_act_args[BULLET_ACT_ARG_ZONE], + null, + null, + ) + // turf refs don't change so while QDELETED() doesn't work this is a close approximate + // until we have a better system or we decide to pay some overhead to track with a number or something + if(old_type != type) + impact_flags |= PROJECTILE_IMPACT_TARGET_DELETED + return ..() + + //! legacy code handling + if((proj.projectile_type & (PROJECTILE_TYPE_ENERGY | PROJECTILE_TYPE_BEAM)) && !proj.nodamage && proj.damage) + burn(2500) + if(proj.damage_type == BURN && proj.damage && !proj.nodamage) + if(thermite) + thermitemelt() + if(proj.ricochet_sounds && prob(15)) + playsound(src, pick(proj.ricochet_sounds), 75, TRUE) + //! end + + return ..() diff --git a/code/game/turfs/simulated/wall/wall.dm b/code/game/turfs/simulated/wall/wall.dm index c0e7a557702c..2112e988f2f5 100644 --- a/code/game/turfs/simulated/wall/wall.dm +++ b/code/game/turfs/simulated/wall/wall.dm @@ -150,7 +150,7 @@ /turf/simulated/wall/adjacent_fire_act(turf/simulated/floor/adj_turf, datum/gas_mixture/adj_air, adj_temp, adj_volume) burn(adj_temp) if(adj_temp > material_outer.melting_point) - inflict_atom_damage(log(RAND_F(0.9, 1.1) * (adj_temp - material_outer.melting_point)), flag = ARMOR_FIRE, gradual = TRUE) + inflict_atom_damage(log(RAND_F(0.9, 1.1) * (adj_temp - material_outer.melting_point)), damage_flag = ARMOR_FIRE, damage_mode = DAMAGE_MODE_GRADUAL) return ..() @@ -182,11 +182,11 @@ ScrapeAway() if(2.0) if(prob(75)) - inflict_atom_damage(rand(150, 250), flag = ARMOR_BOMB) + inflict_atom_damage(rand(150, 250), damage_flag = ARMOR_BOMB) else dismantle_wall(1,1) if(3.0) - inflict_atom_damage(rand(0, 150), flag = ARMOR_BOMB) + inflict_atom_damage(rand(0, 150), damage_flag = ARMOR_BOMB) /turf/simulated/wall/proc/can_melt() return material_outer?.material_flags & MATERIAL_FLAG_UNMELTABLE diff --git a/code/game/turfs/simulated/wall/wall_attacks.dm b/code/game/turfs/simulated/wall/wall_attacks.dm index fcd2707cdfda..210112eb624c 100644 --- a/code/game/turfs/simulated/wall/wall_attacks.dm +++ b/code/game/turfs/simulated/wall/wall_attacks.dm @@ -82,7 +82,7 @@ return 0 -/turf/simulated/wall/attack_hand(mob/user, list/params) +/turf/simulated/wall/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) user.setClickCooldown(user.get_attack_speed()) var/rotting = (locate(/obj/effect/overlay/wallrot) in src) @@ -170,8 +170,8 @@ thermitemelt(user) return - else if( istype(I, /obj/item/melee/energy/blade) ) - var/obj/item/melee/energy/blade/EB = I + else if( istype(I, /obj/item/melee/ninja_energy_blade) ) + var/obj/item/melee/ninja_energy_blade/EB = I EB.spark_system.start() to_chat(user, "You slash \the [src] with \the [EB]; the thermite ignites!") @@ -222,7 +222,7 @@ dismantle_verb = "cutting" dismantle_sound = I.tool_sound // cut_delay *= 0.7 // Tools themselves now can shorten the time it takes. - else if(istype(I,/obj/item/melee/energy/blade)) + else if(istype(I,/obj/item/melee/ninja_energy_blade)) dismantle_sound = /datum/soundbyte/grouped/sparks dismantle_verb = "slicing" cut_delay *= 0.5 diff --git a/code/game/turfs/turf-construction.dm b/code/game/turfs/turf-construction.dm new file mode 100644 index 000000000000..d98289771d89 --- /dev/null +++ b/code/game/turfs/turf-construction.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Deconstruction *// + +/turf/break_apart(method) + ScrapeAway(1, CHANGETURF_INHERIT_AIR) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index d8c34019b0ee..0b4c7320d100 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -254,7 +254,7 @@ /turf/proc/is_solid_structure() return TRUE -/turf/attack_hand(mob/user, list/params) +/turf/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() //QOL feature, clicking on turf can toggle doors, unless pulling something if(!user.pulling) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index b5b07d730e7e..1ad138f9faef 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1659,7 +1659,7 @@ if(!check_rights(R_FUN,0)) removed_paths += dirty_path continue - else if(ispath(path, /obj/item/melee/energy/blade))//Not an item one should be able to spawn./N + else if(ispath(path, /obj/item/melee/ninja_energy_blade))//Not an item one should be able to spawn./N if(!check_rights(R_FUN,0)) removed_paths += dirty_path continue diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm index b8c9b9ee87aa..62daecd72cc6 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm @@ -1057,8 +1057,8 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null v = SSresearch if("SSprojectiles") v = SSprojectiles - if("SSfastprocess") - v = SSfastprocess + if("SSprocess_5fps") + v = SSprocess_5fps if("SSticker") v = SSticker if("SStimer") diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm index 94089a1a4213..77f23e97c33e 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm @@ -5,6 +5,8 @@ /proc/_animate(atom/A, set_vars, time = 10, loop = 1, easing = LINEAR_EASING, flags = null) var/mutable_appearance/MA = new() + // mutable appearance is not FLOAT_PLANE by default + MA.plane = FLOAT_PLANE for(var/v in set_vars) MA.vars[v] = set_vars[v] animate(A, appearance = MA, time, loop, easing, flags) diff --git a/code/modules/admin/verbs/smite.dm b/code/modules/admin/verbs/smite.dm index 6430a74a451a..6163ca1f500b 100644 --- a/code/modules/admin/verbs/smite.dm +++ b/code/modules/admin/verbs/smite.dm @@ -130,13 +130,6 @@ to_chat(target,"You've been hit by bluespace artillery!") log_and_message_admins("[key_name(target)] has been hit by Bluespace Artillery fired by [key_name(user ? user : usr)]") - var/obj/effect/stop/S - S = new /obj/effect/stop - S.victim = target - S.loc = target.loc - spawn(20) - qdel(S) - var/turf/simulated/floor/T = get_turf(target) if(istype(T)) if(prob(80)) T.break_tile_to_plating() diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index b7520338a0e7..ac03d8543b71 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -108,7 +108,7 @@ names = sortList(names) for(var/V in names) if(D.can_vv_get(V)) - variable_html += D.vv_get_var(V) + variable_html += D.vv_get_var(V, TRUE) if(VVING_A_LIST) var/list/L = D for(var/i in 1 to L.len) diff --git a/code/modules/ai/ai_pathing.dm b/code/modules/ai/ai_pathing.dm index 9fe14ef0f440..aa949e6b6f3f 100644 --- a/code/modules/ai/ai_pathing.dm +++ b/code/modules/ai/ai_pathing.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 silicons *// +//* Copyright (c) 2024 Citadel Station Developers *// /** * Pathfinding results. diff --git a/code/modules/ai/ai_tracking.dm b/code/modules/ai/ai_tracking.dm new file mode 100644 index 000000000000..fc962abb4066 --- /dev/null +++ b/code/modules/ai/ai_tracking.dm @@ -0,0 +1,156 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * Datum to track a movable entity for things like motion and other predictive qualities. + * + * todo: either this or spatial grid signals need to allow for signal on projectile fire / attack / etc. + */ +/datum/ai_tracking + //* System *// + /// last time we were requested + var/last_requested + /// time since last requested we should delete at + var/gc_timeout = 30 SECONDS + /// timerid + var/gc_timerid + + //* Movement *// + + // last movement record + var/movement_record_last + + // basic vel x / y immediate ; tracks within a second or two. only useful for current velocity. // + + /// in tiles / second + var/imm_vel_x = 0 + /// in tiles / second + var/imm_vel_y = 0 + + var/static/imm_vel_multiplier = 0.1 + + // average vel x / y ; uses a fast rolling average. use to suppress attempts at baiting shots. // + + /// in tiles / second + var/fast_vel_x = 0 + /// in tiles / second + var/fast_vel_y = 0 + + var/static/fast_vel_multiplier = 2.5 + +/datum/ai_tracking/Destroy() + if(gc_timerid) + deltimer(gc_timerid) + return ..() + +/** + * Notifies us that we've been requested. + */ +/datum/ai_tracking/proc/keep_alive() + src.last_requested = world.time + +//* Movement *// + +/** + * Tracks movement state + * + * todo: this doesn't support forced movement or anything like that that is faster than a tile second + * + * * time - time since last move + * * dir - direction of move. if it's just a Move() or otherwie standing still, this is NONE. + */ +/datum/ai_tracking/proc/track_movement(time, dir) + var/elapsed = max(world.time - movement_record_last, world.tick_lag) + // flushing changes last record + flush_movement() + + var/sx + var/sy + switch(dir) + if(NORTH) + sy = 1 + if(SOUTH) + sy = -1 + if(EAST) + sx = 1 + if(WEST) + sx = -1 + if(NORTHWEST) + sy = 1 + sx = -1 + if(NORTHEAST) + sy = 1 + sx = 1 + if(SOUTHEAST) + sy = -1 + sx = 1 + if(SOUTHWEST) + sy = -1 + sx = -1 + + /// tiles / ds + var/immediate_speed = 10 / time + var/imm_vel_inverse = 1 - imm_vel_multiplier + imm_vel_x = min(immediate_speed, imm_vel_x * imm_vel_multiplier + immediate_speed * imm_vel_inverse * sx) + imm_vel_y = min(immediate_speed, imm_vel_y * imm_vel_multiplier + immediate_speed * imm_vel_inverse * sy) + + var/fast_new_multiplier = clamp(fast_vel_multiplier * (time / 10), 0, 1) + var/fast_old_multiplier = clamp(1 - fast_new_multiplier, 0, 1) + fast_vel_x = (fast_vel_x) * fast_old_multiplier + imm_vel_x * fast_new_multiplier + fast_vel_y = (fast_vel_y) * fast_old_multiplier + imm_vel_y * fast_new_multiplier + +/** + * Tells us to completely drop movement state. + */ +/datum/ai_tracking/proc/reset_movement() + movement_record_last = world.time + imm_vel_x = imm_vel_y = 0 + fast_vel_x = fast_vel_y = 0 + +/** + * Tells us to flush movement state. + * + * * Always call this before checking movement vars. + */ +/datum/ai_tracking/proc/flush_movement() + if(movement_record_last == world.time) + return + + var/elapsed = world.time - movement_record_last + movement_record_last = world.time + + // penalize immediate speed + imm_vel_x = min(imm_vel_x, 10 / elapsed) + imm_vel_y = min(imm_vel_y, 10 / elapsed) + // penalize fast speed + fast_vel_x = min(fast_vel_x, 20 / elapsed) + fast_vel_y = min(fast_vel_y, 20 / elapsed) + +// todo: get projected tile location (): list(center, radius) +// todo: calculate immediate intercept (atom/source, speed (pixels per second)): list(x, y) +// todo: calculate fast intercept (atom/source, speed (pixels per second)): list(x, y) + +//* /atom/movable API *// + +/** + * Requests our AI tracking datum. + * + * * Will make one if it's not there. + * * Will keep an existing one alive. + */ +/atom/movable/proc/request_ai_tracking() + RETURN_TYPE(/datum/ai_tracking) + if(src.ai_tracking) + return src.ai_tracking + src.ai_tracking = new + src.ai_tracking.keep_alive() + src.ai_tracking.gc_timerid = addtimer(CALLBACK(src, PROC_REF(expire_ai_tracking)), src.ai_tracking.gc_timeout, TIMER_LOOP | TIMER_STOPPABLE) + return src.ai_tracking + +/atom/movable/proc/expire_ai_tracking() + if(!ai_tracking) + return + if(ai_tracking.last_requested + ai_tracking.gc_timeout > world.time) + return + deltimer(ai_tracking.gc_timerid) + QDEL_NULL(ai_tracking) diff --git a/code/modules/ai/holders/polaris/ai_holder_cooperation.dm b/code/modules/ai/holders/polaris/ai_holder_cooperation.dm index 3969afff4e34..a34408231de9 100644 --- a/code/modules/ai/holders/polaris/ai_holder_cooperation.dm +++ b/code/modules/ai/holders/polaris/ai_holder_cooperation.dm @@ -32,8 +32,9 @@ // Find another AI-controlled mob in the same faction if possible. var/mob/living/first_friend + // todo: this isn't good because we now use factions for different reasons than binding, so all of the same map is often just friends under this defintion for(var/mob/living/L in living_mob_list) - if(L.faction == holder.faction && L.ai_holder) + if(L.shares_iff_faction(holder) && L.ai_holder) first_friend = L break diff --git a/code/modules/ai/holders/polaris/ai_holder_targeting.dm b/code/modules/ai/holders/polaris/ai_holder_targeting.dm index db99ba0e3f3b..bb119d77f86b 100644 --- a/code/modules/ai/holders/polaris/ai_holder_targeting.dm +++ b/code/modules/ai/holders/polaris/ai_holder_targeting.dm @@ -143,7 +143,7 @@ var/obj/machinery/porta_turret/P = the_target if(P.machine_stat & BROKEN) return FALSE // Already dead. - if(P.faction == holder.faction) + if(holder.has_iff_faction(P.faction)) return FALSE // Don't shoot allied turrets. if(!P.raised && !P.raising) return FALSE // Turrets won't get hurt if they're still in their cover. diff --git a/code/modules/ai/holders/polaris/interfaces.dm b/code/modules/ai/holders/polaris/interfaces.dm index cc6d1943ef7f..61897499e1ec 100644 --- a/code/modules/ai/holders/polaris/interfaces.dm +++ b/code/modules/ai/holders/polaris/interfaces.dm @@ -47,7 +47,7 @@ return say(message) /mob/living/proc/IIsAlly(mob/living/L) - return src.faction == L.faction + return shares_iff_faction(L) /mob/living/simple_mob/IIsAlly(mob/living/L) . = ..() diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 5f83a4fa06b9..b9acfc00dc7e 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -85,7 +85,7 @@ a_right.holder_movement() -/obj/item/assembly_holder/attack_hand(mob/user, list/params)//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess +/obj/item/assembly_holder/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(a_left && a_right) a_left.holder_movement() a_right.holder_movement() diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index 2f95cd1aadf5..48e03c8cbd8b 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -47,7 +47,7 @@ on = TRUE Rebuild() update_icon() - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) /obj/item/assembly/infra/proc/turn_off() if(!on) @@ -58,7 +58,7 @@ on = FALSE Rebuild() update_icon() - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) /obj/item/assembly/infra/update_icon() cut_overlays() diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index 891d109980bc..f06c66e968d7 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -68,7 +68,7 @@ playsound(user.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) -/obj/item/assembly/mousetrap/attack_hand(mob/user, list/params) +/obj/item/assembly/mousetrap/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/atmospherics/environmental/zas/debug.dm b/code/modules/atmospherics/environmental/zas/debug.dm index 40ea525478b0..c4c31b9f9d17 100644 --- a/code/modules/atmospherics/environmental/zas/debug.dm +++ b/code/modules/atmospherics/environmental/zas/debug.dm @@ -1,11 +1,11 @@ -var/image/assigned = image('icons/testing/Zone.dmi', icon_state = "assigned") -var/image/created = image('icons/testing/Zone.dmi', icon_state = "created") -var/image/merged = image('icons/testing/Zone.dmi', icon_state = "merged") -var/image/invalid_zone = image('icons/testing/Zone.dmi', icon_state = "invalid") -var/image/air_blocked = image('icons/testing/Zone.dmi', icon_state = "block") -var/image/zone_blocked = image('icons/testing/Zone.dmi', icon_state = "zoneblock") -var/image/blocked = image('icons/testing/Zone.dmi', icon_state = "fullblock") -var/image/mark = image('icons/testing/Zone.dmi', icon_state = "mark") +GLOBAL_DATUM_INIT(zas_debug_image_assigned, /image, image('icons/testing/Zone.dmi', icon_state = "assigned")) +GLOBAL_DATUM_INIT(zas_debug_image_created, /image, image('icons/testing/Zone.dmi', icon_state = "created")) +GLOBAL_DATUM_INIT(zas_debug_image_merged, /image, image('icons/testing/Zone.dmi', icon_state = "merged")) +GLOBAL_DATUM_INIT(zas_debug_image_invalid_zone, /image, image('icons/testing/Zone.dmi', icon_state = "invalid")) +GLOBAL_DATUM_INIT(zas_debug_image_air_blocked, /image, image('icons/testing/Zone.dmi', icon_state = "block")) +GLOBAL_DATUM_INIT(zas_debug_image_zone_blocked, /image, image('icons/testing/Zone.dmi', icon_state = "zoneblock")) +GLOBAL_DATUM_INIT(zas_debug_image_blocked, /image, image('icons/testing/Zone.dmi', icon_state = "fullblock")) +GLOBAL_DATUM_INIT(zas_debug_image_mark, /image, image('icons/testing/Zone.dmi', icon_state = "mark")) /datum/zas_edge/var/dbg_out = 0 diff --git a/code/modules/atmospherics/machinery/air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm.dm index 96a13304af28..e79db8b4533a 100644 --- a/code/modules/atmospherics/machinery/air_alarm.dm +++ b/code/modules/atmospherics/machinery/air_alarm.dm @@ -514,7 +514,7 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/air_alarm, 26) /obj/machinery/air_alarm/attack_ai(mob/user) ui_interact(user) -/obj/machinery/air_alarm/attack_hand(mob/user, list/params) +/obj/machinery/air_alarm/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/atmospherics/machinery/components/binary_devices/algae_generator_vr.dm b/code/modules/atmospherics/machinery/components/binary_devices/algae_generator_vr.dm index 799768392e2d..fb6a8bf7103e 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/algae_generator_vr.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/algae_generator_vr.dm @@ -137,7 +137,7 @@ to_chat(user, SPAN_NOTICE("You cannot insert this item into \the [src]!")) return -/obj/machinery/atmospherics/component/binary/algae_farm/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/algae_farm/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/heat_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/heat_pump.dm index f9fa8cb4c6a7..5e4bc19db5cd 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/heat_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/heat_pump.dm @@ -109,7 +109,7 @@ "You hear ratchet.") deconstruct() -/obj/machinery/atmospherics/component/binary/heat_pump/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/heat_pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return src.add_fingerprint(usr) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/massive_gas_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/massive_gas_pump.dm index 0ac892f84e8f..02f90da33167 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/massive_gas_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/massive_gas_pump.dm @@ -147,7 +147,7 @@ return data -/obj/machinery/atmospherics/component/binary/massive_gas_pump/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/massive_gas_pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return add_fingerprint(usr) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/massive_heat_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/massive_heat_pump.dm index 6704cfe860e7..bc6706dd316b 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/massive_heat_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/massive_heat_pump.dm @@ -171,7 +171,7 @@ return data -/obj/machinery/atmospherics/component/binary/massive_heat_pump/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/massive_heat_pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return add_fingerprint(usr) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm index 7cbe517906e0..66b1368d7eba 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm @@ -165,7 +165,7 @@ update_icon() return -/obj/machinery/atmospherics/component/binary/passive_gate/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/passive_gate/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return add_fingerprint(usr) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm index 3ad9ead3179c..f800ce95d03e 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm @@ -228,7 +228,7 @@ Thus, the two variables affect pump operation are set in New(): . = ..() ui_interact(user) -/obj/machinery/atmospherics/component/binary/pump/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/binary/pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return add_fingerprint(usr) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm index b7259bbe650e..be7fc2a85e66 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm @@ -127,7 +127,7 @@ /obj/machinery/atmospherics/valve/attack_ai(mob/user as mob) return -/obj/machinery/atmospherics/valve/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/valve/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) src.add_fingerprint(usr) update_icon() sleep(10) @@ -231,7 +231,7 @@ /obj/machinery/atmospherics/valve/digital/attack_ai(mob/user as mob) return src.attack_hand(user) -/obj/machinery/atmospherics/valve/digital/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/valve/digital/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!powered()) return if(!src.allowed(user)) diff --git a/code/modules/atmospherics/machinery/components/omni_devices/omni_base.dm b/code/modules/atmospherics/machinery/components/omni_devices/omni_base.dm index 2fb778a832bf..d0b30901194f 100644 --- a/code/modules/atmospherics/machinery/components/omni_devices/omni_base.dm +++ b/code/modules/atmospherics/machinery/components/omni_devices/omni_base.dm @@ -103,7 +103,7 @@ return TRUE return FALSE -/obj/machinery/atmospherics/component/quaternary/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/quaternary/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm index f5c4892d1606..1af33310211e 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm @@ -94,7 +94,7 @@ data["node2_dir"] = dir_name(node_connects[2],TRUE) return data -/obj/machinery/atmospherics/component/trinary/mixer/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/trinary/mixer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return ui_interact(user) diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/tvalve.dm b/code/modules/atmospherics/machinery/components/trinary_devices/tvalve.dm index d9cc0b620281..6df7bc5a6a39 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/tvalve.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/tvalve.dm @@ -157,7 +157,7 @@ /obj/machinery/atmospherics/tvalve/attack_ai(mob/user as mob) return -/obj/machinery/atmospherics/tvalve/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/tvalve/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) src.add_fingerprint(usr) animation() sleep(10) @@ -281,7 +281,7 @@ /obj/machinery/atmospherics/tvalve/digital/attack_ai(mob/user as mob) return src.attack_hand(user) -/obj/machinery/atmospherics/tvalve/digital/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/tvalve/digital/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!powered()) return if(!src.allowed(user)) diff --git a/code/modules/atmospherics/machinery/components/unary/gas_freezer.dm b/code/modules/atmospherics/machinery/components/unary/gas_freezer.dm index e0df0c43d522..e7c576661f2c 100644 --- a/code/modules/atmospherics/machinery/components/unary/gas_freezer.dm +++ b/code/modules/atmospherics/machinery/components/unary/gas_freezer.dm @@ -59,7 +59,7 @@ /obj/machinery/atmospherics/component/unary/freezer/attack_ai(mob/user as mob) ui_interact(user) -/obj/machinery/atmospherics/component/unary/freezer/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/unary/freezer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/atmospherics/component/unary/freezer/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/atmospherics/machinery/components/unary/gas_heater.dm b/code/modules/atmospherics/machinery/components/unary/gas_heater.dm index 1e3a14a325c5..022f5157f20b 100644 --- a/code/modules/atmospherics/machinery/components/unary/gas_heater.dm +++ b/code/modules/atmospherics/machinery/components/unary/gas_heater.dm @@ -73,7 +73,7 @@ /obj/machinery/atmospherics/component/unary/heater/attack_ai(mob/user as mob) ui_interact(user) -/obj/machinery/atmospherics/component/unary/heater/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/unary/heater/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/atmospherics/component/unary/heater/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/atmospherics/machinery/components/unary/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary/outlet_injector.dm index 9e3fccd31a00..ca0498f07b66 100644 --- a/code/modules/atmospherics/machinery/components/unary/outlet_injector.dm +++ b/code/modules/atmospherics/machinery/components/unary/outlet_injector.dm @@ -193,7 +193,7 @@ broadcast_status() update_icon() -/obj/machinery/atmospherics/component/unary/outlet_injector/attack_hand(mob/user, list/params) +/obj/machinery/atmospherics/component/unary/outlet_injector/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/atmospherics/component/unary/outlet_injector/proc/toggle_injecting() diff --git a/code/modules/atmospherics/machinery/portable/area_atmos_computer.dm b/code/modules/atmospherics/machinery/portable/area_atmos_computer.dm index b7f1e566ce16..6b799e21fdce 100644 --- a/code/modules/atmospherics/machinery/portable/area_atmos_computer.dm +++ b/code/modules/atmospherics/machinery/portable/area_atmos_computer.dm @@ -23,7 +23,7 @@ /obj/machinery/computer/area_atmos/attack_ai(mob/user) return src.attack_hand(user) -/obj/machinery/computer/area_atmos/attack_hand(mob/user, list/params) +/obj/machinery/computer/area_atmos/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..(user)) return ui_interact(user) diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm index c70bc68b6aa6..216eae7ccc77 100644 --- a/code/modules/atmospherics/machinery/portable/canister.dm +++ b/code/modules/atmospherics/machinery/portable/canister.dm @@ -307,7 +307,7 @@ update_flag /obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/portable_atmospherics/canister/attack_hand(mob/user, list/params) +/obj/machinery/portable_atmospherics/canister/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return src.ui_interact(user) /obj/machinery/portable_atmospherics/canister/ui_state() diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm index dc3fa0c1134e..1e314a16cb8f 100644 --- a/code/modules/atmospherics/machinery/portable/pump.dm +++ b/code/modules/atmospherics/machinery/portable/pump.dm @@ -105,7 +105,7 @@ . = ..() return src.attack_hand(user) -/obj/machinery/portable_atmospherics/powered/pump/attack_hand(mob/user, list/params) +/obj/machinery/portable_atmospherics/powered/pump/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/portable_atmospherics/powered/pump/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/awaymissions/bluespaceartillery.dm b/code/modules/awaymissions/bluespaceartillery.dm index 70aa1e5bd534..466351a52353 100644 --- a/code/modules/awaymissions/bluespaceartillery.dm +++ b/code/modules/awaymissions/bluespaceartillery.dm @@ -20,7 +20,7 @@ /obj/structure/artilleryplaceholder/decorative density = 0 -/obj/machinery/artillerycontrol/attack_hand(mob/user, list/params) +/obj/machinery/artillerycontrol/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) var/dat = "Bluespace Artillery Control:
" dat += "Locked on
" diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm deleted file mode 100644 index ade64d30058a..000000000000 --- a/code/modules/awaymissions/gateway.dm +++ /dev/null @@ -1,257 +0,0 @@ -/obj/machinery/gateway - name = "gateway" - desc = "A mysterious gateway built by unknown hands. It allows for faster than light travel to far-flung locations and even alternate realities." - icon = 'icons/obj/machines/gateway.dmi' - // todo: temporary, as this is unbuildable - integrity_flags = INTEGRITY_INDESTRUCTIBLE - icon_state = "off" - density = 1 - anchored = 1 - var/active = 0 - - -/obj/machinery/gateway/Initialize(mapload) - update_icon() - if(dir == SOUTH) - density = 0 - . = ..() - -/obj/machinery/gateway/update_icon() - if(active) - icon_state = "on" - return - icon_state = "off" - - - -//this is da important part wot makes things go -/obj/machinery/gateway/centerstation - density = 1 - icon_state = "offcenter" - use_power = USE_POWER_IDLE - - //warping vars - var/list/linked = list() - var/ready = 0 //have we got all the parts for a gateway? - var/wait = 0 //this just grabs world.time at world start - var/obj/machinery/gateway/centeraway/awaygate = null - -/obj/machinery/gateway/centerstation/Initialize(mapload) - update_icon() - wait = world.time + config_legacy.gateway_delay //+ thirty minutes default - awaygate = locate(/obj/machinery/gateway/centeraway) - . = ..() - density = TRUE - -/obj/machinery/gateway/centerstation/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - -/obj/machinery/gateway/centerstation/process(delta_time) - if(machine_stat & (NOPOWER)) - if(active) toggleoff() - return - - if(active) - use_power(5000) - - -/obj/machinery/gateway/centerstation/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in GLOB.alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) - if(!ready) return - if(linked.len != 8) return - if(!powered()) return - if(!awaygate) - to_chat(user, "Error: No destination found. Please program gateway.") - return - if(world.time < wait) - to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") - return - if(!awaygate.calibrated && !LAZYLEN(awaydestinations)) - to_chat(user, SPAN_NOTICE("Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update.")) - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centerstation/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centerstation/attack_hand(mob/user, list/params) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -//okay, here's the good teleporting stuff -/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) - if(!ready) return - if(!active) return - if(!awaygate) return - - use_power(5000) - SEND_SOUND(M, sound('sound/effects/phasein.ogg')) - playsound(src, 'sound/effects/phasein.ogg', 100, 1) - if(awaygate.calibrated) - M.forceMove(get_step(awaygate.loc, SOUTH)) - M.setDir(SOUTH) - return - else - var/obj/landmark/dest = pick(awaydestinations) - if(dest) - M.forceMove(dest.loc) - M.setDir(SOUTH) - return - -/obj/machinery/gateway/centerstation/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W,/obj/item/multitool)) - if(!awaygate) - awaygate = locate(/obj/machinery/gateway/centeraway) - if(!awaygate) // We still can't find the damn thing because there is no destination. - to_chat(user, "Error: Programming failed. No destination found.") - return - to_chat(user, "Startup programming successful!: A destination in another point of space and time has been detected.") - else - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return - -/////////////////////////////////////Away//////////////////////// - - -/obj/machinery/gateway/centeraway - density = 1 - icon_state = "offcenter" - use_power = USE_POWER_OFF - var/calibrated = 1 - var/list/linked = list() //a list of the connected gateway chunks - var/ready = 0 - var/obj/machinery/gateway/centeraway/stationgate = null - - -/obj/machinery/gateway/centeraway/Initialize(mapload) - update_icon() - stationgate = locate(/obj/machinery/gateway/centerstation) - . = ..() - density = 1 - - -/obj/machinery/gateway/centeraway/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - -/obj/machinery/gateway/centeraway/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in GLOB.alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) - if(!ready) return - if(linked.len != 8) return - if(!stationgate || !calibrated) - to_chat(user, SPAN_NOTICE("Error: No destination found. Please calibrate gateway.")) - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centeraway/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centeraway/attack_hand(mob/user, list/params) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -/obj/machinery/gateway/centeraway/Bumped(atom/movable/M as mob|obj) - if(!ready) return - if(!active) return - if(istype(M, /mob/living/carbon)) - for(var/obj/item/implant/exile/E in M)//Checking that there is an exile implant in the contents - if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket - to_chat(M, "The station gate has detected your exile implant and is blocking your entry.") - return - M.forceMove(get_step(stationgate.loc, SOUTH)) - M.setDir(SOUTH) - SEND_SOUND(M, sound('sound/effects/phasein.ogg')) - playsound(src, 'sound/effects/phasein.ogg', 100, 1) - - -/obj/machinery/gateway/centeraway/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W,/obj/item/multitool)) - if(calibrated && stationgate) - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return - else - stationgate = locate(/obj/machinery/gateway/centerstation) - if(!stationgate) - to_chat(user, "Error: Recalibration failed. No destination found... That can't be good.") - return - else - to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") - calibrated = 1 - return diff --git a/code/modules/awaymissions/loot_vr.dm b/code/modules/awaymissions/loot_vr.dm index 504fd2d144d5..7b50df091c34 100644 --- a/code/modules/awaymissions/loot_vr.dm +++ b/code/modules/awaymissions/loot_vr.dm @@ -128,7 +128,7 @@ prob(10);/obj/item/melee/baton,\ prob(10);/obj/item/melee/telebaton,\ prob(10);/obj/item/melee/classic_baton,\ - prob(10);/obj/item/melee/energy/sword,\ + prob(10);/obj/item/melee/transforming/energy/sword,\ prob(9);/obj/item/gun/ballistic/automatic/wt550/lethal,\ prob(9);/obj/item/gun/ballistic/automatic/pdw,\ prob(9);/obj/item/gun/ballistic/derringer,\ @@ -145,7 +145,7 @@ prob(8);/obj/item/gun/energy/xray,\ prob(8);/obj/item/gun/ballistic/automatic/c20r,\ prob(8);/obj/item/gun/ballistic/automatic/stg,\ - prob(8);/obj/item/melee/energy/sword,\ + prob(8);/obj/item/melee/transforming/energy/sword,\ /* prob(8);/obj/item/gun/ballistic/automatic/m41a,\ */ prob(7);/obj/item/gun/energy/captain,\ prob(7);/obj/item/gun/energy/sniperrifle,\ diff --git a/code/modules/blob2/blobs/base_blob.dm b/code/modules/blob2/blobs/base_blob.dm index 19e8ade9f76a..09cdcd04c83f 100644 --- a/code/modules/blob2/blobs/base_blob.dm +++ b/code/modules/blob2/blobs/base_blob.dm @@ -53,12 +53,14 @@ var/list/blobs = list() return TRUE else if(istype(mover, /mob/living)) var/mob/living/L = mover - if(L.faction == "blob") + if(L.has_iff_faction(MOB_IFF_FACTION_BLOB)) return TRUE else if(istype(mover, /obj/projectile)) var/obj/projectile/P = mover - if(istype(P.firer) && P.firer.faction == "blob") - return TRUE + if(isliving(P.firer)) + var/mob/living/L = P.firer + if(L.has_iff_faction(MOB_IFF_FACTION_BLOB)) + return TRUE return FALSE /obj/structure/blob/examine(mob/user, dist) @@ -250,18 +252,19 @@ var/list/blobs = list() adjust_integrity_blob(-damage) return -/obj/structure/blob/bullet_act(var/obj/projectile/P) - if(!P) - return +/obj/structure/blob/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() - if(istype(P.firer) && P.firer.faction == "blob") - return + if(isliving(proj.firer)) + var/mob/living/L = proj.firer + if(L.has_iff_faction(MOB_IFF_FACTION_BLOB)) + return TRUE - var/damage = P.get_structure_damage() // So tasers don't hurt the blob. + var/damage = proj.get_structure_damage() // So tasers don't hurt the blob. if(!damage) return - switch(P.damage_type) + switch(proj.damage_type) if(BRUTE) if(overmind) damage *= overmind.blob_type.brute_multiplier @@ -270,12 +273,10 @@ var/list/blobs = list() damage *= overmind.blob_type.burn_multiplier if(overmind) - damage = overmind.blob_type.on_received_damage(src, damage, P.damage_type, P.firer) + damage = overmind.blob_type.on_received_damage(src, damage, proj.damage_type, proj.firer) adjust_integrity_blob(-damage) - return ..() - /obj/structure/blob/water_act(amount) if(overmind) overmind.blob_type.on_water(src, amount) @@ -304,4 +305,4 @@ var/list/blobs = list() qdel(src) /turf/simulated/wall/blob_act() - inflict_atom_damage(100, flag = ARMOR_MELEE, attack_type = ATTACK_TYPE_MELEE) + inflict_atom_damage(100, damage_flag = ARMOR_MELEE, attack_type = ATTACK_TYPE_MELEE) diff --git a/code/modules/blob2/blobs/factory.dm b/code/modules/blob2/blobs/factory.dm index 83f5ea4c5a10..12f8fac4b0a5 100644 --- a/code/modules/blob2/blobs/factory.dm +++ b/code/modules/blob2/blobs/factory.dm @@ -35,7 +35,7 @@ var/mob/living/simple_mob/blob/spore/S = null if(overmind) S = new overmind.blob_type.spore_type(src.loc, src) - S.faction = "blob" + S.set_iff_factions(MOB_IFF_FACTION_BLOB) if(istype(S)) S.overmind = overmind overmind.blob_mobs.Add(S) diff --git a/code/modules/blob2/overmind/overmind.dm b/code/modules/blob2/overmind/overmind.dm index 12f4b1763023..bee62d99dfbc 100644 --- a/code/modules/blob2/overmind/overmind.dm +++ b/code/modules/blob2/overmind/overmind.dm @@ -10,7 +10,8 @@ var/list/overminds = list() see_in_dark = 8 invisibility = INVISIBILITY_OBSERVER - faction = "blob" + iff_factions = MOB_IFF_FACTION_BLOB + var/obj/structure/blob/core/blob_core = null // The blob overmind's core var/blob_points = 0 var/max_blob_points = 200 diff --git a/code/modules/blob2/overmind/powers.dm b/code/modules/blob2/overmind/powers.dm index 67429f3e8f76..e94fd9a0a589 100644 --- a/code/modules/blob2/overmind/powers.dm +++ b/code/modules/blob2/overmind/powers.dm @@ -207,7 +207,7 @@ for(var/mob/living/L in view(src)) if(L.stat == DEAD) continue // Already dying or dead. - if(L.faction == "blob") + if(L.has_iff_faction(MOB_IFF_FACTION_BLOB)) continue // No friendly fire. if(locate(/obj/structure/blob) in L.loc) continue // Already has a blob over them. diff --git a/code/modules/blob2/overmind/types.dm b/code/modules/blob2/overmind/types.dm index 9b01a843fb5a..42a56f5df373 100644 --- a/code/modules/blob2/overmind/types.dm +++ b/code/modules/blob2/overmind/types.dm @@ -266,8 +266,7 @@ if(istype(S)) S.overmind = O O.blob_mobs.Add(S) - else - S.faction = "blob" + S.add_iff_faction(MOB_IFF_FACTION_BLOB) S.update_icons() /datum/blob_type/fulminant_organism/on_death(obj/structure/blob/B) @@ -277,8 +276,7 @@ if(istype(S)) S.overmind = B.overmind B.overmind.blob_mobs.Add(S) - else - S.faction = "blob" + S.add_iff_faction(MOB_IFF_FACTION_BLOB) S.update_icons() @@ -500,7 +498,7 @@ for(var/mob/living/L in range(get_turf(victim), 1)) // We don't use orange(), in case there is more than one mob on the target tile. if(L == victim) // Already hit. continue - if(L.faction == "blob") // No friendly fire + if(L.has_iff_faction(MOB_IFF_FACTION_BLOB)) continue L.blob_act() diff --git a/code/modules/clothing/clothing_accessories.dm b/code/modules/clothing/clothing_accessories.dm index 40fc520530b5..74d5a627e049 100644 --- a/code/modules/clothing/clothing_accessories.dm +++ b/code/modules/clothing/clothing_accessories.dm @@ -288,16 +288,6 @@ A.emp_act(severity) ..() -/obj/item/clothing/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - . = ..() - if((. == 0) && LAZYLEN(accessories)) - for(var/obj/item/I in accessories) - var/check = I.handle_shield(user, damage, damage_source, attacker, def_zone, attack_text) - - if(check != 0) // Projectiles sometimes use negatives IIRC, 0 is only returned if something is not blocked. - . = check - break - /obj/item/clothing/strip_menu_options(mob/user) . = ..() if(!length(accessories)) diff --git a/code/modules/clothing/gloves/arm_guards.dm b/code/modules/clothing/gloves/arm_guards.dm index 55642593b120..319237e3d25b 100644 --- a/code/modules/clothing/gloves/arm_guards.dm +++ b/code/modules/clothing/gloves/arm_guards.dm @@ -28,14 +28,6 @@ return FALSE return TRUE -/obj/item/clothing/gloves/arm_guard/laserproof - name = "ablative arm guards" - desc = "These arm guards will protect your hands and arms from energy weapons." - icon_state = "arm_guards_laser" - item_state_slots = list(SLOT_ID_RIGHT_HAND = "swat", SLOT_ID_LEFT_HAND = "swat") - siemens_coefficient = 0.4 //This is worse than the other ablative pieces, to avoid this from becoming the poor warden's insulated gloves. - armor_type = /datum/armor/station/ablative - /obj/item/clothing/gloves/arm_guard/bulletproof name = "ballistic arm guards" desc = "These arm guards will protect your hands and arms from ballistic weapons." diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index fa2f3fdbdd92..6617f54e03e5 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -21,7 +21,7 @@ say_verbs = list("mumbles", "says") // Clumsy folks can't take the mask off themselves. -/obj/item/clothing/mask/muzzle/attack_hand(mob/user, list/params) +/obj/item/clothing/mask/muzzle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.item_by_slot_id(SLOT_ID_MASK) == src && !user.IsAdvancedToolUser()) return 0 ..() diff --git a/code/modules/clothing/sets/armor/ablative.dm b/code/modules/clothing/sets/armor/ablative.dm new file mode 100644 index 000000000000..f8c39360c514 --- /dev/null +++ b/code/modules/clothing/sets/armor/ablative.dm @@ -0,0 +1,64 @@ +// todo: this shouldn't be a signalled shieldcall, as +/obj/item/clothing/suit/armor/laserproof + name = "ablative armor vest" + desc = "A vest that excels in protecting the wearer against energy projectiles." + icon_state = "armor_reflec" + blood_overlay_type = "armor" + armor_type = /datum/armor/station/ablative + siemens_coefficient = 0.1 + +/obj/item/clothing/suit/armor/laserproof/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/clothing/suit/armor/laserproof/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/clothing/suit/armor/laserproof/proc/shieldcall(mob/defending, list/shieldcall_args, fake_attack) + var/damage_source = shieldcall_args[SHIELDCALL_ARG_WEAPON] + var/def_zone = shieldcall_args[SHIELDCALL_ARG_HIT_ZONE] + if(istype(damage_source, /obj/projectile/energy) || istype(damage_source, /obj/projectile/beam)) + var/obj/projectile/P = damage_source + + if(P.reflected) // Can't reflect twice + return + + var/reflectchance = 50 - round(shieldcall_args[SHIELDCALL_ARG_DAMAGE]/3) + if(!(def_zone in list(BP_TORSO, BP_GROIN))) + reflectchance /= 2 + if(P.starting && prob(reflectchance)) + visible_message("\The [defending]'s [src.name] reflects [P]!") + + // Find a turf near or on the original location to bounce to + var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(defending) + + // redirect the projectile + P.legacy_redirect(new_x, new_y, curloc, defending) + P.reflected = 1 + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAG_ATTACK_PASSTHROUGH | SHIELDCALL_FLAG_ATTACK_REDIRECT | SHIELDCALL_FLAG_ATTACK_BLOCKED | SHIELDCALL_FLAG_TERMINATE + +/obj/item/clothing/gloves/arm_guard/laserproof + name = "ablative arm guards" + desc = "These arm guards will protect your hands and arms from energy weapons." + icon_state = "arm_guards_laser" + item_state_slots = list(SLOT_ID_RIGHT_HAND = "swat", SLOT_ID_LEFT_HAND = "swat") + siemens_coefficient = 0.4 //This is worse than the other ablative pieces, to avoid this from becoming the poor warden's insulated gloves. + armor_type = /datum/armor/station/ablative + +/obj/item/clothing/shoes/leg_guard/laserproof + name = "ablative leg guards" + desc = "These will protect your legs and feet from energy weapons." + icon_state = "leg_guards_laser" + item_state_slots = list(SLOT_ID_RIGHT_HAND = "jackboots", SLOT_ID_LEFT_HAND = "jackboots") + siemens_coefficient = 0.1 + armor_type = /datum/armor/station/ablative diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index db94bf4b60fe..8d43ec80d2b9 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -66,7 +66,7 @@ update_icon() -/obj/item/clothing/shoes/attack_hand(mob/user, list/params) +/obj/item/clothing/shoes/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(can_hold_knife == 1 && holding && src.loc == user) draw_knife() return diff --git a/code/modules/clothing/shoes/leg_guards.dm b/code/modules/clothing/shoes/leg_guards.dm index b88fed8ce0a0..e77aa2e7e416 100644 --- a/code/modules/clothing/shoes/leg_guards.dm +++ b/code/modules/clothing/shoes/leg_guards.dm @@ -31,14 +31,6 @@ return FALSE return TRUE -/obj/item/clothing/shoes/leg_guard/laserproof - name = "ablative leg guards" - desc = "These will protect your legs and feet from energy weapons." - icon_state = "leg_guards_laser" - item_state_slots = list(SLOT_ID_RIGHT_HAND = "jackboots", SLOT_ID_LEFT_HAND = "jackboots") - siemens_coefficient = 0.1 - armor_type = /datum/armor/station/ablative - /obj/item/clothing/shoes/leg_guard/bulletproof name = "ballistic leg guards" desc = "These will protect your legs and feet from ballistic weapons." diff --git a/code/modules/clothing/spacesuits/alien.dm b/code/modules/clothing/spacesuits/alien.dm index bf61251514e8..6821a426ccec 100644 --- a/code/modules/clothing/spacesuits/alien.dm +++ b/code/modules/clothing/spacesuits/alien.dm @@ -33,7 +33,7 @@ w_class = WEIGHT_CLASS_NORMAL atom_flags = PHORONGUARD clothing_flags = CLOTHING_THICK_MATERIAL | CLOTHING_INJECTION_PORT - allowed = list(/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs,/obj/item/tank) + allowed = list(/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs,/obj/item/tank) armor_type = /datum/armor/vox/space/armored siemens_coefficient = 0.2 heat_protection_cover = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS diff --git a/code/modules/clothing/spacesuits/syndi.dm b/code/modules/clothing/spacesuits/syndi.dm index 51f0fdf74159..313e9fab76c8 100644 --- a/code/modules/clothing/spacesuits/syndi.dm +++ b/code/modules/clothing/spacesuits/syndi.dm @@ -11,7 +11,7 @@ icon_state = "syndicate" desc = "A crimson spacesuit sporting clean lines and durable plating. Robust, reliable, and slightly suspicious." w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs,/obj/item/tank/emergency/oxygen) + allowed = list(/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs,/obj/item/tank/emergency/oxygen) armor_type = /datum/armor/agent/space siemens_coefficient = 0.6 diff --git a/code/modules/clothing/spacesuits/void/merc.dm b/code/modules/clothing/spacesuits/void/merc.dm index be3f3ab703db..b9252aa9e2f8 100644 --- a/code/modules/clothing/spacesuits/void/merc.dm +++ b/code/modules/clothing/spacesuits/void/merc.dm @@ -20,7 +20,7 @@ weight = ITEM_WEIGHT_VOIDSUIT w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/merc/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 /obj/item/clothing/head/helmet/space/void/merc/fire @@ -38,7 +38,7 @@ desc = "A blackened suit that has had many of its protective plates coated in or replaced with high-grade thermal insulation, to protect against incineration. Property of Gorlex Marauders." armor_type = /datum/armor/merc/space/ghostrider max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs,/obj/item/material/twohanded/fireaxe,/obj/item/flamethrower) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs,/obj/item/material/twohanded/fireaxe,/obj/item/flamethrower) siemens_coefficient = 0.7 //Soviet Void Suit @@ -91,7 +91,7 @@ item_state_slots = list(SLOT_ID_RIGHT_HAND = "syndie_voidsuit", SLOT_ID_LEFT_HAND = "syndie_voidsuit") w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/merc/space/clown - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 //Four below avalible through cargo @@ -111,7 +111,7 @@ desc = "One of the few combat-grade suits avalible in the frontier, and the poster-child of Hephaestus Industries. Comes equipped with a wrist-bound oxygen timer." w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/station/secsuit - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null helmet_type = /obj/item/clothing/head/helmet/space/void/odst @@ -131,7 +131,7 @@ desc = "A standard Icarus line suit that has been repourposed to protect from heavier biohazards." w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/exploration/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null helmet_type = /obj/item/clothing/head/helmet/space/void/odst_med @@ -151,7 +151,7 @@ desc = "Favoured suit of deep-space engineers, comfortable and comparable to suits avalible to Nanotrasen Engineers. Comes equipped with a wrist-bound oxygen timer." w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/engineering/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null helmet_type = /obj/item/clothing/head/helmet/space/void/odst_eng @@ -171,7 +171,7 @@ desc = "Cheaper version of the main Icarus line, often marketed to Frontier settlements. Perfect for Expeditions." w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/exploration/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null helmet_type = /obj/item/clothing/head/helmet/space/void/odst_exp @@ -195,7 +195,7 @@ item_state_slots = list(SLOT_ID_RIGHT_HAND = "syndie_voidsuit", SLOT_ID_LEFT_HAND = "syndie_voidsuit") w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/merc/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null helmet_type = /obj/item/clothing/head/helmet/space/void/odst_necro @@ -207,6 +207,6 @@ item_state_slots = list(SLOT_ID_RIGHT_HAND = "syndie_voidsuit", SLOT_ID_LEFT_HAND = "syndie_voidsuit") w_class = WEIGHT_CLASS_NORMAL armor_type = /datum/armor/merc/space - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword,/obj/item/handcuffs) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/transforming/energy/sword,/obj/item/handcuffs) siemens_coefficient = 0.6 species_restricted = null diff --git a/code/modules/clothing/spacesuits/void/xeno/tajara.dm b/code/modules/clothing/spacesuits/void/xeno/tajara.dm index 44e08971eed1..2c1c2e0bced0 100644 --- a/code/modules/clothing/spacesuits/void/xeno/tajara.dm +++ b/code/modules/clothing/spacesuits/void/xeno/tajara.dm @@ -15,7 +15,7 @@ /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/melee/baton, - /obj/item/melee/energy/sword, + /obj/item/melee/transforming/energy/sword, /obj/item/handcuffs ) species_restricted = list(SPECIES_TAJ) @@ -46,7 +46,7 @@ /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/melee/baton, - /obj/item/melee/energy/sword, + /obj/item/melee/transforming/energy/sword, /obj/item/handcuffs ) species_restricted = list(SPECIES_TAJ) diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 99728ffc838b..5914082a6418 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -79,37 +79,6 @@ item_state_slots = list(SLOT_ID_RIGHT_HAND = "bulletproof_new", SLOT_ID_LEFT_HAND = "bulletproof_new") blood_overlay_type = "armor" -/obj/item/clothing/suit/armor/laserproof - name = "ablative armor vest" - desc = "A vest that excels in protecting the wearer against energy projectiles." - icon_state = "armor_reflec" - blood_overlay_type = "armor" - armor_type = /datum/armor/station/ablative - siemens_coefficient = 0.1 - -/obj/item/clothing/suit/armor/laserproof/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(istype(damage_source, /obj/projectile/energy) || istype(damage_source, /obj/projectile/beam)) - var/obj/projectile/P = damage_source - - if(P.reflected) // Can't reflect twice - return ..() - - var/reflectchance = 40 - round(damage/3) - if(!(def_zone in list(BP_TORSO, BP_GROIN))) - reflectchance /= 2 - if(P.starting && prob(reflectchance)) - visible_message("\The [user]'s [src.name] reflects [attack_text]!") - - // Find a turf near or on the original location to bounce to - var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(user) - - // redirect the projectile - P.redirect(new_x, new_y, curloc, user) - P.reflected = 1 - - return PROJECTILE_CONTINUE // complete projectile permutation /obj/item/clothing/suit/armor/combat name = "combat vest" @@ -193,7 +162,22 @@ blood_overlay_type = "armor" armor_type = /datum/armor/none -/obj/item/clothing/suit/armor/reactive/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/clothing/suit/armor/reactive/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/clothing/suit/armor/reactive/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/clothing/suit/armor/reactive/proc/shieldcall(mob/user, list/shieldcall_args, fake_attack) if(prob(50)) user.visible_message("The reactive teleport system flings [user] clear of the attack!") var/list/turfs = new/list() @@ -211,10 +195,8 @@ spark_system.set_up(5, 0, user.loc) spark_system.start() playsound(user.loc, /datum/soundbyte/grouped/sparks, 50, 1) - - user.loc = picked - return PROJECTILE_FORCE_MISS - return 0 + user.forceMove(picked) + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAG_ATTACK_BLOCKED | SHIELDCALL_FLAG_ATTACK_PASSTHROUGH /obj/item/clothing/suit/armor/reactive/attack_self(mob/user) . = ..() @@ -250,6 +232,13 @@ valid_accessory_slots = null var/block_chance = 20 +/obj/item/clothing/suit/armor/alien/mob_armorcall(mob/defending, list/shieldcall_args, fake_attack) + if(prob(block_chance)) + defending.visible_message(SPAN_DANGER("[src] completely absorbs [RESOLVE_SHIELDCALL_ATTACK_TEXT(shieldcall_args)]!")) + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAGS_FOR_COMPLETE_BLOCK + return + return ..() + /obj/item/clothing/suit/armor/alien/tank name = "alien protection suit" desc = "It's really resilient yet lightweight, so it's probably meant to be armor. Strangely enough it seems to have been designed for a humanoid shape." @@ -260,12 +249,6 @@ armor_type = /datum/armor/alien/heavy block_chance = 40 -/obj/item/clothing/suit/armor/alien/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(prob(block_chance)) - user.visible_message("\The [src] completely absorbs [attack_text]!") - return TRUE - return FALSE - //Non-hardsuit ERT armor. /obj/item/clothing/suit/armor/vest/ert name = "emergency response team armor" diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index ca0607567040..a9c28b8385af 100644 --- a/code/modules/clothing/under/accessories/accessory.dm +++ b/code/modules/clothing/under/accessories/accessory.dm @@ -133,7 +133,7 @@ ..() //default attack_hand behaviour -/obj/item/clothing/accessory/attack_hand(mob/user, list/params) +/obj/item/clothing/accessory/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(accessory_host) return //we aren't an object on the ground so don't call parent ..() diff --git a/code/modules/clothing/under/accessories/armor.dm b/code/modules/clothing/under/accessories/armor.dm index 612445c6eb7b..f42b06b1c0a6 100644 --- a/code/modules/clothing/under/accessories/armor.dm +++ b/code/modules/clothing/under/accessories/armor.dm @@ -178,28 +178,44 @@ armor_type = /datum/armor/station/ablative siemens_coefficient = 0.2 -/obj/item/clothing/accessory/armor/armorplate/ablative/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +/obj/item/clothing/accessory/armor/armorplate/ablative/equipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + // if you're reading this: this is not the right way to do shieldcalls + // this is just a lazy implementation + // signals have highest priority, this as a piece of armor shouldn't have that. + RegisterSignal(user, COMSIG_ATOM_SHIELDCALL, PROC_REF(shieldcall)) + +/obj/item/clothing/accessory/armor/armorplate/ablative/unequipped(mob/user, slot, flags) + . = ..() + if(slot == SLOT_ID_HANDS) + return + UnregisterSignal(user, COMSIG_ATOM_SHIELDCALL) + +/obj/item/clothing/accessory/armor/armorplate/ablative/proc/shieldcall(mob/defending, list/shieldcall_args, fake_attack) + var/damage_source = shieldcall_args[SHIELDCALL_ARG_WEAPON] + var/def_zone = shieldcall_args[SHIELDCALL_ARG_HIT_ZONE] if(istype(damage_source, /obj/projectile/energy) || istype(damage_source, /obj/projectile/beam)) var/obj/projectile/P = damage_source if(P.reflected) - return ..() + return - var/reflectchance = 20 - round(damage/3) + var/reflectchance = 20 - round(shieldcall_args[SHIELDCALL_ARG_DAMAGE]/3) if(!(def_zone in list(BP_TORSO, BP_GROIN))) reflectchance /= 2 if(P.starting && prob(reflectchance)) - visible_message("\The [user]'s [src.name] reflects [attack_text]!") + visible_message("\The [defending]'s [src.name] reflects [P]!") var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) - var/turf/curloc = get_turf(user) + var/turf/curloc = get_turf(defending) - P.redirect(new_x, new_y, curloc, user) + P.legacy_redirect(new_x, new_y, curloc, defending) P.reflected = 1 - - return PROJECTILE_CONTINUE + shieldcall_args[SHIELDCALL_ARG_FLAGS] |= SHIELDCALL_FLAGS_FOR_PROJECTILE_DEFLECT ////////////// //Arm guards diff --git a/code/modules/clothing/under/accessories/holster.dm b/code/modules/clothing/under/accessories/holster.dm index 4107b17d5459..339fda618d4a 100644 --- a/code/modules/clothing/under/accessories/holster.dm +++ b/code/modules/clothing/under/accessories/holster.dm @@ -67,7 +67,7 @@ set_weight_class(initial(w_class)) clear_holster() -/obj/item/clothing/accessory/holster/attack_hand(mob/user, list/params) +/obj/item/clothing/accessory/holster/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (accessory_host && (slot & ACCESSORY_SLOT_UTILITY)) if(holstered) unholster(user) @@ -157,7 +157,7 @@ desc = "A handsome synthetic leather scabbard with matching belt." icon_state = "holster_machete" concealed_holster = 0 - can_hold = list(/obj/item/material/knife/machete, /obj/item/melee/energy/hfmachete, /obj/item/reagent_containers/spray, /obj/item/soap, + can_hold = list(/obj/item/material/knife/machete, /obj/item/melee/transforming/hfmachete, /obj/item/reagent_containers/spray, /obj/item/soap, /obj/item/c_tube, /obj/item/bikehorn) cant_hold = list(/obj/item/material/knife/machete/armblade) sound_in = 'sound/effects/holster/sheathin.ogg' diff --git a/code/modules/detectivework/microscope/dnascanner.dm b/code/modules/detectivework/microscope/dnascanner.dm index 58699c567182..281fdbb77ee9 100644 --- a/code/modules/detectivework/microscope/dnascanner.dm +++ b/code/modules/detectivework/microscope/dnascanner.dm @@ -142,7 +142,7 @@ /obj/machinery/dnaforensics/attack_ai(mob/user) ui_interact(user) -/obj/machinery/dnaforensics/attack_hand(mob/user, list/params) +/obj/machinery/dnaforensics/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/dnaforensics/update_icon() diff --git a/code/modules/detectivework/microscope/microscope.dm b/code/modules/detectivework/microscope/microscope.dm index 5be88931cf1d..696f0910d0c5 100644 --- a/code/modules/detectivework/microscope/microscope.dm +++ b/code/modules/detectivework/microscope/microscope.dm @@ -25,7 +25,7 @@ return return ..() -/obj/machinery/microscope/attack_hand(mob/user, list/params) +/obj/machinery/microscope/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!sample) to_chat(user, "The microscope has no sample to examine.") diff --git a/code/modules/economy/machines/ATM.dm b/code/modules/economy/machines/ATM.dm index e2967158e2c9..3fdd838f12de 100644 --- a/code/modules/economy/machines/ATM.dm +++ b/code/modules/economy/machines/ATM.dm @@ -308,7 +308,7 @@ GLOBAL_LIST_INIT(atm_sounds, list('sound/items/polaroid1.ogg', 'sound/items/pola authenticated_account = null account_security_level = 0 -/obj/machinery/atm/attack_hand(mob/user, list/params) +/obj/machinery/atm/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user, /mob/living/silicon)) to_chat (user, SPAN_WARNING("A firewall prevents you from interfacing with this device!")) return diff --git a/code/modules/economy/machines/Accounts_DB.dm b/code/modules/economy/machines/Accounts_DB.dm index 08cfd60644de..7eef108386f4 100644 --- a/code/modules/economy/machines/Accounts_DB.dm +++ b/code/modules/economy/machines/Accounts_DB.dm @@ -57,7 +57,7 @@ attack_hand(user) -/obj/machinery/account_database/attack_hand(mob/user, list/params) +/obj/machinery/account_database/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return nano_ui_interact(user) diff --git a/code/modules/economy/machines/cash_register.dm b/code/modules/economy/machines/cash_register.dm index 99da9607305b..893f02028bb6 100644 --- a/code/modules/economy/machines/cash_register.dm +++ b/code/modules/economy/machines/cash_register.dm @@ -39,7 +39,7 @@ . += "It's completely empty." -/obj/machinery/cash_register/attack_hand(mob/user, list/params) +/obj/machinery/cash_register/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // Don't be accessible from the wrong side of the machine if(get_dir(src, user) & global.reverse_dir[src.dir]) return diff --git a/code/modules/economy/machines/mint.dm b/code/modules/economy/machines/mint.dm index 858b40fa4ed5..daacc66ee439 100644 --- a/code/modules/economy/machines/mint.dm +++ b/code/modules/economy/machines/mint.dm @@ -81,7 +81,7 @@ if(processed) qdel(O) -/obj/machinery/mineral/mint/attack_hand(mob/user, list/params) +/obj/machinery/mineral/mint/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/dat = "Coin Press
" @@ -474,7 +474,7 @@ /obj/machinery/coinbank/attack_ai(mob/user as mob) attack_hand(user) -/obj/machinery/coinbank/attack_hand(mob/user, list/params) +/obj/machinery/coinbank/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return wires.Interact(user) diff --git a/code/modules/economy/money_bag.dm b/code/modules/economy/money_bag.dm index 04a467c8cff1..334bf4051b5f 100644 --- a/code/modules/economy/money_bag.dm +++ b/code/modules/economy/money_bag.dm @@ -8,7 +8,7 @@ throw_force = 2.0 w_class = WEIGHT_CLASS_BULKY -/obj/item/moneybag/attack_hand(mob/user, list/params) +/obj/item/moneybag/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/amt_supermatter = 0 var/amt_bananium = 0 var/amt_mhydrogen = 0 diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm index 072178188b82..8459e76d4d4c 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -84,7 +84,7 @@ GLOBAL_LIST_INIT(carp_count,list())// a list of Z levels (string), associated wi if(WEST) return locate(clearance, rand(clearance, world.maxy - clearance), Z) -/datum/event/carp_migration/proc/check_gib(var/mob/living/simple_mob/hostile/carp/M) //awesome road kills +/datum/event/carp_migration/proc/check_gib(var/mob/living/simple_mob/animal/space/carp/M) //awesome road kills if(M.health <= 0 && prob(60)) M.gib() diff --git a/code/modules/examine/descriptions/weapons.dm b/code/modules/examine/descriptions/weapons.dm index 835d35882dcc..1120bdcfb6ce 100644 --- a/code/modules/examine/descriptions/weapons.dm +++ b/code/modules/examine/descriptions/weapons.dm @@ -66,7 +66,7 @@ set to 'harm', you will inflict damage when using it, regardless if it is on or not. Each stun reduces the baton's charge, which can be replenished by \ putting it inside a weapon recharger." -/obj/item/melee/energy/sword +/obj/item/melee/transforming/energy/sword description_antag = "The energy sword is a very strong melee weapon, capable of severing limbs easily, if they are targeted. It can also has a chance \ to block projectiles and melee attacks while it is on and being held. The sword can be toggled on or off by using it in your hand. While it is off, \ it can be concealed in your pocket or bag." diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index e553eba475a4..93f79ff03da4 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -328,7 +328,7 @@ proc/check_panel(mob/M) return GLOBAL_LIST_INIT(non_fakeattack_weapons, list(/obj/item/gun/ballistic, /obj/item/ammo_magazine/a357/speedloader,\ - /obj/item/gun/energy/crossbow, /obj/item/melee/energy/sword,\ + /obj/item/gun/energy/crossbow, /obj/item/melee/transforming/energy/sword,\ /obj/item/storage/box/syndicate, /obj/item/storage/box/emps,\ /obj/item/cartridge/syndicate, /obj/item/clothing/under/chameleon,\ /obj/item/clothing/shoes/syndigaloshes, /obj/item/card/id/syndicate,\ diff --git a/code/modules/food/drinkingglass/extras.dm b/code/modules/food/drinkingglass/extras.dm index d51297c70588..94b0d7690eb9 100644 --- a/code/modules/food/drinkingglass/extras.dm +++ b/code/modules/food/drinkingglass/extras.dm @@ -26,7 +26,7 @@ else return ..() -/obj/item/reagent_containers/food/drinks/glass2/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/food/drinks/glass2/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(src != user.get_inactive_held_item()) return ..() diff --git a/code/modules/food/drinks/bottle.dm b/code/modules/food/drinks/bottle.dm index ac9f648ac278..606531dcaec3 100644 --- a/code/modules/food/drinks/bottle.dm +++ b/code/modules/food/drinks/bottle.dm @@ -162,7 +162,7 @@ if(target_zone == "head" && istype(L, /mob/living/carbon/)) user.visible_message("\The [user] smashes [src] over [L]'s head!") if(weaken_duration) - L.apply_effect(min(weaken_duration, 5), WEAKEN, blocked) // Never weaken more than a flash! + L.apply_effect(min(weaken_duration, 5), WEAKEN) // Never weaken more than a flash! else user.visible_message("\The [user] smashes [src] into [L]!") diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm index f0b26d356190..ab89a235a24c 100644 --- a/code/modules/food/food/snacks.dm +++ b/code/modules/food/food/snacks.dm @@ -3273,7 +3273,7 @@ add_overlay(overlays_to_add) -/obj/item/pizzabox/attack_hand(mob/user, list/params) +/obj/item/pizzabox/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if( open && pizza ) user.put_in_hands( pizza ) @@ -4807,7 +4807,7 @@ END CITADEL CHANGE */ bitesize = 1 nutriment_amt = 10 -/obj/item/reagent_containers/food/snacks/chipplate/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/food/snacks/chipplate/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // todo: sigh, no ..(); shift over to on_attack_hand var/obj/item/reagent_containers/food/snacks/returningitem = new vendingobject(loc) returningitem.reagents.clear_reagents() diff --git a/code/modules/food/glass/bottle.dm b/code/modules/food/glass/bottle.dm index 76736ada9951..7e47c25b1cee 100644 --- a/code/modules/food/glass/bottle.dm +++ b/code/modules/food/glass/bottle.dm @@ -26,7 +26,7 @@ ..() update_icon() -/obj/item/reagent_containers/glass/bottle/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/glass/bottle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() update_icon() diff --git a/code/modules/food/machinery/appliance/_appliance.dm b/code/modules/food/machinery/appliance/_appliance.dm index 8bf657ddface..641141ac8286 100644 --- a/code/modules/food/machinery/appliance/_appliance.dm +++ b/code/modules/food/machinery/appliance/_appliance.dm @@ -541,7 +541,7 @@ smoke.set_up(10, 0, get_turf(src), 300) smoke.start() -/obj/machinery/appliance/attack_hand(mob/user, list/params) +/obj/machinery/appliance/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (cooking_objs.len) if (removal_menu(user)) return diff --git a/code/modules/food/machinery/gibber.dm b/code/modules/food/machinery/gibber.dm index 128eed53e599..c5090d677078 100644 --- a/code/modules/food/machinery/gibber.dm +++ b/code/modules/food/machinery/gibber.dm @@ -76,7 +76,7 @@ src.go_out() return -/obj/machinery/gibber/attack_hand(mob/user, list/params) +/obj/machinery/gibber/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return if(operating) diff --git a/code/modules/food/machinery/microwave.dm b/code/modules/food/machinery/microwave.dm index fbd660f080f2..d31dbd2e4ae9 100644 --- a/code/modules/food/machinery/microwave.dm +++ b/code/modules/food/machinery/microwave.dm @@ -162,7 +162,7 @@ if(istype(user, /mob/living/silicon/robot) && Adjacent(user)) attack_hand(user) -/obj/machinery/microwave/attack_hand(mob/user, list/params) +/obj/machinery/microwave/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) interact(user) diff --git a/code/modules/food/machinery/smartfridge.dm b/code/modules/food/machinery/smartfridge.dm index bfa4a59d7a46..d75dd5222df9 100644 --- a/code/modules/food/machinery/smartfridge.dm +++ b/code/modules/food/machinery/smartfridge.dm @@ -202,7 +202,7 @@ /obj/machinery/smartfridge/attack_ai(mob/user as mob) attack_hand(user) -/obj/machinery/smartfridge/attack_hand(mob/user, list/params) +/obj/machinery/smartfridge/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return wires.Interact(user) diff --git a/code/modules/gamemaster/actions/electrified_door.dm b/code/modules/gamemaster/actions/electrified_door.dm index e4ba9c2efb34..c1d4ff1ad914 100644 --- a/code/modules/gamemaster/actions/electrified_door.dm +++ b/code/modules/gamemaster/actions/electrified_door.dm @@ -63,7 +63,7 @@ if(!chosen_door || !chosen_door.arePowerSystemsOn()) return chosen_door.visible_message("\The [chosen_door]'s hydraulics detonate!") - chosen_door.fragmentate(get_turf(chosen_door), rand(5, 10), rand(3, 5), list(/obj/projectile/bullet/pellet/fragment/tank/small)) + chosen_door.shrapnel_explosion(rand(5, 10), rand(3, 5), /obj/projectile/bullet/pellet/fragment/tank/small) explosion(get_turf(chosen_door),-1,-1,2,3) chosen_door.lock() diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm index 7b76176cb2ae..1fb2c99dffed 100644 --- a/code/modules/games/cards.dm +++ b/code/modules/games/cards.dm @@ -67,7 +67,7 @@ return ..() -/obj/item/deck/attack_hand(mob/user, list/params) +/obj/item/deck/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/carbon/human/H = user if(istype(src.loc, /obj/item/storage) || src == H.r_store || src == H.l_store || src.loc == user) // so objects can be removed from storage containers or pockets. also added a catch-all, so if it's in the mob you'll pick it up. ..() diff --git a/code/modules/gateway/README.md b/code/modules/gateway/README.md new file mode 100644 index 000000000000..9acc351a8253 --- /dev/null +++ b/code/modules/gateway/README.md @@ -0,0 +1,11 @@ +# Gateway Module + +WIP module for a series of disjunct mechanics for shunting people across spaces. + +- These are unrelated to telescience module, and therefore will not be balanced by it nor will it be constructible. +- These are mostly for map design, admin spawn, and events. + +## Contains + +- Stargates: Classic SS13 gateways with rudimentary accuracy/precision and destination settings +- Quantum Archways: See-through & Walk-through 'line' gateways diff --git a/code/modules/gateway/stargate/stargate-away.dm b/code/modules/gateway/stargate/stargate-away.dm new file mode 100644 index 000000000000..9a1e694cf287 --- /dev/null +++ b/code/modules/gateway/stargate/stargate-away.dm @@ -0,0 +1,107 @@ +/** + * Preset for away destinations for the stargate system + */ + +/obj/machinery/gateway/centeraway + density = 1 + icon_state = "offcenter" + use_power = USE_POWER_OFF + var/calibrated = 1 + var/list/linked = list() //a list of the connected gateway chunks + var/ready = 0 + var/obj/machinery/gateway/centeraway/stationgate = null + + +/obj/machinery/gateway/centeraway/Initialize(mapload) + update_icon() + stationgate = locate(/obj/machinery/gateway/centerstation) + . = ..() + density = 1 + + +/obj/machinery/gateway/centeraway/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" + +/obj/machinery/gateway/centeraway/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in GLOB.alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) + if(!ready) return + if(linked.len != 8) return + if(!stationgate || !calibrated) + to_chat(user, SPAN_NOTICE("Error: No destination found. Please calibrate gateway.")) + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centeraway/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centeraway/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +/obj/machinery/gateway/centeraway/Bumped(atom/movable/M as mob|obj) + if(!ready) return + if(!active) return + if(istype(M, /mob/living/carbon)) + for(var/obj/item/implant/exile/E in M)//Checking that there is an exile implant in the contents + if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket + to_chat(M, "The station gate has detected your exile implant and is blocking your entry.") + return + M.forceMove(get_step(stationgate.loc, SOUTH)) + M.setDir(SOUTH) + SEND_SOUND(M, sound('sound/effects/phasein.ogg')) + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + + +/obj/machinery/gateway/centeraway/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W,/obj/item/multitool)) + if(calibrated && stationgate) + to_chat(user, "The gate is already calibrated, there is no work for you to do here.") + return + else + stationgate = locate(/obj/machinery/gateway/centerstation) + if(!stationgate) + to_chat(user, "Error: Recalibration failed. No destination found... That can't be good.") + return + else + to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") + calibrated = 1 + return diff --git a/code/modules/gateway/stargate/stargate-destination.dm b/code/modules/gateway/stargate/stargate-destination.dm new file mode 100644 index 000000000000..be34508c68e2 --- /dev/null +++ b/code/modules/gateway/stargate/stargate-destination.dm @@ -0,0 +1,7 @@ +/** + * Stargate destination override; allows you to kick people to those destinations instead of an away gate. + */ +/obj/landmark/stargate_destination_marker + delete_on_roundstart = FALSE + +// todo: impl diff --git a/code/modules/gateway/stargate/stargate-station.dm b/code/modules/gateway/stargate/stargate-station.dm new file mode 100644 index 000000000000..b7e780051a44 --- /dev/null +++ b/code/modules/gateway/stargate/stargate-station.dm @@ -0,0 +1,127 @@ +/** + * Preset for station-side stargate + */ +//this is da important part wot makes things go +/obj/machinery/gateway/centerstation + density = 1 + icon_state = "offcenter" + use_power = USE_POWER_IDLE + + //warping vars + var/list/linked = list() + var/ready = 0 //have we got all the parts for a gateway? + var/wait = 0 //this just grabs world.time at world start + var/obj/machinery/gateway/centeraway/awaygate = null + +/obj/machinery/gateway/centerstation/Initialize(mapload) + update_icon() + wait = world.time + config_legacy.gateway_delay //+ thirty minutes default + awaygate = locate(/obj/machinery/gateway/centeraway) + . = ..() + density = TRUE + +/obj/machinery/gateway/centerstation/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" + +/obj/machinery/gateway/centerstation/process(delta_time) + if(machine_stat & (NOPOWER)) + if(active) toggleoff() + return + + if(active) + use_power(5000) + + +/obj/machinery/gateway/centerstation/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in GLOB.alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) + if(!ready) return + if(linked.len != 8) return + if(!powered()) return + if(!awaygate) + to_chat(user, "Error: No destination found. Please program gateway.") + return + if(world.time < wait) + to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") + return + if(!awaygate.calibrated && !LAZYLEN(awaydestinations)) + to_chat(user, SPAN_NOTICE("Error: Destination gate uncalibrated. Gateway unsafe to use without far-end calibration update.")) + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centerstation/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centerstation/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +//okay, here's the good teleporting stuff +/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) + if(!ready) return + if(!active) return + if(!awaygate) return + + use_power(5000) + SEND_SOUND(M, sound('sound/effects/phasein.ogg')) + playsound(src, 'sound/effects/phasein.ogg', 100, 1) + if(awaygate.calibrated) + M.forceMove(get_step(awaygate.loc, SOUTH)) + M.setDir(SOUTH) + return + else + var/obj/landmark/dest = pick(awaydestinations) + if(dest) + M.forceMove(dest.loc) + M.setDir(SOUTH) + return + +/obj/machinery/gateway/centerstation/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W,/obj/item/multitool)) + if(!awaygate) + awaygate = locate(/obj/machinery/gateway/centeraway) + if(!awaygate) // We still can't find the damn thing because there is no destination. + to_chat(user, "Error: Programming failed. No destination found.") + return + to_chat(user, "Startup programming successful!: A destination in another point of space and time has been detected.") + else + to_chat(user, "The gate is already calibrated, there is no work for you to do here.") + return diff --git a/code/modules/gateway/stargate/stargate.dm b/code/modules/gateway/stargate/stargate.dm new file mode 100644 index 000000000000..41224803ae60 --- /dev/null +++ b/code/modules/gateway/stargate/stargate.dm @@ -0,0 +1,24 @@ +// todo: /obj/machinery/stargate +/obj/machinery/gateway + name = "gateway" + desc = "A mysterious gateway built by unknown hands. It allows for faster than light travel to far-flung locations and even alternate realities." + icon = 'icons/obj/machines/gateway.dmi' + // todo: temporary, as this is unbuildable + integrity_flags = INTEGRITY_INDESTRUCTIBLE + icon_state = "off" + density = 1 + anchored = 1 + var/active = 0 + + +/obj/machinery/gateway/Initialize(mapload) + update_icon() + if(dir == SOUTH) + density = 0 + . = ..() + +/obj/machinery/gateway/update_icon() + if(active) + icon_state = "on" + return + icon_state = "off" diff --git a/code/modules/ghostroles/roles/ashlander.dm b/code/modules/ghostroles/roles/ashlander.dm index b228bc81ce00..e0f0be05ccf1 100644 --- a/code/modules/ghostroles/roles/ashlander.dm +++ b/code/modules/ghostroles/roles/ashlander.dm @@ -112,7 +112,7 @@ /datum/ghostrole_instantiator/human/player_static/ashlander/AfterSpawn(mob/created, list/params) . = ..() - created.faction = "lavaland" + created.set_iff_factions(MOB_IFF_FACTION_BIND_TO_MAP) created.mind.teach_crafting_recipe(ashlander_crafting) created.remove_language(/datum/language/common) diff --git a/code/modules/ghostroles/roles/pirate.dm b/code/modules/ghostroles/roles/pirate.dm index d33d16700f5e..93bfa9ee038d 100644 --- a/code/modules/ghostroles/roles/pirate.dm +++ b/code/modules/ghostroles/roles/pirate.dm @@ -50,7 +50,7 @@ /datum/ghostrole_instantiator/human/player_static/pirate/GetOutfit(client/C, mob/M, list/params) var/datum/outfit/outfit = ..() //var/mob/M = /mob/living/carbon/human/H - M.faction = "pirate" + M.set_iff_factions(MOB_IFF_FACTION_PIRATE) switch(params["fluff"]) if("immigrant") return /datum/outfit/pirate/immigrant diff --git a/code/modules/ghostroles/spawnpoint.dm b/code/modules/ghostroles/spawnpoint.dm index 5d424b155bbf..7439c71193ef 100644 --- a/code/modules/ghostroles/spawnpoint.dm +++ b/code/modules/ghostroles/spawnpoint.dm @@ -20,7 +20,7 @@ GLOBAL_LIST_EMPTY(ghostrole_spawnpoints) var/spawntext /datum/component/ghostrole_spawnpoint/Initialize(role_type, allowed_spawns = INFINITY, list/params, datum/callback/proc_to_call_or_callback, notify_ghosts = TRUE, spawntext) - if((. = ..()) & COMPONENT_INCOMPATIBLE) + if((. = ..()) == COMPONENT_INCOMPATIBLE) return if(!isatom(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/modules/hardsuits/_rig.dm b/code/modules/hardsuits/_rig.dm index 44d584ac1691..a0283f4234a3 100644 --- a/code/modules/hardsuits/_rig.dm +++ b/code/modules/hardsuits/_rig.dm @@ -1136,11 +1136,6 @@ if(!CHECK_MOBILITY(user, MOBILITY_CAN_MOVE)) return - if(locate(/obj/effect/stop/, wearer.loc)) - for(var/obj/effect/stop/S in wearer.loc) - if(S.victim == wearer) - return - if(!wearer.lastarea) wearer.lastarea = get_area(wearer.loc) diff --git a/code/modules/hardsuits/modules/combat.dm b/code/modules/hardsuits/modules/combat.dm index dc2d4874ef62..66cdec37be3d 100644 --- a/code/modules/hardsuits/modules/combat.dm +++ b/code/modules/hardsuits/modules/combat.dm @@ -202,7 +202,7 @@ /obj/item/hardsuit_module/mounted/energy_blade/process(delta_time) if(holder && holder.wearer) - if(!(locate(/obj/item/melee/energy/blade) in holder.wearer)) + if(!(locate(/obj/item/melee/ninja_energy_blade) in holder.wearer)) deactivate() return 0 @@ -219,7 +219,7 @@ deactivate() return - var/obj/item/melee/energy/blade/blade = new(M) + var/obj/item/melee/ninja_energy_blade/blade = new(M) blade.creator = M M.put_in_hands(blade) @@ -232,7 +232,7 @@ if(!M) return - for(var/obj/item/melee/energy/blade/blade in M.contents) + for(var/obj/item/melee/ninja_energy_blade/blade in M.contents) qdel(blade) /obj/item/hardsuit_module/fabricator diff --git a/code/modules/hardsuits/pieces.dm b/code/modules/hardsuits/pieces.dm index 6615b4240656..84688d96acc5 100644 --- a/code/modules/hardsuits/pieces.dm +++ b/code/modules/hardsuits/pieces.dm @@ -85,7 +85,7 @@ hardsuit = null return ..() -/obj/item/clothing/suit/space/hardsuit/attack_hand(mob/user, list/params) +/obj/item/clothing/suit/space/hardsuit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(tacknife) tacknife.loc = get_turf(src) if(user.put_in_active_hand(tacknife)) diff --git a/code/modules/hardsuits/rig_attackby.dm b/code/modules/hardsuits/rig_attackby.dm index 52330ed2c0f6..2e85da1c35e7 100644 --- a/code/modules/hardsuits/rig_attackby.dm +++ b/code/modules/hardsuits/rig_attackby.dm @@ -172,7 +172,7 @@ ..() -/obj/item/hardsuit/attack_hand(mob/user, list/params) +/obj/item/hardsuit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(electrified != 0) if(shock(user)) //Handles removing charge from the cell, as well. No need to do that here. diff --git a/code/modules/hardsuits/rig_pieces.dm b/code/modules/hardsuits/rig_pieces.dm index 31cea56b0346..471a9793cdc9 100644 --- a/code/modules/hardsuits/rig_pieces.dm +++ b/code/modules/hardsuits/rig_pieces.dm @@ -178,7 +178,7 @@ SPECIES_ZORREN_HIGH, ) -/obj/item/clothing/suit/space/hardsuit/attack_hand(mob/user, list/params) +/obj/item/clothing/suit/space/hardsuit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(tacknife) tacknife.loc = get_turf(src) if(user.put_in_active_hand(tacknife)) diff --git a/code/modules/hardsuits/suits/merc.dm b/code/modules/hardsuits/suits/merc.dm index ce1877f6d356..956e6a901cc2 100644 --- a/code/modules/hardsuits/suits/merc.dm +++ b/code/modules/hardsuits/suits/merc.dm @@ -31,7 +31,7 @@ /obj/item/ammo_magazine, /obj/item/ammo_casing, /obj/item/melee/baton, - /obj/item/melee/energy/sword, + /obj/item/melee/transforming/energy/sword, /obj/item/handcuffs, /obj/item/bluespace_radio, ) diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm index b5697ff0b7fe..95b91e324001 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/code/modules/holodeck/HolodeckControl.dm @@ -75,7 +75,7 @@ /obj/machinery/computer/HolodeckControl/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/HolodeckControl/attack_hand(mob/user, list/params) +/obj/machinery/computer/HolodeckControl/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return user.set_machine(src) diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index 0854a36c59cf..73ca10c2a5fb 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -239,16 +239,7 @@ /obj/item/holo/esword/red lcolor = "#FF0000" -/obj/item/holo/esword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(active && default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, user.loc) - spark_system.start() - playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1) - return TRUE - return FALSE +// todo: the parry system was removed from this because that sucks maybe readd it later lol /obj/item/holo/esword/attack_self(mob/user) . = ..() @@ -368,7 +359,7 @@ /obj/machinery/readybutton/attackby(obj/item/W as obj, mob/user as mob) to_chat(user, "The device is a solid button, there's nothing you can do with it!") -/obj/machinery/readybutton/attack_hand(mob/user, list/params) +/obj/machinery/readybutton/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.stat || machine_stat & (NOPOWER|BROKEN)) to_chat(user, "This device is not powered.") @@ -437,11 +428,11 @@ /mob/living/simple_mob/animal/space/carp/holodeck/proc/set_safety(var/safe) if (safe) - faction = "neutral" + set_iff_factions(MOB_IFF_FACTION_NEUTRAL) legacy_melee_damage_lower = 0 legacy_melee_damage_upper = 0 else - faction = "carp" + set_iff_factions(MOB_IFF_FACTION_CARP) legacy_melee_damage_lower = initial(legacy_melee_damage_lower) legacy_melee_damage_upper = initial(legacy_melee_damage_upper) diff --git a/code/modules/holomap/holomap_datum.dm b/code/modules/holomap/holomap_datum.dm index f6d4f61bba54..df8f754888d9 100644 --- a/code/modules/holomap/holomap_datum.dm +++ b/code/modules/holomap/holomap_datum.dm @@ -14,8 +14,8 @@ if(isAI) T = get_turf(user.client.eye) - cursor.pixel_x = (T.x - 6 + HOLOMAP_PIXEL_OFFSET_X(T.z)) * PIXEL_MULTIPLIER - cursor.pixel_y = (T.y - 6 + HOLOMAP_PIXEL_OFFSET_Y(T.z)) * PIXEL_MULTIPLIER + cursor.pixel_x = (T.x - 6 + HOLOMAP_PIXEL_OFFSET_X(T.z)) * (WORLD_ICON_SIZE / 32) + cursor.pixel_y = (T.y - 6 + HOLOMAP_PIXEL_OFFSET_Y(T.z)) * (WORLD_ICON_SIZE / 32) legend.pixel_x = HOLOMAP_LEGEND_X(T.z) legend.pixel_y = HOLOMAP_LEGEND_Y(T.z) diff --git a/code/modules/holomap/station_holomap.dm b/code/modules/holomap/station_holomap.dm index d89f025a7aef..1a68c7e39f6f 100644 --- a/code/modules/holomap/station_holomap.dm +++ b/code/modules/holomap/station_holomap.dm @@ -70,7 +70,7 @@ // floor_markings.layer = FLOOR_DECAL_LAYER update_icon() -/obj/machinery/station_map/attack_hand(mob/user, list/params) +/obj/machinery/station_map/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(watching_mob && (watching_mob != user)) to_chat(user, "Someone else is currently watching the holomap.") return diff --git a/code/modules/hydroponics/beekeeping/beehive.dm b/code/modules/hydroponics/beekeeping/beehive.dm index 5b6cd3714752..7e3e2e941fe0 100644 --- a/code/modules/hydroponics/beekeeping/beehive.dm +++ b/code/modules/hydroponics/beekeeping/beehive.dm @@ -122,7 +122,7 @@ qdel(src) return -/obj/machinery/beehive/attack_hand(mob/user, list/params) +/obj/machinery/beehive/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!closed) if(honeycombs < 100) to_chat(user, "There are no filled honeycombs.") diff --git a/code/modules/hydroponics/seed_machines.dm b/code/modules/hydroponics/seed_machines.dm index b649fc10f1f5..11f538991210 100644 --- a/code/modules/hydroponics/seed_machines.dm +++ b/code/modules/hydroponics/seed_machines.dm @@ -63,7 +63,7 @@ /obj/machinery/botany/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/botany/attack_hand(mob/user, list/params) +/obj/machinery/botany/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/botany/proc/finished_task() diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 2d8ddb5c6885..1c05181dcd54 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -206,7 +206,7 @@ /obj/item/seeds/whitebeetseed = 3 ) -/obj/machinery/seed_storage/attack_hand(mob/user, list/params) +/obj/machinery/seed_storage/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index 08d3b1d3ad03..49a80bab7d2c 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -36,7 +36,7 @@ integrity_enabled = TRUE obj_flags = OBJ_MELEE_TARGETABLE | OBJ_RANGE_TARGETABLE -/obj/effect/dead_plant/attack_hand(mob/user, list/params) +/obj/effect/dead_plant/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) qdel(src) /obj/effect/dead_plant/attackby() diff --git a/code/modules/hydroponics/spreading/spreading_response.dm b/code/modules/hydroponics/spreading/spreading_response.dm index 88657e388d31..016ae848fa3a 100644 --- a/code/modules/hydroponics/spreading/spreading_response.dm +++ b/code/modules/hydroponics/spreading/spreading_response.dm @@ -34,7 +34,7 @@ if(!M.apply_damage(base_damage, BRUTE, target_zone, blocked, soaked, used_weapon=src)) return 0 -/obj/effect/plant/attack_hand(mob/user, list/params) +/obj/effect/plant/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) manual_unbuckle(user) /obj/effect/plant/attack_generic(var/mob/user) diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index e1689c458cbf..707cf82533f7 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -201,26 +201,22 @@ check_health() update_icon() -/obj/machinery/portable_atmospherics/hydroponics/bullet_act(var/obj/projectile/Proj) - +/obj/machinery/portable_atmospherics/hydroponics/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() //Don't act on seeds like dionaea that shouldn't change. if(seed && seed.get_trait(TRAIT_IMMUTABLE) > 0) return //Override for somatoray projectiles. - if(istype(Proj ,/obj/projectile/energy/floramut)&& prob(20)) - if(istype(Proj, /obj/projectile/energy/floramut/gene)) - var/obj/projectile/energy/floramut/gene/G = Proj + if(istype(proj ,/obj/projectile/energy/floramut)&& prob(20)) + if(istype(proj, /obj/projectile/energy/floramut/gene)) + var/obj/projectile/energy/floramut/gene/G = proj if(seed) seed = seed.diverge_mutate_gene(G.gene, get_turf(loc)) //get_turf just in case it's not in a turf. else mutate(1) - return - else if(istype(Proj ,/obj/projectile/energy/florayield) && prob(20)) + else if(istype(proj ,/obj/projectile/energy/florayield) && prob(20)) yield_mod = min(10,yield_mod+rand(1,2)) - return - - ..() /obj/machinery/portable_atmospherics/hydroponics/proc/check_health() if(seed && !dead && health <= 0) @@ -580,7 +576,7 @@ else if(harvest) harvest(user) -/obj/machinery/portable_atmospherics/hydroponics/attack_hand(mob/user, list/params) +/obj/machinery/portable_atmospherics/hydroponics/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(usr,/mob/living/silicon)) return diff --git a/code/modules/industry/README.md b/code/modules/industry/README.md new file mode 100644 index 000000000000..cbfb13208edd --- /dev/null +++ b/code/modules/industry/README.md @@ -0,0 +1,9 @@ +# Industry Module + +tl;dr if it belongs in a game of factorio it belongs here + +contains: + +- disposals +- conveyors +- general package code; while this is equally belonging in cargo and here, it's used here more. diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/industry/conveyors/conveyor.dm similarity index 52% rename from code/modules/recycling/conveyor2.dm rename to code/modules/industry/conveyors/conveyor.dm index fb9373ecc1ae..d0510a97b8ac 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/industry/conveyors/conveyor.dm @@ -4,6 +4,7 @@ //conveyor2 is pretty much like the original, except it supports corners, but not diverters. //note that corner pieces transfer stuff clockwise when running forward, and anti-clockwise backwards. +// todo: main-like stacks of conveyor belts. /obj/machinery/conveyor icon = 'icons/obj/recycling.dmi' @@ -26,11 +27,6 @@ var/id = "" // the control ID - must match controller ID -/obj/machinery/conveyor/centcom_auto - id = "round_end_belt" - - - // create a conveyor /obj/machinery/conveyor/Initialize(mapload, newdir, on = 0) . = ..() @@ -52,6 +48,12 @@ component_parts += new /obj/item/stack/cable_coil(src,5) RefreshParts() +/obj/machinery/conveyor/examine(mob/user, dist) + . = ..() + // give a hint about catastrophic crowding + if(length(loc?.contents) > TURF_CROWDING_HARD_LIMIT) + . += SPAN_WARNING("There's far too many things on [src] for it to move!") + /obj/machinery/conveyor/proc/setmove() if(operating == FORWARDS) movedir = forwards @@ -97,36 +99,43 @@ AM.set_glide_size(conveyor_glide_size) icon_state = "conveyor[operating]" - // machine process - // move items to the target location +// machine process +// move items to the target location /obj/machinery/conveyor/process(delta_time) if(machine_stat & (BROKEN | NOPOWER)) return if(!operating) return use_power(10) - var/list/affecting = loc.contents - src // moved items will be all in loc - addtimer(CALLBACK(src, PROC_REF(convey), affecting), 1) - -/obj/machinery/conveyor/proc/convey(list/affecting) - var/turf/T = get_step(src, movedir) - if(!T) + // check catastrophic crowding + if(length(loc.contents) > TURF_CROWDING_HARD_LIMIT) + return + // todo: this is still kind of tick-dependent, and will result in issues + // todo: conveyors should be on their own subsystem that lets it run a collect-sweep cycle? + addtimer(CALLBACK(src, PROC_REF(convey), loc.contents.Copy()), 1) + +/** + * Conveys a list of movables. + * + * * This does filter to make sure the movables in question are still in us. + * * This is done in a separate proc so that order of operations from process() is canonical. + */ +/obj/machinery/conveyor/proc/convey(list/atom/movable/to_convey) + var/turf/target_turf = get_step(src, movedir) + if(!target_turf) return - affecting.len = max(min(affecting.len, 150 - T.contents.len), 0) - if(!affecting.len) + // limit items to soft crowding limit + to_convey.len = clamp(TURF_CROWDING_SOFT_LIMIT - length(target_turf.contents), 0, length(to_convey)) + if(!length(to_convey)) return - var/items_moved = 0 - for(var/atom/movable/A in affecting) - if(!A.anchored) - if(A.loc == src.loc) // prevents the object from being affected if it's not currently here. - step(A,movedir) - ++items_moved - if(items_moved >= 50) - break -/* - if((A.loc == loc) && A.has_gravity()) - A.ConveyorMove(movedir) -*/ + // move items + for(var/atom/movable/AM in to_convey) + // todo: movement force check? + if(AM.anchored) + continue + if(AM.loc != loc) + continue + step(AM, movedir) // attack with item, place item on conveyor /obj/machinery/conveyor/attackby(var/obj/item/I, mob/user) @@ -153,7 +162,7 @@ return ..() // attack with hand, move pulled object onto conveyor -/obj/machinery/conveyor/attack_hand(mob/user, list/params) +/obj/machinery/conveyor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) return if(isnull(user.pulling) || user.pulling.anchored) @@ -200,137 +209,3 @@ /obj/machinery/conveyor/power_change() ..() update() - -// the conveyor control switch -// -// - -/obj/machinery/conveyor_switch - - name = "conveyor switch" - desc = "A conveyor control switch." - icon = 'icons/obj/recycling.dmi' - icon_state = "switch-off" - var/position = 0 // 0 off, -1 reverse, 1 forward - var/last_pos = -1 // last direction setting - var/operated = 1 // true if just operated - - var/id = "" // must match conveyor IDs to control them - - var/list/conveyors // the list of converyors that are controlled by this switch - anchored = 1 - -/obj/machinery/conveyor_switch/two_way_on - position = 1 - - -/obj/machinery/conveyor_switch/Initialize(mapload) - . = ..() - update() - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/conveyor_switch/LateInitialize() - conveyors = list() - for(var/obj/machinery/conveyor/C in GLOB.machines) - if(C.id == id) - conveyors += C - -// update the icon depending on the position - -/obj/machinery/conveyor_switch/proc/update() - if(position<0) - icon_state = "switch-rev" - else if(position>0) - icon_state = "switch-fwd" - else - icon_state = "switch-off" - - -// timed process -// if the switch changed, update the linked conveyors - -/obj/machinery/conveyor_switch/process(delta_time) - if(!operated) - return - operated = 0 - - for(var/obj/machinery/conveyor/C in conveyors) - C.operating = position - C.setmove() - -// attack with hand, switch position -/obj/machinery/conveyor_switch/attack_hand(mob/user, list/params) - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - if(position == 0) - if(last_pos < 0) - position = 1 - last_pos = 0 - else - position = -1 - last_pos = 0 - else - last_pos = position - position = 0 - - operated = 1 - update() - - // find any switches with same id as this one, and set their positions to match us - for(var/obj/machinery/conveyor_switch/S in GLOB.machines) - if(S.id == src.id) - S.position = position - S.update() - -/obj/machinery/conveyor_switch/attackby(var/obj/item/I, mob/user) - if(default_deconstruction_screwdriver(user, I)) - return - - if(istype(I, /obj/item/weldingtool)) - if(panel_open) - var/obj/item/weldingtool/WT = I - if(!WT.remove_fuel(0, user)) - to_chat(user, "The welding tool must be on to complete this task.") - return - playsound(src, WT.tool_sound, 50, 1) - if(do_after(user, 20 * WT.tool_speed)) - if(!src || !WT.isOn()) return - to_chat(user, "You deconstruct the frame.") - new /obj/item/stack/material/steel( src.loc, 2 ) - qdel(src) - return - - if(istype(I, /obj/item/multitool)) - if(panel_open) - var/input = sanitize(input(usr, "What id would you like to give this conveyor switch?", "Multitool-Conveyor interface", id)) - if(!input) - to_chat(user, "No input found. Please hang up and try your call again.") - return - id = input - conveyors = list() // Clear list so they aren't double added. - for(var/obj/machinery/conveyor/C in GLOB.machines) - if(C.id == id) - conveyors += C - return - -/obj/machinery/conveyor_switch/oneway - var/convdir = 1 //Set to 1 or -1 depending on which way you want the convayor to go. (In other words keep at 1 and set the proper dir on the belts.) - desc = "A conveyor control switch. It appears to only go in one direction." - -// attack with hand, switch position -/obj/machinery/conveyor_switch/oneway/attack_hand(mob/user, list/params) - if(position == 0) - position = convdir - else - position = 0 - - operated = 1 - update() - - // find any switches with same id as this one, and set their positions to match us - for(var/obj/machinery/conveyor_switch/S in GLOB.machines) - if(S.id == src.id) - S.position = position - S.update() diff --git a/code/modules/industry/conveyors/conveyor_switch.dm b/code/modules/industry/conveyors/conveyor_switch.dm new file mode 100644 index 000000000000..19d3acdef2ad --- /dev/null +++ b/code/modules/industry/conveyors/conveyor_switch.dm @@ -0,0 +1,128 @@ +/obj/machinery/conveyor_switch + + name = "conveyor switch" + desc = "A conveyor control switch." + icon = 'icons/obj/recycling.dmi' + icon_state = "switch-off" + var/position = 0 // 0 off, -1 reverse, 1 forward + var/last_pos = -1 // last direction setting + var/operated = 1 // true if just operated + + var/id = "" // must match conveyor IDs to control them + + var/list/conveyors // the list of converyors that are controlled by this switch + anchored = 1 + +/obj/machinery/conveyor_switch/two_way_on + position = 1 + + +/obj/machinery/conveyor_switch/Initialize(mapload) + . = ..() + update() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/conveyor_switch/LateInitialize() + conveyors = list() + for(var/obj/machinery/conveyor/C in GLOB.machines) + if(C.id == id) + conveyors += C + +// update the icon depending on the position + +/obj/machinery/conveyor_switch/proc/update() + if(position<0) + icon_state = "switch-rev" + else if(position>0) + icon_state = "switch-fwd" + else + icon_state = "switch-off" + +// timed process +// if the switch changed, update the linked conveyors + +/obj/machinery/conveyor_switch/process(delta_time) + if(!operated) + return + operated = 0 + + for(var/obj/machinery/conveyor/C in conveyors) + C.operating = position + C.setmove() + +// attack with hand, switch position +/obj/machinery/conveyor_switch/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + if(position == 0) + if(last_pos < 0) + position = 1 + last_pos = 0 + else + position = -1 + last_pos = 0 + else + last_pos = position + position = 0 + + operated = 1 + update() + + // find any switches with same id as this one, and set their positions to match us + for(var/obj/machinery/conveyor_switch/S in GLOB.machines) + if(S.id == src.id) + S.position = position + S.update() + +/obj/machinery/conveyor_switch/attackby(var/obj/item/I, mob/user) + if(default_deconstruction_screwdriver(user, I)) + return + + if(istype(I, /obj/item/weldingtool)) + if(panel_open) + var/obj/item/weldingtool/WT = I + if(!WT.remove_fuel(0, user)) + to_chat(user, "The welding tool must be on to complete this task.") + return + playsound(src, WT.tool_sound, 50, 1) + if(do_after(user, 20 * WT.tool_speed)) + if(!src || !WT.isOn()) return + to_chat(user, "You deconstruct the frame.") + new /obj/item/stack/material/steel( src.loc, 2 ) + qdel(src) + return + + if(istype(I, /obj/item/multitool)) + if(panel_open) + var/input = sanitize(input(usr, "What id would you like to give this conveyor switch?", "Multitool-Conveyor interface", id)) + if(!input) + to_chat(user, "No input found. Please hang up and try your call again.") + return + id = input + conveyors = list() // Clear list so they aren't double added. + for(var/obj/machinery/conveyor/C in GLOB.machines) + if(C.id == id) + conveyors += C + return + +/obj/machinery/conveyor_switch/oneway + var/convdir = 1 //Set to 1 or -1 depending on which way you want the convayor to go. (In other words keep at 1 and set the proper dir on the belts.) + desc = "A conveyor control switch. It appears to only go in one direction." + +// attack with hand, switch position +/obj/machinery/conveyor_switch/oneway/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) + if(position == 0) + position = convdir + else + position = 0 + + operated = 1 + update() + + // find any switches with same id as this one, and set their positions to match us + for(var/obj/machinery/conveyor_switch/S in GLOB.machines) + if(S.id == src.id) + S.position = position + S.update() diff --git a/code/modules/industry/destination_tagger.dm b/code/modules/industry/destination_tagger.dm new file mode 100644 index 000000000000..afa4406dde4a --- /dev/null +++ b/code/modules/industry/destination_tagger.dm @@ -0,0 +1,45 @@ +/obj/item/destTagger + name = "destination tagger" + desc = "Used to set the destination of properly wrapped packages." + icon = 'icons/obj/device.dmi' + icon_state = "dest_tagger" + var/currTag = 0 + + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + slot_flags = SLOT_BELT + +/obj/item/destTagger/ui_state() + return GLOB.inventory_state + +/obj/item/destTagger/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "DestinationTagger", name) + ui.open() + +/obj/item/destTagger/ui_data(mob/user, datum/tgui/ui) + var/list/data = ..() + + data["currTag"] = currTag + data["taggerLocs"] = GLOB.tagger_locations + + return data + +/obj/item/destTagger/attack_self(mob/user) + . = ..() + if(.) + return + ui_interact(user) + +/obj/item/destTagger/ui_act(action, list/params, datum/tgui/ui) + if(..()) + return TRUE + add_fingerprint(usr) + switch(action) + if("set_tag") + var/new_tag = params["tag"] + if(!(new_tag in GLOB.tagger_locations)) + return FALSE + currTag = new_tag + . = TRUE diff --git a/code/modules/industry/disposals/disposal.dm b/code/modules/industry/disposals/disposal.dm new file mode 100644 index 000000000000..d449a288ed28 --- /dev/null +++ b/code/modules/industry/disposals/disposal.dm @@ -0,0 +1,28 @@ +// todo: /machinery/disposal that trunks connect to + +// called when movable is expelled from a disposal pipe or outlet +// by default does nothing, override for special behaviour +/atom/movable/proc/pipe_eject(direction) + return + +// check if mob has client, if so restore client view on eject +/mob/pipe_eject(direction) + update_perspective() + +/obj/effect/debris/cleanable/blood/gibs/pipe_eject(direction) + var/list/dirs + if(direction) + dirs = list( direction, turn(direction, -45), turn(direction, 45)) + else + dirs = GLOB.alldirs.Copy() + + src.streak(dirs) + +/obj/effect/debris/cleanable/blood/gibs/robot/pipe_eject(direction) + var/list/dirs + if(direction) + dirs = list( direction, turn(direction, -45), turn(direction, 45)) + else + dirs = GLOB.alldirs.Copy() + + src.streak(dirs) diff --git a/code/modules/industry/disposals/disposal/chute.dm b/code/modules/industry/disposals/disposal/chute.dm new file mode 100644 index 000000000000..7fd8681bffb5 --- /dev/null +++ b/code/modules/industry/disposals/disposal/chute.dm @@ -0,0 +1,513 @@ +// Disposal bin +// Holds items for disposal into pipe system +// Draws air from turf, gradually charges internal reservoir +// Once full (~1 atm), uses air resv to flush items into the pipes +// Automatically recharges air (unless off), will flush when ready if pre-set +// Can hold items and human size things, no other draggables +// Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation +///kPa - assume the inside of a dispoal pipe is 1 atm, so that needs to be added. +#define SEND_PRESSURE (700 + ONE_ATMOSPHERE) +///L +#define PRESSURE_TANK_VOLUME 150 +///L/s - 4 m/s using a 15 cm by 15 cm inlet +#define PUMP_MAX_FLOW_RATE 90 +// todo: /obj/machinery/disposal/chute +/obj/machinery/disposal + name = "disposal unit" + desc = "A pneumatic waste disposal unit." + icon = 'icons/obj/pipes/disposal.dmi' + icon_state = "disposal" + atom_colouration_system = FALSE + anchored = TRUE + density = TRUE + pass_flags_self = ATOM_PASS_OVERHEAD_THROW + var/datum/gas_mixture/air_contents // internal reservoir + var/mode = 1 // item mode 0=off 1=charging 2=charged + var/flush = FALSE // true if flush handle is pulled + var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk + var/flushing = FALSE // true if flushing in progress + var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush + var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush. + var/last_sound = 0 + active_power_usage = 2200 //the pneumatic pump power. 3 HP ~ 2200W + idle_power_usage = 100 + +// create a new disposal +// find the attached trunk (if present) and init gas resvr. +/obj/machinery/disposal/Initialize(mapload, newdir) + . = ..() + return INITIALIZE_HINT_LATELOAD + +/obj/machinery/disposal/LateInitialize() + . = ..() + trunk = locate() in src.loc + if(!trunk) + mode = 0 + flush = FALSE + else + trunk.linked = src // link the pipe trunk to self + + air_contents = new/datum/gas_mixture(PRESSURE_TANK_VOLUME) + update() + +/obj/machinery/disposal/Destroy() + eject() + if(trunk) + trunk.linked = null + return ..() + +// attack by item places it in to disposal +/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user) + if(user.a_intent != INTENT_HELP) + return ..() + + . = CLICKCHAIN_DO_NOT_PROPAGATE + + if(machine_stat & BROKEN || !I || !user) + return + + add_fingerprint(user, 0, I) + if(mode<=0) // It's off + if(I.is_screwdriver()) + if(contents.len > 0) + to_chat(user, "Eject the items first!") + return + if(mode==0) // It's off but still not unscrewed + mode=-1 // Set it to doubleoff l0l + playsound(src, I.tool_sound, 50, 1) + to_chat(user, "You remove the screws around the power connection.") + return + else if(mode==-1) + mode=0 + playsound(src, I.tool_sound, 50, 1) + to_chat(user, "You attach the screws around the power connection.") + return + else if(istype(I, /obj/item/weldingtool) && mode==-1) + if(contents.len > 0) + to_chat(user, "Eject the items first!") + return + var/obj/item/weldingtool/W = I + if(W.remove_fuel(0,user)) + playsound(src, W.tool_sound, 100, 1) + to_chat(user, "You start slicing the floorweld off the disposal unit.") + + if(do_after(user,20 * W.tool_speed)) + if(!src || !W.isOn()) return + to_chat(user, "You sliced the floorweld off the disposal unit.") + var/obj/structure/disposalconstruct/C = new (src.loc) + src.transfer_fingerprints_to(C) + C.ptype = 6 // 6 = disposal unit + C.anchored = 1 + C.density = 1 + C.update() + qdel(src) + return + else + to_chat(user, "You need more welding fuel to complete this task.") + return + + if(istype(I, /obj/item/storage/bag/trash)) + var/obj/item/storage/bag/trash/T = I + to_chat(user, "You empty the bag.") + for(var/obj/item/O in T.contents) + T.obj_storage.remove(O, src) + T.update_icon() + update() + return + + if(istype(I, /obj/item/material/ashtray)) + var/obj/item/material/ashtray/A = I + if(A.contents.len > 0) + user.visible_message("\The [user] empties \the [A.name] into [src].") + for(var/obj/item/O in A.contents) + O.forceMove(src) + A.update_icon() + update() + return + + var/obj/item/grab/G = I + if(istype(G)) // handle grabbed mob + if(ismob(G.affecting)) + var/mob/GM = G.affecting + for (var/mob/V in viewers(usr)) + V.show_message("[usr] starts putting [GM.name] into the disposal.", 3) + if(do_after(usr, 20)) + GM.forceMove(src) + GM.update_perspective() + for (var/mob/C in viewers(src)) + C.show_message("[GM.name] has been placed in \the [src] by [user].", 3) + qdel(G) + + add_attack_logs(user,GM,"Disposals dunked") + return + + if(!user.attempt_insert_item_for_installation(I, src)) + return + + to_chat(user, "You place \the [I] into \the [src].") + for(var/mob/M in viewers(src)) + if(M == user) + continue + M.show_message("[user.name] places \the [I] into \the [src].", 3) + + update() + +// mouse drop another mob or self +// +/obj/machinery/disposal/MouseDroppedOnLegacy(mob/target, mob/user) + if(!CHECK_MOBILITY(user, MOBILITY_CAN_USE) || !istype(target)) + return + if(target.buckled || get_dist(user, src) > 1 || get_dist(user, target) > 1) + return + + //animals cannot put mobs other than themselves into disposal + if(isanimal(user) && target != user) + return + + src.add_fingerprint(user) + var/target_loc = target.loc + var/msg + for (var/mob/V in viewers(usr)) + if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) + V.show_message("[usr] starts climbing into the disposal.", 3) + if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) + if(target.anchored) return + V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) + if(!do_after(usr, 20)) + return + if(target_loc != target.loc) + return + if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) // if drop self, then climbed in + // must be awake, not stunned or whatever + msg = "[user.name] climbs into \the [src]." + to_chat(user, "You climb into \the [src].") + else if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) + msg = "[user.name] stuffs [target.name] into \the [src]!" + to_chat(user, "You stuff [target.name] into \the [src]!") + + add_attack_logs(user,target,"Disposals dunked") + else + return + target.forceMove(src) + target.update_perspective() + + for (var/mob/C in viewers(src)) + if(C == user) + continue + C.show_message(msg, 3) + + update() + return + +// attempt to move while inside +/obj/machinery/disposal/relaymove(mob/user as mob) + if(user.stat || src.flushing) + return + if(user.loc == src) + src.go_out(user) + return + +// leave the disposal +/obj/machinery/disposal/proc/go_out(mob/user) + user.forceMove(loc) + user.update_perspective() + update() + return + +// ai as human but can't flush +/obj/machinery/disposal/attack_ai(mob/user as mob) + interact(user, 1) + +// human interact with machine +/obj/machinery/disposal/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) + + if(machine_stat & BROKEN) + return + + if(user && user.loc == src) + to_chat(user, "You cannot reach the controls from inside.") + return + + // Clumsy folks can only flush it. + if(user.IsAdvancedToolUser(1)) + interact(user, 0) + else + flush = !flush + update() + return + +// user interaction +/obj/machinery/disposal/interact(mob/user, var/ai=0) + + src.add_fingerprint(user) + if(machine_stat & BROKEN) + user.unset_machine() + return + + var/dat = "Waste Disposal UnitWaste Disposal Unit
" + + if(!ai) // AI can't pull flush handle + if(flush) + dat += "Disposal handle: Disengage Engaged" + else + dat += "Disposal handle: Disengaged Engage" + + dat += "

Eject contents
" + + if(mode <= 0) + dat += "Pump: Off On
" + else if(mode == 1) + dat += "Pump: Off On (pressurizing)
" + else + dat += "Pump: Off On (idle)
" + + var/per = 100* air_contents.return_pressure() / (SEND_PRESSURE) + + dat += "Pressure: [round(per, 1)]%
" + + + user.set_machine(src) + user << browse(dat, "window=disposal;size=360x170") + onclose(user, "disposal") + +// handle machine interaction + +/obj/machinery/disposal/Topic(href, href_list) + if(usr.loc == src) + to_chat(usr, "You cannot reach the controls from inside.") + return + + if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 + to_chat(usr, "The disposal units power is disabled.") + return + if(..()) + return + + if(machine_stat & BROKEN) + return + if(usr.stat || usr.restrained() || src.flushing) + return + + if(istype(src.loc, /turf)) + usr.set_machine(src) + + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=disposal") + return + + if(href_list["pump"]) + if(text2num(href_list["pump"])) + mode = 1 + else + mode = 0 + update() + + if(!isAI(usr)) + if(href_list["handle"]) + flush = text2num(href_list["handle"]) + update() + + if(href_list["eject"]) + eject() + else + usr << browse(null, "window=disposal") + usr.unset_machine() + return + return + +// eject the contents of the disposal unit +/obj/machinery/disposal/proc/eject() + for(var/atom/movable/AM in src) + AM.forceMove(src.loc) + AM.pipe_eject(0) + update() + +// update the icon & overlays to reflect mode & status +/obj/machinery/disposal/proc/update() + cut_overlays() + if(machine_stat & BROKEN) + icon_state = "disposal-broken" + mode = 0 + flush = 0 + return + + var/list/overlays_to_add = list() + + // flush handle + if(flush) + overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-handle") + + // only handle is shown if no power + if(machine_stat & NOPOWER || mode == -1) + add_overlay(overlays_to_add) + return + + // check for items in disposal - occupied light + if(contents.len > 0) + overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-full") + + // charging and ready light + if(mode == 1) + overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-charge") + else if(mode == 2) + overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-ready") + + add_overlay(overlays_to_add) + +// timed process +// charge the gas reservoir and perform flush if ready +/obj/machinery/disposal/process(delta_time) + if(!air_contents || (machine_stat & BROKEN)) // nothing can happen if broken + update_use_power(USE_POWER_OFF) + return + + flush_count++ + if( flush_count >= flush_every_ticks ) + if( contents.len ) + if(mode == 2) + spawn(0) + feedback_inc("disposal_auto_flush",1) + flush() + flush_count = 0 + + src.updateDialog() + + if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power + flush() + + if(mode != 1) //if off or ready, no need to charge + update_use_power(USE_POWER_IDLE) + else if(air_contents.return_pressure() >= SEND_PRESSURE) + mode = 2 //if full enough, switch to ready mode + update() + else + src.pressurize() //otherwise charge + +/obj/machinery/disposal/proc/pressurize() + if(machine_stat & NOPOWER) // won't charge if no power + update_use_power(USE_POWER_OFF) + return + + var/atom/L = loc // recharging from loc turf + var/datum/gas_mixture/env = L.return_air() + + var/power_draw = -1 + if(env && env.temperature > 0) + var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.volume)*env.total_moles //group_multiplier is divided out here + power_draw = pump_gas(src, env, air_contents, transfer_moles, active_power_usage) + + if (power_draw > 0) + use_power(power_draw) + +// perform a flush +/obj/machinery/disposal/proc/flush() + + flushing = 1 + flick("[icon_state]-flush", src) + + var/wrapcheck = 0 + var/obj/structure/disposalholder/H = new() // virtual holder object which actually + // travels through the pipes. + //Hacky test to get drones to mail themselves through disposals. + for(var/mob/living/silicon/robot/drone/D in src) + wrapcheck = 1 + + for(var/obj/item/smallDelivery/O in src) + wrapcheck = 1 + + if(wrapcheck == 1) + H.tomail = 1 + + + sleep(10) + if(last_sound < world.time + 1) + playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) + last_sound = world.time + sleep(5) // wait for animation to finish + + + H.init(src, air_contents) // copy the contents of disposer to holder + air_contents = new(PRESSURE_TANK_VOLUME) // new empty gas resv. + + H.start(src) // start the holder processing movement + flushing = 0 + // now reset disposal state + flush = 0 + if(mode == 2) // if was ready, + mode = 1 // switch to charging + update() + return + + +// called when area power changes +/obj/machinery/disposal/power_change() + ..() // do default setting/reset of stat NOPOWER bit + update() // update icon + return + + +/// Called when holder is expelled from a disposal. +/// Should usually only occur if the pipe network is modified. +/obj/machinery/disposal/proc/expel(obj/structure/disposalholder/H) + + var/turf/target + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it + for(var/atom/movable/AM in H) + target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5)) + + AM.forceMove(src.loc) + AM.pipe_eject(0) + if(!istype(AM,/mob/living/silicon/robot/drone)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z + spawn(1) + if(AM) + AM.throw_at_old(target, 5, 1) + + H.vent_gas(loc) + qdel(H) + +/obj/machinery/disposal/throw_impacted(atom/movable/AM, datum/thrownthing/TT) + if(istype(AM, /obj/item) && !istype(AM, /obj/projectile)) + if(prob(75)) + AM.forceMove(src) + visible_message("\The [AM] lands in \the [src].") + return COMPONENT_THROW_HIT_TERMINATE + else + visible_message("\The [AM] bounces off of \the [src]'s rim!") + return ..() + return ..() + +/obj/machinery/disposal/CanAllowThrough(atom/movable/mover, turf/target) + if(istype(mover, /obj/projectile)) + return TRUE + return ..() + +/obj/machinery/disposal/wall + name = "inset disposal unit" + icon_state = "wall" + + density = FALSE + +/obj/machinery/disposal/wall/Initialize() + . = ..() + + spawn(1 SECOND) // Fixfix for weird interaction with buildmode or other late-spawning. + update() + +/obj/machinery/disposal/wall/update() + ..() + + switch(dir) + if(1) + pixel_x = 0 + pixel_y = -32 + if(2) + pixel_x = 0 + pixel_y = 32 + if(4) + pixel_x = -32 + pixel_y = 0 + if(8) + pixel_x = 32 + pixel_y = 0 diff --git a/code/modules/industry/disposals/disposal/intake.dm b/code/modules/industry/disposals/disposal/intake.dm new file mode 100644 index 000000000000..6b1ba1c81720 --- /dev/null +++ b/code/modules/industry/disposals/disposal/intake.dm @@ -0,0 +1,105 @@ + +// todo: /obj/machinery/disposal/chute/intake +/obj/machinery/disposal/deliveryChute + name = "Delivery chute" + desc = "A chute for big and small packages alike!" + density = 1 + icon_state = "intake" + + var/c_mode = 0 + +/obj/machinery/disposal/deliveryChute/Initialize(mapload, newdir) + . = ..() + spawn(5) + trunk = locate() in src.loc + if(trunk) + trunk.linked = src // link the pipe trunk to self + +/obj/machinery/disposal/deliveryChute/interact() + return + +/obj/machinery/disposal/deliveryChute/update() + return + +/obj/machinery/disposal/deliveryChute/Bumped(var/atom/movable/AM) //Go straight into the chute + if(istype(AM, /obj/projectile) || istype(AM, /obj/effect) || istype(AM, /obj/vehicle/sealed/mecha)) return + switch(dir) + if(NORTH) + if(AM.loc.y != src.loc.y+1) return + if(EAST) + if(AM.loc.x != src.loc.x+1) return + if(SOUTH) + if(AM.loc.y != src.loc.y-1) return + if(WEST) + if(AM.loc.x != src.loc.x-1) return + +// if(istype(AM, has_buckled_mobs()) return // I dont know what im doing @ktoma36 + + if(istype(AM, /obj)) + var/obj/O = AM + O.loc = src + else if(istype(AM, /mob)) + var/mob/M = AM + M.loc = src + src.flush() + +/obj/machinery/disposal/deliveryChute/flush() + flushing = 1 + flick("intake-closing", src) + var/obj/structure/disposalholder/H = new() // virtual holder object which actually + // travels through the pipes. + air_contents = new() // new empty gas resv. + + sleep(10) + playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) + sleep(5) // wait for animation to finish + + H.init(src) // copy the contents of disposer to holder + + H.start(src) // start the holder processing movement + flushing = 0 + // now reset disposal state + flush = 0 + if(mode == 2) // if was ready, + mode = 1 // switch to charging + update() + return + +/obj/machinery/disposal/deliveryChute/attackby(var/obj/item/I, var/mob/user) + if(!I || !user) + return + + if(I.is_screwdriver()) + if(c_mode==0) + c_mode=1 + playsound(src.loc, I.tool_sound, 50, 1) + to_chat(user, "You remove the screws around the power connection.") + return + else if(c_mode==1) + c_mode=0 + playsound(src.loc, I.tool_sound, 50, 1) + to_chat(user, "You attach the screws around the power connection.") + return + else if(istype(I, /obj/item/weldingtool) && c_mode==1) + var/obj/item/weldingtool/W = I + if(W.remove_fuel(0,user)) + playsound(src.loc, W.tool_sound, 50, 1) + to_chat(user, "You start slicing the floorweld off the delivery chute.") + if(do_after(user,20 * W.tool_speed)) + if(!src || !W.isOn()) return + to_chat(user, "You sliced the floorweld off the delivery chute.") + var/obj/structure/disposalconstruct/C = new (src.loc) + C.ptype = 8 // 8 = Delivery chute + C.update() + C.anchored = 1 + C.density = 1 + qdel(src) + return + else + to_chat(user, "You need more welding fuel to complete this task.") + return + +/obj/machinery/disposal/deliveryChute/Destroy() + if(trunk) + trunk.linked = null + ..() diff --git a/code/modules/industry/disposals/disposal/outlet.dm b/code/modules/industry/disposals/disposal/outlet.dm new file mode 100644 index 000000000000..f1a389f60ea8 --- /dev/null +++ b/code/modules/industry/disposals/disposal/outlet.dm @@ -0,0 +1,74 @@ +// the disposal outlet machine +// todo: /obj/machinery/disposal/outlet + +/obj/structure/disposaloutlet + name = "disposal outlet" + desc = "An outlet for the pneumatic disposal system." + icon = 'icons/obj/pipes/disposal.dmi' + icon_state = "outlet" + density = 1 + anchored = 1 + var/active = 0 + var/turf/target // this will be where the output objects are 'thrown' to. + var/mode = 0 + +/obj/structure/disposaloutlet/LateInitialize() + target = get_ranged_target_turf(src, dir, 10) + var/obj/structure/disposalpipe/trunk/trunk = locate() in loc + if(trunk) + trunk.linked = src // link the pipe trunk to self + +// expel the contents of the holder object, then delete it +// called when the holder exits the outlet +/obj/structure/disposaloutlet/proc/expel(obj/structure/disposalholder/H) + + target = get_ranged_target_turf(src, dir, 10) + flick("outlet-open", src) + playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0) + sleep(20) //wait until correct animation frame + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + + if(H) + for(var/atom/movable/AM in H) + AM.forceMove(src.loc) + AM.pipe_eject(dir) + if(!istype(AM,/mob/living/silicon/robot/drone)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z + spawn(5) + AM.throw_at_old(target, 3, 1) + H.vent_gas(src.loc) + qdel(H) + +/obj/structure/disposaloutlet/attackby(obj/item/I, mob/user) + if(!I || !user) + return + src.add_fingerprint(user, 0, I) + if(I.is_screwdriver()) + if(mode==0) + mode=1 + to_chat(user, "You remove the screws around the power connection.") + playsound(src, I.tool_sound, 50, 1) + return + else if(mode==1) + mode=0 + to_chat(user, "You attach the screws around the power connection.") + playsound(src, I.tool_sound, 50, 1) + return + else if(istype(I, /obj/item/weldingtool) && mode==1) + var/obj/item/weldingtool/W = I + if(W.remove_fuel(0,user)) + playsound(src, W.tool_sound, 100, 1) + to_chat(user, "You start slicing the floorweld off the disposal outlet.") + if(do_after(user,20 * W.tool_speed)) + if(!src || !W.isOn()) return + to_chat(user, "You sliced the floorweld off the disposal outlet.") + var/obj/structure/disposalconstruct/C = new (src.loc) + src.transfer_fingerprints_to(C) + C.ptype = 7 // 7 = outlet + C.update() + C.anchored = 1 + C.density = 1 + qdel(src) + return + else + to_chat(user, "You need more welding fuel to complete this task.") + return diff --git a/code/modules/recycling/disposal-construction.dm b/code/modules/industry/disposals/disposal_frame.dm similarity index 100% rename from code/modules/recycling/disposal-construction.dm rename to code/modules/industry/disposals/disposal_frame.dm diff --git a/code/modules/industry/disposals/disposal_holder.dm b/code/modules/industry/disposals/disposal_holder.dm new file mode 100644 index 000000000000..a89da28a3d27 --- /dev/null +++ b/code/modules/industry/disposals/disposal_holder.dm @@ -0,0 +1,150 @@ +// virtual disposal object +// travels through pipes in lieu of actual items +// contents will be items flushed by the disposal +// this allows the gas flushed to be tracked + +// todo: /atom/movable/disposal_holder +/obj/structure/disposalholder + invisibility = 101 + var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point + var/active = 0 // true if the holder is moving, otherwise inactive + dir = 0 + var/count = 2048 //*** can travel 2048 steps before going inactive (in case of loops) + var/destinationTag = "" // changes if contains a delivery container + var/tomail = 0 //changes if contains wrapped package + var/hasmob = 0 //If it contains a mob + + var/partialTag = "" //set by a partial tagger the first time round, then put in destinationTag if it goes through again. + +/obj/structure/disposalholder/Destroy() + QDEL_NULL(gas) + active = 0 + return ..() + +// initialize a holder from the contents of a disposal unit +/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/D, var/datum/gas_mixture/flush_gas) + gas = flush_gas// transfer gas resv. into holder object -- let's be explicit about the data this proc consumes, please. + + //Check for any living mobs trigger hasmob. + //hasmob effects whether the package goes to cargo or its tagged destination. + for(var/mob/living/M in D) + if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone)) + hasmob = 1 + + //Checks 1 contents level deep. This means that players can be sent through disposals... + //...but it should require a second person to open the package. (i.e. person inside a wrapped locker) + for(var/obj/O in D) + if(O.contents) + for(var/mob/living/M in O.contents) + if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone)) + hasmob = 1 + + // now everything inside the disposal gets put into the holder + // note AM since can contain mobs or objs + for(var/atom/movable/AM in D) + AM.forceMove(src) + if(istype(AM, /obj/structure/bigDelivery) && !hasmob) + var/obj/structure/bigDelivery/T = AM + src.destinationTag = T.sortTag + if(istype(AM, /obj/item/smallDelivery) && !hasmob) + var/obj/item/smallDelivery/T = AM + src.destinationTag = T.sortTag + //Drones can mail themselves through maint. + if(istype(AM, /mob/living/silicon/robot/drone)) + var/mob/living/silicon/robot/drone/drone = AM + src.destinationTag = drone.mail_destination + +// start the movement process +// argument is the disposal unit the holder started in +/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/D) + if(!D.trunk) + D.expel(src) // no trunk connected, so expel immediately + return + + forceMove(D.trunk) + active = 1 + setDir(DOWN) + spawn(1) + move() // spawn off the movement process + +// movement process, persists while holder is moving through pipes +/obj/structure/disposalholder/proc/move() + var/obj/structure/disposalpipe/last + // todo: while this is fucking awful? + while(active) + sleep(1) // was 1 + if(!loc) return // check if we got GC'd + + if(hasmob && prob(3)) + for(var/mob/living/H in src) + if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system, + H.take_overall_damage(20, 0, weapon_descriptor = "blunt trauma")//horribly maim any living creature jumping down disposals. c'est la vie + + var/obj/structure/disposalpipe/curr = loc + last = curr + curr = curr.transfer(src) + + if(!loc) return //side effects + + if(!curr) + last.expel(src, loc, dir) + + // + if(!(count--)) + active = 0 + +// find the turf which should contain the next pipe +/obj/structure/disposalholder/proc/nextloc() + return get_step(loc,dir) + +// find a matching pipe on a turf +/obj/structure/disposalholder/proc/findpipe(var/turf/T) + + if(!T) + return null + + var/fdir = turn(dir, 180) // flip the movement direction + for(var/obj/structure/disposalpipe/P in T) + if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir + return P + // if no matching pipe, return null + return null + +// merge two holder objects +// used when a a holder meets a stuck holder +/obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other) + for(var/atom/movable/AM in other) + AM.forceMove(src) // move everything in other holder to this one + if(ismob(AM)) + var/mob/M = AM + M.update_perspective() + qdel(other) + +/obj/structure/disposalholder/proc/settag(var/new_tag) + destinationTag = new_tag + +/obj/structure/disposalholder/proc/setpartialtag(var/new_tag) + if(partialTag == new_tag) + destinationTag = new_tag + partialTag = "" + else + partialTag = new_tag + +// called when player tries to move while in a pipe +/obj/structure/disposalholder/relaymove(mob/user as mob) + if(!istype(user,/mob/living)) + return + + var/mob/living/U = user + if (U.stat || U.last_special <= world.time) + return + U.last_special = world.time+100 + + if(loc) + for (var/mob/M in hearers(src.loc.loc)) + to_chat(M, "CLONG, clong!") + playsound(src, 'sound/effects/clang.ogg', 50, 0, 0) + +// called to vent all gas in holder to a location +/obj/structure/disposalholder/proc/vent_gas(var/atom/location) + location.assume_air(gas) // vent all gas to turf diff --git a/code/modules/industry/disposals/disposal_pipe.dm b/code/modules/industry/disposals/disposal_pipe.dm new file mode 100644 index 000000000000..ec16874fdf64 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe.dm @@ -0,0 +1,265 @@ +// Disposal pipes +/// todo: /obj/structure/disposal_pipe +/obj/structure/disposalpipe + icon = 'icons/obj/pipes/disposal.dmi' + name = "disposal pipe" + desc = "An underfloor disposal pipe." + anchored = 1 + density = 0 + hides_underfloor = OBJ_UNDERFLOOR_ALWAYS + dir = 0 // dir will contain dominant direction for junction pipes + plane = TURF_PLANE + layer = DISPOSAL_LAYER // slightly lower than wires and other pipes. + + integrity = 100 + integrity_max = 100 + + #ifdef IN_MAP_EDITOR // Display disposal pipes etc. above walls in map editors. + alpha = 128 // Set for the benefit of mapping. + #endif + + /// Bitmask of pipe directions. + var/dpdir = 0 + var/sortType = "" + var/subtype = 0 + // new pipe, set the icon_state as on map + +/obj/structure/disposalpipe/Initialize(mapload, dir) + . = ..() + base_icon_state = icon_state + if(!isnull(dir)) + setDir(dir) + +// pipe is deleted +// ensure if holder is present, it is expelled +/obj/structure/disposalpipe/Destroy() + var/obj/structure/disposalholder/H = locate() in src + if(H) + // holder was present + H.active = 0 + var/turf/T = src.loc + if(T.density) + // deleting pipe is inside a dense turf (wall) + // this is unlikely, but just dump out everything into the turf in case + + for(var/atom/movable/AM in H) + AM.forceMove(T) + AM.pipe_eject(0) + qdel(H) + ..() + return + + // otherwise, do normal expel from turf + if(H) + expel(H, T, 0) + ..() + +// returns the direction of the next pipe object, given the entrance dir +// by default, returns the bitmask of remaining directions +/obj/structure/disposalpipe/proc/nextdir(var/fromdir) + return dpdir & (~turn(fromdir, 180)) + +// transfer the holder through this pipe segment +// overriden for special behaviour +// +/obj/structure/disposalpipe/proc/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir) + H.setDir(nextdir) + var/turf/T = H.nextloc() + var/obj/structure/disposalpipe/P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.forceMove(P) + else // if wasn't a pipe, then set loc to turf + H.forceMove(T) + return null + + return P + +// update actual icon_state depending on visibility +// if invisible, append "f" to icon_state to show faded version +// this will be revealed if a T-scanner is used +// if visible, use regular icon_state +/obj/structure/disposalpipe/proc/updateicon() + icon_state = base_icon_state + +// expel the held objects into a turf +// called when there is a break in the pipe +/obj/structure/disposalpipe/proc/expel(var/obj/structure/disposalholder/H, var/turf/T, var/direction) + if(!istype(H)) + return + + // Empty the holder if it is expelled into a dense turf. + // Leaving it intact and sitting in a wall is stupid. + if(T.density) + for(var/atom/movable/AM in H) + AM.loc = T + AM.pipe_eject(0) + qdel(H) + return + + if(!T.is_plating() && istype(T,/turf/simulated/floor)) //intact floor, pop the tile + var/turf/simulated/floor/F = T + F.break_tile() + new /obj/item/stack/tile(H) // add to holder so it will be thrown with other stuff + + var/turf/target + if(direction) // direction is specified + if(istype(T, /turf/space)) // if ended in space, then range is unlimited + target = get_edge_target_turf(T, direction) + else // otherwise limit to 10 tiles + target = get_ranged_target_turf(T, direction, 10) + + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) + for(var/atom/movable/AM in H) + AM.forceMove(T) + AM.pipe_eject(direction) + spawn(1) + if(AM) + AM.throw_at_old(target, 100, 1) + H.vent_gas(T) + qdel(H) + + else // no specified direction, so throw in random direction + + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) + for(var/atom/movable/AM in H) + target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5)) + + AM.forceMove(T) + AM.pipe_eject(0) + spawn(1) + if(AM) + AM.throw_at_old(target, 5, 1) + + H.vent_gas(T) // all gas vent to turf + qdel(H) + +/obj/structure/disposalpipe/deconstructed(method) + . = ..() + src.invisibility = 101 // make invisible (since we won't delete the pipe immediately) + var/obj/structure/disposalholder/H = locate() in src + if(H) + // holder was present + H.active = 0 + var/turf/T = src.loc + if(T.density) + // broken pipe is inside a dense turf (wall) + // this is unlikely, but just dump out everything into the turf in case + + for(var/atom/movable/AM in H) + AM.forceMove(T) + AM.pipe_eject(0) + qdel(H) + return + + // otherwise, do normal expel from turf + if(H) + expel(H, T, 0) + return ..() + +/obj/structure/disposalpipe/drop_products(method, atom/where) + . = ..() + if(method != ATOM_DECONSTRUCT_DISASSEMBLED) + new /obj/structure/disposalpipe/broken(where, dir) + +//attack by item +//weldingtool: unfasten and convert to obj/disposalconstruct + +/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user) + + var/turf/T = src.loc + if(!T.is_plating()) + return // prevent interaction with T-scanner revealed pipes + src.add_fingerprint(user, 0, I) + if(istype(I, /obj/item/weldingtool)) + var/obj/item/weldingtool/W = I + + if(W.remove_fuel(0,user)) + playsound(src, W.tool_sound, 50, 1) + // check if anything changed over 2 seconds + var/turf/uloc = user.loc + var/atom/wloc = W.loc + to_chat(user, "Slicing the disposal pipe.") + sleep(30) + if(!W.isOn()) return + if(user.loc == uloc && wloc == W.loc) + welded() + else + to_chat(user, "You must stay still while welding the pipe.") + else + to_chat(user, "You need more welding fuel to cut the pipe.") + return + +// called when pipe is cut with welder +/obj/structure/disposalpipe/proc/welded() + + var/obj/structure/disposalconstruct/C = new (src.loc) + switch(base_icon_state) + if("pipe-s") + C.ptype = 0 + if("pipe-c") + C.ptype = 1 + if("pipe-j1") + C.ptype = 2 + if("pipe-j2") + C.ptype = 3 + if("pipe-y") + C.ptype = 4 + if("pipe-t") + C.ptype = 5 + if("pipe-j1s") + C.ptype = 9 + C.sortType = sortType + if("pipe-j2s") + C.ptype = 10 + C.sortType = sortType +///// Z-Level stuff + if("pipe-u") + C.ptype = 11 + if("pipe-d") + C.ptype = 12 +///// Z-Level stuff + if("pipe-tagger") + C.ptype = 13 + if("pipe-tagger-partial") + C.ptype = 14 + C.subtype = src.subtype + src.transfer_fingerprints_to(C) + C.setDir(dir) + C.density = 0 + C.anchored = 1 + C.update() + + qdel(src) + +// pipe is deleted +// ensure if holder is present, it is expelled +/obj/structure/disposalpipe/Destroy() + var/obj/structure/disposalholder/H = locate() in src + if(H) + // holder was present + H.active = 0 + var/turf/T = src.loc + if(T.density) + // deleting pipe is inside a dense turf (wall) + // this is unlikely, but just dump out everything into the turf in case + + for(var/atom/movable/AM in H) + AM.forceMove(T) + AM.pipe_eject(0) + qdel(H) + ..() + return + + // otherwise, do normal expel from turf + if(H) + expel(H, T, 0) + ..() diff --git a/code/modules/industry/disposals/disposal_pipe/broken.dm b/code/modules/industry/disposals/disposal_pipe/broken.dm new file mode 100644 index 000000000000..2914d4da38e4 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/broken.dm @@ -0,0 +1,13 @@ +// a broken pipe +/obj/structure/disposalpipe/broken + icon_state = "pipe-b" + dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes + // i.e. will be treated as an empty turf + desc = "A broken piece of disposal pipe." + +// called when welded +// for broken pipe, remove and turn into scrap +/obj/structure/disposalpipe/broken/welded() +// var/obj/item/scrap/S = new(src.loc) +// S.set_components(200,0,0) + qdel(src) diff --git a/code/modules/industry/disposals/disposal_pipe/down.dm b/code/modules/industry/disposals/disposal_pipe/down.dm new file mode 100644 index 000000000000..5bc47b24e8a1 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/down.dm @@ -0,0 +1,48 @@ +/obj/structure/disposalpipe/down + icon_state = "pipe-d" + +/obj/structure/disposalpipe/down/New() + ..() + dpdir = dir + return + +/obj/structure/disposalpipe/down/nextdir(fromdir) + var/nextdir + if(fromdir == 12) + nextdir = dir + else + nextdir = 11 + return nextdir + +/obj/structure/disposalpipe/down/transfer(obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir) + H.dir = nextdir + + var/turf/T + var/obj/structure/disposalpipe/P + + if(nextdir == 11) + T = get_vertical_step(src, DOWN) + if(!T) + H.forceMove(src.loc) + return + else + for(var/obj/structure/disposalpipe/up/F in T) + P = F + + else + T = get_step(src.loc, H.dir) + P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.forceMove(P) + else // if wasn't a pipe, then set loc to turf + H.forceMove(T) + return null + + return P diff --git a/code/modules/industry/disposals/disposal_pipe/junction.dm b/code/modules/industry/disposals/disposal_pipe/junction.dm new file mode 100644 index 000000000000..0d1937f21107 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/junction.dm @@ -0,0 +1,45 @@ +/obj/structure/disposalpipe/junction/yjunction + icon_state = "pipe-y" + +//a three-way junction with dir being the dominant direction +/obj/structure/disposalpipe/junction + icon_state = "pipe-j1" + +/obj/structure/disposalpipe/junction/New() + ..() + if(icon_state == "pipe-j1") + dpdir = dir | turn(dir, -90) | turn(dir,180) + else if(icon_state == "pipe-j2") + dpdir = dir | turn(dir, 90) | turn(dir,180) + else // pipe-y + dpdir = dir | turn(dir,90) | turn(dir, -90) + +// next direction to move +// if coming in from secondary dirs, then next is primary dir +// if coming in from primary dir, then next is equal chance of other dirs +/obj/structure/disposalpipe/junction/nextdir(var/fromdir) + var/flipdir = turn(fromdir, 180) + if(flipdir != dir) // came from secondary dir + return dir // so exit through primary + else // came from primary + // so need to choose either secondary exit + var/mask = ..(fromdir) + + // find a bit which is set + var/setbit = 0 + if(mask & NORTH) + setbit = NORTH + else if(mask & SOUTH) + setbit = SOUTH + else if(mask & EAST) + setbit = EAST + else + setbit = WEST + + if(prob(50)) // 50% chance to choose the found bit or the other one + return setbit + else + return mask & (~setbit) + +/obj/structure/disposalpipe/junction/flipped //for easier and cleaner mapping + icon_state = "pipe-j2" diff --git a/code/modules/industry/disposals/disposal_pipe/segment.dm b/code/modules/industry/disposals/disposal_pipe/segment.dm new file mode 100644 index 000000000000..eb3bad08b814 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/segment.dm @@ -0,0 +1,10 @@ +// a straight or bent segment +/obj/structure/disposalpipe/segment + icon_state = "pipe-s" + +/obj/structure/disposalpipe/segment/New() + ..() + if(icon_state == "pipe-s") + dpdir = dir | turn(dir, 180) + else + dpdir = dir | turn(dir, -90) diff --git a/code/modules/industry/disposals/disposal_pipe/sortjunction.dm b/code/modules/industry/disposals/disposal_pipe/sortjunction.dm new file mode 100644 index 000000000000..02b2f813936d --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/sortjunction.dm @@ -0,0 +1,116 @@ +//a three-way junction that sorts objects +/obj/structure/disposalpipe/sortjunction + name = "sorting junction" + icon_state = "pipe-j1s" + desc = "An underfloor disposal pipe with a package sorting mechanism." + + var/posdir = 0 + var/negdir = 0 + var/sortdir = 0 + +/obj/structure/disposalpipe/sortjunction/proc/updatedesc() + desc = initial(desc) + if(sortType) + desc += "\nIt's filtering objects with the '[sortType]' tag." + +/obj/structure/disposalpipe/sortjunction/proc/updatename() + if(sortType) + name = "[initial(name)] ([sortType])" + else + name = initial(name) + +/obj/structure/disposalpipe/sortjunction/proc/updatedir() + posdir = dir + negdir = turn(posdir, 180) + + if(icon_state == "pipe-j1s") + sortdir = turn(posdir, -90) + else if(icon_state == "pipe-j2s") + sortdir = turn(posdir, 90) + + dpdir = sortdir | posdir | negdir + +/obj/structure/disposalpipe/sortjunction/New() + . = ..() + if(sortType) GLOB.tagger_locations |= sortType + + updatedir() + updatename() + updatedesc() + +/obj/structure/disposalpipe/sortjunction/attackby(obj/item/I, mob/user) + if(..()) + return + + if(istype(I, /obj/item/destTagger)) + var/obj/item/destTagger/O = I + + if(O.currTag)// Tag set + sortType = O.currTag + playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) + to_chat(user, "Changed filter to '[sortType]'.") + updatename() + updatedesc() + +/obj/structure/disposalpipe/sortjunction/proc/divert_check(checkTag) + return sortType == checkTag + +// next direction to move +// if coming in from negdir, then next is primary dir or sortdir +// if coming in from posdir, then flip around and go back to posdir +// if coming in from sortdir, go to posdir +/obj/structure/disposalpipe/sortjunction/nextdir(fromdir, sortTag) + if(fromdir != sortdir) // probably came from the negdir + if(divert_check(sortTag)) + return sortdir + else + return posdir + else // came from sortdir + // so go with the flow to positive direction + return posdir + +/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir, H.destinationTag) + H.setDir(nextdir) + var/turf/T = H.nextloc() + var/obj/structure/disposalpipe/P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.forceMove(P) + else // if wasn't a pipe, then set loc to turf + H.forceMove(T) + return null + + return P + +//a three-way junction that filters all wrapped and tagged items +/obj/structure/disposalpipe/sortjunction/wildcard + name = "wildcard sorting junction" + desc = "An underfloor disposal pipe which filters all wrapped and tagged items." + subtype = 1 + +/obj/structure/disposalpipe/sortjunction/wildcard/divert_check(checkTag) + return checkTag != "" + +//junction that filters all untagged items +/obj/structure/disposalpipe/sortjunction/untagged + name = "untagged sorting junction" + desc = "An underfloor disposal pipe which filters all untagged items." + subtype = 2 + +/obj/structure/disposalpipe/sortjunction/untagged/divert_check(checkTag) + return checkTag == "" + +/obj/structure/disposalpipe/sortjunction/flipped //for easier and cleaner mapping + icon_state = "pipe-j2s" + +/obj/structure/disposalpipe/sortjunction/wildcard/flipped + icon_state = "pipe-j2s" + +/obj/structure/disposalpipe/sortjunction/untagged/flipped + icon_state = "pipe-j2s" diff --git a/code/modules/industry/disposals/disposal_pipe/tagger.dm b/code/modules/industry/disposals/disposal_pipe/tagger.dm new file mode 100644 index 000000000000..8a24e6badf7c --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/tagger.dm @@ -0,0 +1,50 @@ +/obj/structure/disposalpipe/tagger + name = "package tagger" + icon_state = "pipe-tagger" + var/sort_tag = "" + var/partial = 0 + +/obj/structure/disposalpipe/tagger/proc/updatedesc() + desc = initial(desc) + if(sort_tag) + desc += "\nIt's tagging objects with the '[sort_tag]' tag." + +/obj/structure/disposalpipe/tagger/proc/updatename() + if(sort_tag) + name = "[initial(name)] ([sort_tag])" + else + name = initial(name) + +/obj/structure/disposalpipe/tagger/New() + . = ..() + dpdir = dir | turn(dir, 180) + if(sort_tag) GLOB.tagger_locations |= sort_tag + updatename() + updatedesc() + +/obj/structure/disposalpipe/tagger/attackby(obj/item/I, mob/user) + if(..()) + return + + if(istype(I, /obj/item/destTagger)) + var/obj/item/destTagger/O = I + + if(O.currTag)// Tag set + sort_tag = O.currTag + playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) + to_chat(user, "Changed tag to '[sort_tag]'.") + updatename() + updatedesc() + +/obj/structure/disposalpipe/tagger/transfer(obj/structure/disposalholder/H) + if(sort_tag) + if(partial) + H.setpartialtag(sort_tag) + else + H.settag(sort_tag) + return ..() + +/obj/structure/disposalpipe/tagger/partial //needs two passes to tag + name = "partial package tagger" + icon_state = "pipe-tagger-partial" + partial = 1 diff --git a/code/modules/industry/disposals/disposal_pipe/trunk.dm b/code/modules/industry/disposals/disposal_pipe/trunk.dm new file mode 100644 index 000000000000..3d0b03a73a16 --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/trunk.dm @@ -0,0 +1,99 @@ +//a trunk joining to a disposal bin or outlet on the same turf +/obj/structure/disposalpipe/trunk + icon_state = "pipe-t" + var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet + +/obj/structure/disposalpipe/trunk/Initialize(mapload) + . = ..() + dpdir = dir + return INITIALIZE_HINT_LATELOAD + +/obj/structure/disposalpipe/trunk/LateInitialize() + . = ..() + getlinked() +/obj/structure/disposalpipe/trunk/proc/getlinked() + linked = null + var/obj/machinery/disposal/D = locate() in src.loc + if(D) + linked = D + if (!D.trunk) + D.trunk = src + + var/obj/structure/disposaloutlet/O = locate() in src.loc + if(O) + linked = O + return + + // Override attackby so we disallow trunkremoval when somethings ontop +/obj/structure/disposalpipe/trunk/attackby(obj/item/I, mob/user) + + //Disposal bins or chutes + /* + These shouldn't be required + var/obj/machinery/disposal/D = locate() in src.loc + if(D && D.anchored) + return + + //Disposal outlet + var/obj/structure/disposaloutlet/O = locate() in src.loc + if(O && O.anchored) + return + */ + + //Disposal constructors + var/obj/structure/disposalconstruct/C = locate() in src.loc + if(C && C.anchored) + return + + var/turf/T = src.loc + if(!T.is_plating()) + return // prevent interaction with T-scanner revealed pipes + src.add_fingerprint(user, 0, I) + if(istype(I, /obj/item/weldingtool)) + var/obj/item/weldingtool/W = I + + if(W.remove_fuel(0,user)) + playsound(src, W.tool_sound, 100, 1) + // check if anything changed over 2 seconds + var/turf/uloc = user.loc + var/atom/wloc = W.loc + to_chat(user, "Slicing the disposal pipe.") + sleep(30) + if(!W.isOn()) return + if(user.loc == uloc && wloc == W.loc) + welded() + else + to_chat(user, "You must stay still while welding the pipe.") + else + to_chat(user, "You need more welding fuel to cut the pipe.") + return + + // would transfer to next pipe segment, but we are in a trunk + // if not entering from disposal bin, + // transfer to linked object (outlet or bin) + +/obj/structure/disposalpipe/trunk/transfer(var/obj/structure/disposalholder/H) + + if(H.dir == DOWN) // we just entered from a disposer + return ..() // so do base transfer proc + // otherwise, go to the linked object + if(linked) + var/obj/structure/disposaloutlet/O = linked + if(istype(O) && (H)) + O.expel(H) // expel at outlet + else + var/obj/machinery/disposal/D = linked + if(H) + D.expel(H) // expel at disposal + else + if(H) + src.expel(H, src.loc, 0) // expel at turf + return null + + // nextdir + +/obj/structure/disposalpipe/trunk/nextdir(var/fromdir) + if(fromdir == DOWN) + return dir + else + return 0 diff --git a/code/modules/industry/disposals/disposal_pipe/up.dm b/code/modules/industry/disposals/disposal_pipe/up.dm new file mode 100644 index 000000000000..31816509527d --- /dev/null +++ b/code/modules/industry/disposals/disposal_pipe/up.dm @@ -0,0 +1,48 @@ +/obj/structure/disposalpipe/up + icon_state = "pipe-u" + +/obj/structure/disposalpipe/up/New() + ..() + dpdir = dir + return + +/obj/structure/disposalpipe/up/nextdir(fromdir) + var/nextdir + if(fromdir == 11) + nextdir = dir + else + nextdir = 12 + return nextdir + +/obj/structure/disposalpipe/up/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir) + H.setDir(nextdir) + + var/turf/T + var/obj/structure/disposalpipe/P + + if(nextdir == 12) + T = get_vertical_step(src, UP) + if(!T) + H.forceMove(loc) + return + else + for(var/obj/structure/disposalpipe/down/F in T) + P = F + + else + T = get_step(src.loc, H.dir) + P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.forceMove(P) + else // if wasn't a pipe, then set loc to turf + H.forceMove(T) + return null + + return P diff --git a/code/modules/industry/package_wrapper.dm b/code/modules/industry/package_wrapper.dm new file mode 100644 index 000000000000..6d18bf75cbef --- /dev/null +++ b/code/modules/industry/package_wrapper.dm @@ -0,0 +1,93 @@ +// todo: /obj/item/package_wrapper +/obj/item/packageWrap + name = "package wrapper" + icon = 'icons/obj/items.dmi' + icon_state = "deliveryPaper" + w_class = WEIGHT_CLASS_NORMAL + var/amount = 25 + +/obj/item/packageWrap/afterattack(atom/movable/target, mob/user, clickchain_flags, list/params) + if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) return + if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete + return + if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \ + || istype(target, /obj/item/gift) || istype(target, /obj/item/evidencebag)) + return + if(target.anchored) + return + if(target in user) + return + if(user in target) //no wrapping closets that you are inside - it's not physically possible + return + + user.attack_log += "\[[time_stamp()]\] Has used [name] on \ref[target]" + + + if (istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box))) + var/obj/item/O = target + if (src.amount > 1) + var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up! + if(!istype(O.loc, /turf)) + if(user.client) + user.client.screen -= O + P.wrapped = O + O.forceMove(P) + P.set_weight_class(O.w_class) + var/i = round(O.get_weight_class()) + if(i in list(1,2,3,4,5)) + P.icon_state = "deliverycrate[i]" + switch(i) + if(1) P.name = "tiny parcel" + if(3) P.name = "normal-sized parcel" + if(4) P.name = "large parcel" + if(5) P.name = "huge parcel" + if(i < 1) + P.icon_state = "deliverycrate1" + P.name = "tiny parcel" + if(i > 5) + P.icon_state = "deliverycrate5" + P.name = "huge parcel" + P.add_fingerprint(usr) + O.add_fingerprint(usr) + src.add_fingerprint(usr) + src.amount -= 1 + user.visible_message("\The [user] wraps \a [target] with \a [src].",\ + "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ + "You hear someone taping paper around a small object.") + else if (istype(target, /obj/structure/closet/crate)) + var/obj/structure/closet/crate/O = target + if (src.amount > 3 && !O.opened) + var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) + P.icon_state = "deliverycrate" + P.wrapped = O + O.loc = P + src.amount -= 3 + user.visible_message("\The [user] wraps \a [target] with \a [src].",\ + "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ + "You hear someone taping paper around a large object.") + else if(src.amount < 3) + to_chat(user, "You need more paper.") + else if (istype (target, /obj/structure/closet)) + var/obj/structure/closet/O = target + if (src.amount > 3 && !O.opened) + var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) + P.wrapped = O + O.sealed = 1 + O.loc = P + src.amount -= 3 + user.visible_message("\The [user] wraps \a [target] with \a [src].",\ + "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ + "You hear someone taping paper around a large object.") + else if(src.amount < 3) + to_chat(user, "You need more paper.") + else + to_chat(user, "The object you are trying to wrap is unsuitable for the sorting machinery!") + if (src.amount <= 0) + new /obj/item/c_tube( src.loc ) + qdel(src) + return + return + +/obj/item/packageWrap/examine(mob/user, dist) + . = ..() + . += "There are [amount] units of package wrap left!" diff --git a/code/modules/industry/packages/large_parcel.dm b/code/modules/industry/packages/large_parcel.dm new file mode 100644 index 000000000000..c1c40307b0ba --- /dev/null +++ b/code/modules/industry/packages/large_parcel.dm @@ -0,0 +1,118 @@ +// todo: /obj/structure/large_parcel +/obj/structure/bigDelivery + desc = "A big wrapped package." + name = "large parcel" + icon = 'icons/obj/storage.dmi' + icon_state = "deliverycloset" + var/obj/wrapped = null + density = 1 + var/sortTag = null + mouse_drag_pointer = MOUSE_ACTIVE_POINTER + var/examtext = null + var/nameset = 0 + var/label_y + var/label_x + var/tag_x + +/obj/structure/bigDelivery/Destroy() + if(wrapped) //sometimes items can disappear. For example, bombs. --rastaf0 + wrapped.forceMove(get_turf(src)) + if(istype(wrapped, /obj/structure/closet)) + var/obj/structure/closet/O = wrapped + O.sealed = 0 + wrapped = null + var/turf/T = get_turf(src) + for(var/atom/movable/AM in contents) + AM.forceMove(T) + return ..() + +/obj/structure/bigDelivery/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) + unwrap() + +/obj/structure/bigDelivery/proc/unwrap() + // Destroy will drop our wrapped object on the turf, so let it. + qdel(src) + +/obj/structure/bigDelivery/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/destTagger)) + var/obj/item/destTagger/O = W + if(O.currTag) + if(src.sortTag != O.currTag) + to_chat(user, "You have labeled the destination as [O.currTag].") + if(!src.sortTag) + src.sortTag = O.currTag + update_icon() + else + src.sortTag = O.currTag + playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) + else + to_chat(user, "The package is already labeled for [O.currTag].") + else + to_chat(user, "You need to set a destination first!") + + else if(istype(W, /obj/item/pen)) + switch(alert("What would you like to alter?",,"Title","Description", "Cancel")) + if("Title") + var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(user, " Invalid text.") + return + user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\ + "You title \the [src]: \"[str]\"",\ + "You hear someone scribbling a note.") + name = "[name] ([str])" + if(!examtext && !nameset) + nameset = 1 + update_icon() + else + nameset = 1 + if("Description") + var/str = sanitize(input(usr,"Label text?","Set label","")) + if(!str || !length(str)) + to_chat(user, "Invalid text.") + return + if(!examtext && !nameset) + examtext = str + update_icon() + else + examtext = str + user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\ + "You label \the [src]: \"[examtext]\"",\ + "You hear someone scribbling a note.") + return + +/obj/structure/bigDelivery/update_icon() + cut_overlays() + if(nameset || examtext) + var/image/I = new/image('icons/obj/storage.dmi',"delivery_label") + if(icon_state == "deliverycloset") + I.pixel_x = 2 + if(label_y == null) + label_y = rand(-6, 11) + I.pixel_y = label_y + else if(icon_state == "deliverycrate") + if(label_x == null) + label_x = rand(-8, 6) + I.pixel_x = label_x + I.pixel_y = -3 + add_overlay(I) + if(sortTag) + var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag") + if(icon_state == "deliverycloset") + if(tag_x == null) + tag_x = rand(-2, 3) + I.pixel_x = tag_x + I.pixel_y = 9 + else if(icon_state == "deliverycrate") + if(tag_x == null) + tag_x = rand(-8, 6) + I.pixel_x = tag_x + I.pixel_y = -3 + add_overlay(I) + +/obj/structure/bigDelivery/examine(mob/user, dist) + . = ..() + if(sortTag) + . += "It is labeled \"[sortTag]\"" + if(examtext) + . += "It has a note attached which reads, \"[examtext]\"" diff --git a/code/modules/industry/packages/small_parcel.dm b/code/modules/industry/packages/small_parcel.dm new file mode 100644 index 000000000000..433b8444952e --- /dev/null +++ b/code/modules/industry/packages/small_parcel.dm @@ -0,0 +1,107 @@ +// todo: /obj/item/small_parcel +/obj/item/smallDelivery + desc = "A small wrapped package." + name = "small parcel" + icon = 'icons/obj/storage.dmi' + icon_state = "deliverycrate3" + drop_sound = 'sound/items/drop/cardboardbox.ogg' + pickup_sound = 'sound/items/pickup/cardboardbox.ogg' + var/obj/item/wrapped = null + var/sortTag = null + var/examtext = null + var/nameset = 0 + var/tag_x + +/obj/item/smallDelivery/attack_self(mob/user) + . = ..() + if(.) + return + if (wrapped) //sometimes items can disappear. For example, bombs. --rastaf0 + if(ishuman(user)) + user.put_in_hands_or_drop(wrapped) + else + wrapped.forceMove(drop_location()) + wrapped = null + + qdel(src) + +/obj/item/smallDelivery/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/destTagger)) + var/obj/item/destTagger/O = W + if(O.currTag) + if(src.sortTag != O.currTag) + to_chat(user, "You have labeled the destination as [O.currTag].") + if(!src.sortTag) + src.sortTag = O.currTag + update_icon() + else + src.sortTag = O.currTag + playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) + else + to_chat(user, "The package is already labeled for [O.currTag].") + else + to_chat(user, "You need to set a destination first!") + + else if(istype(W, /obj/item/pen)) + switch(alert("What would you like to alter?",,"Title","Description", "Cancel")) + if("Title") + var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(user, " Invalid text.") + return + user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\ + "You title \the [src]: \"[str]\"",\ + "You hear someone scribbling a note.") + name = "[name] ([str])" + if(!examtext && !nameset) + nameset = 1 + update_icon() + else + nameset = 1 + + if("Description") + var/str = sanitize(input(usr,"Label text?","Set label","")) + if(!str || !length(str)) + to_chat(user, "Invalid text.") + return + if(!examtext && !nameset) + examtext = str + update_icon() + else + examtext = str + user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\ + "You label \the [src]: \"[examtext]\"",\ + "You hear someone scribbling a note.") + return + +/obj/item/smallDelivery/update_icon() + cut_overlays() + if((nameset || examtext) && icon_state != "deliverycrate1") + var/image/I = new/image('icons/obj/storage.dmi',"delivery_label") + if(icon_state == "deliverycrate5") + I.pixel_y = -1 + add_overlay(I) + if(sortTag) + var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag") + switch(icon_state) + if("deliverycrate1") + I.pixel_y = -5 + if("deliverycrate2") + I.pixel_y = -2 + if("deliverycrate3") + I.pixel_y = 0 + if("deliverycrate4") + if(tag_x == null) + tag_x = rand(0,5) + I.pixel_x = tag_x + I.pixel_y = 3 + if("deliverycrate5") + I.pixel_y = -3 + add_overlay(I) + +/obj/item/smallDelivery/examine(mob/user, dist) + . = ..() + if(sortTag) + . += "It is labeled \"[sortTag]\"" + if(examtext) + . += "It has a note attached which reads, \"[examtext]\"" diff --git a/code/modules/instruments/instruments/stationary.dm b/code/modules/instruments/instruments/stationary.dm index 390f10f10f11..1adf54e84ac3 100644 --- a/code/modules/instruments/instruments/stationary.dm +++ b/code/modules/instruments/instruments/stationary.dm @@ -23,7 +23,7 @@ return (usr.default_can_use_topic(src) < UI_UPDATE) //can play with MUTATION_TELEKINESIS and while resting because fun. /// CITRP EDIT UNTIL INTERACTION REFACTOR PORT! -/obj/structure/musician/attack_hand(mob/user, list/params) +/obj/structure/musician/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/integrated_electronics/core/assemblies.dm b/code/modules/integrated_electronics/core/assemblies.dm index d9765357ef40..43cfa1a3546b 100644 --- a/code/modules/integrated_electronics/core/assemblies.dm +++ b/code/modules/integrated_electronics/core/assemblies.dm @@ -609,7 +609,7 @@ return ..() -/obj/item/electronic_assembly/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) +/obj/item/electronic_assembly/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(anchored) attack_self(user) return diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index 28db56a4ff61..94a68fb24cbd 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -1080,7 +1080,7 @@ GLOBAL_DATUM_INIT(circuit_translation_context, /datum/translation_context/simple if(signlang) activate_pin(2) -/obj/item/integrated_circuit/input/microphone/sign/hear_signlang(text, verb, datum/language/speaking, mob/M as mob) +/obj/item/integrated_circuit/input/microphone/sign/hear_signlang(mob/M as mob, text, verb, datum/language/speaking) hear_talk(M, text, verb, speaking) return diff --git a/code/modules/integrated_electronics/subtypes/time.dm b/code/modules/integrated_electronics/subtypes/time.dm index ffd8651f9128..645bf29ae8e5 100644 --- a/code/modules/integrated_electronics/subtypes/time.dm +++ b/code/modules/integrated_electronics/subtypes/time.dm @@ -85,7 +85,7 @@ /obj/item/integrated_circuit/time/ticker/Destroy() if(is_running) - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) return ..() /obj/item/integrated_circuit/time/ticker/on_data_written() diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 28d1d9450f14..5df9c106a829 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -54,7 +54,7 @@ else ..() -/obj/structure/bookcase/attack_hand(mob/user, list/params) +/obj/structure/bookcase/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(contents.len) var/obj/item/book/choice = input("Which book would you like to remove from the shelf?") as null|obj in contents if(choice) diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 7a2d668fff97..3980cecb08d0 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -31,7 +31,7 @@ var/category = "Any" var/author -/obj/machinery/librarypubliccomp/attack_hand(mob/user, list/params) +/obj/machinery/librarypubliccomp/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) var/dat = "Library Visitor\n" // switch(screenstate) @@ -160,7 +160,7 @@ var/obj/item/book/M = new path(null) all_books[M.title] = M -/obj/machinery/librarycomp/attack_hand(mob/user, list/params) +/obj/machinery/librarycomp/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) var/dat = "Book Inventory Management\n" // switch(screenstate) @@ -476,7 +476,7 @@ if(!user.attempt_insert_item_for_installation(I, src)) return -/obj/machinery/libraryscanner/attack_hand(mob/user, list/params) +/obj/machinery/libraryscanner/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) var/dat = "Scanner Control Interface\n" // if(cache) diff --git a/code/modules/loot/packs/weapons.dm b/code/modules/loot/packs/weapons.dm index 2443fa9c5f6c..f6a9f723839c 100644 --- a/code/modules/loot/packs/weapons.dm +++ b/code/modules/loot/packs/weapons.dm @@ -3,8 +3,8 @@ /datum/prototype/struct/loot_pack/weapons/melee1 some = list( - /obj/item/melee/energy/sword, - /obj/item/shield/energy, + /obj/item/melee/transforming/energy/sword, + /obj/item/shield/transforming/energy, /obj/item/melee/baton, /obj/item/melee/chainofcommand, /obj/item/melee/nanite_knife, diff --git a/code/modules/mapping/map.dm b/code/modules/mapping/map.dm index 77d3d456985d..cc0283611191 100644 --- a/code/modules/mapping/map.dm +++ b/code/modules/mapping/map.dm @@ -129,8 +129,8 @@ for(var/i in 1 to length(levels)) if(ispath(levels[i])) var/datum/map_level/level_path = levels[i] - var/datum/map_level/level_instance = new level_path - level_instance.hardcoded = TRUE + var/datum/map_level/level_instance = new level_path(src) + level_instance.hardcoded = TRUE // todo: map can just also not be hardcoded levels[i] = level_instance if(levels_match_mangling_id) level_instance.mangling_id = mangling_id || id diff --git a/code/modules/mapping/map_helpers/electrochromatic_linker.dm b/code/modules/mapping/map_helpers/electrochromatic_linker.dm new file mode 100644 index 000000000000..0e6fa3ef7fa2 --- /dev/null +++ b/code/modules/mapping/map_helpers/electrochromatic_linker.dm @@ -0,0 +1,17 @@ +/obj/map_helper/electrochromatic_linker + icon = 'icons/mapping/helpers/electrochromatic_helper.dmi' + icon_state = "electrochromatic" + early = FALSE + late = TRUE + /** + * the ID to bind electrochromatic objects on our tile to + */ + var/id + +/obj/map_helper/electrochromatic_linker/LateInitialize() + for(var/obj/structure/window/reinforced/polarized/chromatic_window in loc) + chromatic_window.id = id + /** + * Same behaviour needs to be applied to polarised airlocks, currently unable too + */ + qdel(src) diff --git a/code/modules/mapping/map_helpers/engine_loader.dm b/code/modules/mapping/map_helpers/engine_loader.dm index 7d0d8e97ea4e..1dc4d35494f3 100644 --- a/code/modules/mapping/map_helpers/engine_loader.dm +++ b/code/modules/mapping/map_helpers/engine_loader.dm @@ -36,7 +36,7 @@ their_for_map = initial(map_path.id) if(their_for_map != src.for_map) continue - var/name = initial(path.name) + var/name = lowertext(initial(path.name)) potential_filtered[path] = isnum(probabilities[name])? probabilities[name] : 1 var/picked_path = pickweightAllowZero(potential_filtered) diff --git a/code/modules/mapping/map_level.dm b/code/modules/mapping/map_level.dm index 38935b04e44d..9c8436799c1c 100644 --- a/code/modules/mapping/map_level.dm +++ b/code/modules/mapping/map_level.dm @@ -45,23 +45,41 @@ var/base_turf = /turf/space /// base area typepath for this level var/base_area = /area/space - /// id of north zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of north zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! var/link_north - /// id of south zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of south zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! can also be set to instance - used for structs. var/link_south - /// id of west zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of west zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! can also be set to instance - used for structs. var/link_west - /// id of east zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of east zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! can also be set to instance - used for structs. var/link_east - /// id of below zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of below zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! can also be set to instance - used for structs. var/link_below - /// id of above zlevel - overrides linkage if set. can be set to path, autoconverts to id on new. - /// can also be set to instance - used for structs. + /// id of above zlevel - overrides linkage if set. + /// + /// * can also be set to path + /// * can also be set to instance - used for structs + /// * do not manually set it to levelpath::id, map levels generate ids dynamically! can also be set to instance - used for structs. var/link_above /// gas string / atmosphere path / atmosphere id for indoors air /// if atmosphere path, it'll be automatically packed to ID on serialize, as we don't want to serialize paths to disk. @@ -152,16 +170,6 @@ if(!isnull(parent_map)) id = "[parent_map.id]-[id]" - #define UNPACK_LINK(vname) if(ispath(vname, /datum/map_level)) { var/datum/map_level/cast_##vname = vname; vname = initial(cast_##vname.id) ; } - UNPACK_LINK(link_north) - UNPACK_LINK(link_south) - UNPACK_LINK(link_east) - UNPACK_LINK(link_west) - UNPACK_LINK(link_below) - UNPACK_LINK(link_above) - BLOCK_BYOND_BUG_2072419 - #undef UNPACK_LINK - /datum/map_level/Destroy() if(loaded) . = QDEL_HINT_LETMELIVE @@ -236,18 +244,36 @@ transition = data["transition"] if(!isnull(data["base_turf"])) base_turf = text2path(data["base_turf"]) + + // Resolve links, including if they got serlalized as typepaths. + // todo: typepaths should be trampled into ids on save instead. + var/resolving_link + var/maybe_link_path if(!isnull(data["link_north"])) - link_north = data["link_north"] + resolving_link = data["north"] + maybe_link_path = text2path(resolving_link) + link_north = ispath(maybe_link_path) ? maybe_link_path : resolving_link if(!isnull(data["link_south"])) - link_south = data["link_south"] + resolving_link = data["south"] + maybe_link_path = text2path(resolving_link) + link_south = ispath(maybe_link_path) ? maybe_link_path : resolving_link if(!isnull(data["link_above"])) - link_above = data["link_above"] + resolving_link = data["above"] + maybe_link_path = text2path(resolving_link) + link_above = ispath(maybe_link_path) ? maybe_link_path : resolving_link if(!isnull(data["link_below"])) - link_below = data["link_below"] + resolving_link = data["below"] + maybe_link_path = text2path(resolving_link) + link_below = ispath(maybe_link_path) ? maybe_link_path : resolving_link if(!isnull(data["link_west"])) - link_west = data["link_west"] + resolving_link = data["west"] + maybe_link_path = text2path(resolving_link) + link_west = ispath(maybe_link_path) ? maybe_link_path : resolving_link if(!isnull(data["link_east"])) - link_east = data["link_east"] + resolving_link = data["east"] + maybe_link_path = text2path(resolving_link) + link_east = ispath(maybe_link_path) ? maybe_link_path : resolving_link + if(!isnull(data["air_indoors"])) air_indoors = data["air_indoors"] if(!isnull(data["air_outdoors"])) diff --git a/code/modules/mapping/spawner/window.dm b/code/modules/mapping/spawner/window.dm index 4e8a8017b04f..a2b05f3380f6 100644 --- a/code/modules/mapping/spawner/window.dm +++ b/code/modules/mapping/spawner/window.dm @@ -44,7 +44,7 @@ var/new_window = new window_full_path(loc) if (spawn_low_wall) new low_wall_path(loc) - if(id && istype(window_full_path, /obj/structure/window/reinforced/polarized)) + if(id && istype(new_window, /obj/structure/window/reinforced/polarized)) var/obj/structure/window/reinforced/polarized/P = new_window P.id = id else diff --git a/code/modules/maps/_shim/away_spawner.dm b/code/modules/maps/_shim/away_spawner.dm index beff6090453e..861b714f3aaa 100644 --- a/code/modules/maps/_shim/away_spawner.dm +++ b/code/modules/maps/_shim/away_spawner.dm @@ -58,7 +58,7 @@ For now this is still usable but bad. my_mob.low_priority = TRUE if(faction) - my_mob.faction = faction + my_mob.set_iff_factions(faction) if(atmos_comp) var/turf/T = get_turf(src) @@ -92,22 +92,6 @@ For now this is still usable but bad. depleted = TRUE return -//Shadekin spawner. Could have them show up on any mission, so it's here. -//Make sure to put them away from others, so they don't get demolished by rude mobs. -/obj/tether_away_spawner/shadekin - name = "Shadekin Spawner" - icon = 'icons/mob/vore_shadekin.dmi' - icon_state = "spawner" - - faction = "shadekin" - prob_spawn = 1 - prob_fall = 1 - //guard = 10 //Don't wander too far, to stay alive. - mobs_to_pick_from = list( - /mob/living/simple_mob/shadekin - ) - - // Underdark mob spawners /obj/tether_away_spawner/underdark_drone_swarm name = "Underdark Drone Swarm Spawner" diff --git a/code/modules/maps/away_missions/140x140/snowfield.dm b/code/modules/maps/away_missions/140x140/snowfield.dm index 376d17f2e380..8c1d71f1ca11 100644 --- a/code/modules/maps/away_missions/140x140/snowfield.dm +++ b/code/modules/maps/away_missions/140x140/snowfield.dm @@ -59,10 +59,10 @@ icon_living = "polarbear" icon_dead = "polarbear-dead" icon_gib = "bear-gib" - vore_active = 1 say_list_type = /datum/say_list/polar_bear - faction = "polar" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + maxHealth = 80 health = 80 // Polar bear will fuck you up. @@ -90,13 +90,10 @@ ..() /mob/living/simple_mob/animal/sif/sakimm/polar - faction = "polar" /mob/living/simple_mob/animal/sif/diyaab/polar - faction = "polar" /mob/living/simple_mob/animal/sif/shantak/polar - faction = "polar" // -- Items -- // diff --git a/code/modules/maps/away_missions/archive/wildwest.dm b/code/modules/maps/away_missions/archive/wildwest.dm index f343556da93c..bb760ae21012 100644 --- a/code/modules/maps/away_missions/archive/wildwest.dm +++ b/code/modules/maps/away_missions/archive/wildwest.dm @@ -20,7 +20,7 @@ var/chargesa = 1 var/insistinga = 0 -/obj/machinery/wish_granter_dark/attack_hand(mob/user, list/params) +/obj/machinery/wish_granter_dark/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) usr.set_machine(src) if(chargesa <= 0) diff --git a/code/modules/maps/misc_maps/lavaland/_lavaland.dm b/code/modules/maps/misc_maps/lavaland/_lavaland.dm index 3b85aae9b434..6c5eb869c54c 100644 --- a/code/modules/maps/misc_maps/lavaland/_lavaland.dm +++ b/code/modules/maps/misc_maps/lavaland/_lavaland.dm @@ -22,7 +22,7 @@ use_power = USE_POWER_IDLE interaction_flags_machine = INTERACT_MACHINE_OFFLINE | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OFFLINE_SILICON -/obj/machinery/lavaland_entryportal/attack_hand(mob/user, list/params) +/obj/machinery/lavaland_entryportal/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user, /mob/living/silicon/ai)) // lets not teleport AI cores return if(inoperable(MAINT)) @@ -58,7 +58,7 @@ anchored = 1 -/obj/effect/lavaland_exitportal/attack_hand(mob/user, list/params) +/obj/effect/lavaland_exitportal/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(usr, /mob/living/silicon/ai)) return if(do_after(user, 10)) diff --git a/code/modules/materials/definitions/metals/steel.dm b/code/modules/materials/definitions/metals/steel.dm index 44cf063ed243..71a4db047970 100644 --- a/code/modules/materials/definitions/metals/steel.dm +++ b/code/modules/materials/definitions/metals/steel.dm @@ -80,7 +80,7 @@ . += create_stack_recipe_datum( name = "rack", product = /obj/structure/table/rack, - cost = 1, + cost = 2, time = 0.5 SECONDS, ) . += create_stack_recipe_datum( diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index 950c64e4e9d0..0b9c51f98d69 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -299,7 +299,7 @@ update_mass() return -/obj/item/stack/material/supermatter/attack_hand(mob/user, list/params) +/obj/item/stack/material/supermatter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() update_mass() diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm index dd1dce7bbf6a..3fc9105b9452 100644 --- a/code/modules/mining/drilling/drill.dm +++ b/code/modules/mining/drilling/drill.dm @@ -190,7 +190,7 @@ return ..() -/obj/machinery/mining/drill/attack_hand(mob/user, list/params) +/obj/machinery/mining/drill/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) check_supports() if (panel_open && cell && user.Adjacent(src)) diff --git a/code/modules/mining/kinetic_crusher.dm b/code/modules/mining/kinetic_crusher.dm index a6e7c3b66276..4bc04ca17d20 100644 --- a/code/modules/mining/kinetic_crusher.dm +++ b/code/modules/mining/kinetic_crusher.dm @@ -311,7 +311,7 @@ damage_type = BRUTE damage_flag = ARMOR_BOMB range = WORLD_ICON_SIZE * 6 - accuracy = INFINITY // NO. + accuracy_disabled = TRUE // log_override = TRUE var/obj/item/kinetic_crusher/hammer_synced @@ -319,7 +319,10 @@ hammer_synced = null return ..() -/obj/projectile/destabilizer/on_hit(atom/target, blocked = FALSE) +/obj/projectile/destabilizer/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) var/mob/living/L = target if(hammer_synced.can_mark(L)) @@ -341,7 +344,6 @@ var/turf/simulated/mineral/M = target_turf new /obj/effect/temp_visual/kinetic_blast(M) M.GetDrilled(firer) - ..() /* //trophies diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index af02717eb6d7..1de34552fa6f 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -30,7 +30,7 @@ inserted_id.forceMove(loc) //Prevents deconstructing from deleting whatever ID was inside it. . = ..() -/obj/machinery/mineral/processing_unit_console/attack_hand(mob/user, list/params) +/obj/machinery/mineral/processing_unit_console/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return interact(user) @@ -199,9 +199,9 @@ speed_process = !speed_process // switching gears if(speed_process) // high gear STOP_MACHINE_PROCESSING(src) - START_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSprocess_5fps, src) else // low gear - STOP_PROCESSING(SSfastprocess, src) + STOP_PROCESSING(SSprocess_5fps, src) START_MACHINE_PROCESSING(src) /obj/machinery/mineral/processing_unit/process(delta_time) diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index 8f93efbe270c..ec39ab65a50a 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -24,7 +24,7 @@ stack_trace("Stacking machine console at [COORD(src)] could not find its machine!") qdel(src) -/obj/machinery/mineral/stacking_unit_console/attack_hand(mob/user, list/params) +/obj/machinery/mineral/stacking_unit_console/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) interact(user) diff --git a/code/modules/mining/machine_unloading.dm b/code/modules/mining/machine_unloading.dm index 0f281c96052c..382d641d0c6c 100644 --- a/code/modules/mining/machine_unloading.dm +++ b/code/modules/mining/machine_unloading.dm @@ -27,7 +27,7 @@ /obj/machinery/mineral/unloading_machine/process(delta_time) if(output && input) - if(length(output.loc.contents) > 100) // let's not! + if(length(output.loc.contents) > TURF_CROWDING_SOFT_LIMIT) // let's not! return var/obj/structure/ore_box/O = locate() in input.loc if(O) diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index 4fc69fa899a7..8b8f630d436a 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -391,7 +391,7 @@ else ..() -/obj/item/stack/flag/attack_hand(mob/user, list/params) +/obj/item/stack/flag/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(upright) upright = 0 icon_state = base_state diff --git a/code/modules/mining/mine_outcrops.dm b/code/modules/mining/mine_outcrops.dm index d5c769d77473..03fb1c355fd1 100644 --- a/code/modules/mining/mine_outcrops.dm +++ b/code/modules/mining/mine_outcrops.dm @@ -108,11 +108,12 @@ return . = ..() -/obj/structure/outcrop/bullet_act(obj/projectile/P, def_zone) - if(P.damage_flag == ARMOR_BOMB) //Intended for kinetic accelerators/daggers to just get rid of this stuff quickly. They're rocks. - GetDrilled() - return +/obj/structure/outcrop/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_TARGET_ABORT) + return + if(proj.damage_flag == ARMOR_BOMB) //Intended for kinetic accelerators/daggers to just get rid of this stuff quickly. They're rocks. + GetDrilled() /obj/structure/outcrop/proc/GetDrilled() new outcropdrop(get_turf(src), rand(mindrop,upperdrop)) diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index c8c81622e3ea..adb4c3169627 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -296,10 +296,10 @@ CREATE_STANDARD_TURFS(/turf/simulated/mineral/floor/ignore_cavegen) new oretype(src) resources[ore] = 0 -/turf/simulated/mineral/bullet_act(var/obj/projectile/Proj) // only emitters for now +/turf/simulated/mineral/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() - if(Proj.excavation_amount) - var/newDepth = excavation_level + Proj.excavation_amount // Used commonly below + if(proj.excavation_amount) + var/newDepth = excavation_level + proj.excavation_amount // Used commonly below if(newDepth >= 200) // first, if the turf is completely drilled then don't bother checking for finds and just drill it GetDrilled(0) return @@ -312,8 +312,8 @@ CREATE_STANDARD_TURFS(/turf/simulated/mineral/floor/ignore_cavegen) if(prob(50)) artifact_debris() - excavation_level += Proj.excavation_amount - update_archeo_overlays(Proj.excavation_amount) + excavation_level += proj.excavation_amount + update_archeo_overlays(proj.excavation_amount) /turf/simulated/mineral/Bumped(AM) diff --git a/code/modules/mining/ore_redemption_machine/engineering_points_vendor.dm b/code/modules/mining/ore_redemption_machine/engineering_points_vendor.dm index b55ba7453580..f2ad1e9bebe6 100644 --- a/code/modules/mining/ore_redemption_machine/engineering_points_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/engineering_points_vendor.dm @@ -167,7 +167,7 @@ order.comment = "Voucher redemption" order.ordered_at = stationdate2text() + " - " + stationtime2text() order.status = SUP_ORDER_APPROVED //auto approved - order.approved_by = "[src]" + order.approved_by = "[user]" order.approved_at = stationdate2text() + " - " + stationtime2text() SSsupply.order_history += order//tell supply the order exists. @@ -224,7 +224,7 @@ name = "Laser reflector voucher" desc = "A voucher redeemable, at any NT cargo department, for a single laser reflector." icon_state = "engineering_voucher" - redeemable_for = new /datum/supply_pack/nanotrasen/engineering/engine/fusion_fuel_compressor + redeemable_for = new /datum/supply_pack/nanotrasen/engineering/reflector /obj/item/engineering_mystical_tech name = "XYE" diff --git a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm index 2b765fa5120a..5b131d329524 100644 --- a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm @@ -73,8 +73,8 @@ new /datum/data/mining_equipment("KA Super Chassis", /obj/item/ka_modkit/chassis_mod, 250), new /datum/data/mining_equipment("KA Hyper Chassis", /obj/item/ka_modkit/chassis_mod/orange, 300), new /datum/data/mining_equipment("KA Range Increase", /obj/item/ka_modkit/range, 1000), - new /datum/data/mining_equipment("KA Damage Increase", /obj/item/ka_modkit/damage, 1000), new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/ka_modkit/cooldown, 1200), + new /datum/data/mining_equipment("KA Capacity Increase", /obj/item/ka_modkit/capacity, 1500), new /datum/data/mining_equipment("KA Holster", /obj/item/clothing/accessory/holster/waist/kinetic_accelerator, 350), new /datum/data/mining_equipment("Fine Excavation Kit - Chisels",/obj/item/storage/excavation, 500), new /datum/data/mining_equipment("Fine Excavation Kit - Measuring Tape",/obj/item/measuring_tape, 125), @@ -119,7 +119,7 @@ else icon_state = "[initial(icon_state)]-off" -/obj/machinery/mineral/equipment_vendor/attack_hand(mob/user, list/params) +/obj/machinery/mineral/equipment_vendor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return interact(user) diff --git a/code/modules/mining/shelter_atoms.dm b/code/modules/mining/shelter_atoms.dm index 8cdb65a886d4..619a349bdb74 100644 --- a/code/modules/mining/shelter_atoms.dm +++ b/code/modules/mining/shelter_atoms.dm @@ -128,7 +128,7 @@ id = "placeholder_id_do_not_use" //This has to be this way, otherwise it will control ALL doors if left blank. var/obj/machinery/door/airlock/voidcraft/survival_pod/door -/obj/machinery/button/remote/airlock/survival_pod/attack_hand(obj/item/W, mob/user as mob) +/obj/machinery/button/remote/airlock/survival_pod/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return 1 //1 is failure on machines (for whatever reason) if(!door) var/turf/dT = get_step(src,dir) @@ -209,7 +209,7 @@ return FALSE -/obj/item/gps/computer/attack_hand(mob/user, list/params) +/obj/item/gps/computer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) attack_self(user) //Bed diff --git a/code/modules/mob/defense.dm b/code/modules/mob/defense.dm deleted file mode 100644 index fae10ea95e3a..000000000000 --- a/code/modules/mob/defense.dm +++ /dev/null @@ -1,83 +0,0 @@ -/** - * calculates the resulting damage from an attack, taking into account our armor and soak - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * target_zone - where it's impacting - * - * @return args as list. - */ -/mob/proc/check_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/list/returned = check_armor(damage, tier, flag, mode, attack_type, weapon) - damage = returned[1] - mode = returned[4] - return args.Copy() - -/** - * runs armor against an incoming attack - * this proc can have side effects - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * target_zone - where it's impacting - * - * @return args as list. - */ -/mob/proc/run_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/list/returned = run_armor(damage, tier, flag, mode, attack_type, weapon) - damage = returned[1] - mode = returned[4] - return args.Copy() - -/** - * check overall armor - * does not support modifying damage modes. - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * target_zone - where it's impacting - * - * @return args as list. - */ -/mob/proc/check_overall_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/list/returned = check_armor(damage, tier, flag, mode, attack_type, weapon) - damage = returned[1] - mode = returned[4] - return args.Copy() - -/** - * runs overall armor against an incoming attack - * this proc can have side effects - * does not support modifying damage modes. - * - * @params - * * damage - raw damage - * * tier - penetration / attack tier - * * flag - armor flag as seen in [code/__DEFINES/combat/armor.dm] - * * mode - damage_mode - * * attack_type - (optional) attack type flags from [code/__DEFINES/combat/attack_types.dm] - * * weapon - (optional) attacking /obj/item for melee or thrown, /obj/projectile for ranged, /mob for unarmed - * * target_zone - where it's impacting - * - * @return args as list. - */ -/mob/proc/run_overall_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/list/returned = run_armor(damage, tier, flag, mode, attack_type, weapon) - damage = returned[1] - mode = returned[4] - return args.Copy() diff --git a/code/modules/mob/grab.dm b/code/modules/mob/grab.dm index aad94cc6f478..d42f8c6c55b3 100644 --- a/code/modules/mob/grab.dm +++ b/code/modules/mob/grab.dm @@ -1,21 +1,39 @@ /** * returns everyone we're grabbing associated to state */ -/mob/proc/grabbing() +/mob/proc/get_grabbing() RETURN_TYPE(/list) . = list() for(var/obj/item/grab/G in get_held_items()) .[G.affecting] = G.state +/** + * returns everyone we're grabbing that are at least the given grab state + */ +/mob/proc/get_grabbing_of_state(state) + RETURN_TYPE(/list) + . = list() + for(var/obj/item/grab/G in get_held_items()) + if(G.state < state) + continue + . += G.affecting + /** * returns everyone we're grabbing, recursively; this can include ourselves! + * + * @return grabbed mobs associated to states */ -/mob/proc/grabbing_recursive(list/L = list()) +/mob/proc/get_grabbing_recursive(list/L = list(), safety = 15, list/processed = list()) RETURN_TYPE(/list) + if(processed[src]) + return + processed[src] = TRUE + if(safety <= 0) + CRASH("infinite loop guard tripped") . = L for(var/obj/item/grab/G in get_held_items()) .[G.affecting] = max(.[G.affecting], G.state) - grabbing_recursive(G.affecting) + G.affecting.get_grabbing_recursive(., --safety, processed) /** * check the grab state of us to someone @@ -242,7 +260,7 @@ /obj/item/grab/throw_resolve_override(atom/movable/resolved, mob/user) return TRUE -/obj/item/grab/melee_object_hit(atom/target, datum/event_args/actor/clickchain/clickchain, clickchain_flags, mult) +/obj/item/grab/melee_object_hit(atom/target, datum/event_args/actor/clickchain/clickchain, clickchain_flags) switch(state) if(GRAB_PASSIVE) clickchain.visible_feedback( diff --git a/code/modules/mob/inventory/items.dm b/code/modules/mob/inventory/items.dm index 59b2a1d438c0..e0c62b3f61b9 100644 --- a/code/modules/mob/inventory/items.dm +++ b/code/modules/mob/inventory/items.dm @@ -34,7 +34,7 @@ /obj/item/proc/equipped(mob/user, slot, flags) SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot, flags) - SEND_SIGNAL(user, COMSIG_MOB_ITEM_EQUIPPED, user, slot, flags) + SEND_SIGNAL(user, COMSIG_MOB_ITEM_EQUIPPED, src, slot, flags) worn_slot = slot if(!(flags & INV_OP_IS_ACCESSORY)) // todo: shouldn't be in here @@ -68,7 +68,7 @@ /obj/item/proc/unequipped(mob/user, slot, flags) SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ITEM_UNEQUIPPED, user, slot, flags) - SEND_SIGNAL(user, COMSIG_MOB_ITEM_UNEQUIPPED, user, slot, flags) + SEND_SIGNAL(user, COMSIG_MOB_ITEM_UNEQUIPPED, src, slot, flags) worn_slot = null if(!(flags & INV_OP_IS_ACCESSORY)) // todo: shouldn't be in here @@ -145,11 +145,20 @@ */ /obj/item/proc/pickup(mob/user, flags, atom/oldLoc) SHOULD_CALL_PARENT(TRUE) + + // we load the component here as it hooks equipped, + // so loading it here means it can still handle the equipped signal. + if(passive_parry) + LoadComponent(/datum/component/passive_parry, passive_parry) + SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user, flags, oldLoc) SEND_SIGNAL(user, COMSIG_MOB_ITEM_PICKUP, src, flags, oldLoc) + reset_pixel_offsets() hud_layerise() + item_flags |= ITEM_IN_INVENTORY + // todo: should this be here transform = null if(isturf(oldLoc) && !(flags & (INV_OP_SILENT | INV_OP_DIRECTLY_EQUIPPING))) @@ -374,6 +383,8 @@ * checks if we're held in hand * * if so, returns mob we're in + * + * @return the mob holding us */ /obj/item/proc/is_held() return (worn_slot == SLOT_ID_HANDS)? worn_mob() : null diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm index d019d087d085..7e1da92fc895 100644 --- a/code/modules/mob/living/bot/cleanbot.dm +++ b/code/modules/mob/living/bot/cleanbot.dm @@ -140,7 +140,7 @@ else icon_state = "cleanbot[on]" -/mob/living/bot/cleanbot/attack_hand(mob/user, list/params) +/mob/living/bot/cleanbot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /mob/living/bot/cleanbot/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/mob/living/bot/farmbot.dm b/code/modules/mob/living/bot/farmbot.dm index bbccdd32b11d..be45888d51a6 100644 --- a/code/modules/mob/living/bot/farmbot.dm +++ b/code/modules/mob/living/bot/farmbot.dm @@ -69,7 +69,7 @@ return data -/mob/living/bot/farmbot/attack_hand(mob/user, list/params) +/mob/living/bot/farmbot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return @@ -403,5 +403,5 @@ created_name = t -/obj/item/farmbot_arm_assembly/attack_hand(mob/user, list/params) +/obj/item/farmbot_arm_assembly/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return //it's a converted watertank, no you cannot pick it up and put it in your backpack diff --git a/code/modules/mob/living/bot/floorbot.dm b/code/modules/mob/living/bot/floorbot.dm index 81e324c9c20e..a2bd72cba7eb 100644 --- a/code/modules/mob/living/bot/floorbot.dm +++ b/code/modules/mob/living/bot/floorbot.dm @@ -89,7 +89,7 @@ return data -/mob/living/bot/floorbot/attack_hand(mob/user, list/params) +/mob/living/bot/floorbot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /mob/living/bot/floorbot/emag_act(var/remaining_charges, var/mob/user) diff --git a/code/modules/mob/living/bot/medibot.dm b/code/modules/mob/living/bot/medibot.dm index bc25e6d098d8..895abfebedcc 100644 --- a/code/modules/mob/living/bot/medibot.dm +++ b/code/modules/mob/living/bot/medibot.dm @@ -221,7 +221,7 @@ busy = FALSE update_appearance() -/mob/living/bot/medibot/attack_hand(mob/user, list/params) +/mob/living/bot/medibot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index 182892376d35..7047005d3290 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -163,7 +163,7 @@ return data -/mob/living/bot/secbot/attack_hand(mob/user, list/params) +/mob/living/bot/secbot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /mob/living/bot/secbot/ui_act(action, list/params, datum/tgui/ui) @@ -226,9 +226,9 @@ if(health < curhealth && on == TRUE) react_to_attack_polaris(user) -/mob/living/bot/secbot/bullet_act(var/obj/projectile/P) +/mob/living/bot/secbot/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) var/curhealth = health - var/mob/shooter = P.firer + var/mob/shooter = proj.firer . = ..() //if we already have a target just ignore to avoid lots of checking if(!target && health < curhealth && shooter && (shooter in view(world.view, src))) diff --git a/code/modules/mob/living/carbon/alien/alien_attacks.dm b/code/modules/mob/living/carbon/alien/alien_attacks.dm index bac33078a131..41b16b48f56a 100644 --- a/code/modules/mob/living/carbon/alien/alien_attacks.dm +++ b/code/modules/mob/living/carbon/alien/alien_attacks.dm @@ -3,7 +3,7 @@ /mob/living/carbon/alien/attack_ui(slot_id) return -/mob/living/carbon/alien/attack_hand(mob/user, list/params) +/mob/living/carbon/alien/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index b542ea2d1d49..64741a5c14fc 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -16,7 +16,7 @@ language = "Hivemind" maxHealth = 25 health = 25 - faction = "xeno" + iff_factions = MOB_IFF_FACTION_XENOMORPH catalogue_data = list(/datum/category_item/catalogue/fauna/alien/larva) /mob/living/carbon/alien/larva/Initialize(mapload) diff --git a/code/modules/mob/living/carbon/alien/progression.dm b/code/modules/mob/living/carbon/alien/progression.dm index a76fc409c230..c082f7d2e515 100644 --- a/code/modules/mob/living/carbon/alien/progression.dm +++ b/code/modules/mob/living/carbon/alien/progression.dm @@ -30,8 +30,7 @@ transfer_languages(src, adult) - if(src.faction != "neutral") - adult.faction = src.faction + adult.copy_iff_factions(src) if(mind) mind.transfer(adult) diff --git a/code/modules/mob/living/carbon/carbon-defense.dm b/code/modules/mob/living/carbon/carbon-defense.dm index 63a455454c09..8471cf172b62 100644 --- a/code/modules/mob/living/carbon/carbon-defense.dm +++ b/code/modules/mob/living/carbon/carbon-defense.dm @@ -1,5 +1,17 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station developers. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Projectile Handling *// + +/mob/living/carbon/process_bullet_miss(obj/projectile/proj, impact_flags, def_zone, efficiency) + . = ..() + if(!.) + return + // perform normal baymiss + . = get_zone_with_miss_chance(., src, -10, TRUE) + // check if we even have that organ; if not, they automatically miss + if(!get_organ(.)) + return null //* Misc Effects *// diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 366bba485291..4a47c832a90c 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -38,7 +38,7 @@ N.show_message("[M] bursts out of [src]!", 2) ..() -/mob/living/carbon/attack_hand(mob/user, list/params) +/mob/living/carbon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/carbon/M = user if(!istype(M)) return ..() diff --git a/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm b/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm index c43d9c05500a..d0275e768bdc 100644 --- a/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm +++ b/code/modules/mob/living/carbon/human/ai_controlled/ai_controlled.dm @@ -117,8 +117,6 @@ generate_gender = TRUE identifying_gender = NEUTER - faction = "xeno" - to_wear_helmet = /obj/item/clothing/head/helmet/dermal to_wear_glasses = /obj/item/clothing/glasses/goggles to_wear_mask = /obj/item/clothing/mask/gas/half diff --git a/code/modules/mob/living/carbon/human/defense.dm b/code/modules/mob/living/carbon/human/defense.dm index c5b686488c2c..f1035982f304 100644 --- a/code/modules/mob/living/carbon/human/defense.dm +++ b/code/modules/mob/living/carbon/human/defense.dm @@ -1,37 +1,20 @@ -/mob/living/carbon/human/check_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/obj/item/organ/external/part = get_organ(target_zone) - for(var/obj/item/I as anything in inventory?.items_that_cover(part.body_part_flags)) - var/list/results = I.checking_mob_armor(arglist(args)) - damage = results[1] - mode = results[4] - return ..() +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// -/mob/living/carbon/human/run_mob_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/obj/item/organ/external/part = get_organ(target_zone) - for(var/obj/item/I as anything in inventory?.items_that_cover(part.body_part_flags)) - var/list/results = I.running_mob_armor(arglist(args)) - damage = results[1] - mode = results[4] - return ..() +/mob/living/carbon/human/run_armorcalls(list/shieldcall_args, fake_attack, filter_zone) + ..() // perform default /mob level -/mob/living/carbon/human/check_overall_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) - var/total = 0 - var/total_size = 0 - for(var/key in organs_by_name) - var/rel_size = organ_rel_size[key] - if(!rel_size) - continue - var/obj/item/organ/external/part = organs_by_name[key] - var/resultant = damage + if(filter_zone) + // just one zone + var/obj/item/organ/external/part = get_organ(filter_zone) for(var/obj/item/I as anything in inventory?.items_that_cover(part.body_part_flags)) - var/list/results = I.checking_mob_armor(resultant, tier, flag, mode, attack_type, weapon, target_zone) - resultant = results[1] - total += resultant * rel_size - total_size += rel_size - damage = total / total_size - return ..() + I.mob_armorcall(src, shieldcall_args, fake_attack) + if(shieldcall_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_TERMINATE) + break + return -/mob/living/carbon/human/run_overall_armor(damage, tier, flag, mode, attack_type, datum/weapon, target_zone) + var/damage = shieldcall_args[SHIELDCALL_ARG_DAMAGE] + // all zones, uh oh, this is about to get very ugly var/total = 0 var/total_size = 0 for(var/key in organs_by_name) @@ -41,9 +24,10 @@ var/obj/item/organ/external/part = organs_by_name[key] var/resultant = damage for(var/obj/item/I as anything in inventory?.items_that_cover(part.body_part_flags)) - var/list/results = I.running_mob_armor(resultant, tier, flag, mode, attack_type, weapon, target_zone) - resultant = results[1] + var/list/copied_args = args.Copy() + copied_args[SHIELDCALL_ARG_DAMAGE] = resultant + I.mob_armorcall(src, copied_args, fake_attack) + resultant = copied_args[SHIELDCALL_ARG_DAMAGE] total += resultant * rel_size total_size += rel_size - damage = total / total_size - return ..() + shieldcall_args[SHIELDCALL_ARG_DAMAGE] = total / total_size diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human-damage-legacy.dm similarity index 98% rename from code/modules/mob/living/carbon/human/human_damage.dm rename to code/modules/mob/living/carbon/human/human-damage-legacy.dm index 212de7a6f405..f5a2e375af4f 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human-damage-legacy.dm @@ -350,9 +350,6 @@ This function restores all organs. current_organ.rejuvenate_legacy(ignore_prosthetic_prefs) /mob/living/carbon/human/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/soaked = 0, var/sharp = 0, var/edge = 0, var/obj/used_weapon = null) - if(GLOB.Debug2) - log_world("## DEBUG: human/apply_damage() was called on [src], with [damage] damage, an armor value of [blocked], and a soak value of [soaked].") - var/obj/item/organ/external/organ = null if(isorgan(def_zone)) organ = def_zone diff --git a/code/modules/mob/living/carbon/human/human-damage.dm b/code/modules/mob/living/carbon/human/human-damage.dm new file mode 100644 index 000000000000..1e411fffb295 --- /dev/null +++ b/code/modules/mob/living/carbon/human/human-damage.dm @@ -0,0 +1,4 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +// todo: start translating code over diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human-defense-legacy.dm similarity index 90% rename from code/modules/mob/living/carbon/human/human_defense.dm rename to code/modules/mob/living/carbon/human/human-defense-legacy.dm index c5ac3393588d..663da91a0673 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human-defense-legacy.dm @@ -1,44 +1,3 @@ -/* -Contains most of the procs that are called when a mob is attacked by something - -bullet_act -legacy_ex_act -meteor_act - -*/ - -/mob/living/carbon/human/bullet_act(var/obj/projectile/P, var/def_zone) - def_zone = check_zone(def_zone) - if(!has_organ(def_zone)) - return PROJECTILE_FORCE_MISS //if they don't have the organ in question then the projectile just passes by. - - var/obj/item/organ/external/organ = get_organ() - - //Shields - var/shield_check = check_shields(P.damage, P, null, def_zone, "the [P.name]") - if(shield_check) // If the block roll succeeded, this is true. - if(shield_check < 0) // The shield did something weird and the bullet needs to keep doing things (e.g. it was reflected). - return shield_check // Likely equal to PROJECTILE_FORCE_MISS or PROJECTILE_CONTINUE. - else // Otherwise we blocked normally and stopped all the damage. - return 0 - - if(!P.nodamage) - organ.add_autopsy_data("[P.name]", P.damage) - - //Shrapnel - if(P.can_embed()) - var/armor = getarmor_organ(organ, "bullet") - if(!prob(armor/2)) //Even if the armor doesn't stop the bullet from hurting you, it might stop it from embedding. - var/hit_embed_chance = P.embed_chance + (P.damage - armor) //More damage equals more chance to embed - if(prob(max(hit_embed_chance, 0))) - var/obj/item/material/shard/shrapnel/SP = new() - SP.name = (P.name != "shrapnel")? "[P.name] shrapnel" : "shrapnel" - SP.desc = "[SP.desc] It looks like it was fired from [P.shot_from]." - SP.loc = organ - organ.embed(SP) - - return ..() - /mob/living/carbon/human/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone) var/obj/item/organ/external/affected = get_organ(check_zone(def_zone)) var/siemens_coeff = get_siemens_coefficient_organ(affected) @@ -207,14 +166,8 @@ meteor_act return gear return null -/mob/living/carbon/human/proc/check_shields(var/damage = 0, var/atom/damage_source = null, var/mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - for(var/obj/item/shield in list(l_hand, r_hand, wear_suit)) - if(!shield) continue - . = shield.handle_shield(src, damage, damage_source, attacker, def_zone, attack_text) - if(.) return - return 0 - /mob/living/carbon/human/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) + SEND_SIGNAL(src, COMSIG_MOB_LEGACY_RESOLVE_ITEM_ATTACK, I, user, target_zone) if(check_neckgrab_attack(I, user, target_zone)) return null @@ -226,7 +179,9 @@ meteor_act if(!hit_zone) return null - if(check_shields(I.damage_force, I, user, target_zone, "the [I.name]")) + var/shieldcall_results = atom_shieldcall_handle_item_melee(I, new /datum/event_args/actor/clickchain(user), FALSE, NONE) + // todo: clickchain should be checked for damage mult + if(shieldcall_results & SHIELDCALL_FLAGS_BLOCK_ATTACK) return var/obj/item/organ/external/affecting = get_organ(hit_zone) @@ -377,17 +332,29 @@ meteor_act miss_chance = max(5 * (distance - 2), 0) zone = get_zone_with_miss_chance(zone, src, miss_chance, ranged_attack=1) - if(zone && TT.thrower != src) - var/shield_check = check_shields(throw_damage, O, TT.thrower, zone, "[O]") - if(shield_check == PROJECTILE_FORCE_MISS) - zone = null - else if(shield_check) - return + var/force_pierce = FALSE + var/no_attack = FALSE + if(zone) + // perform shieldcall + // todo: reconcile all the way down to /atom, or at least a higher level than /human. + var/retval + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + retval |= shieldcall.handle_throw_impact(src, TT) + if(retval & SHIELDCALL_FLAGS_SHOULD_TERMINATE) + break + if(retval & SHIELDCALL_FLAGS_SHOULD_PROCESS) + if(retval & SHIELDCALL_FLAGS_PIERCE_ATTACK) + force_pierce = TRUE + if(retval & SHIELDCALL_FLAGS_BLOCK_ATTACK) + no_attack = TRUE if(!zone) visible_message("\The [O] misses [src] narrowly!") return COMPONENT_THROW_HIT_NEVERMIND | COMPONENT_THROW_HIT_PIERCE + if(no_attack) + return force_pierce? COMPONENT_THROW_HIT_PIERCE | COMPONENT_THROW_HIT_NEVERMIND : NONE + var/obj/item/organ/external/affecting = get_organ(zone) var/hit_area = affecting.name @@ -406,7 +373,6 @@ meteor_act if(armor < 100) apply_damage(throw_damage, dtype, zone, armor, soaked, is_sharp(O), has_edge(O), O) - //thrown weapon embedded object code. if(dtype == BRUTE && istype(O,/obj/item)) var/obj/item/I = O @@ -450,6 +416,8 @@ meteor_act anchored = TRUE pinned += O + return force_pierce? COMPONENT_THROW_HIT_PIERCE | COMPONENT_THROW_HIT_NEVERMIND : NONE + // This does a prob check to catch the thing flying at you, with a minimum of 1% /mob/living/carbon/human/proc/can_catch(var/obj/O) if(!get_active_held_item()) // If active hand is empty diff --git a/code/modules/mob/living/carbon/human/human-defense.dm b/code/modules/mob/living/carbon/human/human-defense.dm index 09f8eb24c177..4de1b17baac5 100644 --- a/code/modules/mob/living/carbon/human/human-defense.dm +++ b/code/modules/mob/living/carbon/human/human-defense.dm @@ -1,6 +1,34 @@ //* This file is explicitly licensed under the MIT license. *// //* Copyright (c) 2024 Citadel Station developers. *// +//* Projectile Handling *// + +/mob/living/carbon/human/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_TARGET_ABORT) + return + + if(impact_flags & PROJECTILE_IMPACT_BLOCKED) + return + + // todo: this shit shouldn't be here + var/obj/item/organ/external/organ = get_organ() + + if(!proj.nodamage) + organ.add_autopsy_data("[proj.name]", proj.damage) + + //Shrapnel + if(proj.can_embed()) + var/armor = getarmor_organ(organ, "bullet") + if(!prob(armor/2)) //Even if the armor doesn't stop the bullet from hurting you, it might stop it from embedding. + var/hit_embed_chance = proj.embed_chance + (proj.damage - armor) //More damage equals more chance to embed + if(prob(max(hit_embed_chance, 0))) + var/obj/item/material/shard/shrapnel/SP = new() + SP.name = (proj.name != "shrapnel")? "[proj.name] shrapnel" : "shrapnel" + SP.desc = "[SP.desc] It looks like it was fired from [proj.shot_from]." + SP.loc = organ + organ.embed(SP) + //* Misc Effects *// /mob/living/carbon/human/slip_act(slip_class, source, hard_strength, soft_strength, suppressed) diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 56cf6cf52b3e..99fe7baf87c9 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -25,7 +25,7 @@ return u_attack return null -/mob/living/carbon/human/attack_hand(mob/user, list/params) +/mob/living/carbon/human/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/datum/gender/TT = GLOB.gender_datums[user.get_visible_gender()] var/mob/living/carbon/human/H = user if(istype(H)) @@ -53,9 +53,11 @@ visible_message("[H] reaches for [src], but misses!") return FALSE - if(H != src && check_shields(0, null, H, H.zone_sel.selecting, H.name)) - H.do_attack_animation(src) - return FALSE + if(user.a_intent != INTENT_HARM) + var/shieldcall_results = atom_shieldcall_handle_touch(new /datum/event_args/actor/clickchain(user)) + if(shieldcall_results & SHIELDCALL_FLAGS_BLOCK_ATTACK) + H.do_attack_animation(src) + return FALSE if(istype(user,/mob/living/carbon)) var/mob/living/carbon/C = user @@ -195,6 +197,11 @@ if(!attack) return FALSE + var/shieldcall_results = atom_shieldcall_handle_unarmed_melee(attack, new /datum/event_args/actor/clickchain(user)) + if(shieldcall_results & SHIELDCALL_FLAGS_BLOCK_ATTACK) + H.do_attack_animation(src) + return FALSE + if(attack.unarmed_override(H, src, hit_zone)) return FALSE diff --git a/code/modules/mob/living/carbon/human/traits/weaver_objs.dm b/code/modules/mob/living/carbon/human/traits/weaver_objs.dm index b08f5cd4bb27..19bf346ed2ef 100644 --- a/code/modules/mob/living/carbon/human/traits/weaver_objs.dm +++ b/code/modules/mob/living/carbon/human/traits/weaver_objs.dm @@ -22,9 +22,9 @@ var/global/list/weavable_items = list() visible_message("\The [src] has been [W.get_attack_verb(src, user)] with \the [W][(user ? " by [user]." : ".")]") qdel(src) -/obj/effect/weaversilk/bullet_act(var/obj/projectile/Proj) - ..() - if(Proj.get_structure_damage()) +/obj/effect/weaversilk/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.get_structure_damage()) qdel(src) /obj/effect/weaversilk/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) @@ -34,7 +34,7 @@ var/global/list/weavable_items = list() if(damage) qdel(src) -/obj/effect/weaversilk/attack_hand(mob/user, list/params) +/obj/effect/weaversilk/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(user.a_intent == INTENT_HARM) to_chat(user,"You easily tear down [name].") @@ -81,7 +81,7 @@ var/global/list/weavable_items = list() return ..() -/obj/structure/bed/double/weaversilk_nest/attack_hand(mob/user, list/params) +/obj/structure/bed/double/weaversilk_nest/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(user.a_intent == INTENT_HARM && !has_buckled_mobs()) to_chat(user,"You easily tear down [name].") diff --git a/code/modules/mob/living/inventory.dm b/code/modules/mob/living/inventory.dm index 8f179980acf6..c9db1ad494af 100644 --- a/code/modules/mob/living/inventory.dm +++ b/code/modules/mob/living/inventory.dm @@ -185,40 +185,6 @@ SLOT_ID_MASK ) -/mob/living/ret_grab(obj/effect/list_container/mobl/L as obj, flag) - if ((!( istype(l_hand, /obj/item/grab) ) && !( istype(r_hand, /obj/item/grab) ))) - if (!( L )) - return null - else - return L.container - else - if (!( L )) - L = new /obj/effect/list_container/mobl( null ) - L.container += src - L.master = src - if (istype(l_hand, /obj/item/grab)) - var/obj/item/grab/G = l_hand - if (!( L.container.Find(G.affecting) )) - L.container += G.affecting - if (G.affecting) - G.affecting.ret_grab(L, 1) - if (istype(r_hand, /obj/item/grab)) - var/obj/item/grab/G = r_hand - if (!( L.container.Find(G.affecting) )) - L.container += G.affecting - if (G.affecting) - G.affecting.ret_grab(L, 1) - if (!( flag )) - if (L.master == src) - var/list/temp = list( ) - temp += L.container - //L = null - qdel(L) - return temp - else - return L.container - return - /mob/living/abiotic(full_body) if(full_body) if(item_considered_abiotic(wear_mask)) diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/living-damage.dm similarity index 93% rename from code/modules/mob/living/damage_procs.dm rename to code/modules/mob/living/living-damage.dm index 379abddbf02e..ac09b0d13bc5 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/living-damage.dm @@ -206,3 +206,17 @@ return radiation = max(0, radiation - amt) +//* Damage Instance Handling *// + +/mob/living/inflict_damage_instance(SHIELDCALL_PROC_HEADER) + if(inflict_damage_type_special(args)) + return + + var/weapon_descriptor = RESOLVE_SHIELDCALL_WEAPON_DESCRIPTOR(args) + var/brute = damage_type == BRUTE? damage : 0 + var/burn = damage_type == BURN? damage : 0 + + if(hit_zone) + take_targeted_damage(brute, burn, damage_mode, hit_zone, weapon_descriptor) + else + take_overall_damage(brute, burn, damage_mode, weapon_descriptor) diff --git a/code/modules/mob/living/defense.dm b/code/modules/mob/living/living-defense-legacy.dm similarity index 86% rename from code/modules/mob/living/defense.dm rename to code/modules/mob/living/living-defense-legacy.dm index 1986b015bc07..1e994c49ede8 100644 --- a/code/modules/mob/living/defense.dm +++ b/code/modules/mob/living/living-defense-legacy.dm @@ -92,10 +92,11 @@ return 0 // Clicking with an empty hand -/mob/living/attack_hand(mob/user, list/params) +/mob/living/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return + SEND_SIGNAL(src, COMSIG_MOB_LEGACY_ATTACK_HAND_INTERCEPT, user, e_args) var/mob/living/L = user if(!istype(L)) return @@ -110,65 +111,6 @@ else afflict_radiation(strength * RAD_MOB_ACT_COEFFICIENT - RAD_MOB_ACT_PROTECTION_PER_WAVE_SOURCE, TRUE) -/mob/living/bullet_act(var/obj/projectile/P, var/def_zone) - - //Being hit while using a deadman switch - if(istype(get_active_held_item(),/obj/item/assembly/signaler)) - var/obj/item/assembly/signaler/signaler = get_active_held_item() - if(signaler.deadman && prob(80)) - log_and_message_admins("has triggered a signaler deadman's switch") - src.visible_message("[src] triggers their deadman's switch!") - signaler.signal() - - if(ai_holder && P.firer) - ai_holder.react_to_attack_polaris(P.firer) - - //Armor - var/soaked = get_armor_soak(def_zone, P.damage_flag, P.armor_penetration) - var/absorb = run_armor_check(def_zone, P.damage_flag, P.armor_penetration) - var/proj_sharp = is_sharp(P) - var/proj_edge = has_edge(P) - var/final_damage = P.get_final_damage(src) - - if ((proj_sharp || proj_edge) && (soaked >= round(P.damage*0.8))) - proj_sharp = 0 - proj_edge = 0 - - if ((proj_sharp || proj_edge) && prob(legacy_mob_armor(def_zone, P.damage_flag))) - proj_sharp = 0 - proj_edge = 0 - - var/list/impact_sounds = islist(P.impact_sounds)? LAZYACCESS(P.impact_sounds, get_bullet_impact_effect_type(def_zone)) : P.impact_sounds - if(length(impact_sounds)) - playsound(src, pick(impact_sounds), 75) - else if(!isnull(impact_sounds)) - playsound(src, impact_sounds, 75) - - //Stun Beams - if(P.taser_effect) - stun_effect_act(0, P.agony, def_zone, P) - to_chat(src, "You have been hit by [P]!") - if(!P.nodamage) - apply_damage(final_damage, P.damage_type, def_zone, absorb, soaked, 0, P, sharp=proj_sharp, edge=proj_edge) - qdel(P) - return - - if(!P.nodamage) - apply_damage(final_damage, P.damage_type, def_zone, absorb, soaked, 0, P, sharp=proj_sharp, edge=proj_edge) - P.on_hit(src, absorb, soaked, def_zone) - - if(absorb == 100) - return 2 - else if (absorb >= 0) - return 1 - else - return 0 - -// return absorb - -/mob/living/get_bullet_impact_effect_type(var/def_zone) - return BULLET_IMPACT_MEAT - //Handles the effects of "stun" weapons /mob/living/proc/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null) flash_pain() @@ -185,7 +127,7 @@ apply_effect(EYE_BLUR, agony_amount/10) /mob/living/proc/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null, var/stun = 1) - return 0 //only carbon liveforms have this proc + return 0 //only carbon liveforms have this proc /mob/living/emp_act(severity) var/list/L = src.get_equipped_items(TRUE, TRUE) @@ -194,7 +136,7 @@ ..() /mob/living/blob_act(var/obj/structure/blob/B) - if(stat == DEAD || faction == "blob") + if(stat == DEAD || has_iff_faction(MOB_IFF_FACTION_BLOB)) return var/damage = rand(30, 40) @@ -231,6 +173,13 @@ apply_damage(damage, damage_type, def_zone, absorb, soaked) /mob/living/proc/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) + SEND_SIGNAL(src, COMSIG_MOB_LEGACY_RESOLVE_ITEM_ATTACK, I, user, target_zone) + + var/shieldcall_results = atom_shieldcall_handle_item_melee(I, new /datum/event_args/actor/clickchain(user), FALSE, NONE) + // todo: clickchain should be checked for damage mult + if(shieldcall_results & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return + return target_zone //Called when the mob is hit with an item in combat. Returns the blocked result @@ -284,6 +233,32 @@ visible_message("\The [O] misses [src] narrowly!") return COMPONENT_THROW_HIT_PIERCE | COMPONENT_THROW_HIT_NEVERMIND + var/force_pierce = FALSE + var/no_attack = FALSE + + var/zone + if (istype(TT.thrower, /mob/living)) + zone = check_zone(TT.target_zone) + else + zone = ran_zone(BP_TORSO,75) //Hits a random part of the body, geared towards the chest + + if(zone) + // perform shieldcall + // todo: reconcile all the way down to /atom, or at least a higher level than /human. + var/retval + for(var/datum/shieldcall/shieldcall as anything in shieldcalls) + retval |= shieldcall.handle_throw_impact(src, TT) + if(retval & SHIELDCALL_FLAGS_SHOULD_TERMINATE) + break + if(retval & SHIELDCALL_FLAGS_SHOULD_PROCESS) + if(retval & SHIELDCALL_FLAGS_PIERCE_ATTACK) + force_pierce = TRUE + if(retval & SHIELDCALL_FLAGS_BLOCK_ATTACK) + no_attack = TRUE + + if(no_attack) + return force_pierce? COMPONENT_THROW_HIT_PIERCE | COMPONENT_THROW_HIT_NEVERMIND : NONE + src.visible_message("[src] has been hit by [O].") var/armor = run_armor_check(null, "melee") var/soaked = get_armor_soak(null, "melee") @@ -325,11 +300,13 @@ var/turf/T = near_wall(dir,2) if(T) - src.loc = T + src.forceMove(T) visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!") src.anchored = 1 src.pinned += O + return force_pierce? COMPONENT_THROW_HIT_PIERCE | COMPONENT_THROW_HIT_NEVERMIND : NONE + /mob/living/proc/embed(var/obj/O, var/def_zone=null) O.loc = src src.embedded += O @@ -487,6 +464,7 @@ /mob/living/proc/reagent_permeability() return 1 +// todo: rework // Returns a number to determine if something is harder or easier to hit than normal. /mob/living/proc/get_evasion() var/result = evasion // First we get the 'base' evasion. Generally this is zero. @@ -495,14 +473,6 @@ result += M.evasion return result +// todo: rework /mob/living/proc/get_accuracy_penalty() - // Certain statuses make it harder to score a hit. - var/accuracy_penalty = 0 - if(has_status_effect(/datum/status_effect/sight/blindness)) - accuracy_penalty += 75 - if(eye_blurry) - accuracy_penalty += 30 - if(confused) - accuracy_penalty += 45 - - return accuracy_penalty + return 0 diff --git a/code/modules/mob/living/living-defense.dm b/code/modules/mob/living/living-defense.dm index 3bdb111c5bc8..bce917a898b5 100644 --- a/code/modules/mob/living/living-defense.dm +++ b/code/modules/mob/living/living-defense.dm @@ -1,5 +1,115 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station developers. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Projectile Handling *// + +/mob/living/bullet_act(obj/projectile/proj, impact_flags, def_zone, efficiency) + //! LEGACY + + // Using someone as a shield + // todo: need a counter to this.. + for(var/mob/living/victim in get_grabbing_of_state(GRAB_NECK)) + if(victim.stat == DEAD) + // small mobs are penalized; this is a holdover. + var/shield_chance = min(80, (30 * (mob_size / 10))) + if(prob(shield_chance)) + visible_message("\The [src] uses [victim] as a shield!") + if(!(proj.impact_redirect(victim, args) | (PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH | PROJECTILE_IMPACT_DUPLICATE))) + return + else + visible_message("\The [src] tries to use [victim] as a shield, but fails!") + else + visible_message("\The [src] uses [victim] as a shield!") + if(!(proj.impact_redirect(victim, args) | (PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH | PROJECTILE_IMPACT_DUPLICATE))) + return + // Process baymiss & zonemiss + def_zone = process_bullet_miss(proj, impact_flags, def_zone, efficiency) + def_zone = proj.process_zone_miss(src, def_zone, proj.distance_travelled, TRUE) + if(!def_zone) + if(!proj.silenced) + visible_message(SPAN_WARNING("\The [proj] misses [src] narrowly!")) + playsound(src, pick(proj.miss_sounds), 60, TRUE) + add_attack_logs( + proj.firer, + src, + "shot with [src] ([type]) (missed)", + ) + impact_flags |= PROJECTILE_IMPACT_PASSTHROUGH + return ..() + + //! END + + return ..() + +/mob/living/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + // todo: better logging + if(impact_flags & PROJECTILE_IMPACT_FLAGS_TARGET_ABORT) + add_attack_logs( + proj.firer, + src, + "shot with [src] ([type]) (aborted)", + ) + return + add_attack_logs( + proj.firer, + src, + "shot with [src] ([type])[(impact_flags & PROJECTILE_IMPACT_BLOCKED)? " (blocked)" : ""]", + ) + // emit feedback + if(!(impact_flags & PROJECTILE_IMPACT_BLOCKED)) + if(proj.silenced) + to_chat(src, SPAN_DANGER("You've been hit in the [parse_zone(bullet_act_args[BULLET_ACT_ARG_ZONE])] with \the [proj]!")) + else + visible_message(SPAN_DANGER("\The [src] is hit by [proj] in the [parse_zone(bullet_act_args[BULLET_ACT_ARG_ZONE])]")) + + //! LEGACY + + //Being hit while using a deadman switch + for(var/obj/item/assembly/signaler/signaler in get_held_items()) + if(signaler.deadman && prob(80)) + log_and_message_admins("has triggered a signaler deadman's switch") + visible_message("[src] triggers their deadman's switch!") + signaler.signal() + + if(ai_holder && proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + + //! END + + if(!(impact_flags & (PROJECTILE_IMPACT_BLOCKED | PROJECTILE_IMPACT_SKIP_STANDARD_DAMAGE))) + // todo: this should just be in base projectile on_impact + impact_flags |= proj.inflict_impact_damage(src, bullet_act_args[BULLET_ACT_ARG_EFFICIENCY], impact_flags, bullet_act_args[BULLET_ACT_ARG_ZONE]) + return ..() + +/mob/living/get_bullet_impact_effect_type(var/def_zone) + return BULLET_IMPACT_MEAT + +/** + * @return zone to hit, or null to miss + */ +/mob/living/proc/process_bullet_miss(obj/projectile/proj, impact_flags, def_zone, efficiency) + var/hit_probability = process_baymiss(proj) + if(!prob(hit_probability)) + return null + return def_zone + +/** + * * our_opinion is intentionally mutable; it is however only mutable from before ..(), so call ..() after modifying for pre-modification + * * our_opinion and impact_check are defaulted in the base function; this means that if you need to use it before, default it yourself. + * + * todo: 0 to 100 for accuracy might not be amazing; maybe allow negative values evasion-style? + * todo: don't default our_opinion and impact_check so early wtf; BYOND proc structure disagrees with the design here. + * + * @params + * * proj - the projectile + * * our_opinion - base probability of hitting + * * impact_check - are we checking if we should impact at all? used by pellets. + * + * @return 0 to 100 % probability of hitting + */ +/mob/living/proc/process_baymiss(obj/projectile/proj, our_opinion = 100, impact_check = TRUE) + our_opinion = clamp(our_opinion - get_evasion(), 5, INFINITY) + return proj.process_accuracy(src, our_opinion, null, impact_check) //* Misc Effects *// @@ -20,3 +130,5 @@ */ /mob/living/proc/slip_act(slip_class, source, hard_strength, soft_strength, suppressed) return 1 +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index d65c614a6c67..9312e6a33a18 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -73,7 +73,9 @@ /// This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks. var/failed_last_breath = 0 - var/lastpuke = 0 + var/lastpuke = 0//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + /// Makes attacks harder to land. Negative numbers increase hit chance. var/evasion = 0 diff --git a/code/modules/mob/living/movement.dm b/code/modules/mob/living/movement.dm index c67d50f33fa6..f75f2182feb7 100644 --- a/code/modules/mob/living/movement.dm +++ b/code/modules/mob/living/movement.dm @@ -396,7 +396,7 @@ // riding same thing, don't block each other return TRUE // can't throw blob stuff through blob stuff - if(istype(mover, /obj/structure/blob) && faction == "blob" && !mover.throwing) //Blobs should ignore things on their faction. + if(istype(mover, /obj/structure/blob) && has_iff_faction(MOB_IFF_FACTION_BLOB) && !mover.throwing) //Blobs should ignore things on their faction. return TRUE /mob/living/CheckExit(atom/movable/AM, atom/newLoc) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 8b7ac8908cc5..cac79cb47bed 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -236,7 +236,7 @@ var/list/channel_to_radio_key = new verb = speaking.speech_verb w_not_heard = "[speaking.speech_verb] something [w_adverb]" - var/list/message_args = list("message" = message, "whispering" = whispering, "cancelled" = FALSE) + var/list/message_args = list("message" = message, "whispering" = whispering, "cancelled" = FALSE, "message_mode" = message_mode) SEND_SIGNAL(src, COMSIG_MOB_SAY, message_args) diff --git a/code/modules/mob/living/silicon/pai/defense.dm b/code/modules/mob/living/silicon/pai/defense.dm index 2811411b7799..d6faec813e36 100644 --- a/code/modules/mob/living/silicon/pai/defense.dm +++ b/code/modules/mob/living/silicon/pai/defense.dm @@ -36,7 +36,7 @@ else . = ..() -/mob/living/silicon/pai/attack_hand(mob/user, list/params) +/mob/living/silicon/pai/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(. & CLICKCHAIN_DO_NOT_PROPAGATE) return diff --git a/code/modules/mob/living/silicon/pai/hologram_effect.dm b/code/modules/mob/living/silicon/pai/hologram_effect.dm index 8c84bf80b10c..8bc6cc288ab8 100644 --- a/code/modules/mob/living/silicon/pai/hologram_effect.dm +++ b/code/modules/mob/living/silicon/pai/hologram_effect.dm @@ -10,7 +10,7 @@ /obj/effect/pai_hologram/attackby(obj/item/W, mob/user) hologram_destroy(user) -/obj/effect/pai_hologram/attack_hand(mob/user, list/params) +/obj/effect/pai_hologram/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) hologram_destroy(user) /obj/effect/pai_hologram/proc/hologram_destroy(mob/user) diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm index e11d2eab337b..5687625be2ef 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -19,7 +19,7 @@ return UI_CLOSE return ..() -/obj/machinery/computer/drone_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/drone_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return diff --git a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm index 3e7214da92d6..0f453fe6feac 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm @@ -184,7 +184,7 @@ var/obj/machinery/drone_fabricator/chosen_fabricator = all_fabricators[choice] chosen_fabricator.create_drone(src.client) -/obj/machinery/drone_fabricator/attack_hand(mob/user, list/params) +/obj/machinery/drone_fabricator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!is_spawn_safe) is_spawn_safe = TRUE to_chat(user, "You inform the fabricator that it is safe for drones to roam around.") diff --git a/code/modules/mob/living/silicon/robot/drone/swarm.dm b/code/modules/mob/living/silicon/robot/drone/swarm.dm index 5cb7268301db..8a45b86d4181 100644 --- a/code/modules/mob/living/silicon/robot/drone/swarm.dm +++ b/code/modules/mob/living/silicon/robot/drone/swarm.dm @@ -3,7 +3,7 @@ real_name = "drone" icon = 'icons/mob/swarmbot.dmi' icon_state = "swarmer" - faction = "swarmer" + iff_factions = MOB_IFF_FACTION_SWARMER maxHealth = 75 health = 75 cell_emp_mult = 0.5 @@ -69,7 +69,7 @@ real_name = "drone" icon = 'icons/mob/swarmbot.dmi' icon_state = "swarmer_ranged" - faction = "swarmer" + iff_factions = MOB_IFF_FACTION_SWARMER law_type = /datum/ai_laws/swarm_drone/soldier module_type = /obj/item/robot_module/drone/swarm/ranged @@ -85,7 +85,7 @@ real_name = "drone" icon = 'icons/mob/swarmbot.dmi' icon_state = "swarmer_melee" - faction = "swarmer" + iff_factions = MOB_IFF_FACTION_SWARMER law_type = /datum/ai_laws/swarm_drone/soldier module_type = /obj/item/robot_module/drone/swarm/melee diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 9c4ea9a0aecf..89781e21ebb3 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -533,11 +533,13 @@ /mob/living/silicon/robot/restrained() return 0 -/mob/living/silicon/robot/bullet_act(var/obj/projectile/Proj) - ..(Proj) - if(prob(75) && Proj.damage > 0) +/mob/living/silicon/robot/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + // todo: why is this in bullet act and not where we take damage maybe? + if(prob(75) && proj.damage > 0) spark_system.start() - return 2 /mob/living/silicon/robot/attackby(obj/item/W as obj, mob/user as mob) if (istype(W, /obj/item/handcuffs)) // fuck i don't even know why isrobot() in handcuff code isn't working so this will have to do @@ -830,7 +832,7 @@ module = null updatename("Default") -/mob/living/silicon/robot/attack_hand(mob/user, list/params) +/mob/living/silicon/robot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(. & CLICKCHAIN_DO_NOT_PROPAGATE) return diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station/misc.dm b/code/modules/mob/living/silicon/robot/robot_modules/station/misc.dm index 1d21b94abe8a..bffbbf6c866f 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station/misc.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station/misc.dm @@ -37,7 +37,7 @@ /obj/item/robot_module/robot/standard/handle_special_module_init(mob/living/silicon/robot/R) . = ..() - src.emag = new /obj/item/melee/energy/sword(src) + src.emag = new /obj/item/melee/transforming/energy/sword(src) /obj/item/robot_module/robot/quad/basic name = "Standard Quadruped module" @@ -59,4 +59,4 @@ . = ..() // These get a larger water synth. synths_by_kind[MATSYN_WATER]:max_energy = 1000 - src.emag = new /obj/item/melee/energy/sword(src) + src.emag = new /obj/item/melee/transforming/energy/sword(src) diff --git a/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm b/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm index 543a007c2d6d..5012c5031a35 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm @@ -27,4 +27,4 @@ /obj/item/robot_module/drone/swarm/melee/get_modules() . = ..() - . |= /obj/item/melee/energy/sword/ionic_rapier/lance + . |= /obj/item/melee/transforming/energy/sword/ionic_rapier/lance diff --git a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm index 02c593c5337c..fcb77a94c8f2 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm @@ -35,7 +35,7 @@ . = ..() . |= list( /obj/item/pinpointer/shuttle/merc, - /obj/item/melee/energy/sword + /obj/item/melee/transforming/energy/sword ) /obj/item/robot_module/robot/syndicate/handle_special_module_init(mob/living/silicon/robot/R) @@ -90,7 +90,7 @@ /obj/item/multitool/ai_detector, /obj/item/pickaxe/plasmacutter, /obj/item/rcd/electric/mounted/borg/lesser, // Can't eat rwalls to prevent AI core cheese. - /obj/item/melee/energy/sword/ionic_rapier, + /obj/item/melee/transforming/energy/sword/ionic_rapier, // FBP repair. /obj/item/robotanalyzer, diff --git a/code/modules/mob/living/silicon/silicon-damage.dm b/code/modules/mob/living/silicon/silicon-damage.dm new file mode 100644 index 000000000000..d22faa575803 --- /dev/null +++ b/code/modules/mob/living/silicon/silicon-damage.dm @@ -0,0 +1,14 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Damage Instance Handling *// + +/mob/living/silicon/inflict_damage_instance(damage, damage_type, damage_tier, damage_flag, damage_mode, attack_type, datum/weapon, shieldcall_flags, hit_zone, list/additional, datum/event_args/actor/clickchain/clickchain) + if(inflict_damage_type_special(args)) + return + // we only care about those + switch(damage_type) + if(BRUTE) + adjustBruteLoss(damage) + if(BURN) + adjustBruteLoss(damage) diff --git a/code/modules/mob/living/silicon/silicon-defense-legacy.dm b/code/modules/mob/living/silicon/silicon-defense-legacy.dm new file mode 100644 index 000000000000..bf80122caf1f --- /dev/null +++ b/code/modules/mob/living/silicon/silicon-defense-legacy.dm @@ -0,0 +1,57 @@ +/mob/living/silicon/emp_act(severity) + switch(severity) + if(1) + src.take_random_targeted_damage(brute = 0, burn = 20, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") + Confuse(5) + if(2) + src.take_random_targeted_damage(brute = 0, burn = 15, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") + Confuse(4) + if(3) + src.take_random_targeted_damage(brute = 0, burn = 10, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") + Confuse(3) + if(4) + src.take_random_targeted_damage(brute = 0, burn = 5, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") + Confuse(2) + flash_eyes(affect_silicon = 1) + to_chat(src, "*BZZZT*") + to_chat(src, "Warning: Electromagnetic pulse detected.") + ..() + +/mob/living/silicon/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null) + return //immune + +/mob/living/silicon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null, var/stun = 1) + if(shock_damage > 0) + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(5, 1, loc) + s.start() + + shock_damage *= siemens_coeff //take reduced damage + take_overall_damage(0, shock_damage) + visible_message("[src] was shocked by \the [source]!", \ + "Energy pulse detected, system damaged!", \ + "You hear an electrical crack.") + if(prob(20)) + afflict_stun(20 * 2) + return + +/mob/living/silicon/legacy_ex_act(severity) + if(!has_status_effect(/datum/status_effect/sight/blindness)) + flash_eyes() + + switch(severity) + if(1.0) + if (stat != 2) + adjustBruteLoss(100) + adjustFireLoss(100) + if(!anchored) + gib() + if(2.0) + if (stat != 2) + adjustBruteLoss(60) + adjustFireLoss(60) + if(3.0) + if (stat != 2) + adjustBruteLoss(30) + + update_health() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 479e26ccf902..0f0a42b46378 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -112,62 +112,9 @@ else src.bodytemp.icon_state = "temp-2" -/mob/living/silicon/emp_act(severity) - switch(severity) - if(1) - src.take_random_targeted_damage(brute = 0, burn = 20, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") - Confuse(5) - if(2) - src.take_random_targeted_damage(brute = 0, burn = 15, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") - Confuse(4) - if(3) - src.take_random_targeted_damage(brute = 0, burn = 10, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") - Confuse(3) - if(4) - src.take_random_targeted_damage(brute = 0, burn = 5, damage_mode = DAMAGE_MODE_INTERNAL, weapon_descriptor = "electromagnetic surge") - Confuse(2) - flash_eyes(affect_silicon = 1) - to_chat(src, "*BZZZT*") - to_chat(src, "Warning: Electromagnetic pulse detected.") - ..() - -/mob/living/silicon/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone, var/used_weapon=null) - return //immune - -/mob/living/silicon/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null, var/stun = 1) - if(shock_damage > 0) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, loc) - s.start() - - shock_damage *= siemens_coeff //take reduced damage - take_overall_damage(0, shock_damage) - visible_message("[src] was shocked by \the [source]!", \ - "Energy pulse detected, system damaged!", \ - "You hear an electrical crack.") - if(prob(20)) - afflict_stun(20 * 2) - return - -/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) - return - /mob/living/silicon/IsAdvancedToolUser() return 1 -/mob/living/silicon/bullet_act(var/obj/projectile/Proj) - - if(!Proj.nodamage) - switch(Proj.damage_type) - if(BRUTE) - adjustBruteLoss(Proj.get_final_damage(src)) - if(BURN) - adjustFireLoss(Proj.get_final_damage(src)) - - Proj.on_hit(src,2) - update_health() - return 2 - /mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/check_protection = 1) return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now @@ -300,27 +247,6 @@ /mob/living/silicon/binarycheck() return 1 -/mob/living/silicon/legacy_ex_act(severity) - if(!has_status_effect(/datum/status_effect/sight/blindness)) - flash_eyes() - - switch(severity) - if(1.0) - if (stat != 2) - adjustBruteLoss(100) - adjustFireLoss(100) - if(!anchored) - gib() - if(2.0) - if (stat != 2) - adjustBruteLoss(60) - adjustFireLoss(60) - if(3.0) - if (stat != 2) - adjustBruteLoss(30) - - update_health() - /mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) if(!next_alarm_notice) next_alarm_notice = world.time + SecondsToTicks(10) diff --git a/code/modules/mob/living/simple_animal/constructs/constructs.dm b/code/modules/mob/living/simple_animal/constructs/constructs.dm index 5f2b43820182..32e833179976 100644 --- a/code/modules/mob/living/simple_animal/constructs/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs/constructs.dm @@ -210,7 +210,7 @@ weakened = 0 ..() -/mob/living/simple_animal/construct/armoured/bullet_act(var/obj/projectile/P) +/mob/living/simple_animal/construct/armoured/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) // if(istype(P, /obj/projectile/energy) || istype(P, /obj/projectile/beam)) //If it's going to be slow, it's probably going to need every reflect it can get. var/reflectchance = 80 - round(P.damage/3) if(prob(reflectchance)) @@ -364,7 +364,7 @@ /spell/targeted/construct_advanced/slam ) -/mob/living/simple_animal/construct/behemoth/bullet_act(var/obj/projectile/P) +/mob/living/simple_animal/construct/behemoth/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) var/reflectchance = 80 - round(P.damage/3) if(prob(reflectchance)) visible_message("The [P.name] gets reflected by [src]'s shell!", \ diff --git a/code/modules/mob/living/simple_mob/combat.dm b/code/modules/mob/living/simple_mob/combat.dm index 9ac88e373eea..e226cbbfded4 100644 --- a/code/modules/mob/living/simple_mob/combat.dm +++ b/code/modules/mob/living/simple_mob/combat.dm @@ -62,10 +62,10 @@ playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1) return FALSE // We missed. - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.check_shields(damage = damage_to_do, damage_source = src, attacker = src, def_zone = null, attack_text = "the attack")) - return FALSE // We were blocked. + var/datum/event_args/actor/clickchain/simulated_clickchain = new(src, target = L) + var/list/shieldcall_result = L.atom_shieldcall(damage_to_do, BRUTE, MELEE_TIER_MEDIUM, ARMOR_MELEE, NONE, ATTACK_TYPE_MELEE, clickchain = simulated_clickchain) + if(shieldcall_result[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return FALSE if(apply_attack(A, damage_to_do)) apply_melee_effects(A) @@ -138,7 +138,7 @@ // For some reason there isn't an argument for accuracy, so access the projectile directly instead. // Also, placing dispersion here instead of in forced_spread will randomize the chosen angle between dispersion and -dispersion in fire() instead of having to do that here. - P.accuracy += calculate_accuracy() + P.accuracy_overall_modify *= 1 + calculate_accuracy() / 100 P.dispersion += calculate_dispersion() P.launch_projectile(target = A, target_zone = null, user = src, params = null, angle_override = null, forced_spread = 0) diff --git a/code/modules/mob/living/simple_mob/defense.dm b/code/modules/mob/living/simple_mob/defense.dm index 5bb0b7f077ec..af337354f3ea 100644 --- a/code/modules/mob/living/simple_mob/defense.dm +++ b/code/modules/mob/living/simple_mob/defense.dm @@ -1,5 +1,5 @@ // When someone clicks us with an empty hand -/mob/living/simple_mob/attack_hand(mob/user, list/params) +/mob/living/simple_mob/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/mob/living/simple_mob/simple_mob.dm b/code/modules/mob/living/simple_mob/simple_mob.dm index af9e9d3dd7bc..909771c1d520 100644 --- a/code/modules/mob/living/simple_mob/simple_mob.dm +++ b/code/modules/mob/living/simple_mob/simple_mob.dm @@ -14,6 +14,8 @@ mob_swap_flags = ~HEAVY mob_push_flags = ~HEAVY + iff_factions = MOB_IFF_FACTION_BIND_AUTO + //? Attacks - Basic /// melee style var/datum/unarmed_attack/melee_style diff --git a/code/modules/mob/living/simple_mob/simple_mob_vr.dm b/code/modules/mob/living/simple_mob/simple_mob_vr.dm index 73b8dab42235..ec7c1836ebe5 100644 --- a/code/modules/mob/living/simple_mob/simple_mob_vr.dm +++ b/code/modules/mob/living/simple_mob/simple_mob_vr.dm @@ -1,45 +1,8 @@ -// Flags for specifying which states we have vore icon_states for. -#define SA_ICON_LIVING 0x01 -#define SA_ICON_DEAD 0x02 -#define SA_ICON_REST 0x04 - /mob/living/simple_mob base_attack_cooldown = 15 var/temperature_range = 40 // How close will they get to environmental temperature before their body stops changing its heat - var/vore_active = 0 // If vore behavior is enabled for this mob - - var/vore_capacity = 1 // The capacity (in people) this person can hold - var/vore_max_size = RESIZE_HUGE // The max size this mob will consider eating - var/vore_min_size = RESIZE_TINY // The min size this mob will consider eating - var/vore_bump_chance = 0 // Chance of trying to eat anyone that bumps into them, regardless of hostility - var/vore_bump_emote = "grabs hold of" // Allow messages for bumpnom mobs to have a flavorful bumpnom - var/vore_pounce_chance = 5 // Chance of this mob knocking down an opponent - var/vore_pounce_cooldown = 0 // Cooldown timer - if it fails a pounce it won't pounce again for a while - var/vore_pounce_successrate = 100 // Chance of a pounce succeeding against a theoretical 0-health opponent - var/vore_pounce_falloff = 1 // Success rate falloff per %health of target mob. - var/vore_pounce_maxhealth = 80 // Mob will not attempt to pounce targets above this %health - var/vore_standing_too = 0 // Can also eat non-stunned mobs - var/vore_ignores_undigestable = 1 // Refuse to eat mobs who are undigestable by the prefs toggle. - var/swallowsound = null // What noise plays when you succeed in eating the mob. - - var/vore_default_mode = DM_DIGEST // Default bellymode (DM_DIGEST, DM_HOLD, DM_ABSORB) - var/vore_default_flags = 0 // No flags - var/vore_digest_chance = 25 // Chance to switch to digest mode if resisted - var/vore_absorb_chance = 0 // Chance to switch to absorb mode if resisted - var/vore_escape_chance = 25 // Chance of resisting out of mob - - var/vore_stomach_name // The name for the first belly if not "stomach" - var/vore_stomach_flavor // The flavortext for the first belly if not the default - - var/vore_default_item_mode = IM_DIGEST_FOOD //How belly will interact with items - var/vore_default_contaminates = TRUE //Will it contaminate? - var/vore_default_contamination_flavor = "Generic" //Contamination descriptors - var/vore_default_contamination_color = "green" //Contamination color - - var/vore_fullness = 0 // How "full" the belly is (controls icons) - var/vore_icons = 0 // Bitfield for which fields we have vore icons for. var/life_disabled = 0 // For performance reasons var/obj/item/radio/headset/mob_headset/mob_radio //Adminbus headset for simplemob shenanigans. @@ -47,67 +10,20 @@ // Release belly contents before being gc'd! /mob/living/simple_mob/Destroy() release_vore_contents() - prey_excludes.Cut() - . = ..() + return ..() //For all those ID-having mobs /mob/living/simple_mob/GetIdCard() if(access_card) return access_card -// Update fullness based on size & quantity of belly contents -/mob/living/simple_mob/proc/update_fullness() - var/new_fullness = 0 - for(var/belly in vore_organs) - var/obj/belly/B = belly - for(var/mob/living/M in B) - new_fullness += M.size_multiplier - new_fullness = round(new_fullness, 1) // Because intervals of 0.25 are going to make sprite artists cry. - vore_fullness = min(vore_capacity, new_fullness) - -/mob/living/simple_mob/update_icon() - . = ..() - if(vore_active) - update_fullness() - if(!vore_fullness) - return 0 - else if((stat == CONSCIOUS) && (!icon_rest || !resting || !incapacitated(INCAPACITATION_DISABLED)) && (vore_icons & SA_ICON_LIVING)) - icon_state = "[icon_living]-[vore_fullness]" - else if(stat >= DEAD && (vore_icons & SA_ICON_DEAD)) - icon_state = "[icon_dead]-[vore_fullness]" - else if(((stat == UNCONSCIOUS) || resting || incapacitated(INCAPACITATION_DISABLED) ) && icon_rest && (vore_icons & SA_ICON_REST)) - icon_state = "[icon_rest]-[vore_fullness]" - -/mob/living/simple_mob/proc/will_eat(var/mob/living/M) - return FALSE // no more mobvore - -// Attempt to eat target -// TODO - Review this. Could be some issues here -/mob/living/simple_mob/proc/EatTarget(var/mob/living/M) - var/old_target = M - set_AI_busy(1) - . = animal_nom(M) - playsound(src, swallowsound, 50, 1) - update_icon() - - if(.) - // If we succesfully ate them, lose the target - set_AI_busy(0) - return old_target - else if(old_target == M) - // If we didn't but they are still our target, go back to attack. - // but don't run the handler immediately, wait until next tick - // Otherwise we'll be in a possibly infinate loop - set_AI_busy(0) - /mob/living/simple_mob/death() release_vore_contents() - . = ..() + return ..() // Make sure you don't call ..() on this one, otherwise you duplicate work. /mob/living/simple_mob/init_vore() - if(!vore_active || no_vore) - return + . = ..() if(!IsAdvancedToolUser()) add_verb(src, /mob/living/simple_mob/proc/animal_nom) @@ -116,63 +32,12 @@ if(LAZYLEN(vore_organs)) return - // Since they have bellies, add verbs to toggle settings on them. - add_verb(src, /mob/living/simple_mob/proc/toggle_digestion) - add_verb(src, /mob/living/simple_mob/proc/toggle_fancygurgle) - - //A much more detailed version of the default /living implementation - var/obj/belly/B = new /obj/belly(src) - vore_selected = B - B.immutable = 1 - B.name = vore_stomach_name ? vore_stomach_name : "stomach" - B.desc = vore_stomach_flavor ? vore_stomach_flavor : "Your surroundings are warm, soft, and slimy. Makes sense, considering you're inside \the [name]." - B.digest_mode = vore_default_mode - B.mode_flags = vore_default_flags - B.item_digest_mode = vore_default_item_mode - B.contaminates = vore_default_contaminates - B.contamination_flavor = vore_default_contamination_flavor - B.contamination_color = vore_default_contamination_color - B.escapable = vore_escape_chance > 0 - B.escapechance = vore_escape_chance - B.digestchance = vore_digest_chance - B.absorbchance = vore_absorb_chance - B.human_prey_swallow_time = swallowTime - B.nonhuman_prey_swallow_time = swallowTime - B.vore_verb = "swallow" - B.emote_lists[DM_HOLD] = list( // We need more that aren't repetitive. I suck at endo. -Ace - "The insides knead at you gently for a moment.", - "The guts glorp wetly around you as some air shifts.", - "The predator takes a deep breath and sighs, shifting you somewhat.", - "The stomach squeezes you tight for a moment, then relaxes harmlessly.", - "The predator's calm breathing and thumping heartbeat pulses around you.", - "The warm walls kneads harmlessly against you.", - "The liquids churn around you, though there doesn't seem to be much effect.", - "The sound of bodily movements drown out everything for a moment.", - "The predator's movements gently force you into a different position.") - B.emote_lists[DM_DIGEST] = list( - "The burning acids eat away at your form.", - "The muscular stomach flesh grinds harshly against you.", - "The caustic air stings your chest when you try to breathe.", - "The slimy guts squeeze inward to help the digestive juices soften you up.", - "The onslaught against your body doesn't seem to be letting up; you're food now.", - "The predator's body ripples and crushes against you as digestive enzymes pull you apart.", - "The juices pooling beneath you sizzle against your sore skin.", - "The churning walls slowly pulverize you into meaty nutrients.", - "The stomach glorps and gurgles as it tries to work you into slop.") - // Checks to see if mob doesn't like this kind of turf /mob/living/simple_mob/IMove(turf/newloc, safety = TRUE) if(istype(newloc,/turf/simulated/floor/sky)) return MOVEMENT_FAILED //Mobs aren't that stupid, probably return ..() // Procede as normal. -//Grab = Nomf -/mob/living/simple_mob/UnarmedAttack(var/atom/A, var/proximity) - . = ..() - - if(a_intent == INTENT_GRAB && isliving(A) && !has_hands) - animal_nom(A) - // todo: shitcode, rewrite on say rewrite /mob/living/simple_mob/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name) switch(message_mode) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm index 0944f22c199e..1aa2a52144f3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm @@ -51,7 +51,6 @@ var/roundstart = FALSE // If true, spawning won't try to pull a ghost. var/used_dominate // world.time when the dominate power was last used. - /mob/living/simple_mob/animal/borer/roundstart roundstart = TRUE @@ -267,3 +266,9 @@ continue else if(M.stat == DEAD && M.get_preference_toggle(/datum/game_preference_toggle/observer/ghost_ears)) to_chat(M, "[src.true_name] whispers to [host], \"[message]\"") + +/mob/living/simple_mob/animal/borer/proc/surgically_remove(mob/living/carbon/human/target, obj/item/organ/external/chest/removing_from) + if(controlling) + target.release_control() + detatch() + leave_host() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm index dd1534a2565c..4c253e499ba0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/cow.dm @@ -62,7 +62,7 @@ if(udder && prob(5)) udder.add_reagent("milk", rand(5, 10)) -/mob/living/simple_mob/animal/passive/cow/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/cow/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/M = user if(!istype(M)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm index bc4762728287..9eb1d0be798c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/goat.dm @@ -16,7 +16,9 @@ icon_dead = "goat_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/livestock/goat) - faction = "goat" + iff_factions = list( + MOB_IFF_FACTION_FARM_ANIMAL, + ) health = 40 maxHealth = 40 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/lythios.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/lythios.dm index b6000dcdd3c3..79981b31e3d7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/lythios.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/lythios.dm @@ -22,7 +22,7 @@ icon_dead = "icegoat_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/livestock/icegoat) - faction = "goat" + iff_factions = MOB_IFF_FACTION_FARM_ANIMAL minbodytemp = 180 maxbodytemp = 275 @@ -117,7 +117,7 @@ icon_dead = "woolie_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/livestock/woolie) - faction = "goat" + iff_factions = MOB_IFF_FACTION_FARM_ANIMAL minbodytemp = 180 maxbodytemp = 300 @@ -189,7 +189,8 @@ minbodytemp = 180 maxbodytemp = 350 - faction = "grubs" + iff_factions = MOB_IFF_FACTION_FARM_ANIMAL + maxHealth = 50 health = 50 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm index ecf24ffe0e56..a59f1056122f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm @@ -69,7 +69,8 @@ icon_dead = "guard_dead" has_eye_glow = TRUE - faction = "spiders" + iff_factions = MOB_IFF_FACTION_SPIDER + maxHealth = 200 health = 200 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm index e3aca574fd8e..f59fa86255d4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/carrier.dm @@ -60,7 +60,7 @@ swarmling.health = swarm_health swarmling.legacy_melee_damage_lower = swarm_dam_lower swarmling.legacy_melee_damage_upper = swarm_dam_upper - swarmling.faction = swarmling_faction + swarmling.set_iff_factions(swarmling_faction) swarmling.adjust_scale(0.75) new_spiders += swarmling else if(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm index 6161a011dda2..9589a72493c2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/hunter.dm @@ -87,10 +87,9 @@ if(L == src) continue - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.check_shields(damage = 0, damage_source = src, attacker = src, def_zone = null, attack_text = "the leap")) - continue // We were blocked. + var/list/shieldcall_result = L.atom_shieldcall(40, BRUTE, MELEE_TIER_MEDIUM, ARMOR_MELEE, NONE, ATTACK_TYPE_MELEE) + if(shieldcall_result[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAGS_BLOCK_ATTACK) + continue victim = L break diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm index 237bf4f729fd..00cdeb796809 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/lurker.dm @@ -116,7 +116,7 @@ ..() // For the poison. // Force unstealthing if attacked. -/mob/living/simple_mob/animal/giant_spider/lurker/bullet_act(obj/projectile/P) +/mob/living/simple_mob/animal/giant_spider/lurker/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() break_cloak() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm index d4188d3fbd5b..f14c48c6dbb1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/crab.dm @@ -10,7 +10,9 @@ name = "crab" desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." tt_desc = "E Cancer bellianus" - faction = "crabs" + + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + catalogue_data = list(/datum/category_item/catalogue/fauna/crab) icon_state = "crab" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm index e6c26cb9f8bb..96d69d7ed51c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/fish.dm @@ -110,7 +110,7 @@ var/obj/belly/B = loc sting(B.owner) -/mob/living/simple_mob/animal/passive/fish/koi/poisonous/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/fish/koi/poisonous/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm index cdfebcaf0d36..c555b146fa42 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm @@ -145,7 +145,7 @@ emote_hear = list("squeeks","squeaks","squiks") emote_see = list("runs in a circle", "shakes", "scritches at something") -/mob/living/simple_mob/animal/passive/mouse/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/mouse/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/hander = user if(!istype(hander)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm index 1a3aff961f17..1dd44c59b331 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/passive.dm @@ -3,3 +3,7 @@ /mob/living/simple_mob/animal/passive ai_holder_type = /datum/ai_holder/polaris/simple_mob/passive mob_bump_flag = 0 + iff_factions = list( + MOB_IFF_FACTION_NEUTRAL, + MOB_IFF_FACTION_FARM_PET, + ) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm index 1a3f1d0d3574..c3fbb8385e20 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/cat_vr.dm @@ -1,35 +1,3 @@ -/mob/living/simple_mob/animal/passive/cat/runtime/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "Stomach" - B.desc = "The slimy wet insides of Runtime! Not quite as clean as the cat on the outside." - - B.emote_lists[DM_HOLD] = list( - "Runtime's stomach kneads gently on you and you're fairly sure you can hear her start purring.", - "Most of what you can hear are slick noises, Runtime breathing, and distant purring.", - "Runtime seems perfectly happy to have you in there. She lays down for a moment to groom and squishes you against the walls.", - "The CMO's pet seems to have found a patient of her own, and is treating them with warm, wet kneading walls.", - "Runtime mostly just lazes about, and you're left to simmer in the hot, slick guts unharmed.", - "Runtime's master might let you out of this fleshy prison, eventually. Maybe. Hopefully?") - - B.emote_lists[DM_DIGEST] = list( - "Runtime's stomach is treating you rather like a mouse, kneading acids into you with vigor.", - "A thick dollop of bellyslime drips from above while the CMO's pet's gut works on churning you up.", - "Runtime seems to have decided you're food, based on the acrid air in her guts and the pooling fluids.", - "Runtime's stomach tries to claim you, kneading and pressing inwards again and again against your form.", - "Runtime flops onto their side for a minute, spilling acids over your form as you remain trapped in them.", - "The CMO's pet doesn't seem to think you're any different from any other meal. At least, their stomach doesn't.") - - B.digest_messages_prey = list( - "Runtime's stomach slowly melts your body away. Her stomach refuses to give up it's onslaught, continuing until you're nothing more than nutrients for her body to absorb.", - "After an agonizing amount of time, Runtime's stomach finally manages to claim you, melting you down and adding you to her stomach.", - "Runtime's stomach continues to slowly work away at your body before tightly squeezing around you once more, causing the remainder of your body to lose form and melt away into the digesting slop around you.", - "Runtime's slimy gut continues to constantly squeeze and knead away at your body, the bulge you create inside of her stomach growing smaller as time progresses before soon dissapearing completely as you melt away.", - "Runtime's belly lets off a soft groan as your body finally gives out, the cat's eyes growing heavy as it settles down to enjoy it's good meal.", - "Runtime purrs happily as you slowly slip away inside of her gut, your body's nutrients are then used to put a layer of padding on the now pudgy cat.", - "The acids inside of Runtime's stomach, aided by the constant motions of the smooth walls surrounding you finally manage to melt you away into nothing more mush. She curls up on the floor, slowly kneading the air as her stomach moves its contents — including you — deeper into her digestive system.", - "Your form begins to slowly soften and break apart, rounding out Runtime's swollen belly. The carnivorous cat rumbles and purrs happily at the feeling of such a filling meal.") - // Ascian's Tactical Kitten /obj/item/holder/cat/fluff/tabiranth name = "Spirit" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm index 8f48f46ea5c3..81fd2f00028b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/fox_vr.dm @@ -24,7 +24,7 @@ mob_size = MOB_SMALL //Foxes are not smaller than cats so bumping them up to small randomized = TRUE - faction = "fox" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP response_help = "scritches" response_disarm = "gently pushes aside" @@ -62,28 +62,6 @@ wander = TRUE base_wander_delay = 4 -/mob/living/simple_mob/animal/passive/fox/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "Stomach" - B.desc = "Slick foxguts. Cute on the outside, slimy on the inside!" - - B.emote_lists[DM_HOLD] = list( - "The foxguts knead and churn around you harmlessly.", - "With a loud glorp, some air shifts inside the belly.", - "A thick drop of warm bellyslime drips onto you from above.", - "The fox turns suddenly, causing you to shift a little.", - "During a moment of relative silence, you can hear the fox breathing.", - "The slimey stomach walls squeeze you lightly, then relax.") - - B.emote_lists[DM_DIGEST] = list( - "The guts knead at you, trying to work you into thick soup.", - "You're ground on by the slimey walls, treated like a mouse.", - "The acrid air is hard to breathe, and stings at your lungs.", - "You can feel the acids coating you, ground in by the slick walls.", - "The fox's stomach churns hungrily over your form, trying to take you.", - "With a loud glorp, the stomach spills more acids onto you.") - /mob/living/simple_mob/animal/passive/fox/apply_melee_effects(var/atom/A) if(ismouse(A)) var/mob/living/simple_mob/animal/passive/mouse/mouse = A @@ -144,58 +122,6 @@ var/datum/ai_holder/polaris/AI = ai_holder AI.set_follow(friend) -/* Old fox friend AI, I'm not sure how to add the fancy "friend is dead" stuff so I'm commenting it out for someone else to figure it out, this is just baseline stuff. -//Basic friend AI -/mob/living/simple_mob/animal/passive/fox/fluff - var/mob/living/carbon/human/friend - var/befriend_job = null - -/mob/living/simple_mob/animal/passive/fox/fluff/Life(seconds, times_fired) - . = ..() - if(!. || !friend) return - - var/friend_dist = get_dist(src,friend) - - if (friend_dist <= 4) - if(stance == STANCE_IDLE) - if(set_follow(friend)) - handle_stance(STANCE_FOLLOW) - - if (friend_dist <= 1) - if (friend.stat >= DEAD || friend.health <= config_legacy.health_threshold_softcrit) - if (prob((friend.stat < DEAD)? 50 : 15)) - var/verb = pick("yaps", "howls", "whines") - audible_emote(pick("[verb] in distress.", "[verb] anxiously.")) - else - if (prob(5)) - visible_emote(pick("nips [friend].", - "brushes against [friend].", - "tugs on [friend].", - "chrrrrs.")) - else if (friend.health <= 50) - if (prob(10)) - var/verb = pick("yaps", "howls", "whines") - audible_emote("[verb] anxiously.") - -/mob/living/simple_mob/animal/passive/fox/fluff/verb/friend() - set name = "Become Friends" - set category = VERB_CATEGORY_IC - set src in view(1) - - if(friend && usr == friend) - setDir(get_dir(src, friend)) - say("Yap!") - return - - if (!(ishuman(usr) && befriend_job && usr.job == befriend_job)) - to_chat(usr, "[src] ignores you.") - return - - friend = usr - - setDir(get_dir(src, friend)) - say("Yap!") -*/ /obj/item/reagent_containers/food/snacks/meat/fox name = "Fox meat" desc = "The fox doesn't say a goddamn thing, now." @@ -212,28 +138,6 @@ makes_dirt = FALSE // No more dirt randomized = FALSE -/mob/living/simple_mob/animal/passive/fox/renault/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "Stomach" - B.desc = "Slick foxguts. They seem somehow more regal than perhaps other foxes!" - - B.emote_lists[DM_HOLD] = list( - "Renault's stomach walls squeeze around you more tightly for a moment, before relaxing, as if testing you a bit.", - "There's a sudden squeezing as Renault presses a forepaw against his gut over you, squeezing you against the slick walls.", - "The 'head fox' has a stomach that seems to think you belong to it. It might be hard to argue, as it kneads at your form.", - "If being in the captain's fox is a promotion, it might not feel like one. The belly just coats you with more thick foxslime.", - "It doesn't seem like Renault wants to let you out. The stomach and owner possessively squeeze around you.", - "Renault's stomach walls squeeze closer, as he belches quietly, before swallowing more air. Does he do that on purpose?") - - B.emote_lists[DM_DIGEST] = list( - "Renault's stomach walls grind hungrily inwards, kneading acids against your form, and treating you like any other food.", - "The captain's fox impatiently kneads and works acids against you, trying to claim your body for fuel.", - "The walls knead in firmly, squeezing and tossing you around briefly in disorienting aggression.", - "Renault belches, letting the remaining air grow more acrid. It burns your lungs with each breath.", - "A thick glob of acids drip down from above, adding to the pool of caustic fluids in Renault's belly.", - "There's a loud gurgle as the stomach declares the intent to make you a part of Renault.") - /mob/living/simple_mob/animal/passive/fox/syndicate name = "syndi-fox" desc = "It's a DASTARDLY fox! The horror! Call the shuttle!" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm index 93b952241395..a58cfae66a3f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/parrot.dm @@ -72,7 +72,7 @@ return ..() // Clicked on by empty hand. -/mob/living/simple_mob/animal/passive/bird/parrot/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/bird/parrot/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm b/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm index 98d4713d3de1..649c396a415b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/roach/roach.dm @@ -62,7 +62,8 @@ see_in_dark = 6 universal_understand = 1 - faction = "roaches" + + iff_factions = MOB_IFF_FACTION_ROACH mob_size = MOB_SMALL pass_flags = ATOM_PASS_TABLE @@ -377,7 +378,7 @@ item_state = "uberfallen" icon_living = "uberfallen" icon_dead = "uberfallen_dead" - faction = "synthtide" + catalogue_data = list(/datum/category_item/catalogue/fauna/roach/uberfallen) maxHealth = 30 health = 30 @@ -537,7 +538,7 @@ ..() // For the poison. // Force unstealthing if attacked. -/mob/living/simple_mob/animal/roach/zeitraum/bullet_act(obj/projectile/P) +/mob/living/simple_mob/animal/roach/zeitraum/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() break_cloak() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm index 0e68b5967b9a..d1f50ecf6464 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/diyaab.dm @@ -19,7 +19,7 @@ tt_desc = "S Choeros hirtus" //diyaab and shantak are technically reletives! catalogue_data = list(/datum/category_item/catalogue/fauna/diyaab) - faction = "diyaab" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "diyaab" icon_living = "diyaab" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm index 34b8aef5de3b..5a3124821ec5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/duck.dm @@ -19,7 +19,7 @@ tt_desc = "S Anatidae vitriae" catalogue_data = list(/datum/category_item/catalogue/fauna/crystalduck) - faction = "duck" + iff_factions = MOB_IFF_FACTION_FARM_NEUTRAL icon_state = "duck" icon_living = "duck" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm index d09429d0ead5..0369de91df86 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/frostfly.dm @@ -30,7 +30,7 @@ tt_desc = "S Carabidae glacios" catalogue_data = list(/datum/category_item/catalogue/fauna/frostfly) - faction = "diyaab" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "firefly" icon_living = "firefly" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm index d37f68a4758b..3664d5976761 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/glitterfly.dm @@ -28,7 +28,7 @@ tt_desc = "S Lepidoptera adamas" catalogue_data = list(/datum/category_item/catalogue/fauna/glitterfly) - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL icon_state = "butterfly" icon_living = "butterfly" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm index 09bc573d8298..1afaf0aec5be 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm @@ -36,7 +36,7 @@ mod_min = 100 mod_max = 150 - faction = "crabs" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP maxHealth = 200 health = 200 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm index aa8b855bdc03..9874709f3121 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/kururak.dm @@ -23,7 +23,7 @@ tt_desc = "S Felidae fluctursora" catalogue_data = list(/datum/category_item/catalogue/fauna/kururak) - faction = "kururak" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "bigcat" icon_living = "bigcat" @@ -292,7 +292,7 @@ continue if(!K.ai_holder) continue - if(K.faction != src.faction) + if(!K.shares_iff_faction(src)) continue var/datum/ai_holder/polaris/AI = K.ai_holder to_chat(K, SPAN_NOTICE("The pack leader wishes for you to follow them.")) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm index d031352454d9..1306b7868f26 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/leech.dm @@ -33,7 +33,7 @@ tt_desc = "S Hirudinea phorus" catalogue_data = list(/datum/category_item/catalogue/fauna/iceleech) - faction = "leech" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "leech" item_state = "brainslug" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm index 293d140e762a..f607f5267a71 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/racoon.dm @@ -17,7 +17,7 @@ tt_desc = "S Procyon cogitae" catalogue_data = list(/datum/category_item/catalogue/fauna/sakimm) - faction = "sakimm" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "raccoon" icon_living = "raccoon" @@ -349,4 +349,4 @@ /mob/living/simple_mob/animal/sif/sakimm/dexter name = "Dexter" desc = "A tame, oversized rodent with hands. It seems really friendly." - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm index b5f2bc6d75dd..5ac610fd67e2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/savik.dm @@ -21,7 +21,7 @@ name = "savik" tt_desc = "S Pistris tellus" //landshark catalogue_data = list(/datum/category_item/catalogue/fauna/savik) - faction = "savik" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "savik" icon_living = "savik" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm index 73cd15895dab..f715b466daf4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm @@ -25,7 +25,7 @@ tt_desc = "S Choeros shantak" catalogue_data = list(/datum/category_item/catalogue/fauna/shantak) - faction = "shantak" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP icon_state = "shantak" icon_living = "shantak" @@ -74,7 +74,7 @@ continue if(!S.ai_holder) continue - if(S.faction != src.faction) + if(!S.shares_iff_faction(src)) continue var/datum/ai_holder/polaris/AI = S.ai_holder AI.set_follow(src) @@ -94,4 +94,4 @@ name = "Scruffy" ai_holder_type = /datum/ai_holder/polaris/simple_mob/passive makes_dirt = 0 - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm index 405b220f8374..0aeff455be7b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm @@ -26,6 +26,9 @@ desc = "Warriors serve as the primary combat caste within a Hive Structure, while having fewer numbers than the endless drone hordes, they are none-the-less extremely formidable. " value = CATALOGUER_REWARD_MEDIUM +/mob/living/simple_mob/animal/space/alien + iff_factions = MOB_IFF_FACTION_XENOMORPH + /mob/living/simple_mob/animal/space/alien/warrior name = "xenomorph warrior" desc = "A feral Xenomorph that plays the part of the Hive Structures main fighter. Standing at an even larger stance than a drone, its exoskeleton is fully militarized, intended to take hits from both melee and ranged alike. Its claws can easily tear through armor and flesh, while its acid does the rest." @@ -46,7 +49,6 @@ catalogue_data = list(/datum/category_item/catalogue/fauna/feral_alien/warrior) ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee/evasive - faction = "xeno" mob_class = MOB_CLASS_ABERRATION @@ -97,7 +99,6 @@ legacy_melee_damage_lower = 20 legacy_melee_damage_upper = 20 base_attack_cooldown = 6 - faction = "xeno" attack_sound = 'sound/mobs/biomorphs/drone_attack.ogg' movement_sound = 'sound/mobs/biomorphs/drone_move.ogg' catalogue_data = list(/datum/category_item/catalogue/fauna/feral_alien/drone) @@ -128,7 +129,6 @@ health = 200 legacy_melee_damage_lower = 10 legacy_melee_damage_upper = 10 - faction = "xeno" base_pixel_x = -8 movement_cooldown = 3 projectiletype = /obj/projectile/energy/neurotoxin @@ -153,7 +153,6 @@ health = 250 legacy_melee_damage_lower = 20 legacy_melee_damage_upper = 20 - faction = "xeno" movement_cooldown = 2 base_pixel_x = -8 base_pixel_y = 1 @@ -190,7 +189,6 @@ ) legacy_melee_damage_lower = 50 legacy_melee_damage_upper = 50 - faction = "xeno" movement_cooldown = 2 base_pixel_x = -17 base_pixel_y = 6 @@ -332,7 +330,6 @@ legacy_melee_damage_lower = 40 legacy_melee_damage_upper = 40 attack_armor_type = DAMAGE_MODE_PIERCE | DAMAGE_MODE_SHARP - faction = "xeno" movement_cooldown = 3 base_pixel_x = -18 base_pixel_y = 2 @@ -381,7 +378,6 @@ legacy_melee_damage_lower = 70 legacy_melee_damage_upper = 50 attack_armor_pen = 60 - faction = "xeno" movement_cooldown = 4 base_pixel_x = -15 base_pixel_y = 6 @@ -515,7 +511,6 @@ health = 300 legacy_melee_damage_lower = 35 legacy_melee_damage_upper = 40 - faction = "xeno" movement_cooldown = 0 icon_scale_x = 0.7 icon_scale_y = 0.7 @@ -687,7 +682,6 @@ health = 250 legacy_melee_damage_lower = 10 legacy_melee_damage_upper = 10 - faction = "xeno" movement_cooldown = 4 base_pixel_x = -8 base_pixel_y = 1 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm index e325e4fbdb77..cb3f6064c4b5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/bats.dm @@ -17,7 +17,7 @@ icon_gib = "bat_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/bats) - faction = "scarybat" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP maxHealth = 20 health = 20 @@ -57,7 +57,7 @@ // Spookiest of bats /mob/living/simple_mob/animal/space/bats/cult - faction = "cult" + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT supernatural = TRUE /mob/living/simple_mob/animal/space/bats/cult/cultify() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm index b9c903035ee9..777ad7974b70 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/bear.dm @@ -19,7 +19,7 @@ icon_gib = "bear_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/space_bear) - faction = "russian" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP maxHealth = 125 health = 125 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm index b15d52832f90..59c791db93c9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm @@ -34,7 +34,8 @@ icon_dead = "carp_dead" icon_gib = "carp_gib" - faction = "carp" + iff_factions = MOB_IFF_FACTION_CARP + maxHealth = 25 health = 25 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm index 1d97e5bc9a52..f9213088c026 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/gaslamp_vr.dm @@ -32,7 +32,8 @@ TODO: Make them light up and heat the air when exposed to oxygen. icon_dead = "gaslamp-dead" icon = 'icons/mob/vore32x64.dmi' - faction = "virgo3b" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + maxHealth = 100 health = 100 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm index c6f00f33cf26..725f4d1a47e7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/goose.dm @@ -14,7 +14,7 @@ icon_dead = "goose_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/goose) - faction = "geese" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP maxHealth = 30 health = 30 @@ -52,6 +52,3 @@ set category = "Abilities" add_modifier(/datum/modifier/berserk, 30 SECONDS) - -/mob/living/simple_mob/animal/space/goose/virgo3b - faction = "virgo3b" diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm index bf95597dc568..d490b5426318 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/horing.dm @@ -9,7 +9,7 @@ icon_rest = "thrumbo_rest" maxHealth = 500 health = 500 - faction = "horing" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP pixel_x = -16 special_attack_min_range = 3 special_attack_max_range = 8 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm index 57d8d7963a95..123116061f79 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/mouse_army.dm @@ -35,7 +35,8 @@ icon_living = "mouse_gray" icon_dead = "mouse_gray_dead" icon_rest = "mouse_gray_sleep" - faction = "mouse_army" + + iff_factions = MOB_IFF_FACTION_FARM_PEST maxHealth = 50 health = 50 @@ -417,8 +418,10 @@ ..() // For the poison. // Force unstealthing if attacked. -/mob/living/simple_mob/animal/space/mouse_army/stealth/bullet_act(obj/projectile/P) +/mob/living/simple_mob/animal/space/mouse_army/stealth/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return break_cloak() /mob/living/simple_mob/animal/space/mouse_army/stealth/hit_with_weapon(obj/item/O, mob/living/user, effective_force, hit_zone) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm index 1cf183f2550f..9ee40ccb70c4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/worm.dm @@ -15,7 +15,7 @@ movement_cooldown = 0 movement_sound = 'sound/effects/sand_step.ogg' - faction = "worm" + iff_factions = MOB_IFF_FACTION_WORM status_flags = 0 universal_speak = 0 @@ -143,7 +143,7 @@ var/mob/living/simple_mob/animal/space/space_worm/newSegment = new segment_type(loc) current.Attach(newSegment) current = newSegment - current.faction = faction + current.copy_iff_factions(src) /mob/living/simple_mob/animal/space/space_worm/head/verb/toggle_devour() set name = "Toggle Feeding" @@ -404,7 +404,7 @@ /mob/living/simple_mob/animal/space/space_worm/proc/update_body_faction() if(next) // Keep us on the same page, here. - faction = next.faction + copy_iff_factions(next) if(previous) previous.update_body_faction() return 1 diff --git a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm index 361a27500e45..9d815960f40d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm +++ b/code/modules/mob/living/simple_mob/subtypes/blob/blob.dm @@ -33,7 +33,7 @@ /mob/living/simple_mob/blob icon = 'icons/mob/blob.dmi' pass_flags = ATOM_PASS_BLOB | ATOM_PASS_TABLE - faction = "blob" + iff_factions = MOB_IFF_FACTION_BLOB catalogue_data = list(/datum/category_item/catalogue/fauna/blob) heat_damage_per_tick = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm index 592bba73f355..256157269dea 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Eddy.dm @@ -12,7 +12,6 @@ icon_living = "Eddy" icon_dead = "e_head" icon_rest = "Eddy" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Eddy) @@ -48,11 +47,11 @@ playsound(src, 'sound/h_sounds/headcrab.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Eddy/bullet_act() +/mob/living/simple_mob/horror/Eddy/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() -/mob/living/simple_mob/horror/Eddy/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Eddy/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm index e17dcaf7b3c3..749a490f3304 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Master.dm @@ -12,7 +12,6 @@ icon_living = "Helix" icon_dead = "m_dead" icon_rest = "Helix" - faction = "horror" icon = 'icons/mob/horror_show/master.dmi' icon_gib = "generic_gib" anchored = 1 @@ -49,11 +48,11 @@ playsound(src, 'sound/h_sounds/imbeciles.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Master/bullet_act() +/mob/living/simple_mob/horror/Master/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() -/mob/living/simple_mob/horror/Master/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Master/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm index bd5b19c6b2e3..c38bbbc1d272 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Rickey.dm @@ -12,7 +12,6 @@ icon_living = "Rickey" icon_dead = "r_head" icon_rest = "Rickey" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Rickey) @@ -50,11 +49,11 @@ playsound(src, 'sound/h_sounds/headcrab.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Rickey/bullet_act() +/mob/living/simple_mob/horror/Rickey/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() -/mob/living/simple_mob/horror/Rickey/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Rickey/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm index 4a33ac456be5..3faa6b9225dd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Smiley.dm @@ -12,7 +12,6 @@ icon_living = "Smiley" icon_dead = "s_head" icon_rest = "Smiley" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Smiley) @@ -49,11 +48,11 @@ playsound(src, 'sound/h_sounds/lynx.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Helix/bullet_act() +/mob/living/simple_mob/horror/Helix/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() -/mob/living/simple_mob/horror/Helix/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Helix/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm index eb80a126aa91..775510d1c5e5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Steve.dm @@ -12,7 +12,6 @@ icon_living = "Steve" icon_dead = "sg_head" icon_rest = "Steve" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Steve) @@ -54,11 +53,11 @@ playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Steve/bullet_act() +/mob/living/simple_mob/horror/Steve/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() -/mob/living/simple_mob/horror/Steve/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Steve/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm index 35fc24509ee5..6d8e707a5130 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/Willy.dm @@ -12,7 +12,6 @@ icon_living = "Willy" icon_dead = "w_head" icon_rest = "Willy" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Willy) @@ -50,11 +49,11 @@ playsound(src, 'sound/h_sounds/sampler.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Willy/bullet_act() +/mob/living/simple_mob/horror/Willy/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() -/mob/living/simple_mob/horror/Willy/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Willy/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm index 5dce24cb5faa..7fe387538841 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/bradley.dm @@ -12,7 +12,6 @@ icon_living = "Bradley" icon_dead = "b_head" icon_rest = "Bradley" - faction = "horror" icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/bradley) @@ -48,11 +47,11 @@ playsound(src, 'sound/h_sounds/mumble.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/bradley/bullet_act() +/mob/living/simple_mob/horror/bradley/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() -/mob/living/simple_mob/horror/bradley/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/bradley/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm b/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm index f14bd678effe..e543b849daa3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/horror .dm @@ -27,7 +27,7 @@ /mob/living/simple_mob/horror tt_desc = "Homo Horrificus" - faction = "horror" + iff_factions = MOB_IFF_FACTION_MUTANT icon = 'icons/mob/horror_show/GHPS.dmi' icon_gib = "generic_gib" taser_kill = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm index 0d4af8cfc580..429cdc27508e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/sally.dm @@ -12,7 +12,6 @@ icon_living = "Sally" icon_dead = "ws_head" icon_rest = "Sally" - faction = "horror" icon = 'icons/mob/horror_show/widehorror.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/Sally) @@ -47,11 +46,11 @@ playsound(src, 'sound/h_sounds/lynx.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/Sally/bullet_act() +/mob/living/simple_mob/horror/Sally/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() -/mob/living/simple_mob/horror/Sally/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/Sally/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm index 8667b99e7321..eb0140b42200 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/shittytim.dm @@ -12,7 +12,6 @@ icon_living = "shitty_tim" icon_dead = "tst_head" icon_rest = "shitty_tim" - faction = "horror" icon = 'icons/mob/horror_show/tallhorror.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/BigTim) @@ -48,11 +47,11 @@ playsound(src, 'sound/h_sounds/shitty_tim.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/BigTim/bullet_act() +/mob/living/simple_mob/horror/BigTim/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() -/mob/living/simple_mob/horror/BigTim/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/BigTim/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm index f6c26f137cf4..68218e0440ee 100644 --- a/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm +++ b/code/modules/mob/living/simple_mob/subtypes/horror/timling.dm @@ -12,7 +12,6 @@ icon_living = "timling" icon_dead = "tt_head" icon_rest = "timling" - faction = "horror" icon = 'icons/mob/horror_show/tallhorror.dmi' icon_gib = "generic_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/horror/TinyTim) @@ -48,11 +47,11 @@ playsound(src, 'sound/h_sounds/shitty_tim.ogg', 50, 1) ..() -/mob/living/simple_mob/horror/TinyTim/bullet_act() +/mob/living/simple_mob/horror/TinyTim/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) - ..() -/mob/living/simple_mob/horror/TinyTim/attack_hand(mob/user, list/params) +/mob/living/simple_mob/horror/TinyTim/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) playsound(src, 'sound/h_sounds/holla.ogg', 50, 1) ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm index e9e9ee7e338a..f659511aa4b8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/clown.dm @@ -30,7 +30,7 @@ icon_gib = "clown_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/clown) - faction = "clown" + iff_factions = MOB_IFF_FACTION_MERCENARY_GROUP("clown") loot_list = list(/obj/item/bikehorn = 100) @@ -145,7 +145,7 @@ var/mob_count = 0 // Are there enough mobs to consider grenading? var/turf/T = get_turf(A) for(var/mob/M in range(T, 2)) - if(M.faction == faction) // Don't grenade our friends + if(shares_iff_faction(M)) return FALSE if(M in oview(src, special_attack_max_range)) // And lets check if we can actually see at least two people before we throw a grenade if(!M.stat) // Dead things don't warrant a grenade diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm index fd903d2671f3..cb3ef4c86221 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/cultist.dm @@ -38,7 +38,7 @@ tt_desc = "NULL" icon = 'icons/mob/cult.dmi' icon_state = "initiate" - faction = "cult" + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT mob_class = MOB_CLASS_DEMONIC /datum/category_item/catalogue/fauna/cultist/human @@ -222,8 +222,6 @@ health = 75 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/tesh) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -273,8 +271,6 @@ health = 200 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/lizard) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -324,8 +320,6 @@ health = 150 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/caster) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -375,8 +369,6 @@ health = 150 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/initiate) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -423,8 +415,6 @@ health = 75 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/castertesh) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -472,7 +462,6 @@ desc = "A heavily armed cultist with a mirror shield that hurts to look at." icon_state = "cult_elite" icon_living = "cult_elite" - faction = "cult" catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/elite) status_flags = 0 @@ -508,17 +497,15 @@ to_chat(user, "This weapon is ineffective, it does no damage.") visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/humanoid/cultist/elite/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/humanoid/cultist/elite/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(50)) - visible_message("[Proj] disappears into the mirror world as it hits the shield.") - if(Proj.firer) + visible_message("[proj] disappears into the mirror world as it hits the shield.") + if(proj.firer) if(istype(src.ai_holder, /datum/ai_holder/polaris)) var/datum/ai_holder/polaris/ai_holder = src.ai_holder - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_DELETE + return ..() /mob/living/simple_mob/humanoid/cultist/elite/death() new /obj/effect/decal/remains/human (src.loc) @@ -552,8 +539,6 @@ health = 300 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/magus) - faction = "cult" - status_flags = 0 response_help = "pokes" @@ -625,8 +610,6 @@ health = 300 catalogue_data = list(/datum/category_item/catalogue/fauna/cultist/hunter) - faction = "cult" - status_flags = 0 response_help = "pokes" diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm index f21bd3b164c7..24322713677a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/humanoid_vr.dm @@ -3,9 +3,3 @@ /mob/living/simple_mob/humanoid/pirate/ranged ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged - -/mob/living/simple_mob/humanoid/russian - ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee - -/mob/living/simple_mob/humanoid/russian/ranged - ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm index 59acf04dd4dc..d4e4f8e79968 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs.dm @@ -54,7 +54,7 @@ icon_gib = "syndicate_gib" catalogue_data = list(/datum/category_item/catalogue/fauna/mercenary/human) - faction = "syndicate" + iff_factions = MOB_IFF_FACTION_MERCENARY movement_cooldown = 2 status_flags = 0 @@ -95,7 +95,7 @@ var/mob_count = 0 // Are there enough mobs to consider grenading? var/turf/T = get_turf(A) for(var/mob/M in range(T, 2)) - if(M.faction == faction) // Don't grenade our friends + if(shares_iff_faction(M)) return FALSE if(M in oview(src, special_attack_max_range)) // And lets check if we can actually see at least two people before we throw a grenade if(!M.stat) // Dead things don't warrant a grenade @@ -161,7 +161,7 @@ attack_edge = 1 attacktext = list("slashed") - loot_list = list(/obj/item/melee/energy/sword = 100, /obj/item/shield/energy = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword = 100, /obj/item/shield/transforming/energy = 100) // They have a shield, so they try to block /mob/living/simple_mob/humanoid/merc/melee/sword/attackby(var/obj/item/O as obj, var/mob/user as mob) @@ -177,16 +177,13 @@ to_chat(user, "This weapon is ineffective, it does no damage.") visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/humanoid/merc/melee/sword/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/humanoid/merc/melee/sword/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(35)) - visible_message("[src] blocks [Proj] with its shield!") - if(Proj.firer) - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() - + visible_message("[src] blocks [proj] with its shield!") + if(proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_BLOCKED + return ..() //////////////////////////////// // Ranged @@ -553,15 +550,13 @@ else visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/humanoid/merc/ranged/space/suppressor/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/humanoid/merc/ranged/space/suppressor/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(50)) - visible_message("[src] blocks [Proj] with its shield!") - if(Proj.firer) - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() + visible_message("[src] blocks [proj] with its shield!") + if(proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_BLOCKED + return ..() //////////////////////////////// // PoI Mercs @@ -612,7 +607,7 @@ icon_living = "voxpirate" icon_dead = "voxpirate_dead" - faction = "voxpirate" + iff_factions = MOB_IFF_FACTION_PIRATE movement_cooldown = 4 status_flags = 0 @@ -683,7 +678,7 @@ ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee/evasive corpse = /obj/spawner/corpse/vox/boarder_m - loot_list = list(/obj/item/melee/energy/sword = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword = 100) // They're good with the swords? I dunno. I like the idea they can deflect. /mob/living/simple_mob/humanoid/merc/voxpirate/boarder/attackby(var/obj/item/O, var/mob/user) @@ -699,15 +694,13 @@ to_chat(user, "This weapon is ineffective, it does no damage.") visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/humanoid/merc/voxpirate/boarder/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/humanoid/merc/voxpirate/boarder/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(35)) - visible_message("[src] blocks [Proj] with its sword!") - if(Proj.firer) - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() + visible_message("[src] blocks [proj] with its sword!") + if(proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_BLOCKED + return ..() //////////////////////////////// // Vox Ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm index d7716b6f4471..2a8d48e8be01 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/pirates.dm @@ -25,7 +25,7 @@ icon_dead = "piratemelee_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/pirate) - faction = "pirate" + iff_factions = MOB_IFF_FACTION_PIRATE response_help = "pushes" response_disarm = "shoves" @@ -122,7 +122,7 @@ attack_sound = 'sound/weapons/blade1.ogg' - loot_list = list(/obj/item/melee/energy/sword/pirate = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100) corpse = /obj/spawner/corpse/pirate/melee_energy @@ -134,7 +134,7 @@ icon_living = "piratemelee-las-armor" movement_cooldown = 4 armor_legacy_mob = list(melee = 30, bullet = 20, laser = 20, energy = 5, bomb = 5, bio = 100, rad = 100) - loot_list = list(/obj/item/melee/energy/sword/pirate = 100, /obj/item/clothing/accessory/armor/armorplate/stab = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100, /obj/item/clothing/accessory/armor/armorplate/stab = 100) corpse = /obj/spawner/corpse/pirate/melee_energy_armor @@ -164,15 +164,13 @@ to_chat(user, "This weapon is ineffective, it does no damage.") visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/humanoid/merc/melee/sword/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/humanoid/merc/melee/sword/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(25)) - visible_message("[src] blocks [Proj] with its shield!") - if(Proj.firer) - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() + visible_message("[src] blocks [proj] with its shield!") + if(proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_BLOCKED + return ..() // Armored Variant /mob/living/simple_mob/humanoid/pirate/shield/armored @@ -352,7 +350,7 @@ armor_legacy_mob = list(melee = 30, bullet = 20, laser = 20, energy = 5, bomb = 5, bio = 100, rad = 100) - loot_list = list(/obj/item/melee/energy/sword/pirate = 100, /obj/item/clothing/suit/armor/riot/alt = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100, /obj/item/clothing/suit/armor/riot/alt = 100) corpse = /obj/spawner/corpse/pirate/mate @@ -526,13 +524,13 @@ icon_state = "old-piratemelee-las" icon_living = "old-piratemelee-las" icon_dead = "old-piratemelee_dead" - loot_list = list(/obj/item/melee/energy/sword/pirate = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100) //Armored Variant /mob/living/simple_mob/humanoid/pirate/las/armored/old icon_state = "old-piratemelee-las-armor" icon_living = "old-piratemelee-las-armor" - loot_list = list(/obj/item/melee/energy/sword/pirate = 100, /obj/item/clothing/suit/armor/material/makeshift = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100, /obj/item/clothing/suit/armor/material/makeshift = 100) //Shield Pirate /mob/living/simple_mob/humanoid/pirate/shield/old @@ -653,7 +651,7 @@ armor_legacy_mob = list(melee = 30, bullet = 20, laser = 20, energy = 5, bomb = 5, bio = 100, rad = 100) - loot_list = list(/obj/item/melee/energy/sword/pirate = 100, /obj/item/clothing/suit/pirate = 100) + loot_list = list(/obj/item/melee/transforming/energy/sword/cutlass = 100, /obj/item/clothing/suit/pirate = 100) /////////////////////////////// diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/possessed.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/possessed.dm index c3e1564fca60..e539869578ab 100644 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/possessed.dm +++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/possessed.dm @@ -8,7 +8,7 @@ icon = 'icons/mob/possessed.dmi' icon_state = "eva-rig" - faction = "Possessed" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP movement_cooldown = 10 health = 200 diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm deleted file mode 100644 index 7de7e01468eb..000000000000 --- a/code/modules/mob/living/simple_mob/subtypes/humanoid/russian.dm +++ /dev/null @@ -1,48 +0,0 @@ -/datum/category_item/catalogue/fauna/russian - name = "Russians" - desc = "After the Human Diaspora, but before the Final War, many \ - nations hosted their own colonial efforts out into the stars. Although \ - most human settlers live in culturally diverse environments, some of \ - those habitats which trace their lineage back to the Diaspora have remained \ - culturally homogenous. Sometimes xenophobic, and sometimes simply \ - nationalistic, these cultures function just as well as their counterparts. \ - Many of these communities draw their ancestry directly back to Old Russia. \ - Due to this, ethnic and genetic Russians remain heavily represented on the \ - Galactic stage." - value = CATALOGUER_REWARD_TRIVIAL - -/mob/living/simple_mob/humanoid/russian - name = "russian" - desc = "For the Motherland!" - tt_desc = "E Homo sapiens" - icon_state = "russianmelee" - icon_living = "russianmelee" - icon_dead = "russianmelee_dead" - icon_gib = "syndicate_gib" - catalogue_data = list(/datum/category_item/catalogue/fauna/russian) - - faction = "russian" - - response_help = "pokes" - response_disarm = "shoves" - response_harm = "hits" - - harm_intent_damage = 5 - legacy_melee_damage_lower = 15 - legacy_melee_damage_upper = 15 - attacktext = list("punched") - - loot_list = list(/obj/item/material/knife = 100) - - corpse = /obj/spawner/corpse/russian - -/mob/living/simple_mob/humanoid/russian/ranged - icon_state = "russianranged" - icon_living = "russianranged" - - projectiletype = /obj/projectile/bullet - projectilesound = 'sound/weapons/Gunshot4.ogg' - - loot_list = list(/obj/item/gun/ballistic/revolver/mateba = 100) - - corpse = /obj/spawner/corpse/russian/ranged diff --git a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm index b724d80556b8..721c948243db 100644 --- a/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm +++ b/code/modules/mob/living/simple_mob/subtypes/illusion/illusion.dm @@ -45,16 +45,12 @@ return -/mob/living/simple_mob/illusion/bullet_act(obj/projectile/P) - if(!P) - return - +/mob/living/simple_mob/illusion/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(realistic) return ..() + return PROJECTILE_IMPACT_PHASE - return PROJECTILE_FORCE_MISS - -/mob/living/simple_mob/illusion/attack_hand(mob/user, list/params) +/mob/living/simple_mob/illusion/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/M = user if(!istype(M)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/lavaland/goliath.dm b/code/modules/mob/living/simple_mob/subtypes/lavaland/goliath.dm index c9cc8d269a2b..ff11f5cdb894 100644 --- a/code/modules/mob/living/simple_mob/subtypes/lavaland/goliath.dm +++ b/code/modules/mob/living/simple_mob/subtypes/lavaland/goliath.dm @@ -74,7 +74,8 @@ hide_amount = 10 exotic_amount = 10 - faction = "lavaland" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + speak_emote = list("bellows") say_list_type = /datum/say_list/goliath ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee/goliath diff --git a/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm b/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm index 6efce8ef4378..efea68a50c1c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm +++ b/code/modules/mob/living/simple_mob/subtypes/lavaland/gutshank.dm @@ -66,7 +66,8 @@ bone_type = /obj/item/stack/material/chitin bone_amount = 5 - faction = "lavaland" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + speak_emote = list("chatters") say_list_type = /datum/say_list/gutshank ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee @@ -187,7 +188,8 @@ bone_type = /obj/item/stack/material/chitin bone_amount = 5 - faction = "lavaland" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + speak_emote = list("chatters") say_list_type = /datum/say_list/gutshank //I changed the ai_holder from simple/melee to retaliate/coop because when riding a Shank, it would override user inputs to charge non-faction mobs. Which is annoying. @@ -219,8 +221,9 @@ /mob/living/simple_mob/animal/shank/proc/RenameMount() var/mob/M = usr - if(!M.mind) return 0 - if(!M.faction == src.faction) + if(!M.mind) + return 0 + if(!shares_iff_faction(M)) to_chat(M, "You don't feel familiar enough with this beast to name it.") return 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/lavaland/stormdrifter.dm b/code/modules/mob/living/simple_mob/subtypes/lavaland/stormdrifter.dm index 3e7d894f96e7..7538a59ae6af 100644 --- a/code/modules/mob/living/simple_mob/subtypes/lavaland/stormdrifter.dm +++ b/code/modules/mob/living/simple_mob/subtypes/lavaland/stormdrifter.dm @@ -67,7 +67,8 @@ bone_amount = 0 exotic_amount = 1 - faction = "lavaland" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + speak_emote = list("rumbles") say_list_type = /datum/say_list/stormdrifter ai_holder_type = /datum/ai_holder/polaris/simple_mob/stormdrifter @@ -172,8 +173,9 @@ /mob/living/simple_mob/animal/stormdrifter/bull/proc/RenameMount() var/mob/M = usr - if(!M.mind) return 0 - if(!M.faction == src.faction) + if(!M.mind) + return 0 + if(!shares_iff_faction(M)) to_chat(M, "You don't feel familiar enough with this beast to name it.") return 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm index 1b69c4182085..ccc4a81b2d33 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/combat_drone.dm @@ -39,7 +39,7 @@ icon_dead = "drone_dead" has_eye_glow = TRUE - faction = "malf_drone" + iff_factions = MOB_IFF_FACTION_MERCENARY maxHealth = 50 // Shield has 150 for total of 200. health = 50 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm index 289d3f1f124e..782fa602fdd9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/corrupt_maint_drone_vr.dm @@ -26,7 +26,7 @@ icon_living = "corrupt-repairbot" hovering = FALSE // Can trigger landmines. - faction = "hivebot" + iff_factions = MOB_IFF_FACTION_HIVEBOT maxHealth = 25 health = 25 movement_cooldown = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm index c61bba406f0f..ffc0042e0b93 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/cyber_horror/cyber_horror.dm @@ -22,7 +22,7 @@ icon_gib = "cyber_horror_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/cyberhorror) - faction = "synthtide" + iff_factions = MOB_IFF_FACTION_MUTANT ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee/evasive @@ -182,11 +182,9 @@ if(L == src) continue - if(ishuman(L)) - var/mob/living/carbon/human/H = L - if(H.check_shields(damage = 0, damage_source = src, attacker = src, def_zone = null, attack_text = "the leap")) - // We were blocked. - continue + var/list/shieldcall_result = L.atom_shieldcall(40, BRUTE, MELEE_TIER_MEDIUM, ARMOR_MELEE, NONE, ATTACK_TYPE_MELEE) + if(shieldcall_result[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAGS_BLOCK_ATTACK) + continue victim = L break @@ -299,7 +297,7 @@ ..() // For the poison. // Force unstealthing if attacked. -/mob/living/simple_mob/mechanical/cyber_horror/tajaran/bullet_act(obj/projectile/P) +/mob/living/simple_mob/mechanical/cyber_horror/tajaran/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() break_cloak() @@ -435,7 +433,10 @@ damage = 15 damage_type = BRUTE -/obj/projectile/arc/blue_energy/priest/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/arc/blue_energy/priest/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ishuman(target)) var/mob/living/carbon/human/M = target M.Confuse(rand(3,5)) @@ -461,7 +462,7 @@ var/mob_count = 0 // Are there enough mobs? var/turf/T = get_turf(A) for(var/mob/M in range(T, 2)) - if(M.faction == faction) // Don't grenade our friends + if(shares_iff_faction(M)) return FALSE if(M in oview(src, special_attack_max_range)) if(!M.stat) @@ -500,8 +501,11 @@ icon_state = "plasma3" rad_power = RAD_INTENSITY_PROJ_ARC_HORROR_PRIEST -/obj/projectile/arc/radioactive/priest/on_impact(turf/T) +/obj/projectile/arc/radioactive/priest/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(!isturf(target)) + return + var/turf/T = target new /obj/effect/explosion(T) explosion(T, 0, 1, 4) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm index 5b2946eed9f5..8ed30cebead3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/disbot_vr.dm @@ -35,7 +35,9 @@ response_help = "pets the" response_disarm = "gently pushes aside the" response_harm = "hits the" - faction = "vagrant" + + iff_factions = MOB_IFF_FACTION_SWARMER + harm_intent_damage = 3 legacy_melee_damage_lower = 6 legacy_melee_damage_upper = 9 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm index 1ae4a5474156..d13630487081 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/golem.dm @@ -20,7 +20,7 @@ maxHealth = 300 catalogue_data = list(/datum/category_item/catalogue/technology/drone/technomancer_golem) - faction = "golem" + iff_factions = MOB_IFF_FACTION_NEUTRAL // bad faction to use but whatever response_help = "pets" response_disarm = "pushes away" diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm new file mode 100644 index 000000000000..3b4b35fd9261 --- /dev/null +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/enigma_hivebot.dm @@ -0,0 +1,596 @@ +// This is the dedicated file for Enigma Hivebots. +// Enigma hivebots are usually significantly tougher than baseline Hivebots, and are modified with salvaged abductor parts. +// These Hivebots are often meant for Event purposes, and carry a different faction than baseline Hivebots. Use these carefully! + +/mob/living/simple_mob/mechanical/hivebot/enigma + iff_factions = MOB_IFF_FACTION_HIVEBOT + +// Code Stuff +/mob/living/simple_mob/mechanical/hivebot/enigma/death() + ..() + visible_message(SPAN_WARNING("\The [src] demateralizes in a flash of energy!")) + new /obj/effect/debris/cleanable/blood/gibs/robot(src.loc) + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(3, 1, src) + s.start() + qdel(src) + +/mob/living/simple_mob/mechanical/hivebot/enigma/director/handle_special() + for(var/mob/living/L in range(16, src)) + if(L == src) + continue // Don't buff ourselves. + if(IIsAlly(L) && L.isSynthetic()) // Don't buff enemies. + L.add_modifier(/datum/modifier/aura/hivebot_commander_buff/enigma, null, src) + +/datum/modifier/aura/hivebot_commander_buff/enigma + name = "Strategicals" + on_created_text = "Signal established with commander. Optimizating combat performance..." + on_expired_text = "Lost signal to commander. Optimization halting." + stacks = MODIFIER_STACK_FORBID + aura_max_distance = 12 + mob_overlay_state = "signal_blue" + + disable_duration_percent = 0.7 + outgoing_melee_damage_percent = 1.3 + attack_speed_percent = 1.3 + accuracy = 30 + slowdown = -1 + evasion = 30 + +/mob/living/simple_mob/mechanical/hivebot/enigma/archaeologist/handle_special() + if(last_resupply + resupply_cooldown > world.time) + return // On cooldown. + + for(var/mob/living/simple_mob/SM in hearers(resupply_range, src)) + if(SM == src) + continue // We don't use charges buuuuut in case that changes in the future... + if(IIsAlly(SM)) // Don't resupply enemies. + if(!isnull(SM.special_attack_charges) && SM.special_attack_charges < initial(SM.special_attack_charges)) + SM.special_attack_charges += 1 + to_chat(SM, SPAN_NOTICE("\The [src] has resupplied you, and you can use your special ability one additional time.")) + to_chat(src, SPAN_NOTICE("You have resupplied \the [SM].")) + last_resupply = world.time + break // Only one resupply per pulse. + +/mob/living/simple_mob/mechanical/hivebot/enigma/custodian/apply_melee_effects(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(L.mob_size <= MOB_MEDIUM) + visible_message(SPAN_DANGER("\The [src] sends \the [L] flying with their hydraulic fists!")) + playsound(src, 'sound/enigma/enigma_hit2.ogg', 50, 1) + var/throw_dir = get_dir(src, L) + var/throw_dist = L.incapacitated(INCAPACITATION_DISABLED) ? 4 : 1 + L.throw_at_old(get_edge_target_turf(L, throw_dir), throw_dist, 1, src) + else + to_chat(L, SPAN_WARNING( "\The [src] punches you with incredible force, but you remain in place.")) + +// Melee + +/mob/living/simple_mob/mechanical/hivebot/enigma/custodian + name = "custodian" + icon = 'icons/mob/enigma.dmi' + desc = "A strangely shaped humanoid synthetic, standing taller than the average Human. Its armor seems reinforced against common energy and laser weapons, however likely less so against ballistics. Power seems to course through its arms, probably best to not let it hit you... A odd elaborate golden 'E' is etched into the side of its chassis." + icon_state = "custodian" + icon_living = "custodian" + maxHealth = 500 + health = 500 + armor_legacy_mob = list( + "melee" = 20, + "bullet" = 0, + "laser" = 50, + "energy" = 50, + "bomb" = 100, + "bio" = 100, + "rad" = 100 + ) + legacy_melee_damage_lower = 40 + legacy_melee_damage_upper = 40 + movement_cooldown = 4 + icon_scale_x = 1.4 + icon_scale_y = 1.4 + attack_sound = 'sound/enigma/enigma_hit2.ogg' + movement_sound = 'sound/enigma/enigma_move2.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/hivebot + +/mob/living/simple_mob/mechanical/hivebot/enigma/custodian/Initialize(mapload) + var/shield_type = /obj/item/shield_projector/rectangle{ + shield_health = 150; + max_shield_health = 150; + shield_regen_delay = 10 SECONDS; + shield_regen_amount = 10; + size_x = 1; + size_y = 1; + always_on = TRUE; + } + var/obj/item/shield_projector/shield_projector = new shield_type(src) + shield_projector.create_shields() + return ..() + +/mob/living/simple_mob/mechanical/hivebot/enigma/disassembler + name = "disassembler" + icon = 'icons/mob/enigma.dmi' + desc = "A small drone, decorated in hues of pink and purple material. Two sets of claws comprise its front legs and back legs, and thhere seems to be some sort of golden 'E' symbol which marks the under-chassis. It appears to move quite fast and fit into small spaces." + icon_state = "disassembler" + icon_living = "disassembler" + maxHealth = 200 + health = 200 + armor_legacy_mob = list( + "melee" = 10, + "bullet" = 0, + "laser" = 30, + "energy" = 30, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 20 + legacy_melee_damage_upper = 20 + base_attack_cooldown = 6 + movement_cooldown = 1 + attack_sound = 'sound/enigma/enigma_hit.ogg' + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/hivebot + +// Support + +/mob/living/simple_mob/mechanical/hivebot/enigma/director + name = "research director" + icon = 'icons/mob/124x124_enigma.dmi' + desc = "A towering machine which stands well above the average person. Its makeup is entirely alien, and its hull seems to shiver and move constantly. Contained within a dome shaped head appears to be some sort of impossibly advanced neural center. It looks to be directing the machines around it, as if it's some sort of Prophet." + icon_state = "research_director" + icon_living = "research_director" + maxHealth = 600 + health = 600 + armor_legacy_mob = list( + "melee" = 30, + "bullet" = 20, + "laser" = 50, + "energy" = 50, + "bomb" = 70, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 25 + legacy_melee_damage_upper = 25 + movement_cooldown = 5 + base_pixel_x = 1.5 + base_pixel_y = 1.5 + attack_sound = 'sound/weapons/slash.ogg' + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/event + +/mob/living/simple_mob/mechanical/hivebot/enigma/archaeologist + name = "xeno archaeologist" + icon = 'icons/mob/enigma.dmi' + desc = "A humanoid synthetic, standing at around the height of the average Human. This one seems off however, and if one were to look closer it has a number of archaeological tools integrated seemlessly into its chassis. A hum of energy from its advanced sensor package follows it where-ever it goes, ready to scan and dig." + icon_state = "archaeologist" + icon_living = "archaeologist" + maxHealth = 150 + health = 150 + armor_legacy_mob = list( + "melee" = 0, + "bullet" = 0, + "laser" = 30, + "energy" = 30, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 10 + legacy_melee_damage_upper = 10 + movement_cooldown = 2 + attack_sound = 'sound/items/drill_hit.ogg' + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/event + var/resupply_range = 5 + var/resupply_cooldown = 4 SECONDS + var/last_resupply = null + +// Ranged + +/mob/living/simple_mob/mechanical/hivebot/enigma/atomizer + name = "atomizer" + icon = 'icons/mob/enigma.dmi' + desc = "A floating orb which utilizes some sort of advanced anti-gravity technology. It's decorated in gold plating, and seems to be coursing with barely contained energy. On the side of its chassis is a odd golden 'E' shape." + icon_state = "atomizer" + icon_living = "atomizer" + maxHealth = 350 + health = 350 + armor_legacy_mob = list( + "melee" = 0, + "bullet" = 0, + "laser" = 40, + "energy" = 40, + "bomb" = 10, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 0 + legacy_melee_damage_upper = 0 + movement_cooldown = 6 + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting + projectiletype = /obj/projectile/beam/cyan/hivebot + +/mob/living/simple_mob/mechanical/hivebot/enigma/atomizer/Initialize(mapload) + var/shield_type = /obj/item/shield_projector/rectangle{ + shield_health = 150; + max_shield_health = 150; + shield_regen_delay = 10 SECONDS; + shield_regen_amount = 10; + size_x = 1; + size_y = 1; + always_on = TRUE; + } + var/obj/item/shield_projector/shield_projector = new shield_type(src) + shield_projector.create_shields() + return ..() + + +/mob/living/simple_mob/mechanical/hivebot/enigma/sweeper + name = "sweeper" + icon = 'icons/mob/enigma.dmi' + desc = "A towering mechanical construct, radiating with power. Its gauntlet contains some sort of inbuild shield projector which it uses to advance without sustaining harm, while some sort of shoulder mounted armament can be seen on its back, pointing at anything it intends to obliterate. A odd golden 'E' symbol can be seen on its breast-plate." + icon_state = "sweeper" + icon_living = "sweeper" + maxHealth = 400 + health = 400 + armor_legacy_mob = list( + "melee" = 20, + "bullet" = 30, + "laser" = 50, + "energy" = 50, + "bomb" = 40, + "bio" = 100, + "rad" = 100 + ) + legacy_melee_damage_lower = 20 + legacy_melee_damage_upper = 20 + movement_cooldown = 6 + attack_sound = 'sound/enigma/enigma_hit2.ogg' + movement_sound = 'sound/enigma/enigma_move2.ogg' + icon_scale_x = 1.1 + icon_scale_y = 1.1 + ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting + projectiletype = /obj/projectile/arc/fragmentation/mortar + base_attack_cooldown = 30 + +/mob/living/simple_mob/mechanical/hivebot/enigma/sweeper/Initialize(mapload) + var/shield_type = /obj/item/shield_projector/rectangle{ + shield_health = 250; + max_shield_health = 250; + shield_regen_delay = 5 SECONDS; + shield_regen_amount = 10; + size_x = 1; + size_y = 1; + always_on = TRUE; + } + var/obj/item/shield_projector/shield_projector = new shield_type(src) + shield_projector.create_shields() + return ..() + + + +// Troopers + +/mob/living/simple_mob/mechanical/hivebot/enigma/trooper/basic + name = "trooper" + icon = 'icons/mob/enigma.dmi' + desc = "A small robotic unit with a humanoid form. It carries gold markings on its head and face plate, along with shouldering some sort of alien energy weapon." + icon_living = "trooper" + icon_state = "trooper" + maxHealth = 250 + health = 250 + armor_legacy_mob = list( + "melee" = 0, + "bullet" = 0, + "laser" = 20, + "energy" = 20, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 5 + legacy_melee_damage_upper = 5 + movement_cooldown = 2 + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/hivebot + projectiletype = /obj/projectile/beam/weak + +/mob/living/simple_mob/mechanical/hivebot/enigma/trooper/marksman + name = "marksman trooper" + icon = 'icons/mob/enigma.dmi' + desc = "A slightly taller robotic unit with a humanoid form. It has dark green markings on its faceplate, and seems to carry some sort of advanced energy sniper." + icon_state = "marksman" + icon_living = "marksman" + maxHealth = 200 + health = 200 + armor_legacy_mob = list( + "melee" = 0, + "bullet" = 0, + "laser" = 50, + "energy" = 50, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 5 + legacy_melee_damage_upper = 5 + movement_cooldown = 4 + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/sniper + projectiletype = /obj/projectile/beam/xray + +/mob/living/simple_mob/mechanical/hivebot/enigma/trooper/magnetic + name = "magnetic trooper" + icon = 'icons/mob/enigma.dmi' + desc = "Another mechanical construct, this one looks more advanced than the others. Equipped with a extensively modified Ion weapon sporting abductor technology, better steer any machinery clear of this. It sports blue markings." + icon_state = "magnetic" + icon_living = "magnetic" + maxHealth = 300 + health = 300 + armor_legacy_mob = list( + "melee" = 0, + "bullet" = 20, + "laser" = 30, + "energy" = 30, + "bomb" = 0, + "bio" = 100, + "rad" = 100 + ) + + legacy_melee_damage_lower = 0 + legacy_melee_damage_upper = 0 + movement_cooldown = 2 + movement_sound = 'sound/enigma/enigma_move.ogg' + ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting + projectiletype = /obj/projectile/ion + base_attack_cooldown = 25 +// Boss + + +// VERY POWERFUL! Uses ripped Advanced Dark Gygax code. +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir + name = "Mimir" + desc = "A heavy exosuit, entirely made up of salvaged Abductor technology and materials. It's one of a kind, and extremely dangerous. Attempting to take it on is thought to be utter suicide due to its wide range of armaments." + icon = 'icons/mob/enigma.dmi' + movement_sound = 'sound/enigma/enigma_move2.ogg' + attack_sound = 'sound/enigma/enigma_hit2.ogg' + icon_state = "mimir" + icon_living = "mimir" + icon_scale_x = 1.3 + icon_scale_y = 1.3 + movement_cooldown = 3 + maxHealth = 1000 + health = 1000 + armor_legacy_mob = list( + "melee" = 50, + "bullet" = 50, + "laser" = 70, + "energy" = 70, + "bomb" = 50, + "bio" = 100, + "rad" = 100 + ) + + special_attack_min_range = 1 + special_attack_max_range = 7 + special_attack_cooldown = 10 SECONDS + projectiletype = /obj/projectile/beam/cyan/hivebot + ai_holder_type = /datum/ai_holder/polaris/simple_mob/intentional/adv_dark_gygax + var/obj/effect/overlay/energy_ball/energy_ball = null + +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/Initialize(mapload) + var/shield_type = /obj/item/shield_projector/rectangle{ + shield_health = 500; + max_shield_health = 500; + shield_regen_delay = 5 SECONDS; + shield_regen_amount = 30; + size_x = 1; + size_y = 1; + always_on = TRUE; + } + var/obj/item/shield_projector/shield_projector = new shield_type(src) + shield_projector.create_shields() + return ..() + +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/Destroy() + if(energy_ball) + energy_ball.stop_orbit() + qdel(energy_ball) + return ..() + +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/do_special_attack(atom/A) + . = TRUE // So we don't fire a bolt as well. + switch(a_intent) + if(INTENT_DISARM) // Side gun + electric_defense(A) + if(INTENT_HARM) // Rockets + launch_rockets(A) + if(INTENT_GRAB) // Micro-singulo + launch_microsingularity(A) + +#define ELECTRIC_ZAP_POWER 20000 + +// Charges a tesla shot, while emitting a dangerous electric field. The exosuit is immune to electric damage while this is ongoing. +// It also briefly blinds anyone looking directly at the mech without flash protection. +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/proc/electric_defense(atom/target) + set waitfor = FALSE + + // Temporary immunity to shock to avoid killing themselves with their own attack. + var/old_shock_resist = shock_resist + shock_resist = 1 + + // Make the energy ball. This is purely visual since the tesla ball is hyper-deadly. + energy_ball = new(loc) + energy_ball.adjust_scale(0.5) + energy_ball.orbit(src, 32, TRUE, 1 SECOND) + + visible_message(SPAN_WARNING( "\The [src] creates \an [energy_ball] around itself!")) + + playsound(src.loc, 'sound/effects/lightning_chargeup.ogg', 100, 1, extrarange = 30) + + // Shock nearby things that aren't ourselves. + for(var/i = 1 to 10) + energy_ball.adjust_scale(0.5 + (i/10)) + energy_ball.set_light(i/2, i/2, "#0000FF") + for(var/thing in range(3, src)) + // This is stupid because mechs are stupid and not mobs. + if(isliving(thing)) + var/mob/living/L = thing + + if(L == src) + continue + if(L.stat) + continue // Otherwise it can get pretty laggy if there's loads of corpses around. + L.inflict_shock_damage(i * 2) + if(L && L.has_polaris_AI()) // Some mobs delete themselves when dying. + L.ai_holder.react_to_attack_polaris(src) + + else if(istype(thing, /obj/vehicle/sealed/mecha)) + var/obj/vehicle/sealed/mecha/M = thing + M.take_damage_legacy(i * 2, "energy") // Mechs don't have a concept for siemens so energy armor check is the best alternative. + + sleep(1 SECOND) + + // Shoot a tesla bolt, and flashes people who are looking at the mecha without sufficent eye protection. + visible_message(SPAN_WARNING( "\The [energy_ball] explodes in a flash of light, sending a shock everywhere!")) + playsound(src.loc, 'sound/effects/lightningbolt.ogg', 100, 1, extrarange = 30) + tesla_zap(src.loc, 5, ELECTRIC_ZAP_POWER, FALSE) + for(var/mob/living/L in viewers(src)) + if(L == src) + continue + var/dir_towards_us = get_dir(L, src) + if(L.dir && L.dir & dir_towards_us) + to_chat(L, SPAN_DANGER("The flash of light blinds you briefly.")) + L.flash_eyes(intensity = FLASH_PROTECTION_MODERATE, override_blindness_check = FALSE, affect_silicon = TRUE) + + // Get rid of our energy ball. + energy_ball.stop_orbit() + qdel(energy_ball) + + sleep(1 SECOND) + // Resist resistance to old value. + shock_resist = old_shock_resist // Not using initial() in case the value gets modified by an admin or something. + +#undef ELECTRIC_ZAP_POWER + +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/proc/launch_rockets(atom/target) + set waitfor = FALSE + + // Telegraph our next move. + Beam(target, icon_state = "sat_beam", time = 3.5 SECONDS, maxdistance = INFINITY) + visible_message(SPAN_WARNING( "\The [src] deploys a missile rack!")) + playsound(src, 'sound/effects/turret/move1.wav', 50, 1) + sleep(0.5 SECONDS) + + for(var/i = 1 to 3) + if(target) // Might get deleted in the meantime. + var/turf/T = get_turf(target) + if(T) + visible_message(SPAN_WARNING( "\The [src] fires a rocket into the air!")) + playsound(src, 'sound/weapons/rpg.ogg', 70, 1) + face_atom(T) + var/obj/projectile/arc/explosive_rocket/rocket = new(loc) + rocket.old_style_target(T, src) + rocket.fire() + sleep(1 SECOND) + + visible_message(SPAN_WARNING( "\The [src] retracts the missile rack.")) + playsound(src, 'sound/effects/turret/move2.wav', 50, 1) + +// Arcing rocket projectile that produces a weak explosion when it lands. +// Shouldn't punch holes in the floor, but will still hurt. +/obj/projectile/arc/explosive_rocket + name = "rocket" + icon_state = "mortar" + +/obj/projectile/arc/explosive_rocket/on_impact(turf/T) + new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently. + explosion(T, 0, 0, 2, adminlog = FALSE) + +/mob/living/simple_mob/mechanical/hivebot/enigma/mimir/proc/launch_microsingularity(atom/target) + var/turf/T = get_turf(target) + visible_message(SPAN_WARNING( "\The [src] fires an energetic sphere into the air!")) + playsound(src, 'sound/weapons/Laser.ogg', 50, 1) + face_atom(T) + var/obj/projectile/arc/microsingulo/sphere = new(loc) + sphere.old_style_target(T, src) + sphere.fire() + +/obj/projectile/arc/microsingulo + name = "micro singularity" + icon_state = "bluespace" + +/obj/projectile/arc/microsingulo/on_impact(turf/T) + new /obj/effect/temporary_effect/pulse/microsingulo(T) + + +/obj/effect/temporary_effect/pulse/microsingulo + name = "micro singularity" + desc = "It's sucking everything in!" + icon = 'icons/obj/objects.dmi' + icon_state = "bhole3" + light_range = 4 + light_power = 5 + light_color = "#2ECCFA" + pulses_remaining = 10 + pulse_delay = 2 SECONDS + +/obj/effect/temporary_effect/pulse/microsingulo/on_pulse() + for(var/atom/A in range(pull_radius, src)) + A.singularity_pull(src, pull_strength) +// Used to control the mob's positioning based on which special attack it has done. +// Note that the intent will not change again until the next special attack is about to happen. + +// Changes the mob's intent, which controls which special attack is used. +// INTENT_DISARM causes Electric Defense, INTENT_GRAB causes Micro-Singularity, and INTENT_HARM causes Missile Barrage. +/datum/ai_holder/polaris/simple_mob/intentional/adv_dark_gygax/pre_special_attack(atom/A) + if(isliving(A)) + var/mob/living/target = A + + // If we're surrounded, Electric Defense will quickly fix that. + var/tally = 0 + var/list/potential_targets = list_targets() // Returns list of mobs and certain objects like mechs and turrets. + for(var/atom/movable/AM in potential_targets) + if(get_dist(holder, AM) > electric_defense_radius) + continue + if(!can_attack(AM)) + continue + tally++ + + // Should we shock them? + if(tally >= electric_defense_threshold || get_dist(target, holder) <= electric_defense_radius) + holder.a_intent = INTENT_DISARM + return + + // Otherwise they're a fair distance away and we're not getting mobbed up close. + // See if we should use missiles or microsingulo. + tally = 0 // Let's recycle the var. + for(var/atom/movable/AM in potential_targets) + if(get_dist(target, AM) > microsingulo_radius) // Deliberately tests distance between target and nearby targets and not the holder. + continue + if(!can_attack(AM)) + continue + if(AM.anchored) // Microsingulo doesn't do anything to anchored things. + tally-- + else + tally++ + + // Lots of people means minisingulo would be more useful. + if(tally >= microsingulo_threshold) + holder.a_intent = INTENT_GRAB + else // Otherwise use rockets. + holder.a_intent = INTENT_HARM + + else + if(get_dist(holder, A) >= rocket_explosive_radius + 1) + holder.a_intent = INTENT_HARM // Fire rockets if it's an obj/turf. + else + holder.a_intent = INTENT_DISARM // Electricity might not work but it's safe up close. diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm index 5f5f37fc1c0f..f16bc3c8923f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/hivebot.dm @@ -29,7 +29,7 @@ icon_living = "basic" catalogue_data = list(/datum/category_item/catalogue/technology/drone/hivebot/basic) - faction = "hivebot" + iff_factions = MOB_IFF_FACTION_HIVEBOT maxHealth = 3 LASERS_TO_KILL health = 3 LASERS_TO_KILL diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm index 0f3ce3a23194..ba2be85fb94c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm @@ -169,15 +169,23 @@ /obj/projectile/arc/emp_blast name = "emp blast" icon_state = "bluespace" - -/obj/projectile/arc/emp_blast/on_impact(turf/T) - empulse(T, 2, 4, 7, 10) // Normal EMP grenade. - return ..() - -/obj/projectile/arc/emp_blast/weak/on_impact(turf/T) - empulse(T, 1, 2, 3, 4) // Sec EMP grenade. - return ..() - + var/emp_dev = 2 + var/emp_heavy = 4 + var/emp_med = 7 + var/emp_light = 10 + +/obj/projectile/arc/emp_blast/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + empulse(target, emp_dev, emp_heavy, emp_med, emp_light) // Normal EMP grenade. + return . | PROJECTILE_IMPACT_DELETE + +/obj/projectile/arc/emp_blast/weak + emp_dev = 1 + emp_heavy = 2 + emp_med = 3 + emp_light = 4 // Fires shots that irradiate the tile hit. /mob/living/simple_mob/mechanical/hivebot/ranged_damage/siege/radiation diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm index c6a5eb97c478..55356b748a8c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/support.dm @@ -54,7 +54,7 @@ for(var/mob/living/L in hearers(7, src)) if(!L.ai_holder) continue - if(L.faction != src.faction) + if(!L.shares_iff_faction(src)) continue var/datum/ai_holder/polaris/AI = L.ai_holder AI.set_follow(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm index 7c5cef5f8a12..478bc8fd8720 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm @@ -227,7 +227,11 @@ name = "rocket" icon_state = "mortar" -/obj/projectile/arc/explosive_rocket/on_impact(turf/T) +/obj/projectile/arc/explosive_rocket/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target new /obj/effect/explosion(T) // Weak explosions don't produce this on their own, apparently. explosion(T, 0, 0, 2, adminlog = FALSE) @@ -244,10 +248,13 @@ name = "micro singularity" icon_state = "bluespace" -/obj/projectile/arc/microsingulo/on_impact(turf/T) +/obj/projectile/arc/microsingulo/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target new /obj/effect/temporary_effect/pulse/microsingulo(T) - /obj/effect/temporary_effect/pulse/microsingulo name = "micro singularity" desc = "It's sucking everything in!" diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm index 53e146c324fa..560154dc30d1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/mecha.dm @@ -7,7 +7,8 @@ desc = "A big stompy mech!" icon = 'icons/mecha/mecha.dmi' - faction = "syndicate" + iff_factions = MOB_IFF_FACTION_MERCENARY + movement_cooldown = 5 movement_sound = "mechstep" // This gets fed into playsound(), which can also take strings as a 'group' of sound files. turn_sound = 'sound/mecha/mechturn.ogg' @@ -70,7 +71,7 @@ // 'Eject' our pilot, if one exists. if(pilot_type) var/mob/living/L = new pilot_type(loc) - L.faction = src.faction + L.copy_iff_factions(src) new wreckage(loc) // Leave some wreckage. @@ -90,7 +91,7 @@ if(has_repair_droid) add_overlay(image(icon = 'icons/mecha/mecha_equipment.dmi', icon_state = "repair_droid")) -/mob/living/simple_mob/mechanical/mecha/bullet_act() +/mob/living/simple_mob/mechanical/mecha/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() sparks.start() @@ -111,11 +112,11 @@ return ..() */ -/mob/living/simple_mob/mechanical/mecha/bullet_act(obj/projectile/P) +/mob/living/simple_mob/mechanical/mecha/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(deflect_chance)) - visible_message(SPAN_WARNING( "\The [P] is deflected by \the [src]'s armor!")) + visible_message(SPAN_WARNING( "\The [proj] is deflected by \the [src]'s armor!")) deflect_sprite() - return 0 + return PROJECTILE_IMPACT_BLOCKED return ..() /mob/living/simple_mob/mechanical/mecha/proc/deflect_sprite() diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/micro.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/micro.dm index e1db2ddcf482..2c0974106082 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/micro.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/micro.dm @@ -19,7 +19,8 @@ icon = 'icons/mecha/micro.dmi' icon_state = "whisker" wreckage = /obj/structure/loot_pile/mecha/mouse_tank - faction = "mouse_army" + + iff_factions = MOB_IFF_FACTION_FARM_PEST maxHealth = 150 armor_legacy_mob = list( diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm index bc6b49659e4c..9781a4618770 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/odysseus.dm @@ -67,14 +67,13 @@ damage = 5 // Getting hit with a launched syringe probably hurts, and makes it at least slightly relevant against synthetics. var/piercing = FALSE // If true, ignores thick material. -/obj/projectile/fake_syringe/on_hit(atom/target, blocked = 0, def_zone = null) +/obj/projectile/fake_syringe/on_impact(atom/target, impact_flags, def_zone, efficiency) if(isliving(target)) var/mob/living/L = target if(!L.can_inject(null, null, def_zone, piercing)) - return FALSE + return impact_flags | PROJECTILE_IMPACT_BLOCKED L.custom_pain(SPAN_WARNING("You feel a tiny prick!"), 1, TRUE) - return ..() // This will add the modifier and return the correct value. - + return ..() // Fake syringe, which inflicts a long lasting modifier that slowly kills them. /obj/projectile/fake_syringe/poison diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm index 5a39727e5990..af36e6f16cfd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/ripley.dm @@ -81,7 +81,7 @@ desc = "A Ripley modified by pirates. Sports additional riveted armor plating and a jury rigged machine gun in addition to its hull piercing drill." catalogue_data = list(/datum/category_item/catalogue/technology/ripley) icon_state = "pirate" - faction = "pirate" + iff_factions = MOB_IFF_FACTION_PIRATE wreckage = /obj/structure/loot_pile/mecha/ripley/pirate maxHealth = 250 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm index 6dff71602413..2ff147313e01 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mechanical.dm @@ -22,7 +22,7 @@ return TRUE /mob/living/simple_mob/mechanical/speech_bubble_appearance() - return faction != "neutral" ? "synthetic_evil" : "machine" + return !has_iff_faction(MOB_IFF_FACTION_NEUTRAL) ? "synthetic_evil" : "machine" // Fix for Virgo 2's Surface /mob/living/simple_mob/mechanical diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm index e813fda8edb6..1aaa2b0cfae5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm @@ -27,7 +27,7 @@ icon_living = "viscerator_attack" hovering = TRUE // Won't trigger landmines. - faction = "syndicate" + iff_factions = MOB_IFF_FACTION_MERCENARY maxHealth = 15 health = 15 movement_cooldown = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm index 0d8dfd044783..00646d8cf5d5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/monitor_ward.dm @@ -40,7 +40,7 @@ // For PoIs. /mob/living/simple_mob/mechanical/ward/monitor/syndicate - faction = "syndicate" + iff_factions = MOB_IFF_FACTION_MERCENARY /mob/living/simple_mob/mechanical/ward/monitor/crew icon_state = "ward-nt" diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm index 4d5f2614f1b1..faed7952ab98 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/ward/ward.dm @@ -13,7 +13,8 @@ response_help = "pets" response_disarm = "swats away" response_harm = "punches" - faction = "wards" // Needed as most human mobs are in neutral faction. The owner is generally except from any ward hostility regardless. + + iff_factions = MOB_IFF_FACTION_HOSTILE // Needed as most human mobs are in neutral faction. The owner is generally except from any ward hostility regardless. maxHealth = 15 health = 15 diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm index 03085a303469..b191aaf8ba44 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/_construct.dm @@ -81,7 +81,7 @@ "rad" = 100) can_be_antagged = TRUE - faction = "cult" + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT supernatural = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm index 172854fd9d55..56ddbc516b3e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm @@ -63,46 +63,45 @@ . = ..() AddComponent(/datum/component/horror_aura/strong) -/mob/living/simple_mob/construct/juggernaut/bullet_act(var/obj/projectile/P) - var/reflectchance = 80 - round(P.damage/3) - if(prob(reflectchance)) +/mob/living/simple_mob/construct/juggernaut/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + var/reflectchance = 80 - round(proj.damage/3) + if(prob(reflectchance) && !istype(src, /mob/living/simple_mob/construct/juggernaut/behemoth)) var/damage_mod = rand(2,4) - var/projectile_dam_type = P.damage_type - var/incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3))) - var/armorcheck = run_armor_check(null, P.damage_flag) - var/soakedcheck = get_armor_soak(null, P.damage_flag) - if(!(istype(P, /obj/projectile/energy) || istype(P, /obj/projectile/beam))) - visible_message("The [P.name] bounces off of [src]'s shell!", \ - "The [P.name] bounces off of [src]'s shell!") + var/projectile_dam_type = proj.damage_type + var/incoming_damage = (round(proj.damage / damage_mod) - (round((proj.damage / damage_mod) * 0.3))) + var/armorcheck = run_armor_check(null, proj.damage_flag) + var/soakedcheck = get_armor_soak(null, proj.damage_flag) + if(!(istype(proj, /obj/projectile/energy) || istype(proj, /obj/projectile/beam))) + visible_message("The [proj.name] bounces off of [src]'s shell!", \ + "The [proj.name] bounces off of [src]'s shell!") new /obj/item/material/shard/shrapnel(src.loc) - if(!(P.damage_type == BRUTE || P.damage_type == BURN)) + if(!(proj.damage_type == BRUTE || proj.damage_type == BURN)) projectile_dam_type = BRUTE incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to brute for physical projectiles, though severely decreased. - apply_damage(incoming_damage, projectile_dam_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P) - return -1 //Doesn't reflect non-beams or non-energy projectiles. They just smack and drop with little to no effect. + apply_damage(incoming_damage, projectile_dam_type, null, armorcheck, soakedcheck, is_sharp(proj), has_edge(proj), proj) + return ..() else - visible_message("The [P.name] gets reflected by [src]'s shell!", \ - "The [P.name] gets reflected by [src]'s shell!") + visible_message("The [proj.name] gets reflected by [src]'s shell!", \ + "The [proj.name] gets reflected by [src]'s shell!") damage_mod = rand(3,5) - incoming_damage = (round(P.damage / damage_mod) - (round((P.damage / damage_mod) * 0.3))) - if(!(P.damage_type == BRUTE || P.damage_type == BURN)) + incoming_damage = (round(proj.damage / damage_mod) - (round((proj.damage / damage_mod) * 0.3))) + if(!(proj.damage_type == BRUTE || proj.damage_type == BURN)) projectile_dam_type = BURN incoming_damage = round(incoming_damage / 4) //Damage from strange sources is converted to burn for energy-type projectiles, though severely decreased. - apply_damage(incoming_damage, P.damage_type, null, armorcheck, soakedcheck, is_sharp(P), has_edge(P), P) + apply_damage(incoming_damage, proj.damage_type, null, armorcheck, soakedcheck, is_sharp(proj), has_edge(proj), proj) // Find a turf near or on the original location to bounce to - if(P.starting) - var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) - var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + if(proj.starting) + var/new_x = proj.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/new_y = proj.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) var/turf/curloc = get_turf(src) // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = 1 - - return -1 // complete projectile permutation + proj.legacy_redirect(new_x, new_y, curloc, src) + proj.reflected = 1 - return (..(P)) + return PROJECTILE_IMPACT_REFLECT + return ..() /* * The Behemoth. Admin-allowance only, still try to keep it in some guideline of 'Balanced', even if it means Security has to be fully geared to be so. @@ -138,22 +137,21 @@ /spell/targeted/construct_advanced/slam ) -/mob/living/simple_mob/construct/juggernaut/behemoth/bullet_act(var/obj/projectile/P) - var/reflectchance = 80 - round(P.damage/3) +/mob/living/simple_mob/construct/juggernaut/behemoth/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + var/reflectchance = 80 - round(proj.damage/3) if(prob(reflectchance)) - visible_message("The [P.name] gets reflected by [src]'s shell!", \ - "The [P.name] gets reflected by [src]'s shell!") + visible_message("The [proj.name] gets reflected by [src]'s shell!", \ + "The [proj.name] gets reflected by [src]'s shell!") // Find a turf near or on the original location to bounce to - if(P.starting) - var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) - var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + if(proj.starting) + var/new_x = proj.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) + var/new_y = proj.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3) var/turf/curloc = get_turf(src) // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = 1 + proj.legacy_redirect(new_x, new_y, curloc, src) + proj.reflected = 1 - return -1 // complete projectile permutation - - return (..(P)) + return PROJECTILE_IMPACT_REFLECT + return ..() diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm b/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm index 31c898afc523..39252ebffce2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/creature.dm @@ -15,7 +15,7 @@ mob_class = MOB_CLASS_ABERRATION - faction = "creature" + iff_factions = MOB_IFF_FACTION_MUTANT maxHealth = 40 health = 40 @@ -61,7 +61,7 @@ /mob/living/simple_mob/creature/cult mob_class = MOB_CLASS_DEMONIC - faction = "cult" + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT min_oxy = 0 max_oxy = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm b/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm index 21bd5de9e9e6..c48e9849195d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/faithless.dm @@ -12,7 +12,7 @@ icon_dead = "faithless_dead" catalogue_data = list(/datum/category_item/catalogue/fauna/horror) - faction = "faithless" + iff_factions = MOB_IFF_FACTION_MUTANT mob_class = MOB_CLASS_DEMONIC @@ -74,7 +74,7 @@ // Cult Variant /mob/living/simple_mob/faithless/cult - faction = "cult" + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT supernatural = TRUE /mob/living/simple_mob/faithless/cult/cultify() diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/living_statue.dm b/code/modules/mob/living/simple_mob/subtypes/occult/living_statue.dm index 55c7f5b6b9a3..52506c83b6b3 100644 --- a/code/modules/mob/living/simple_mob/subtypes/occult/living_statue.dm +++ b/code/modules/mob/living/simple_mob/subtypes/occult/living_statue.dm @@ -7,7 +7,7 @@ icon_state = "human_male" gender = NEUTER - faction = list("statue") + iff_factions = MOB_IFF_FACTION_STATUE mob_class = MOB_CLASS_ABERRATION diff --git a/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm b/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm index c90f7124de0a..9501930ae69b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm +++ b/code/modules/mob/living/simple_mob/subtypes/plant/tomato.dm @@ -15,7 +15,7 @@ mob_class = MOB_CLASS_PLANT - faction = "plants" + iff_factions = MOB_IFF_FACTION_PLANT maxHealth = 15 health = 15 poison_resist = 1.0 diff --git a/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm b/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm index d9bfb0a66cec..c68277f1d10e 100644 --- a/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm +++ b/code/modules/mob/living/simple_mob/subtypes/plant/tree.dm @@ -18,7 +18,11 @@ mob_class = MOB_CLASS_PLANT - faction = "plants" + iff_factions = list( + MOB_IFF_FACTION_BIND_TO_MAP, + MOB_IFF_FACTION_PLANT, + ) + maxHealth = 250 health = 250 poison_resist = 1.0 diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm index 349fd1bfe428..6165ec87ae73 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm @@ -73,9 +73,11 @@ icon_scale_y = 2 sharp = TRUE -/obj/projectile/icicle/on_impact(atom/A) - playsound(get_turf(A), "shatter", 70, 1) - return ..() +/obj/projectile/icicle/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + playsound(get_turf(target), "shatter", 70, 1) /obj/projectile/icicle/get_structure_damage() return damage / 2 // They're really deadly against mobs, but less effective against solid things. diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm index 228f3d154988..227fb8b5be9c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm @@ -24,7 +24,7 @@ gender = NEUTER catalogue_data = list(/datum/category_item/catalogue/fauna/slime) - faction = "slime" // Note that slimes are hostile to other slimes of different color regardless of faction (unless Unified). + iff_factions = MOB_IFF_FACTION_SLIME // Note that slimes are hostile to other slimes of different color regardless of faction (unless Unified). maxHealth = 150 movement_cooldown = 0 pass_flags = ATOM_PASS_TABLE @@ -159,7 +159,7 @@ adjustBruteLoss(-1) // Clicked on by empty hand. -/mob/living/simple_mob/slime/attack_hand(mob/user, list/params) +/mob/living/simple_mob/slime/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm index 7d0d0eccb655..dcf8e437aa8c 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm @@ -2,7 +2,7 @@ // Clicked on by empty hand. // Handles trying to wrestle a slime off of someone being eatten. -/mob/living/simple_mob/slime/xenobio/attack_hand(mob/user, list/params) +/mob/living/simple_mob/slime/xenobio/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm index 9bee1fc035f0..8923e1610597 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm @@ -199,12 +199,11 @@ log_and_message_admins("[src] ignited due to exposure to fire.") ignite() -/mob/living/simple_mob/slime/xenobio/dark_purple/bullet_act(var/obj/projectile/P, var/def_zone) - if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. - log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") +/mob/living/simple_mob/slime/xenobio/dark_purple/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.damage_type && proj.damage_type == BURN && proj.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] ignited due to bring hit by a burning projectile[proj.firer ? " by [key_name(proj.firer)]" : ""].") ignite() - else - return ..() /mob/living/simple_mob/slime/xenobio/dark_purple/attackby(var/obj/item/W, var/mob/user) if(istype(W) && W.damage_force && W.damtype == BURN) @@ -285,21 +284,20 @@ /mob/living/simple_mob/slime/xenobio/amber ) -/mob/living/simple_mob/slime/xenobio/silver/bullet_act(var/obj/projectile/P, var/def_zone) - if(istype(P,/obj/projectile/beam) || istype(P, /obj/projectile/energy)) - visible_message(SPAN_DANGER("\The [src] reflects \the [P]!")) +/mob/living/simple_mob/slime/xenobio/silver/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(istype(proj,/obj/projectile/beam) || istype(proj, /obj/projectile/energy)) + visible_message(SPAN_DANGER("\The [src] reflects \the [proj]!")) // Find a turf near or on the original location to bounce to - var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2) - var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2) + var/new_x = proj.starting.x + pick(0, 0, 0, -1, 1, -2, 2) + var/new_y = proj.starting.y + pick(0, 0, 0, -1, 1, -2, 2) var/turf/curloc = get_turf(src) // redirect the projectile - P.redirect(new_x, new_y, curloc, src) - P.reflected = TRUE - return PROJECTILE_CONTINUE // complete projectile permutation - else - return ..() + proj.legacy_redirect(new_x, new_y, curloc, src) + proj.reflected = TRUE + impact_flags |= PROJECTILE_IMPACT_REFLECT + return ..() // Tier 3 @@ -654,12 +652,11 @@ log_and_message_admins("[src] exploded due to exposure to fire.") explode() -/mob/living/simple_mob/slime/xenobio/oil/bullet_act(obj/projectile/P, def_zone) - if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. - log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].") +/mob/living/simple_mob/slime/xenobio/oil/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.damage_type && proj.damage_type == BURN && proj.damage) // Most bullets won't trigger the explosion, as a mercy towards Security. + log_and_message_admins("[src] exploded due to bring hit by a burning projectile[proj.firer ? " by [key_name(proj.firer)]" : ""].") explode() - else - return ..() /mob/living/simple_mob/slime/xenobio/oil/attackby(obj/item/W, mob/living/user) if(istype(W) && W.damage_force && W.damtype == BURN) diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm index 9bbc247c3f5c..be5220876445 100644 --- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm +++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm @@ -175,7 +175,7 @@ var/datum/ai_holder/polaris/simple_mob/xenobio_slime/AI = ai_holder AI.pacify() - faction = "neutral" + set_iff_factions(MOB_IFF_FACTION_NEUTRAL) // If for whatever reason the mob AI (or player) decides to try to attack something anyways. legacy_melee_damage_upper = 0 @@ -183,7 +183,6 @@ update_mood() - // These are verbs so that player slimes can evolve/split. /mob/living/simple_mob/slime/xenobio/verb/evolve() set category = "Slime" @@ -279,7 +278,7 @@ if(untamable_inherit) baby.untamable = untamable baby.untamable_inherit = untamable_inherit - baby.faction = faction + baby.copy_iff_factions(src) baby.friends = friends.Copy() if(no_step != 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm b/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm index e11e90aa7ec8..106cc664ccc5 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/bee.dm @@ -44,7 +44,7 @@ max_n2 = 0 minbodytemp = 0 - faction = "bee" + iff_factions = MOB_IFF_FACTION_FARM_ANIMAL var/poison_type = "spidertoxin" // The reagent that gets injected when it attacks, can be changed to different toxin. var/poison_chance = 10 // Chance for injection to occur. @@ -55,8 +55,6 @@ // Activate Noms! /mob/living/simple_mob/vore/bee - vore_active = 1 - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/bee/apply_melee_effects(var/atom/A) if(isliving(A)) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/c_pet.dm b/code/modules/mob/living/simple_mob/subtypes/vore/c_pet.dm index 55598d1e9c19..666d3f0f6fd8 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/c_pet.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/c_pet.dm @@ -40,7 +40,7 @@ has_langs = list("Coulrian") -/mob/living/simple_mob/animal/passive/honkpet/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/honkpet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_DISARM) return icon_state = pick("c_pet", "m_pet") .=..() @@ -78,7 +78,7 @@ "rad" = 0 ) -/mob/living/simple_mob/animal/passive/mimepet/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/passive/mimepet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_DISARM) icon_state = pick("dave1", "dave2", "dave3", "dave5" , "dave6" , "dave7" , "dave8" , "dave9" , "dave10") .=..() diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/cookiegirl.dm b/code/modules/mob/living/simple_mob/subtypes/vore/cookiegirl.dm index 2317205d8d88..4011ebb24d1d 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/cookiegirl.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/cookiegirl.dm @@ -20,14 +20,6 @@ // Activate Noms! /mob/living/simple_mob/vore/cookiegirl - vore_active = 1 - vore_bump_chance = 2 - vore_pounce_chance = 25 - vore_standing_too = 1 - vore_ignores_undigestable = 0 // Do they look like they care? - vore_default_mode = DM_HOLD // They're cookiepeople, what do you expect? - vore_digest_chance = 10 // Gonna become as sweet as sugar, soon. - vore_icons = SA_ICON_LIVING | SA_ICON_REST /datum/ai_holder/polaris/simple_mob/passive/cookiegirl/on_hear_say(mob/living/speaker, message) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm index 49f0ff6b825d..406dc242ca98 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm @@ -22,7 +22,7 @@ icon = 'icons/mob/vore64x32.dmi' has_eye_glow = TRUE - faction = "hivebot" + iff_factions = MOB_IFF_FACTION_HIVEBOT maxHealth = 200 health = 200 @@ -60,12 +60,6 @@ buckle_flags = BUCKLING_NO_USER_BUCKLE_OTHER_TO_SELF buckle_lying = FALSE - vore_active = TRUE - vore_capacity = 1 - vore_pounce_chance = 15 - vore_icons = SA_ICON_LIVING | SA_ICON_REST - vore_stomach_name = "fuel processor" - vore_stomach_flavor = "You have ended up in the fuel processor of this corrupted machine. This place was definitely not designed with safety and comfort in mind. The heated and cramped surroundings oozing potent fluids all over your form, eager to do nothing less than breaking you apart to fuel its rampage for the next few days... hours... minutes? Oh dear..." loot_list = list(/obj/item/borg/upgrade/syndicate = 6, /obj/item/borg/upgrade/vtec = 6, /obj/item/material/knife/ritual = 6, /obj/item/disk/nifsoft/compliance = 6) @@ -77,8 +71,6 @@ icon_dead = "prettyboi-dead" icon_rest = "prettyboi_rest" - vore_pounce_chance = 40 - attacktext = list("malsnuggled","scrunched","squeezed","assaulted","violated") say_list_type = /datum/say_list/corrupthound_prettyboi @@ -96,9 +88,6 @@ projectiletype = /obj/projectile/beam/sniper projectilesound = 'sound/weapons/gauss_shoot.ogg' - - vore_pounce_chance = 0 //It does ranged attacks anyway - ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/sniper /mob/living/simple_mob/vore/aggressive/corrupthound/gunner @@ -117,13 +106,8 @@ projectiletype = /obj/projectile/bullet/rifle/a556 projectilesound = 'sound/weapons/Gunshot_light.ogg' - - vore_pounce_chance = 0 //It does ranged attacks anyway - ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting - - /mob/living/simple_mob/vore/aggressive/corrupthound/sword name = "fencer hound" desc = "Good boy machine broke. Who thought it was a good idea to install an energy sword in his tail?" @@ -139,8 +123,6 @@ attack_edge = 1 attacktext = list("slashed") - vore_pounce_chance = 0 //No... - /mob/living/simple_mob/vore/aggressive/corrupthound/sword/attackby(var/obj/item/O as obj, var/mob/user as mob) if(O.damage_force) if(prob(20)) @@ -154,16 +136,13 @@ to_chat(user, "This weapon is ineffective, it does no damage.") visible_message("\The [user] gently taps [src] with \the [O].") -/mob/living/simple_mob/vore/aggressive/corrupthound/sword/bullet_act(var/obj/projectile/Proj) - if(!Proj) return +/mob/living/simple_mob/vore/aggressive/corrupthound/sword/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(prob(35)) - visible_message("[src] deflects [Proj] with its sword tail!") - if(Proj.firer) - ai_holder.react_to_attack_polaris(Proj.firer) - return - else - ..() - + visible_message("[src] deflects [proj] with its sword tail!") + if(proj.firer) + ai_holder.react_to_attack_polaris(proj.firer) + return PROJECTILE_IMPACT_BLOCKED + return ..() /mob/living/simple_mob/vore/aggressive/corrupthound/isSynthetic() return TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm index b39bf0d73d02..5f90d6cc94ae 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm @@ -17,7 +17,7 @@ attacktext = list("mauled") - faction = "deathclaw" + iff_factions = MOB_IFF_FACTION_MUTANT maxHealth = 200 health = 200 @@ -35,12 +35,6 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/deathclaw - vore_active = 1 - vore_capacity = 2 - vore_max_size = RESIZE_HUGE - vore_min_size = RESIZE_SMALL - vore_pounce_chance = 0 // Beat them into crit before eating. - vore_icons = SA_ICON_LIVING /datum/ai_holder/polaris/simple_mob/melee/deathclaw can_breakthrough = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm index e4ce752d3828..ffdace5c0602 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm @@ -11,9 +11,10 @@ icon_rest = "boxfox_rest" icon = 'icons/mob/demon_vr.dmi' - faction = "demon" - maxHealth = 30 - health = 30 + iff_factions = MOB_IFF_FACTION_SANGUINE_CULT + + maxHealth = 300 + health = 300 movement_cooldown = 0 vision_innate = /datum/vision/baseline/demon @@ -33,24 +34,16 @@ response_disarm = "pushes" response_harm = "hits" - legacy_melee_damage_lower = 3 - legacy_melee_damage_upper = 1 + legacy_melee_damage_lower = 10 + legacy_melee_damage_upper = 20 attacktext = list("clawed") - vore_active = TRUE - vore_icons = SA_ICON_LIVING var/shifted_out = FALSE var/shift_state = AB_SHIFT_NONE var/last_shift = 0 var/is_shifting = FALSE -/mob/living/simple_mob/vore/demon/init_vore() - ..() - var/obj/belly/B = vore_selected - B.name = "Stomach" - B.desc = "You slide down the slick, slippery gullet of the creature. It's warm, and the air is thick. You can feel the doughy walls of the creatures gut push and knead into your form! Slimy juices coat your form stinging against your flesh as they waste no time to start digesting you. The creature's heartbeat and the gurgling of their stomach are all you can hear as your jostled about, treated like nothing but food." - /mob/living/simple_mob/vore/demon/UnarmedAttack() if(shifted_out) return FALSE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm index 4cd39bf1719e..438035b6f823 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_subtypes.dm @@ -6,7 +6,6 @@ icon_dead = "engorge_dead" icon_rest = "engorge_rest" - vore_icons = null /mob/living/simple_mob/vore/demon/zellic name = "Zellic" @@ -16,4 +15,3 @@ icon_dead = "zellic_dead" icon_rest = null - vore_icons = null diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm b/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm index 11afced1cf8e..3483b9ed344a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/dino.dm @@ -12,6 +12,7 @@ randomized = TRUE ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP // By default, this is what most vore mobs are capable of. response_help = "pets" @@ -42,9 +43,6 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/dino - vore_active = 1 swallowTime = 1 SECOND // Hungry little bastards. - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/aggressive/dino/virgo3b - faction = "virgo3b" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm index cd92391c01c9..6f282cb1e882 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/dragon.dm @@ -7,7 +7,8 @@ icon_state = "reddragon" icon = 'icons/mob/vore64x64.dmi' - faction = "dragon" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + maxHealth = 500 // Boss health = 500 randomized = TRUE @@ -38,16 +39,10 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/dragon - vore_active = 1 - vore_capacity = 2 - vore_pounce_chance = 0 // Beat them into crit before eating. - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/aggressive/dragon/virgo3b maxHealth = 200 health = 200 - faction = "virgo3b" - /datum/say_list/dragonboss say_got_target = list("roars and snaps it jaws!") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm b/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm index cd7ccba29cfe..d0fb4cf782dd 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/fennec.dm @@ -17,7 +17,8 @@ icon_rest = "fennec_rest" icon = 'icons/mob/vore.dmi' - faction = "fennec" + iff_factions = MOB_IFF_FACTION_FARM_NEUTRAL + maxHealth = 30 health = 30 randomized = TRUE @@ -36,12 +37,6 @@ // Activate Noms! /mob/living/simple_mob/vore/fennec - vore_active = 1 - vore_bump_chance = 10 - vore_bump_emote = "playfully lunges at" - vore_pounce_chance = 40 - vore_default_mode = DM_HOLD - vore_icons = SA_ICON_LIVING /datum/say_list/fennec speak = list("SKREEEE!","Chrp?","Ararrrararr.") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm b/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm index 6273c8adc28b..8a3bbd6545a7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/fennix.dm @@ -8,7 +8,8 @@ icon_dead = "fennix_dead" icon = 'icons/mob/vore.dmi' - faction = "fennec" // Will protec other fenfens + iff_factions = MOB_IFF_FACTION_FARM_NEUTRAL + maxHealth = 60 health = 60 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/frog.dm b/code/modules/mob/living/simple_mob/subtypes/vore/frog.dm index 81eb61dbe020..89c8a3357801 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/frog.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/frog.dm @@ -38,9 +38,6 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/frog - vore_active = 1 - vore_pounce_chance = 50 - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/aggressive/frog/space name = "space frog" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/hippo.dm b/code/modules/mob/living/simple_mob/subtypes/vore/hippo.dm index e86b3a697e35..dce5180b32e0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/hippo.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/hippo.dm @@ -55,19 +55,6 @@ // Activate Noms! /mob/living/simple_mob/vore/hippo //I don't know why it's in a seperate line but everyone does it so i do it - vore_active = 1 - vore_capacity = 1 - vore_bump_chance = 15 - vore_bump_emote = "lazily wraps its tentacles around" - vore_standing_too = 1 - vore_ignores_undigestable = 0 - vore_default_mode = DM_HOLD - vore_digest_chance = 10 - vore_escape_chance = 20 - vore_pounce_chance = 35 //it's hippo sized it doesn't care it just eats you - vore_stomach_name = "rumen" //First stomach of a ruminant. It's where the pre digestion bacteria stuff happens. Very warm. - vore_stomach_flavor = "You are squeezed into the sweltering insides of the herbivore rumen." - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/vore/hippo/MouseDroppedOnLegacy(mob/living/M, mob/living/user) return diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm b/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm index 99a1e2098356..65e1f008a2bf 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/horse.dm @@ -19,7 +19,8 @@ icon_dead = "horse-dead" icon = 'icons/mob/animal.dmi' - faction = "horse" + iff_factions = MOB_IFF_FACTION_FARM_ANIMAL + maxHealth = 60 health = 60 randomized = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm b/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm index eae1a874b442..84be80f16999 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/jelly.dm @@ -16,7 +16,6 @@ icon_state = "jelly" icon = 'icons/mob/vore.dmi' - faction = "virgo2" maxHealth = 50 health = 50 randomized = TRUE @@ -33,9 +32,6 @@ // Activate Noms! /mob/living/simple_mob/animal/space/jelly - vore_active = 1 - vore_pounce_chance = 0 - vore_icons = SA_ICON_LIVING swallowTime = 2 SECONDS // Hungry little bastards. /datum/say_list/jelly diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm b/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm index c35c59a9603b..c6739e6c7143 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/mimic.dm @@ -63,7 +63,7 @@ icon_living = "mimicopen" icon = 'icons/mob/animal.dmi' - faction = "mimic" + iff_factions = MOB_IFF_FACTION_CHIMERIC maxHealth = 125 health = 125 @@ -97,11 +97,7 @@ showvoreprefs = 0 //Hides mechanical vore prefs for mimics. You can't see their gaping maws when they're just sitting idle. /mob/living/simple_mob/vore/aggressive/mimic - vore_active = 1 - vore_pounce_chance = 10 swallowTime = 3 SECONDS - vore_capacity = 1 - vore_default_mode = DM_DIGEST /datum/ai_holder/polaris/mimic wander = FALSE @@ -355,8 +351,6 @@ icon_state = "wmimicopen" icon_living = "wmimicopen" - faction = "mimic" - maxHealth = 100 health = 100 movement_cooldown = 5 @@ -399,8 +393,6 @@ icon_state = "tmimicopen" icon_living = "tmimicopen" - faction = "mimic" - maxHealth = 125 health = 125 movement_cooldown = 7 @@ -440,8 +432,6 @@ icon_state = "pmimicopen" icon_living = "pmimicopen" - faction = "mimic" - maxHealth = 150 health = 150 movement_cooldown = 7 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm index c8623e75ce07..1e3d344a322f 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm @@ -40,7 +40,6 @@ meat_amount = 2 showvoreprefs = 0 - vore_active = 1 var/morphed = FALSE var/tooltip = TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm index f8baf102e1f1..4caa88ad60a4 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/otie.dm @@ -15,7 +15,6 @@ icon_living = "otie" icon_dead = "otie-dead" icon_rest = "otie_rest" - faction = "otie" maxHealth = 150 health = 150 randomized = TRUE @@ -44,10 +43,6 @@ // Activate Noms! /mob/living/simple_mob/otie - vore_active = 1 - vore_capacity = 1 - vore_pounce_chance = 20 - vore_icons = SA_ICON_LIVING | SA_ICON_REST /mob/living/simple_mob/otie/feral //gets the pet2tame feature. starts out hostile tho so get gamblin' name = "mutated feral otie" @@ -57,7 +52,6 @@ icon_living = "photie" icon_dead = "photie-dead" icon_rest = "photie_rest" - faction = "virgo3b" tame_chance = 5 // Only a 1 in 20 chance of success. It's feral. What do you expect? // Lazy way of making sure this otie survives outside. min_oxy = 0 @@ -78,7 +72,6 @@ icon_living = "hotie" icon_dead = "hotie-dead" icon_rest = "hotie_rest" - faction = "cult" tame_chance = 20 // Lazy way of making sure this otie survives outside. min_oxy = 0 @@ -95,13 +88,13 @@ /mob/living/simple_mob/otie/red/friendly //gets the pet2tame feature and doesn't kill you right away name = "red otie" desc = "Seems this ominous looking longdog has been infused with wicked infernal forces. This one seems rather peaceful though." - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL tamed = 1 /mob/living/simple_mob/otie/friendly //gets the pet2tame feature and doesn't kill you right away name = "otie" desc = "The classic bioengineered longdog. This one might even tolerate you!" - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL tamed = 1 /mob/living/simple_mob/otie/cotie //same as above but has a little collar :v @@ -110,7 +103,7 @@ icon_state = "cotie" icon_living = "cotie" icon_rest = "cotie_rest" - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL tamed = 1 /mob/living/simple_mob/otie/cotie/phoron //friendly phoron pup with collar @@ -138,16 +131,11 @@ icon_living = "sotie" icon_rest = "sotie_rest" icon_dead = "sotie-dead" - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL maxHealth = 200 //armored or something health = 200 tamed = 1 has_eye_glow = TRUE - loot_list = list(/obj/item/clothing/glasses/sunglasses/sechud,/obj/item/clothing/suit/armor/vest/alt) - vore_pounce_chance = 60 // Good boys don't do too much police brutality. - - var/check_records = 0 // If true, arrests people without a record. - var/check_arrest = 1 // If true, arrests people who are set to arrest. /mob/living/simple_mob/otie/security/phoron name = "mutated guard otie" @@ -182,44 +170,9 @@ mod_min = 150 mod_max = 150 -/mob/living/simple_mob/otie/attackby(var/obj/item/O, var/mob/user) // Trade donuts for bellybrig victims. - if(istype(O, /obj/item/reagent_containers/food)) - qdel(O) - playsound(src.loc,'sound/items/eatfood.ogg', rand(10,50), 1) - if(!has_polaris_AI())//No autobarf on player control. - return - if(istype(O, /obj/item/reagent_containers/food/snacks/donut) && istype(src, /mob/living/simple_mob/otie/security)) - to_chat(user,"The guard pup accepts your offer for their catch.") - release_vore_contents() - else if(prob(2)) //Small chance to get prey out from non-sec oties. - to_chat(user,"The pup accepts your offer for their catch.") - release_vore_contents() - return - . = ..() - -/mob/living/simple_mob/otie/security/feed_grabbed_to_self(var/mob/living/user, var/mob/living/prey) // Make the gut start out safe for bellybrigging. - if(ishuman(prey)) - vore_selected.digest_mode = DM_HOLD - if(check_threat(prey) >= 4) - GLOB.global_announcer.autosay("[src] has detained suspect [target_name(prey)] in [get_area(src)].", "SmartCollar oversight", "Security") - if(istype(prey,/mob/living/simple_mob/animal/passive/mouse)) - vore_selected.digest_mode = DM_DIGEST - . = ..() - -/mob/living/simple_mob/otie/security/proc/check_threat(var/mob/living/M) - if(!M || !ishuman(M) || M.stat == DEAD || src == M) - return 0 - return M.assess_perp(0, 0, 0, check_records, check_arrest) - -/mob/living/simple_mob/otie/security/proc/target_name(mob/living/T) - if(ishuman(T)) - var/mob/living/carbon/human/H = T - return H.get_id_name("unidentified person") - return "unidentified lifeform" - //Pet 4 friendly -/mob/living/simple_mob/otie/attack_hand(mob/user, list/params) +/mob/living/simple_mob/otie/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/M = user if(!istype(M)) @@ -237,7 +190,7 @@ AI.set_follow(friend) if(tamed != 1) tamed = 1 - faction = M.faction + copy_iff_factions(M) sleep(1 SECOND) if(INTENT_GRAB) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm b/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm index 615dbd13f257..d2fdfb753142 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/panther.dm @@ -17,7 +17,8 @@ icon_dead = "panther-dead" icon = 'icons/mob/vore64x64.dmi' - faction = "panther" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP + maxHealth = 200 health = 200 randomized = TRUE @@ -36,10 +37,6 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/panther - vore_active = 1 - vore_capacity = 2 - vore_pounce_chance = 10 - vore_icons = SA_ICON_LIVING | SA_ICON_REST /datum/say_list/panther speak = list("RAWR!","Rawr!","GRR!","Growl!") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm b/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm index 5706bd267e6b..a7fafe8a6de9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/rat.dm @@ -15,7 +15,7 @@ icon_living = "rous" icon_dead = "rous-dead" icon_rest = "rous_rest" - faction = "mouse" + iff_factions = MOB_IFF_FACTION_FARM_PEST icon = 'icons/mob/vore64x32.dmi' maxHealth = 150 @@ -36,10 +36,6 @@ icon_x_dimension = 64 icon_y_dimension = 32 - vore_active = TRUE - vore_capacity = 1 - vore_pounce_chance = 45 - vore_icons = SA_ICON_LIVING | SA_ICON_REST var/life_since_foodscan = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm b/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm index 5bb907bab6eb..02948915e3b6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/redpanda.dm @@ -18,7 +18,8 @@ icon_rest = "wah_rest" icon = 'icons/mob/vore.dmi' - faction = "redpanda" //stop naming stuff vaguely + iff_factions = MOB_IFF_FACTION_NEUTRAL + maxHealth = 30 health = 30 randomized = TRUE @@ -35,15 +36,6 @@ say_list_type = /datum/say_list/redpanda ai_holder_type = /datum/ai_holder/polaris/simple_mob/passive -// Activate Noms! -/mob/living/simple_mob/vore/redpanda - vore_active = 1 - vore_bump_chance = 10 - vore_bump_emote = "playfully lunges at" - vore_pounce_chance = 40 - vore_default_mode = DM_HOLD // above will only matter if someone toggles it anyway - vore_icons = SA_ICON_LIVING - /mob/living/simple_mob/vore/redpanda/fae name = "dark wah" desc = "Ominous, but still cute!" @@ -54,10 +46,6 @@ icon_dead = "wah_fae_dead" icon_rest = "wah_fae_rest" - vore_ignores_undigestable = 0 // wah don't care you're edible or not, you still go in - vore_digest_chance = 0 // instead of digesting if you struggle... - vore_absorb_chance = 20 // you get to become adorable purple wahpudge. - vore_bump_chance = 75 maxHealth = 100 health = 100 legacy_melee_damage_lower = 10 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm deleted file mode 100644 index b59ca72eea99..000000000000 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_objects.dm +++ /dev/null @@ -1,168 +0,0 @@ -/obj/effect/shadekin_ability - name = "" - desc = "" - icon = 'icons/mob/screen_spells.dmi' - var/ability_name = "FIX ME" - var/cost = 50 - var/mob/living/simple_mob/shadekin/my_kin - var/shift_mode = NOT_WHILE_SHIFTED - var/ab_sound - -/obj/effect/shadekin_ability/Initialize(mapload) - . = ..() - my_kin = loc - moveToNullspace() - -/obj/effect/shadekin_ability/Destroy() - my_kin = null - return ..() - -/obj/effect/shadekin_ability/proc/atom_button_text() - var/shift_denial - - if(shift_mode == NOT_WHILE_SHIFTED && (my_kin.ability_flags & AB_PHASE_SHIFTED)) - shift_denial = "Physical Only" - else if(shift_mode == ONLY_WHILE_SHIFTED && !(my_kin.ability_flags & AB_PHASE_SHIFTED)) - shift_denial = "Shifted Only" - - if(shift_denial) - name = shift_denial - else - name = my_kin.energy >= cost ? "Activate" : "No Energy" - return src - -/obj/effect/shadekin_ability/Click(var/location, var/control, var/params) - if(my_kin.stat) return - - var/list/clickprops = params2list(params) - var/opts = clickprops["shift"] - - if(opts) - to_chat(my_kin,"[name] (Cost: [cost]%) - [desc]") - else - do_ability(my_kin) - -/obj/effect/shadekin_ability/proc/do_ability() - if(my_kin.stat) - to_chat(my_kin,"Can't use that ability in your state!") - return FALSE - if(shift_mode == NOT_WHILE_SHIFTED && (my_kin.ability_flags & AB_PHASE_SHIFTED)) - to_chat(my_kin,"Can't use that ability while phase shifted!") - return FALSE - else if(shift_mode == ONLY_WHILE_SHIFTED && !(my_kin.ability_flags & AB_PHASE_SHIFTED)) - to_chat(my_kin,"Can only use that ability while phase shifted!") - return FALSE - else if(my_kin.energy < cost) - to_chat(my_kin,"Not enough energy for that ability!") - return FALSE - - my_kin.energy -= cost - if(ab_sound) - playsound(src,ab_sound,75,1) - - return TRUE - -///////////////////////////////////////////////////////////////// -/obj/effect/shadekin_ability/phase_shift - ability_name = "Phase Shift" - desc = "Shift yourself out of alignment with realspace to travel quickly between dark areas (or light areas, with a price)." - icon_state = "tech_passwall" - cost = 100 - shift_mode = SHIFTED_OR_NOT - ab_sound = 'sound/effects/stealthoff.ogg' -/obj/effect/shadekin_ability/phase_shift/do_ability() - if(!..()) - return - my_kin.phase_shift() - if(my_kin.ability_flags & AB_PHASE_SHIFTED) - cost = 0 //Shifting back is free (but harmful in light) - else - cost = initial(cost) -///////////////////////////////////////////////////////////////// -/obj/effect/shadekin_ability/heal_boop - ability_name = "Regenerate Other" - desc = "Spend energy to heal physical wounds in another creature." - icon_state = "tech_biomedaura" - cost = 50 - shift_mode = NOT_WHILE_SHIFTED - ab_sound = 'sound/effects/EMPulse.ogg' -/obj/effect/shadekin_ability/heal_boop/do_ability() - if(!..()) - return - if(!my_kin.mend_other()) - my_kin.energy += cost //Refund due to abort -/* -/datum/modifier/shadekin/heal_boop - name = "Shadekin Regen" - desc = "You feel serene and well rested." - mob_overlay_state = "green_sparkles" - - on_created_text = "Sparkles begin to appear around you, and all your ills seem to fade away." - on_expired_text = "The sparkles have faded, although you feel much healthier than before." - stacks = MODIFIER_STACK_EXTEND - -/datum/modifier/shadekin/heal_boop/tick() - if(!holder.getBruteLoss() && !holder.getFireLoss() && !holder.getToxLoss() && !holder.getOxyLoss() && !holder.getCloneLoss()) // No point existing if the spell can't heal. - expire() - return - holder.adjustBruteLoss(-2) - holder.adjustFireLoss(-2) - holder.adjustToxLoss(-2) - holder.adjustOxyLoss(-2) - holder.adjustCloneLoss(-2) -*/ -///////////////////////////////////////////////////////////////// -/obj/effect/shadekin_ability/create_shade - ability_name = "Create Shade" - desc = "Create a field of darkness that follows you." - icon_state = "tech_dispelold" - cost = 25 - shift_mode = NOT_WHILE_SHIFTED - ab_sound = 'sound/effects/bamf.ogg' -/obj/effect/shadekin_ability/create_shade/do_ability() - if(!..()) - return - my_kin.add_modifier(/datum/modifier/shadekin/create_shade,20 SECONDS) -/* -/datum/modifier/shadekin/create_shade - name = "Shadekin Shadegen" - desc = "Darkness envelops you." - mob_overlay_state = "" - - on_created_text = "You drag part of The Dark into realspace, enveloping yourself." - on_expired_text = "You lose your grasp on The Dark and realspace reasserts itself." - stacks = MODIFIER_STACK_EXTEND - var/mob/living/simple_mob/shadekin/my_kin - -/datum/modifier/shadekin/create_shade/tick() - if(my_kin.ability_flags & AB_PHASE_SHIFTED) - expire() - -/datum/modifier/shadekin/create_shade/on_applied() - my_kin = holder - holder.glow_toggle = TRUE - holder.glow_range = 8 - holder.glow_intensity = -10 - holder.glow_color = "#FFFFFF" - holder.set_light(8, -10, "#FFFFFF") - -/datum/modifier/shadekin/create_shade/on_expire() - holder.glow_toggle = initial(holder.glow_toggle) - holder.glow_range = initial(holder.glow_range) - holder.glow_intensity = initial(holder.glow_intensity) - holder.glow_color = initial(holder.glow_color) - holder.set_light(0) - my_kin = null -*/ -/* -///////////////////////////////////////////////////////////////// -/obj/effect/shadekin_ability/energy_feast - ability_name = "Devour Energy" - desc = "Devour the energy from another creature (potentially fatal)." - icon_state = "gen_eat" - cost = 25 - shift_mode = NOT_WHILE_SHIFTED -/obj/effect/shadekin_ability/energy_feast/do_ability() - if(!..()) - return -*/ diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm deleted file mode 100644 index cd42c0628b68..000000000000 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm +++ /dev/null @@ -1,131 +0,0 @@ -// Phase shifting procs (and related procs) -/mob/living/simple_mob/shadekin/proc/phase_shift() - var/turf/T = get_turf(src) - if(!T.CanPass(src,T) || loc != T) - to_chat(src,"You can't use that here!") - return FALSE - - forceMove(T) - set_stunned(0) - set_paralyzed(0) - if(buckled) - buckled.unbuckle_mob() - if(pulledby) - pulledby.stop_pulling() - stop_pulling() - - //Shifting in - if(ability_flags & AB_PHASE_SHIFTED) - ability_flags &= ~AB_PHASE_SHIFTED - mouse_opacity = 2 - name = real_name - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.escapable = initial(B.escapable) - - cut_overlays() - alpha = initial(alpha) - invisibility = initial(invisibility) - see_invisible = initial(see_invisible) - incorporeal_move = initial(incorporeal_move) - density = initial(density) - force_max_speed = initial(force_max_speed) - - //Cosmetics mostly - flick("tp_in",src) - custom_emote(1,"phases in!") - sleep(5) //The duration of the TP animation - - //Potential phase-in vore - if(can_be_drop_pred) //Toggleable in vore panel - var/list/potentials = living_mobs(0) - if(potentials.len) - var/mob/living/target = pick(potentials) - if(istype(target) && vore_selected) - target.forceMove(vore_selected) - to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") - - // Do this after the potential vore, so we get the belly - update_icon() - - //Affect nearby lights - var/destroy_lights = 0 - if(eye_state == RED_EYES) - destroy_lights = 80 - if(eye_state == PURPLE_EYES) - destroy_lights = 25 - - for(var/obj/machinery/light/L in GLOB.machines) - if(L.z != z || get_dist(src,L) > 10) - continue - - if(prob(destroy_lights)) - spawn(rand(5,25)) - L.broken() - else - L.flicker(10) - - //Shifting out - else - ability_flags |= AB_PHASE_SHIFTED - mouse_opacity = 0 - custom_emote(1,"phases out!") - real_name = name - name = "Something" - - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.escapable = FALSE - - cut_overlays() - flick("tp_out",src) - sleep(5) - invisibility = INVISIBILITY_LEVEL_TWO - see_invisible = INVISIBILITY_LEVEL_TWO - update_icon() - alpha = 127 - - incorporeal_move = TRUE - density = FALSE - force_max_speed = TRUE - -/mob/living/simple_mob/shadekin/UnarmedAttack() - if(ability_flags & AB_PHASE_SHIFTED) - return FALSE //Nope. - - . = ..() - -/mob/living/simple_mob/shadekin/can_fall() - if(ability_flags & AB_PHASE_SHIFTED) - return FALSE //Nope! - - return ..() - -/mob/living/simple_mob/shadekin/zMove(direction) - if(ability_flags & AB_PHASE_SHIFTED) - var/turf/destination = get_vertical_step(src, direction) - if(destination) - forceMove(destination) - return TRUE - - return ..() - -// Healing others -/mob/living/simple_mob/shadekin/proc/mend_other() - //I hate to crunch a view() but I only want ones I can see - var/list/viewed = oview(1) - var/list/targets = list() - for(var/mob/living/L in viewed) - targets += L - if(!targets.len) - to_chat(src,"Nobody nearby to mend!") - return FALSE - - var/mob/living/target = input(src,"Pick someone to mend:","Mend Other") as null|anything in targets - if(!target) - return FALSE - - target.add_modifier(/datum/modifier/shadekin/heal_boop,1 MINUTE) - visible_message("\The [src] gently places a hand on \the [target]...") - face_atom(target) - return TRUE diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm deleted file mode 100644 index f3229b147d27..000000000000 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm +++ /dev/null @@ -1,388 +0,0 @@ -/datum/vision/baseline/shadekin - hard_darksight = 0 - -/mob/living/simple_mob/shadekin //Spawning the prototype spawns a random one, see initialize() - name = "shadekin" - desc = "Some sort of fluffer. Big ears, long tail." - catalogue_data = list(/datum/category_item/catalogue/fauna/shadekin) - icon = 'icons/mob/vore_shadekin.dmi' - icon_state = "map_example" - icon_living = "map_example" - faction = "shadekin" - ui_icons = 'icons/screen/hud/common/shadekin.dmi' - mob_class = MOB_CLASS_HUMANOID - mob_bump_flag = HUMAN - - maxHealth = 200 - health = 200 - - movement_cooldown = 2 - vision_innate = /datum/vision/baseline/shadekin - attack_sound = 'sound/weapons/bladeslice.ogg' - has_langs = list(LANGUAGE_GALCOM,LANGUAGE_SHADEKIN) - - legacy_melee_damage_lower = 10 - legacy_melee_damage_upper = 20 - - min_oxy = 0 - max_oxy = 0 - min_tox = 0 - max_tox = 0 - min_co2 = 0 - max_co2 = 0 - min_n2 = 0 - max_n2 = 0 - minbodytemp = 0 - maxbodytemp = 900 - - say_list_type = /datum/say_list/shadekin - - response_help = "pets the" - response_disarm = "bops the" - response_harm = "hits the" - - attacktext = list("mauled","slashed","clawed") - friendly = list("boops", "pawbs", "mars softly at", "sniffs on") - - vore_active = TRUE - vore_pounce_chance = 10 - vore_icons = SA_ICON_LIVING - swallowTime = 2 SECONDS - vore_escape_chance = 25 - - //None, they stay as their defaults. - vore_digest_chance = 0 - vore_absorb_chance = 0 - vore_bump_chance = 0 //They follow people, this would be DENGEROUS - - var/eye_state = RED_EYES //Eye color/energy gain/loss mode - var/eye_icon_state = null //Default, changed in init - var/eye_desc //Eye color description added to examine - - var/mob/living/carbon/human/henlo_human //Human we're stalking currently - - //Behavior - var/stalker = TRUE //Do we creep up on humans - var/shy_approach = FALSE //Do we creep up slowly on humans to boop them - - //Icon handling - var/image/tailimage //Cached tail image - - //Darknesssss - var/energy = 100 //For abilities - var/energy_adminbuse = FALSE //For adminbuse infinite energy - var/dark_gains = 0 //Last tick's change in energy - var/ability_flags = 0 //Flags for active abilities - var/atom/movable/screen/darkhud //Holder to update this icon - var/atom/movable/screen/energyhud //Holder to update this icon - - var/list/shadekin_abilities - -/mob/living/simple_mob/shadekin/Initialize(mapload) - //You spawned the prototype, and want a totally random one. - if(type == /mob/living/simple_mob/shadekin) - - //I'm told by VerySoft these are the liklihood values - var/list/sk_types = list( - /mob/living/simple_mob/shadekin/red = 20, //Actively seek people out to nom, so fairly common to see (relatively speaking), - /mob/living/simple_mob/shadekin/blue = 15, //Explorers that like to interact with people, so still fairly common, - /mob/living/simple_mob/shadekin/purple = 15, //Also explorers that may or may not homf people, - /mob/living/simple_mob/shadekin/yellow = 1 //Very rare, usually never leaves their home - ) - var/new_type = pickweight(sk_types) - - new new_type(loc) - return INITIALIZE_HINT_QDEL - - if(icon_state == "map_example") - icon_state = pick("white","dark","brown") - - icon_living = icon_state - - switch(eye_state) - if(BLUE_EYES) - eye_icon_state = "e_blue" - if(RED_EYES) - eye_icon_state = "e_red" - if(PURPLE_EYES) - eye_icon_state = "e_purple" - if(YELLOW_EYES) - eye_icon_state = "e_yellow" - if(GREEN_EYES) - eye_icon_state = "e_green" - if(ORANGE_EYES) - eye_icon_state = "e_orange" - else - eye_icon_state = "e_red" - - tailimage = image('icons/mob/vore_shadekin64.dmi',null,icon_state) - tailimage.pixel_x = -16 - - if(eye_desc) - desc += " This one has [eye_desc]!" - - var/list/ability_types = subtypesof(/obj/effect/shadekin_ability) - shadekin_abilities = list() - for(var/type in ability_types) - shadekin_abilities += new type(null, src) - - update_icon() - - return ..() - -/mob/living/simple_mob/shadekin/Destroy() - QDEL_LIST_NULL(shadekin_abilities) - . = ..() - -/mob/living/simple_mob/shadekin/init_vore() - if(LAZYLEN(vore_organs)) - return - - var/obj/belly/B = new /obj/belly(src) - vore_selected = B - B.immutable = 1 - B.name = vore_stomach_name ? vore_stomach_name : "stomach" - B.desc = vore_stomach_flavor ? vore_stomach_flavor : "Your surroundings are warm, soft, and slimy. Makes sense, considering you're inside \the [name]." - B.digest_mode = vore_default_mode - B.escapable = vore_escape_chance > 0 - B.escapechance = vore_escape_chance - B.digestchance = vore_digest_chance - B.absorbchance = vore_absorb_chance - B.human_prey_swallow_time = swallowTime - B.nonhuman_prey_swallow_time = swallowTime - B.vore_verb = "swallow" - // TODO - Customizable per mob - B.emote_lists[DM_HOLD] = list( - "The walls gently squeeze against you. The wet sounds of shifting flesh against your form fill the air.", - "The hot, humid air rushes around you for a moment as the creature urps. The walls clench in around you for a moment, before relaxing again.", - "Your body is soaked in the fluids that cling to the churning walls. They squeeze across your form gently, conforming to your shape.", - "You can feel the world around you shift and sway as the creature moves! The flesh is stretchy, doughy. You can sink into it a little ways before it bounces back, curling you into a small shape." - ) - B.emote_lists[DM_DIGEST] = list( - "The walls slop thick slime across your body! It tingles briefly before the sting and ache sets in!", - "The sound of your body slipping and sliding against the powerfully churning stomach fills the air!", - "The grip of that stomach is harsh. Eagerly mushing and rubbing that slime into your body in attempts to break you down!", - "The intense churning and grinding jostles your around within the thick slime as you're slowly broken down!" - ) - B.emote_lists[DM_ABSORB] = list( - "The walls cling to you awfully close... It's almost like you're sinking into them.", - "You can feel the walls press in tightly against you, clinging to you posessively!", - "It almost feels like you're sinking into the soft, doughy flesh!", - "You can feel the walls press in around you. Almost molten, so squishy!!" - ) - B.emote_lists[DM_DRAIN] = list( - "The walls churn down on you heavily!! It's hard to move!", - "You can feel yourself getting weaker with every moment! The doughy walls sap your strength!", - "You're practically smothered in the oppressive heat of the creature's stomach!", - "It's hot, wet and tight!" - ) - B.emote_lists[DM_HEAL] = list( - "The walls pulse against you almost rhythmically. It feels nice, almost like a massage.", - "You're gently squeezed in pleasant warmth, softly churned.", - "The doughy feel of the heavy flesh clinging to you makes you feel a little stronger with every passing moment.", - "The flesh caresses across your body gently as you're held." - ) - B.digest_messages_prey = list( - "Your body is steadily softened more and more over time! Eventually you pass out. The creature's stomach rumbles powerfully as you are reduced to paste, processed for energy!", - "The creature's slimy gut lets out a heavy groan as you're slowly melted away. Gushing deeper through the creature.", - "The stinging and aching gives way to numbness as you're slowly smothered out. Your body is steadily reduced to nutrients and energy for the creature to continue on its way.", - "The chaos of being digested fades as you're snuffed out by a harsh clench! You're steadily broken down into a thick paste, processed and absorbed by the predator!" - ) - -/mob/living/simple_mob/shadekin/Life(seconds, times_fired) - if((. = ..())) - return - - if(ability_flags & AB_PHASE_SHIFTED) - density = FALSE - -/mob/living/simple_mob/shadekin/BiologicalLife(seconds, times_fired) - if((. = ..())) - return - - //Convert spare nutrition into energy at a certain ratio - if(. && nutrition > initial(nutrition) && energy < 100) - nutrition = max(0, nutrition-5) - energy = min(100,energy+1) - -/mob/living/simple_mob/shadekin/update_icon() - . = ..() - - cut_overlay(tailimage) - - tailimage.icon_state = icon_state - - add_overlay(tailimage) - add_overlay(eye_icon_state) - -/mob/living/simple_mob/shadekin/statpanel_data(client/C) - . = ..() - if(C.statpanel_tab("Shadekin")) - STATPANEL_DATA_LINE("") - for(var/A in shadekin_abilities) - var/obj/effect/shadekin_ability/ability = A - ability.atom_button_text() - STATPANEL_DATA_CLICK("[ability.ability_name]", "[ability.name]", "\ref[ability]") - -//They phase back to the dark when killed -/mob/living/simple_mob/shadekin/death(gibbed, deathmessage = "phases to somewhere far away!") - cut_overlays() - icon_state = "" - flick("tp_out",src) - spawn(1 SECOND) - qdel(src) //Back from whence you came! - - . = ..(FALSE, deathmessage) - -//They reach nutritional equilibrium (important for blue-eyes healbelly) -/mob/living/simple_mob/shadekin/Life(seconds, times_fired) - if((. = ..())) - handle_shade() - -/mob/living/simple_mob/shadekin/is_incorporeal() - if(ability_flags & AB_PHASE_SHIFTED) - return TRUE - return FALSE - -/mob/living/simple_mob/shadekin/handle_atmos() - if(ability_flags & AB_PHASE_SHIFTED) - return - else - return .=..() - -/mob/living/simple_mob/shadekin/proc/handle_shade() - //Shifted kin don't gain/lose energy (and save time if we're at the cap) - var/darkness = 1 - - - var/turf/T = get_turf(src) - if(!T) - dark_gains = 0 - return - - var/brightness = T.get_lumcount() //Brightness in 0.0 to 1.0 - darkness = 1-brightness //Invert - - if(ability_flags & AB_PHASE_SHIFTED) - dark_gains = 0 - else - //Heal (very) slowly in good darkness - if(darkness >= 0.75) - adjustFireLoss(-0.05) - adjustBruteLoss(-0.05) - adjustToxLoss(-0.05) - - switch(eye_state) - //Blue has constant, steady (slow) regen and ignores darkness. - if(BLUE_EYES) - dark_gains = 0.5 - //Red has extremely tiny energy buildup in dark, none in light, and hunts for energy. - if(RED_EYES) - if(darkness >= 0.75) - dark_gains = 0.25 - //Purple eyes have moderate gains in darkness and loss in light. - if(PURPLE_EYES) - dark_gains = round((darkness - 0.5) * 2, 0.1) - //Yellow has extreme gains in darkness and loss in light. - if(YELLOW_EYES) - dark_gains = round((darkness - 0.5) * 4, 0.1) - //Similar to blues, but passive is less, and affected by dark - if(GREEN_EYES) - dark_gains = 0.25 - dark_gains += round((darkness - 0.5), 0.1) - //More able to get energy out of the dark, worse attack gains tho - if(ORANGE_EYES) - if(darkness >= 0.65) - dark_gains = 0.30 - - energy = max(0,min(initial(energy),energy + dark_gains)) - - if(energy_adminbuse) - energy = 100 - - //Update turf darkness hud - if(darkhud) - switch(darkness) - if(0.80 to 1.00) - darkhud.icon_state = "dark2" - if(0.60 to 0.80) - darkhud.icon_state = "dark1" - if(0.40 to 0.60) - darkhud.icon_state = "dark" - if(0.20 to 0.40) - darkhud.icon_state = "dark-1" - if(0.00 to 0.20) - darkhud.icon_state = "dark-2" - - //Update energy storage hud - if(energyhud) - switch(energy) - if(80 to INFINITY) - energyhud.icon_state = "energy0" - if(60 to 80) - energyhud.icon_state = "energy1" - if(40 to 60) - energyhud.icon_state = "energy2" - if(20 to 40) - energyhud.icon_state = "energy3" - if(0 to 20) - energyhud.icon_state = "energy4" - -/mob/living/simple_mob/shadekin/speech_bubble_appearance() - return "ghost" - -/mob/living/simple_mob/shadekin/apply_melee_effects(var/atom/A) - . = ..(A) - if(isliving(A)) //We punched something! - var/mob/living/L = A - if(L.stat != DEAD) - var/gains = 0 - switch(eye_state) - if(RED_EYES) - gains = 8 - if(BLUE_EYES) - gains = 1 - if(PURPLE_EYES) - gains = 4 - if(YELLOW_EYES) - gains = 3 - if(GREEN_EYES) - gains = 1 - if(ORANGE_EYES) - gains = 5 - - energy += gains - -//Special hud elements for darkness and energy gains -/mob/living/simple_mob/shadekin/extra_huds(var/datum/hud/hud,var/icon/ui_style,var/list/hud_elements) - //Darkness hud - darkhud = new /atom/movable/screen() - darkhud.icon = ui_style - darkhud.icon_state = "dark" - darkhud.name = "darkness" - darkhud.screen_loc = "CENTER-2:16,SOUTH:5" //Left of the left hand - darkhud.alpha = 150 - hud_elements |= darkhud - - //Energy hud - energyhud = new /atom/movable/screen() - energyhud.icon = ui_style - energyhud.icon_state = "energy0" - energyhud.name = "energy" - energyhud.screen_loc = "CENTER+1:16,SOUTH:5" //Right of the right hand - energyhud.alpha = 150 - hud_elements |= energyhud - -// When someone clicks us with an empty hand -/mob/living/simple_mob/shadekin/attack_hand(mob/user, list/params) - . = ..() - if(user.a_intent == INTENT_HELP) - shy_approach = FALSE //ACCLIMATED - -/datum/say_list/shadekin - speak = list("Marrr.", "Marrr?", "Marrr!") - emote_hear = list("chrrrrrs", "wurbles", "wrrrrbles") - emote_see = list("tailtwitches", "earflicks") - say_maybe_target = list("...mar?") - say_got_target = list("MAR!!!") - //reactions = list("Mar?" = "Marrr!", "Mar!" = "Marrr???", "Mar." = "Marrr.") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm deleted file mode 100644 index 810735642908..000000000000 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/types.dm +++ /dev/null @@ -1,326 +0,0 @@ -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/red - name = "red-eyed shadekin" - eye_state = RED_EYES - //hostile = TRUE - //animal = TRUE - //stop_when_pulled = FALSE - //destroy_surroundings = TRUE - armor_legacy_mob = list( - "melee" = 30, - "bullet" = 20, - "laser" = 20, - "energy" = 50, - "bomb" = 10, - "bio" = 100, - "rad" = 100) - - eye_desc = "red eyes" - - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, \ - slippery gullet of the creature. It's warm, and the air is thick. You can hear \ - its body squelch and shift around you as you settle into its stomach! Thick digestive \ - enzymes cling to you within that dark space, tingling and stinging immediately! The weight of \ - the doughy walls press in around you instantly, churning you up as you begin to digest!" - - player_msg = "You hunt for energy to fuel yourself, not minding in the least \ - if you strip it off unsuspecting prey. You're stronger than other shadekin, faster, and more capable in \ - a brawl, but you barely generate any of your own energy. You can stand in a dark spot to gather scraps \ - of energy in a pinch, but otherwise need to take it, by force if necessary." - -/mob/living/simple_mob/shadekin/red/white - icon_state = "white" -/mob/living/simple_mob/shadekin/red/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/red/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/red/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee - -/mob/living/simple_mob/shadekin/red/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/red/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/red/ai/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/blue - name = "blue-eyed shadekin" - eye_state = BLUE_EYES - health = 100 - //hostile = FALSE - //animal = FALSE - //stop_when_pulled = TRUE - //specific_targets = TRUE //For finding injured people - //destroy_surroundings = FALSE - vore_default_mode = DM_HEAL - vore_escape_chance = 75 - vore_standing_too = 1 - vore_pounce_chance = 100 - swallowTime = 4 SECONDS //A little longer to compensate for the above - vore_ignores_undigestable = FALSE - attacktext = list("shoved") - armor_legacy_mob = list( - "melee" = 5, - "bullet" = 5, - "laser" = 5, - "energy" = 5, - "bomb" = 0, - "bio" = 100, - "rad" = 100) - - eye_desc = "blue eyes" - shy_approach = TRUE - stalker = TRUE - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, \ - slippery gullet of the creature. It's warm, and the air is thick. You can hear its body \ - squelch and shift around you as you settle into its stomach! It's oddly calm, and very dark. \ - The doughy flesh rolls across your form in gentle waves. The aches and pains across your form slowly begin to \ - diminish, your body is healing much faster than normal! You're also soon soaked in harmless slime." - - player_msg = "You've chosen to generate your own energy rather than taking \ - it from others. Most of the time, anyway. You don't have a need to steal energy from others, and gather it up \ - without doing so, albeit slowly. Dark and light are irrelevant to you, they are just different places to explore and \ - discover new things and new people." - -/mob/living/simple_mob/shadekin/blue/white - icon_state = "white" -/mob/living/simple_mob/shadekin/blue/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/blue/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/blue/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/passive - -/mob/living/simple_mob/shadekin/blue/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/blue/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/blue/ai/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/purple - name = "purple-eyed shadekin" - eye_state = PURPLE_EYES - health = 150 - //hostile = FALSE - //animal = TRUE - //stop_when_pulled = FALSE - //destroy_surroundings = TRUE - vore_default_mode = DM_HOLD - vore_digest_chance = 25 - vore_absorb_chance = 25 - armor_legacy_mob = list( - "melee" = 15, - "bullet" = 15, - "laser" = 15, - "energy" = 15, - "bomb" = 15, - "bio" = 100, - "rad" = 100) - - eye_desc = "purple eyes" - shy_approach = TRUE - stalker = TRUE - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, slippery gullet of the creature. \ - It's warm, and the air is thick. You can hear its body squelch and shift around you as you settle into its stomach! \ - It's relatively calm inside the dark organ. Wet and almost molten for how gooey your surroundings feel. \ - You can feel the doughy walls cling to you posessively... It's almost like you could sink into them. \ - There is also an ominous gurgling from somewhere nearby..." - - player_msg = "You're familiar with generating your own energy, but occasionally \ - steal it from others when it suits you. You generate energy at a moderate pace in dark areas, and staying in well-lit \ - areas is taxing on your energy. You can harvest energy from others in a fight, but since you don't need to, you may \ - just choose to simply not fight." - -/mob/living/simple_mob/shadekin/purple/white - icon_state = "white" -/mob/living/simple_mob/shadekin/purple/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/purple/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/purple/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/retaliate - -/mob/living/simple_mob/shadekin/purple/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/purple/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/purple/ai/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/yellow - name = "yellow-eyed shadekin" - eye_state = YELLOW_EYES - health = 100 - //hostile = FALSE - //animal = TRUE - //stop_when_pulled = FALSE - //destroy_surroundings = TRUE - vore_default_mode = DM_DRAIN - vore_digest_chance = 5 - vore_ignores_undigestable = FALSE - armor_legacy_mob = list( - "melee" = 5, - "bullet" = 5, - "laser" = 5, - "energy" = 5, - "bomb" = 0, - "bio" = 100, - "rad" = 100) - - eye_desc = "yellow eyes" - stalker = FALSE - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, slippery gullet \ - of the creature. It's warm, and the air is thick. You can hear its body squelch and shift around you \ - as you settle into its stomach! The doughy walls within cling to you heavily, churning down on you, wearing \ - you out!! There doesn't appear to be any actual danger here, harmless slime clings to you, but it's getting \ - harder and harder to move as those walls press in on you insistently!" - - player_msg = "Your kind rarely ventures into realspace. Being in any well-lit \ - area is very taxing on you, but you gain energy extremely fast in any very dark area. You're weaker than other \ - shadekin, but your fast energy generation in the dark allows you to phase shift more often." - -/mob/living/simple_mob/shadekin/yellow/white - icon_state = "white" -/mob/living/simple_mob/shadekin/yellow/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/yellow/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/yellow/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee/hit_and_run - -/mob/living/simple_mob/shadekin/yellow/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/yellow/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/yellow/ai/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/yellow/ai/retaliate - ai_holder_type = /datum/ai_holder/polaris/simple_mob/retaliate - -/mob/living/simple_mob/shadekin/yellow/ai/retaliate/white - icon_state = "white" -/mob/living/simple_mob/shadekin/yellow/ai/retaliate/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/yellow/ai/retaliate/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/green - name = "green-eyed shadekin" - eye_state = GREEN_EYES - health = 125 - //hostile = FALSE - //animal = TRUE - //stop_when_pulled = FALSE - //destroy_surroundings = TRUE - vore_default_mode = DM_DRAIN - vore_digest_chance = 0 - vore_ignores_undigestable = FALSE - armor_legacy_mob = list( - "melee" = 5, - "bullet" = 5, - "laser" = 5, - "energy" = 5, - "bomb" = 0, - "bio" = 100, - "rad" = 100) - - eye_desc = "green eyes" - stalker = TRUE - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, slippery gullet \ - of the creature. It's warm, and the air is thick. You can hear its body squelch and shift around you \ - as you settle into its stomach! The doughy walls within cling to you heavily, churning down on you, wearing \ - you out!! There doesn't appear to be any actual danger here, harmless slime clings to you, but it's getting \ - harder and harder to move as those walls press in on you insistently!" - - player_msg = "Your kind rarely ventures into realspace. Being in any well-lit area is very taxing on you, but you \ - have more experience than your yellow-eyed cousins. You gain energy decently fast in any very dark area. You're weaker than other \ - shadekin, but your slight energy generation constnatly, and especially in the dark allows for a good mix of uses." - -/mob/living/simple_mob/shadekin/green/white - icon_state = "white" -/mob/living/simple_mob/shadekin/green/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/green/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/green/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/passive - -/mob/living/simple_mob/shadekin/green/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/green/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/green/ai/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -/mob/living/simple_mob/shadekin/orange - name = "orange-eyed shadekin" - eye_state = ORANGE_EYES - health = 175 - //hostile = TRUE - //animal = TRUE - //stop_when_pulled = FALSE - //destroy_surroundings = TRUE - armor_legacy_mob = list( - "melee" = 20, - "bullet" = 15, - "laser" = 15, - "energy" = 25, - "bomb" = 10, - "bio" = 100, - "rad" = 100) - - eye_desc = "orange eyes" - - vore_stomach_flavor = "You slip past pointy triangle teeth and down the slick, \ - slippery gullet of the creature. It's warm, and the air is thick. You can hear \ - its body squelch and shift around you as you settle into its stomach! Thick digestive \ - enzymes cling to you within that dark space, tingling and stinging immediately! The weight of \ - the doughy walls press in around you instantly, churning you up as you begin to digest!" - - player_msg = "You usually hunt for energy to fuel yourself, though not as often as your red-eyed cousins. \ - You're stronger than most shadekin, faster, and more capable in a brawl, but you don't generate much of your own energy. \ - You can stand in a dark spot to gather some energy, but otherwise need to take it, by force if necessary." - -/mob/living/simple_mob/shadekin/orange/white - icon_state = "white" -/mob/living/simple_mob/shadekin/orange/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/orange/brown - icon_state = "brown" - -/mob/living/simple_mob/shadekin/orange/ai - ai_holder_type = /datum/ai_holder/polaris/simple_mob/melee - -/mob/living/simple_mob/shadekin/orange/ai/white - icon_state = "white" -/mob/living/simple_mob/shadekin/orange/ai/dark - icon_state = "dark" -/mob/living/simple_mob/shadekin/orange/ai/brown - icon_state = "brown" - -///////////////////////////////////////////////////////////////// -//Fluffy specific fluffer -/mob/living/simple_mob/shadekin/blue/rivyr - name = "Rivyr" - desc = "She appears to be a fluffer of some sort. Deep blue eyes and curious attitude." - icon_state = "rivyr" - eye_desc = "" - vore_stomach_flavor = "Blue flesh gleams in the fading light as you slip down the little mar's gullet! \ - Gooey flesh and heat surrounds your form as you're tucked away into the darkness of her stomach! Thick slimes cling \ - to you, but they seem to be harmless. The organ gently churns around you, clinging to your shape and forcing \ - you to curl up a bit. You can feel her rub at you some through the layers of flesh and fluff, while aches \ - and pains begin to fade away across your body." - player_msg = "Mar? Mar mar. Mar mar mar. Mar. Mar mar? Mar! Mar. Marrrr." diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm b/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm index 0509c018843b..52c594aab27a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/snake.dm @@ -19,7 +19,8 @@ icon_state = "snake" icon = 'icons/mob/vore64x64.dmi' - faction = "snake" + iff_factions = MOB_IFF_FACTION_FARM_PEST + maxHealth = 200 health = 200 randomized = TRUE @@ -34,7 +35,4 @@ // Activate Noms! /mob/living/simple_mob/vore/aggressive/giant_snake - vore_active = 1 - vore_pounce_chance = 25 - vore_icons = SA_ICON_LIVING swallowTime = 2 SECONDS // Hungry little bastards. diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm index c2541a3f631f..c5548a658e13 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub.dm @@ -35,7 +35,8 @@ GLOBAL_LIST_EMPTY(solargrubs) //var/adult_forms = "/mob/living/simple_mob/vore/solarmoth" // CHOMPEDIT VAR that decides what mob the queen form is. ex /mob/living/simple_mob/subtypes/vore/solarmoth; CitRP: Without lunarmoth, quoted out for fun; // CHOMPEDIT End, Rykka waz here. *pawstamp* - faction = "grubs" + iff_factions = MOB_IFF_FACTION_GRUB + maxHealth = 50 //grubs can take a lot of harm health = 50 @@ -120,8 +121,6 @@ GLOBAL_LIST_EMPTY(solargrubs) if(prob(1) && charge >= 32000 && can_evolve == 1) // CitRP: We can quote this out and see what happens; && moth_amount <= 1) //it's reading from the moth_amount global list to determine if it can evolve. There should only ever be a maxcap of 1 existing solar moth alive at any time. TODO: make the code decrease the list after 1 has spawned this shift. anchored = 0 PN = attached.powernet - release_vore_contents() - prey_excludes.Cut() GLOB.moth_amount += 1 //CitRP: There was some magic going on around this here part, it might actualy be working. death_star() @@ -131,14 +130,6 @@ GLOBAL_LIST_EMPTY(solargrubs) new chosen_form(get_turf(src)) qdel(src) -/mob/living/simple_mob/vore/solargrub //active noms - vore_bump_chance = 50 - vore_bump_emote = "applies minimal effort to try and slurp up" - vore_active = 1 - vore_capacity = 1 - vore_pounce_chance = 0 //grubs only eat incapacitated targets - vore_default_mode = DM_DIGEST - /mob/living/simple_mob/vore/solargrub/apply_melee_effects(var/atom/A) if(isliving(A)) var/mob/living/L = A diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm index 16fa82ea6908..e2a65637aa01 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solargrub_larva.dm @@ -19,7 +19,7 @@ var/global/list/grub_machine_overlays = list() meat_amount = 2 meat_type = /obj/item/reagent_containers/food/snacks/meat/grubmeat - faction = "grubs" + iff_factions = MOB_IFF_FACTION_GRUB response_help = "pats" response_disarm = "nudges" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth_ch.dm b/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth.dm similarity index 96% rename from code/modules/mob/living/simple_mob/subtypes/vore/solarmoth_ch.dm rename to code/modules/mob/living/simple_mob/subtypes/vore/solarmoth.dm index ca127910d87a..0f07603df036 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth_ch.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/solarmoth.dm @@ -30,7 +30,8 @@ var/emp_light = 7 var/emp_long = 10 - faction = "grubs" + iff_factions = MOB_IFF_FACTION_GRUB + maxHealth = 200 // Tanky fuckers. health = 200 // Tanky fuckers. @@ -153,12 +154,6 @@ /mob/living/simple_mob/vore/solarmoth //active noms - vore_bump_chance = 50 - vore_bump_emote = "applies minimal effort to try and slurp up" - vore_active = 1 - vore_capacity = 1 - vore_pounce_chance = 0 //moths only eat incapacitated targets. It's too lazy burning you to a crisp to try to pounce you - vore_default_mode = DM_DIGEST /mob/living/simple_mob/vore/solarmoth/lunarmoth name = "Lunarmoth" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm b/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm index 796d736e8448..2aefb32844ac 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/vore.dm @@ -1,6 +1,6 @@ +// todo: get rid of /vore and /vore/aggressive /mob/living/simple_mob/vore mob_class = MOB_CLASS_ANIMAL - mob_bump_flag = 0 meat_type = /obj/item/reagent_containers/food/snacks/meat bone_type = /obj/item/stack/material/bone hide_type = /obj/item/stack/animalhide diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm index ed724697a757..5eca2385fca7 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolf.dm @@ -30,14 +30,12 @@ // Activate Noms! /mob/living/simple_mob/animal/wolf - vore_active = 1 - vore_icons = SA_ICON_LIVING // Adds Phoron Wolf /mob/living/simple_mob/animal/wolf/phoron + iff_factions = MOB_IFF_FACTION_BIND_TO_LEVEL - faction = "underdark" movement_cooldown = 0 harm_intent_damage = 5 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm index 2150e4c39dac..10ed8ab01633 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm @@ -8,7 +8,6 @@ icon_dead = "wolfgirl-dead" icon = 'icons/mob/vore.dmi' - faction = "wolfgirl" maxHealth = 30 health = 30 @@ -35,9 +34,6 @@ */ // Activate Noms! /mob/living/simple_mob/vore/wolfgirl - vore_active = 1 - vore_pounce_chance = 40 - vore_icons = SA_ICON_LIVING /datum/ai_holder/polaris/simple_mob/retaliate/cooperative/wolfgirl/on_hear_say(mob/living/speaker, message) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm b/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm index ee00e8ce8330..4b1aa4a4c0d0 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/zz_vore_overrides.dm @@ -1,115 +1,26 @@ -// -// This file overrides settings on upstream simple animals to turn on vore behavior -// - -/* -## For anything that previously inhertited from: /mob/living/simple_mob/hostile/vore ## - - vore_active = 1 - icon = 'icons/mob/vore.dmi' - -## For anything that previously inhertied from: /mob/living/simple_mob/hostile/vore/large ## - - vore_active = 1 - icon = 'icons/mob/vore64x64.dmi' - old_x = -16 - old_y = -16 - pixel_x = -16 - pixel_y = -16 - vore_pounce_chance = 50 -*/ - -// -// Okay! Here we go! -// - /mob/living/simple_mob/animal/space/bear - vore_active = 1 icon = 'icons/mob/vore.dmi' icon_state = "spacebear" icon_living = "spacebear" icon_dead = "spacebear-dead" icon_gib = "bear-gib" - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/animal/space/bear/hudson name = "Hudson" /mob/living/simple_mob/animal/space/bear/brown - vore_active = 1 icon = 'icons/mob/vore.dmi' name = "brown bear" icon_state = "brownbear" icon_living = "brownbear" icon_dead = "brownbear-dead" icon_gib = "bear-gib" - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/animal/space/carp icon = 'icons/mob/vore.dmi' - vore_active = 1 - vore_icons = SA_ICON_LIVING /mob/living/simple_mob/animal/space/carp/large - vore_icons = 0 /mob/living/simple_mob/animal/space/carp/large/huge - vore_icons = 0 /mob/living/simple_mob/animal/space/carp/holographic - vore_icons = 0 - -/mob/living/simple_mob/animal/passive/cat - vore_active = 1 - //specific_targets = 0 // Targeting UNLOCKED - vore_max_size = RESIZE_TINY - -/mob/living/simple_mob/animal/passive/cat/fluff - vore_ignores_undigestable = 0 - vore_pounce_chance = 100 - vore_digest_chance = 0 // just use the toggle - vore_default_mode = DM_HOLD //can use the toggle if you wanna be catfood - vore_standing_too = TRUE //gonna get pounced - -/mob/living/simple_mob/animal/passive/fox - vore_active = 1 - vore_max_size = RESIZE_TINY - -/mob/living/simple_mob/fox/fluff - vore_ignores_undigestable = 0 - vore_pounce_chance = 100 - vore_digest_chance = 0 // just use the toggle - vore_default_mode = DM_HOLD //can use the toggle if you wanna be foxfood - vore_standing_too = TRUE // gonna get pounced - -/mob/living/simple_mob/animal/space/goose - vore_active = 1 - vore_max_size = RESIZE_SMALL - -/mob/living/simple_mob/animal/passive/penguin - vore_active = 1 - vore_max_size = RESIZE_SMALL - - -/mob/living/simple_mob/hostile/carp/pike - vore_active = 1 /mob/living/simple_mob/animal/space/carp/holographic - vore_icons = 0 - vore_digest_chance = 0 - vore_absorb_chance = 0 - -// Override stuff for holodeck carp to make them not digest when set to safe! -/mob/living/simple_mob/animal/space/carp/holographic/init_vore() - . = ..() - var/safe = (faction == "neutral") - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.digest_mode = safe ? DM_HOLD : vore_default_mode - -/mob/living/simple_mob/animal/space/carp/holographic/set_safety(var/safe) - . = ..() - for(var/belly in vore_organs) - var/obj/belly/B = belly - B.digest_mode = safe ? DM_HOLD : vore_default_mode - -/mob/living/simple_mob/animal/passive/mouse - faction = "mouse" //Giving mice a faction so certain mobs can get along with them. diff --git a/code/modules/mob/mob-damage.dm b/code/modules/mob/mob-damage.dm new file mode 100644 index 000000000000..f797f95b8702 --- /dev/null +++ b/code/modules/mob/mob-damage.dm @@ -0,0 +1,7 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Damage Instance Handling *// + +/mob/inflict_damage_instance(SHIELDCALL_PROC_HEADER) + return diff --git a/code/modules/mob/mob-defense.dm b/code/modules/mob/mob-defense.dm new file mode 100644 index 000000000000..632159f429c6 --- /dev/null +++ b/code/modules/mob/mob-defense.dm @@ -0,0 +1,121 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 silicons *// + +//* Armor Handling *// + +/** + * An override of /atom/proc/run_armorcalls(), with zone filter capability. + * + * This is what you should override, instead of the check/run procs. + * + * * At current moment, due to expensiveness reasons, not providing filter_zone will result in only SHIELDCALL_ARG_DAMAGE being modified. + * + * @params + * * shieldcall_args - passed in shieldcall args list to modify + * * fake_attack - are we just checking armor? + * * filter_zone - confine to a certain hit zone. this is **required** for full processing, otherwise we just check overall damage. + */ +/mob/run_armorcalls(list/shieldcall_args, fake_attack, filter_zone) + ..() // perform default /atom level + +/** + * Generic, low-level armor check for inbound attacks + * + * * This will filter armor by zone. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/check_mob_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, TRUE, hit_zone) // by default, use atom/var/armor on ourselves + +/** + * Generic, low-level armor processing for inbound attacks + * + * * This will filter armor by zone. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/run_mob_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, FALSE, hit_zone) // by default, use atom/var/armor on ourselves + +/** + * Checks the average armor for a full-body attack. + * + * * this is used for lazy-sim's like explosion where we're not simulating every limb's individual damage tick. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/check_mob_overall_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, TRUE) // by default, use atom/var/armor on ourselves + +/** + * Checks the average armor for a full-body attack. + * + * * this is used for lazy-sim's like explosion where we're not simulating every limb's individual damage tick. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/run_mob_overall_armor(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, FALSE) // by default, use atom/var/armor on ourselves + +//* Defense Handling *// + +/** + * Generic, low-level defense check for inbound attacks + * + * * This will filter defense by zone. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/check_mob_defense(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, TRUE, hit_zone) // by default, use atom/var/armor on ourselves + run_shieldcalls(args, TRUE) + +/** + * Generic, low-level defense processing for inbound attacks + * + * * This will filter defense by zone. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/run_mob_defense(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, FALSE, hit_zone) // by default, use atom/var/armor on ourselves + run_shieldcalls(args, FALSE) + +/** + * Checks the average defense for a full-body attack. + * + * * this is used for lazy-sim's like explosion where we're not simulating every limb's individual damage tick. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/check_mob_overall_defense(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, TRUE) // by default, use atom/var/armor on ourselves + run_shieldcalls(args, TRUE) + +/** + * Checks the average defense for a full-body attack. + * + * * this is used for lazy-sim's like explosion where we're not simulating every limb's individual damage tick. + * * This operates like [atom_shieldcall()]. + * + * @return modified argument list, with SHIELDCALL_ARG_* defines as indices. + */ +/mob/proc/run_mob_overall_defense(SHIELDCALL_PROC_HEADER) + SHOULD_NOT_OVERRIDE(TRUE) + run_armorcalls(args, FALSE) // by default, use atom/var/armor on ourselves + run_shieldcalls(args, FALSE) diff --git a/code/modules/mob/mob-iff.dm b/code/modules/mob/mob-iff.dm new file mode 100644 index 000000000000..b4d9ec466f74 --- /dev/null +++ b/code/modules/mob/mob-iff.dm @@ -0,0 +1,117 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +//* Basic mob-level IFF system. *// +//* *// +//* This is not on mob AI holder so non-AI holders can access it too. *// + +// todo: typelist them where possible + +//* Init *// + +/mob/proc/init_iff() + if(!iff_factions) + return + set_iff_factions(iff_factions) + +/** + * * descriptor can be a string or a list + * + * @return string faction + */ +/mob/proc/process_iff_faction(descriptor) + if(islist(descriptor)) + switch(descriptor[1]) + if(MOB_IFF_FACTION_BIND_TO_LEVEL) + // if get z returns null it works as an assoc lookup returning null + var/prepend = SSmapping.ordered_levels[get_z(src)]?.id || "unkw" + return "[prepend]-[descriptor[descriptor[1]]]" + if(MOB_IFF_FACTION_BIND_TO_MAP) + // if get z returns null it works as an assoc lookup returning null + var/datum/map_level/level = SSmapping.ordered_levels[get_z(src)] + var/prepend = level?.parent_map?.id || level?.id || "unkw" + return "bind-[prepend]-[descriptor[descriptor[1]]]" + switch(descriptor) + if(MOB_IFF_FACTION_BIND_TO_LEVEL) + // if get z returns null it works as an assoc lookup returning null + var/prepend = SSmapping.ordered_levels[get_z(src)]?.id || "unkw" + return "bind-[prepend]" + if(MOB_IFF_FACTION_BIND_TO_MAP) + // if get z returns null it works as an assoc lookup returning null + var/datum/map_level/level = SSmapping.ordered_levels[get_z(src)] + var/prepend = level?.parent_map?.id || level?.id || "unkw" + return "bind-[prepend]" + else + return descriptor + +//* Direct Operations *// + +/mob/proc/add_iff_faction(string) + if(islist(iff_factions)) + iff_factions |= string + else if(iff_factions) + iff_factions = list(iff_factions, string) + else + iff_factions = string + +/mob/proc/remove_iff_faction(string) + if(islist(iff_factions)) + iff_factions -= string + if(!length(iff_factions)) + iff_factions = null + else if(iff_factions == string) + iff_factions = null + +/mob/proc/clear_iff_factions() + iff_factions = null + +/mob/proc/set_iff_factions(new_factions) + if(islist(new_factions)) + for(var/i in 1 to length(new_factions)) + var/faction = new_factions[i] + if(islist(faction) || ispath(faction) || faction[1] == "!") + faction = process_iff_faction(faction) + new_factions[i] = faction + continue // nothing otherwise to do yet + else if(islist(new_factions) || ispath(new_factions) || new_factions[1] == "!") + new_factions = process_iff_faction(new_factions) + + iff_factions = new_factions + +/mob/proc/has_iff_faction(string) + return islist(iff_factions) ? (string in iff_factions) : (iff_factions == string) + +//* Generation / Resolution *// + +/** + * Gets, or generates an unique IFF faction for ourselves. + * + * * Behavior depends on DF_USE_TAG being in use for us. + */ +/mob/proc/unique_iff_faction() + var/unique_faction = "mob-[tag]" + if(!has_iff_faction(unique_faction)) + add_iff_faction(unique_faction) + return unique_faction + +//* Operations - With Other Mob *// + +/mob/proc/copy_iff_factions(mob/other) + iff_factions = islist(other.iff_factions)? other.iff_factions:Copy() : other.iff_factions + +/** + * Returns a truthy value if we share atleast one IFF faction string with another mob, falsy otherwise. + * + * * does not necessarily return TRUE or FALSE! + */ +/mob/proc/shares_iff_faction(mob/other) + if(islist(iff_factions)) + if(islist(other.iff_factions)) + return length(iff_factions & other.iff_factions) + else if(other.iff_factions) + return other.iff_factions in iff_factions + else if(iff_factions) + if(islist(other.iff_factions)) + return iff_factions in other.iff_factions + else if(other.iff_factions) + return iff_factions == other.iff_factions diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 74257bf1b8e0..359eaaac1a18 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -47,6 +47,8 @@ initialize_actionspeed() // ssd overlay update_ssd_overlay() + // iff factions + init_iff() return ..() /mob/Destroy() @@ -427,9 +429,6 @@ . += M . -= src -/mob/proc/ret_grab(obj/effect/list_container/mobl/L as obj, flag) - return - /** * Get the notes of this mob * diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index f8d9fc7ab7f9..5484c23990ae 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -79,9 +79,9 @@ /// Next world.time we will be able to move. var/move_delay = 0 /// Last world.time we finished a normal, non relay/intercepted move - var/last_move_time = 0 + var/last_self_move = 0 /// Last world.time we turned in our spot without moving (see: facing directions) - var/last_turn = 0 + var/last_self_turn = 0 /// Tracks if we have gravity from environment right now. var/in_gravity @@ -117,6 +117,13 @@ /// our inventory datum, if any. var/datum/inventory/inventory + //* IFF *// + /// our IFF factions + /// + /// * Do not read directly, use [code/modules/mob/mob-iff.dm] helpers. + /// * can be set to a string, or a list of strings. + var/iff_factions = MOB_IFF_FACTION_NEUTRAL + //! Size //! todo kill this with fire it should just be part of icon_scale_x/y. /// our size multiplier @@ -293,7 +300,7 @@ var/voice_name = "unidentifiable voice" ///Used for checking whether hostile simple animals will attack you, possibly more stuff later. - var/faction = "neutral" + var/_faction = "neutral" /// To prevent pAIs/mice/etc from getting antag in autotraitor and future auto- modes. Uses inheritance instead of a bunch of typechecks. // todo: what the fuck var/can_be_antagged = FALSE diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 47ea91b24636..2696567e57ef 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -500,7 +500,7 @@ var/list/intents = list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM) if(!istype(src.ai_holder, /datum/ai_holder/polaris)) return SAFE_PERP var/datum/ai_holder/polaris/ai_holder = src.ai_holder - if(has_polaris_AI() && ai_holder.hostile && faction != "neutral") + if(has_polaris_AI() && ai_holder.hostile && !has_iff_faction(MOB_IFF_FACTION_NEUTRAL)) threatcount += 4 return threatcount diff --git a/code/modules/mob/movement.dm b/code/modules/mob/movement.dm index a42342538f4a..e9a8621df515 100644 --- a/code/modules/mob/movement.dm +++ b/code/modules/mob/movement.dm @@ -47,9 +47,6 @@ . = ..() if(.) return - if(istype(mover, /obj/projectile)) - var/obj/projectile/P = mover - return !P.can_hit_target(src, P.permutated, src == P.original, TRUE) // thrown things still hit us even when nondense if(can_cross_under(mover)) return TRUE @@ -61,8 +58,11 @@ return TRUE return ..() +/** + * Can something cross under us without being blocked by us? + */ /mob/proc/can_cross_under(atom/movable/mover) - return !mover.density && !mover.throwing + return !mover.density && !mover.throwing && !istype(mover, /obj/projectile) /** * Toggle the move intent of the mob @@ -284,7 +284,7 @@ //Something with pulling things if(locate(/obj/item/grab, mob)) add_delay_grab = 7 - var/list/grabbed = mob.ret_grab() + var/list/grabbed = mob.get_grabbing_recursive() + src // im fucking screaming; this is because old code always considers self as grabbed. if(grabbed) if(grabbed.len == 2) grabbed -= mob @@ -352,14 +352,14 @@ // preserve momentum: for non-evenly-0.5-multiple movespeeds (HELLO, DIAGONAL MOVES), // we need to store how much we're cheated out of our tick and carry it through // make an intelligent guess at if they're trying to keep moving, tho! - if(mob.last_move_time > (world.time - add_delay * 1.25)) + if(mob.last_self_move > (world.time - add_delay * 1.25)) mob.move_delay = old_delay + add_delay else mob.move_delay = world.time + add_delay SMOOTH_GLIDE_SIZE(mob, DELAY_TO_GLIDE_SIZE(add_delay)) - mob.last_move_time = world.time + mob.last_self_move = world.time /mob/proc/SelfMove(turf/T, dir) in_selfmove = TRUE @@ -537,7 +537,7 @@ * * we are not restrained */ /mob/proc/canface() - if(world.time <= last_turn) + if(world.time <= last_self_turn) return FALSE if(stat == DEAD || stat == UNCONSCIOUS) return FALSE @@ -554,7 +554,7 @@ if(!canface()) return FALSE setDir(EAST) - last_turn = world.time + last_self_turn = world.time return TRUE ///Hidden verb to turn west @@ -564,7 +564,7 @@ if(!canface()) return FALSE setDir(WEST) - last_turn = world.time + last_self_turn = world.time return TRUE ///Hidden verb to turn north @@ -574,7 +574,7 @@ if(!canface()) return FALSE setDir(NORTH) - last_turn = world.time + last_self_turn = world.time return TRUE ///Hidden verb to turn south @@ -584,7 +584,7 @@ if(!canface()) return FALSE setDir(SOUTH) - last_turn = world.time + last_self_turn = world.time return TRUE //! Pixel Shifting diff --git a/code/modules/modular_computers/NTNet/NTNet_relay.dm b/code/modules/modular_computers/NTNet/NTNet_relay.dm index c7e503ee6c59..0e6a94718880 100644 --- a/code/modules/modular_computers/NTNet/NTNet_relay.dm +++ b/code/modules/modular_computers/NTNet/NTNet_relay.dm @@ -70,7 +70,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/ntnet_relay/attack_hand(mob/user, list/params) +/obj/machinery/ntnet_relay/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) /obj/machinery/ntnet_relay/Topic(href, href_list) diff --git a/code/modules/modular_computers/computers/modular_computer/damage.dm b/code/modules/modular_computers/computers/modular_computer/damage.dm index 64d34486891a..32da9c40a242 100644 --- a/code/modules/modular_computers/computers/modular_computer/damage.dm +++ b/code/modules/modular_computers/computers/modular_computer/damage.dm @@ -51,12 +51,12 @@ * "Burn" damage is equally strong against internal components and exterior casing * "Brute" damage mostly damages the casing. */ -/obj/item/modular_computer/bullet_act(obj/projectile/Proj) +/obj/item/modular_computer/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() - switch(Proj.damage_type) + switch(proj.damage_type) if(BRUTE) - take_damage_legacy(Proj.damage, Proj.damage / 2) + take_damage_legacy(proj.damage, proj.damage / 2) if(HALLOSS) - take_damage_legacy(Proj.damage, Proj.damage / 3, 0) + take_damage_legacy(proj.damage, proj.damage / 3, 0) if(BURN) - take_damage_legacy(Proj.damage, Proj.damage / 1.5) + take_damage_legacy(proj.damage, proj.damage / 1.5) diff --git a/code/modules/modular_computers/computers/modular_computer/interaction.dm b/code/modules/modular_computers/computers/modular_computer/interaction.dm index 28fc9219d1e6..9207f187b5de 100644 --- a/code/modules/modular_computers/computers/modular_computer/interaction.dm +++ b/code/modules/modular_computers/computers/modular_computer/interaction.dm @@ -109,7 +109,7 @@ /obj/item/modular_computer/attack_ai(mob/user) return attack_self(user) -/obj/item/modular_computer/attack_hand(mob/user, list/params) +/obj/item/modular_computer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(anchored) return attack_self(user) return ..() diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm index fc48f029d79f..ddc02dbf3030 100644 --- a/code/modules/modular_computers/laptop_vendor.dm +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -214,7 +214,7 @@ return 1 return 0 -/obj/machinery/lapvend/attack_hand(mob/user, list/params) +/obj/machinery/lapvend/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) /obj/machinery/lapvend/nano_ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) diff --git a/code/modules/movespeed/movespeed_modifier.dm b/code/modules/movespeed/movespeed_modifier.dm index 0cc2c6291271..97be21d22456 100644 --- a/code/modules/movespeed/movespeed_modifier.dm +++ b/code/modules/movespeed/movespeed_modifier.dm @@ -290,14 +290,14 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache) cached_multiplicative_slowdown = min(., 10 / MOVESPEED_ABSOLUTE_MINIMUM_TILES_PER_SECOND) if(!client) return - var/diff = (last_move_time - move_delay) - cached_multiplicative_slowdown + var/diff = (last_self_move - move_delay) - cached_multiplicative_slowdown if(diff > 0) // your delay decreases, "give" the delay back to the client if(move_delay > world.time + 1.5) move_delay -= diff #ifdef SMOOTH_MOVEMENT var/timeleft = world.time - move_delay - var/elapsed = world.time - last_move_time + var/elapsed = world.time - last_self_move var/glide_size_current = glide_size if((timeleft <= 0) || (elapsed > 20)) SMOOTH_GLIDE_SIZE(src, 16, TRUE) diff --git a/code/modules/multiz/atoms.dm b/code/modules/multiz/atoms.dm index 9aa67f7f61cf..5abbda59d9a8 100644 --- a/code/modules/multiz/atoms.dm +++ b/code/modules/multiz/atoms.dm @@ -23,4 +23,4 @@ * new_loc - new turf */ /atom/proc/z_pass_out(atom/movable/AM, dir, turf/new_loc) - return !AM || Uncross(AM) + return !AM || Uncross(AM, new_loc) diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 650e4f85d0dc..907f3d5a4106 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -314,7 +314,7 @@ return 1 var/atom/A = find_fall_target(oldloc, landing) - if(special_fall_handle(A) || !A || !A.check_impact(src)) + if(special_fall_handle(A) || !A || !A.check_z_impact(src)) return var/mob/drop_mob = locate(/mob, landing) if(drop_mob && !(drop_mob == src) && ismob(drop_mob) && isliving(drop_mob)) //Shitload of checks. This is because the game finds various ways to screw me over. @@ -371,16 +371,14 @@ return TRUE return prevent_z_fall(falling_atom, 0, NONE) & (FALL_TERMINATED | FALL_BLOCKED) - /** * If you are hit: how is it handled. * Return TRUE if the generic fall_impact should be called. * Return FALSE if you handled it yourself or if there's no effect from hitting you. */ -/atom/proc/check_impact(atom/movable/falling_atom) +/atom/proc/check_z_impact(atom/movable/falling_atom) return TRUE - /** * Called by CheckFall when we actually hit something. Various Vars will be described below. * hit_atom is the thing we fall on. diff --git a/code/modules/multiz/structures/hoist.dm b/code/modules/multiz/structures/hoist.dm index f2459acaf4bf..aceb2dfcde03 100644 --- a/code/modules/multiz/structures/hoist.dm +++ b/code/modules/multiz/structures/hoist.dm @@ -28,7 +28,7 @@ var/obj/structure/hoist/source_hoist description_info = "Click and drag someone (or any object) to this to attach them to the clamp. If you are within reach, when you click and drag this to a turf adjacent to you, it will move the attached object there and release it." -/obj/effect/hoist_hook/attack_hand(mob/user, list/params) +/obj/effect/hoist_hook/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return // This has to be overridden so that it works properly. /obj/effect/hoist_hook/MouseDroppedOnLegacy(atom/movable/AM,mob/user) @@ -175,7 +175,7 @@ return -/obj/structure/hoist/attack_hand(mob/user, list/params) +/obj/structure/hoist/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (!ishuman(user)) return diff --git a/code/modules/multiz/structures/ladder.dm b/code/modules/multiz/structures/ladder.dm index 0ac65d6d7c9c..96ebae23d1e7 100644 --- a/code/modules/multiz/structures/ladder.dm +++ b/code/modules/multiz/structures/ladder.dm @@ -39,7 +39,7 @@ attack_hand(user) return -/obj/structure/ladder/attack_hand(mob/user, list/params) +/obj/structure/ladder/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/multiz/structures/vorestation_portals.dm b/code/modules/multiz/structures/vorestation_portals.dm index 9e1989fbfce7..3150788bf837 100644 --- a/code/modules/multiz/structures/vorestation_portals.dm +++ b/code/modules/multiz/structures/vorestation_portals.dm @@ -27,7 +27,7 @@ return //do not send ghosts, zshadows, ai eyes, etc teleport(AM) -/obj/structure/portal_subtle/attack_hand(mob/user, list/params) +/obj/structure/portal_subtle/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(istype(user) && !(istype(user,/mob/living))) return //do not send ghosts, zshadows, ai eyes, etc spawn(0) diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm index a4261ffa6b4a..e35652e4e2bd 100644 --- a/code/modules/multiz/turf.dm +++ b/code/modules/multiz/turf.dm @@ -113,9 +113,6 @@ return TRUE // impact! return ..() -/turf/check_impact(atom/movable/falling_atom) - return TRUE - //* lookups /turf/proc/above() diff --git a/code/modules/multiz/zmimic/mimic_movable.dm b/code/modules/multiz/zmimic/mimic_movable.dm index 95f6cf27cd4d..adb074785f54 100644 --- a/code/modules/multiz/zmimic/mimic_movable.dm +++ b/code/modules/multiz/zmimic/mimic_movable.dm @@ -170,7 +170,7 @@ /atom/movable/openspace/mimic/attackby(obj/item/W, mob/user) to_chat(user, SPAN_NOTICE("\The [src] is too far away.")) -/atom/movable/openspace/mimic/attack_hand(mob/user) +/atom/movable/openspace/mimic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) /atom/movable/openspace/mimic/examine(...) @@ -216,7 +216,7 @@ /atom/movable/openspace/turf_proxy/attackby(obj/item/W, mob/user) loc.attackby(W, user) -/atom/movable/openspace/turf_proxy/attack_hand(mob/user, list/params) +/atom/movable/openspace/turf_proxy/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) loc.attack_hand(user) /atom/movable/openspace/turf_proxy/attack_generic(mob/user as mob) @@ -243,7 +243,7 @@ /atom/movable/openspace/turf_mimic/attackby(obj/item/W, mob/user) loc.attackby(W, user) -/atom/movable/openspace/turf_mimic/attack_hand(mob/user, list/params) +/atom/movable/openspace/turf_mimic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) to_chat(user, SPAN_NOTICE("You cannot reach \the [src] from here.")) /atom/movable/openspace/turf_mimic/attack_generic(mob/user as mob) diff --git a/code/modules/organs/external/external.dm b/code/modules/organs/external/external.dm index a7861e3289a5..ded3e53b1096 100644 --- a/code/modules/organs/external/external.dm +++ b/code/modules/organs/external/external.dm @@ -1269,7 +1269,7 @@ Note that amputating the affected organ does in fact remove the infection from t /obj/item/organ/external/proc/embed(var/obj/item/W, var/silent = 0) if(!owner || loc != owner) return - if(owner.species.species_flags & IS_SLIME) + if(owner.species.reagent_tag == IS_SLIME) create_wound( CUT, 15 ) //fixes proms being bugged into paincrit;instead whatever would embed now just takes a chunk out src.visible_message("[owner] has been seriously wounded by [W]!") W.add_blood(owner) diff --git a/code/modules/organs/internal/augment/armmounted.dm b/code/modules/organs/internal/augment/armmounted.dm index b6e32224a72a..c4b1c99dd71c 100644 --- a/code/modules/organs/internal/augment/armmounted.dm +++ b/code/modules/organs/internal/augment/armmounted.dm @@ -70,7 +70,7 @@ /obj/item/organ/internal/augment/armmounted/hand/sword name = "energy blade implant" - integrated_object_type = /obj/item/melee/energy/sword + integrated_object_type = /obj/item/melee/transforming/energy/sword /* * Shoulder augment. diff --git a/code/modules/organs/internal/species/shadekin.dm b/code/modules/organs/internal/species/shadekin.dm index bc529e693f57..0cfe9e48c2ef 100644 --- a/code/modules/organs/internal/species/shadekin.dm +++ b/code/modules/organs/internal/species/shadekin.dm @@ -5,6 +5,40 @@ var/max_dark_energy = 100 var/dark_energy_infinite = FALSE + organ_actions = list( + /datum/action/organ_action/shadekin_storage, + ) + +/datum/action/organ_action/shadekin_storage + name = "Access Storage" + desc = "Access your dimensional pocket." + +/obj/item/organ/internal/brain/shadekin/Initialize(mapload) + . = ..() + obj_storage = new /datum/object_system/storage/shadekin(src) + obj_storage.indirect(src) + +/obj/item/organ/internal/brain/shadekin/on_insert(mob/owner, initializing) + . = ..() + RegisterSignal(owner, COMSIG_ATOM_REACHABILITY_DIRECTACCESS, PROC_REF(handle_storage_reachability)) + +/obj/item/organ/internal/brain/shadekin/on_remove(mob/owner) + . = ..() + UnregisterSignal(owner, COMSIG_ATOM_REACHABILITY_DIRECTACCESS) + +/obj/item/organ/internal/brain/shadekin/proc/handle_storage_reachability(atom/source, list/direct_access) + var/atom/movable/storage_indirection/indirection = locate() in contents + if(!indirection) + return + direct_access += indirection + +/obj/item/organ/internal/brain/shadekin/ui_action_click(datum/action/action, datum/event_args/actor/actor) + obj_storage.show(actor.performer) + +/datum/object_system/storage/shadekin + max_single_weight_class = WEIGHT_CLASS_SMALL + max_items = 7 + /obj/item/organ/internal/brain/shadekin/crewkin dark_energy = 50 max_dark_energy = 50 diff --git a/code/modules/overmap/bounds.dm b/code/modules/overmap/bounds.dm index 4ba1da1d2983..b373fff61c76 100644 --- a/code/modules/overmap/bounds.dm +++ b/code/modules/overmap/bounds.dm @@ -20,6 +20,8 @@ var/mutable_appearance/MA = new . = MA MA.appearance_flags = KEEP_APART | RESET_TRANSFORM + // mutable appearance is not FLOAT_PLANE by default + MA.plane = FLOAT_PLANE MA.icon = I MA.color = color MA.alpha = 160 @@ -59,6 +61,8 @@ var/mutable_appearance/MA = new . = MA MA.appearance_flags = KEEP_APART | RESET_TRANSFORM + // mutable appearance is not FLOAT_PLANE by default + MA.plane = FLOAT_PLANE MA.icon = I MA.alpha = 160 MA.filters = filter( diff --git a/code/modules/overmap/legacy/overmap_shuttle.dm b/code/modules/overmap/legacy/overmap_shuttle.dm index 325e06299cc1..441aeb93949c 100644 --- a/code/modules/overmap/legacy/overmap_shuttle.dm +++ b/code/modules/overmap/legacy/overmap_shuttle.dm @@ -138,7 +138,7 @@ opened = 1 //shows open so you can diagnose 'oops, no gas' easily icon_state = "fuel_port_empty" //set the default state just to be safe -/obj/structure/fuel_port/attack_hand(mob/user, list/params) +/obj/structure/fuel_port/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!opened) to_chat(user, "The door is secured tightly. You'll need a crowbar to open it.") return diff --git a/code/modules/overmap/legacy/ships/computers/computer_shims.dm b/code/modules/overmap/legacy/ships/computers/computer_shims.dm index 616cb4f694f7..0c25c5edf1b6 100644 --- a/code/modules/overmap/legacy/ships/computers/computer_shims.dm +++ b/code/modules/overmap/legacy/ships/computers/computer_shims.dm @@ -80,7 +80,7 @@ // If you don't call parent in this proc, you must make all appropriate checks yourself. // If you do, you must respect the return value. -/obj/machinery/computer/ship/attack_hand(mob/user, list/params) +/obj/machinery/computer/ship/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return if(!allowed(user)) diff --git a/code/modules/overmap/legacy/ships/computers/sensors.dm b/code/modules/overmap/legacy/ships/computers/sensors.dm index ad0e255d7373..f142942fcc8b 100644 --- a/code/modules/overmap/legacy/ships/computers/sensors.dm +++ b/code/modules/overmap/legacy/ships/computers/sensors.dm @@ -219,9 +219,9 @@ else if(health < max_health * 0.75) . += "It shows signs of damage!" -/obj/machinery/shipsensors/bullet_act(var/obj/projectile/Proj) - take_damage_legacy(Proj.get_structure_damage()) - ..() +/obj/machinery/shipsensors/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + take_damage_legacy(proj.get_structure_damage()) /obj/machinery/shipsensors/proc/toggle() if(!use_power && (health == 0 || !in_vacuum())) diff --git a/code/modules/overmap/legacy/ships/panicbutton.dm b/code/modules/overmap/legacy/ships/panicbutton.dm index 0cd9d8c53f2e..33d0cdb90642 100644 --- a/code/modules/overmap/legacy/ships/panicbutton.dm +++ b/code/modules/overmap/legacy/ships/panicbutton.dm @@ -23,7 +23,7 @@ else icon_state = "[initial(icon_state)]" -/obj/structure/panic_button/attack_hand(mob/user, list/params) +/obj/structure/panic_button/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user)) return ..() diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index e6c91e650d3a..0e2e66eff904 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -32,7 +32,7 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins if(!(("[department]" in alldepartments) || ("[department]" in admin_departments())) ) alldepartments |= department -/obj/machinery/photocopier/faxmachine/attack_hand(mob/user, list/params) +/obj/machinery/photocopier/faxmachine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) nano_ui_interact(user) diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index b9ecb5dd6704..d6361a68ddce 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -61,7 +61,7 @@ else to_chat(user, SPAN_NOTICE("You can't put [P] in [src]!")) -/obj/structure/filingcabinet/attack_hand(mob/user, list/params) +/obj/structure/filingcabinet/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(contents.len <= 0) to_chat(user, SPAN_NOTICE("\The [src] is empty.")) return @@ -150,7 +150,7 @@ virgin = 0 //tabbing here is correct- it's possible for people to try and use it //before the records have been generated, so we do this inside the loop. -/obj/structure/filingcabinet/security/attack_hand(mob/user, list/params) +/obj/structure/filingcabinet/security/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) populate() ..() @@ -187,7 +187,7 @@ virgin = 0 //tabbing here is correct- it's possible for people to try and use it //before the records have been generated, so we do this inside the loop. -/obj/structure/filingcabinet/medical/attack_hand(mob/user, list/params) +/obj/structure/filingcabinet/medical/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) populate() ..() diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index 1076fc731af3..b2bcd9a135a1 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -38,7 +38,7 @@ return -/obj/item/paper_bin/attack_hand(mob/user, list/params) +/obj/item/paper_bin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(ishuman(user)) var/mob/living/carbon/human/H = user var/obj/item/organ/external/temp = H.organs_by_name["r_hand"] @@ -116,7 +116,7 @@ icon_state = "paper_bundle" papers = /obj/item/paper/natural -/obj/item/paper_bin/bundlenatural/attack_hand(mob/user, list/params) +/obj/item/paper_bin/bundlenatural/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(amount < 1) qdel(src) return ..() diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 632c340da4a9..7b943d43c9a2 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -27,7 +27,7 @@ /obj/machinery/photocopier/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/photocopier/attack_hand(mob/user, list/params) +/obj/machinery/photocopier/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) nano_ui_interact(user) diff --git a/code/modules/power/antimatter/computer.dm b/code/modules/power/antimatter/computer.dm index 23d25beed55c..5996d5abc212 100644 --- a/code/modules/power/antimatter/computer.dm +++ b/code/modules/power/antimatter/computer.dm @@ -64,7 +64,7 @@ /obj/machinery/computer/am_engine/attack_paw(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/am_engine/attack_hand(mob/user, list/params) +/obj/machinery/computer/am_engine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return user.machine = src diff --git a/code/modules/power/antimatter/control.dm b/code/modules/power/antimatter/control.dm index 75ad385a6001..e9cae20586ad 100644 --- a/code/modules/power/antimatter/control.dm +++ b/code/modules/power/antimatter/control.dm @@ -8,6 +8,7 @@ use_power = USE_POWER_IDLE idle_power_usage = 100 active_power_usage = 1000 + integrity_flags = INTEGRITY_INDESTRUCTIBLE var/list/obj/machinery/am_shielding/linked_shielding var/list/obj/machinery/am_shielding/linked_cores @@ -116,12 +117,10 @@ check_stability() return - -/obj/machinery/power/am_control_unit/bullet_act(var/obj/projectile/Proj) - if(Proj.damage_flag != "bullet") - stability -= Proj.damage - return 0 - +/obj/machinery/power/am_control_unit/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(proj.damage_flag != ARMOR_BULLET) + stability -= proj.damage + return ..() /obj/machinery/power/am_control_unit/power_change() ..() @@ -177,7 +176,7 @@ return -/obj/machinery/power/am_control_unit/attack_hand(mob/user, list/params) +/obj/machinery/power/am_control_unit/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(anchored) interact(user) return diff --git a/code/modules/power/antimatter/shielding.dm b/code/modules/power/antimatter/shielding.dm index 8a8784bc5ab9..d7997462c6da 100644 --- a/code/modules/power/antimatter/shielding.dm +++ b/code/modules/power/antimatter/shielding.dm @@ -98,12 +98,11 @@ check_stability() return +/obj/machinery/am_shielding/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() -/obj/machinery/am_shielding/bullet_act(var/obj/projectile/Proj) - if(Proj.damage_flag != "bullet") - stability -= Proj.damage/2 - return 0 - + if(proj.damage_flag != "bullet") + stability -= proj.damage/2 /obj/machinery/am_shielding/update_icon() cut_overlays() diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 92209ce67943..e42290cc3c60 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -787,7 +787,7 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/power/apc, 22) wires.cut_all() update_icon() -/obj/machinery/power/apc/attack_hand(mob/user, list/params) +/obj/machinery/power/apc/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) // if (!can_use(user)) This already gets called in interact() and in topic() // return if(!user) diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm index 591cc56f449f..8314a7a6f63a 100644 --- a/code/modules/power/breaker_box.dm +++ b/code/modules/power/breaker_box.dm @@ -72,7 +72,7 @@ busy = 0 -/obj/machinery/power/breakerbox/attack_hand(mob/user, list/params) +/obj/machinery/power/breakerbox/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(update_locked) to_chat(user, "System locked. Please try again later.") return diff --git a/code/modules/power/fission/computer.dm b/code/modules/power/fission/computer.dm index 6c7b62a33a58..4916174d0246 100644 --- a/code/modules/power/fission/computer.dm +++ b/code/modules/power/fission/computer.dm @@ -37,7 +37,7 @@ /obj/machinery/computer/fission_monitor/attack_ai(mob/user) attack_hand(user) -/obj/machinery/computer/fission_monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/fission_monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/modules/power/fission/engine.dm b/code/modules/power/fission/engine.dm index 07109bcc683a..49d976e0af4f 100644 --- a/code/modules/power/fission/engine.dm +++ b/code/modules/power/fission/engine.dm @@ -108,7 +108,7 @@ var/power = (decay_heat / REACTOR_RADS_TO_MJ) * max(healthmul, 0.1) radiation_pulse(src, max(power * REACTOR_RADIATION_MULTIPLIER, 0), RAD_FALLOFF_ENGINE_FISSION) -/obj/machinery/power/fission/attack_hand(mob/user, list/params) +/obj/machinery/power/fission/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) /obj/machinery/power/fission/attack_robot(mob/user) @@ -479,7 +479,7 @@ I'm commenting this out until I have time to make this less stupid. // fuck this /obj/nuclear_mistake_spawner name = "the Underdark's revenge" - desc = "hardcoded piece of that that should never be seen PLEASE report this if you do" + desc = "hardcoded piece of trash that should never be seen PLEASE report this if you do" icon = 'icons/mob/screen1.dmi' icon_state = "x" invisibility = 101 @@ -536,7 +536,8 @@ I'm commenting this out until I have time to make this less stupid. my_mob.low_priority = TRUE if(faction) - my_mob.faction = faction + my_mob.clear_iff_factions() + my_mob.add_iff_faction(faction) if(atmos_comp) var/turf/T = get_turf(src) diff --git a/code/modules/power/fusion/core/_core.dm b/code/modules/power/fusion/core/_core.dm index eff1ef5682ba..207915ae7461 100644 --- a/code/modules/power/fusion/core/_core.dm +++ b/code/modules/power/fusion/core/_core.dm @@ -87,9 +87,10 @@ var/list/fusion_cores = list() owned_field.AddParticles(name, quantity) . = 1 -/obj/machinery/power/fusion_core/bullet_act(var/obj/projectile/Proj) +/obj/machinery/power/fusion_core/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(owned_field) - . = owned_field.bullet_act(Proj) + return proj.impact_redirect(owned_field, args) + return ..() /obj/machinery/power/fusion_core/proc/set_strength(var/value) value = clamp(value, MIN_FIELD_STR, MAX_FIELD_STR) @@ -98,7 +99,7 @@ var/list/fusion_cores = list() if(owned_field) owned_field.ChangeFieldStrength(value) -/obj/machinery/power/fusion_core/attack_hand(mob/user, list/params) +/obj/machinery/power/fusion_core/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!Adjacent(user)) // As funny as it was for the AI to hug-kill the tokamak field from a distance... return visible_message("\The [user] hugs \the [src] to make it feel better!") diff --git a/code/modules/power/fusion/core/core_control.dm b/code/modules/power/fusion/core/core_control.dm index 08f267e39747..488c92cf2113 100644 --- a/code/modules/power/fusion/core/core_control.dm +++ b/code/modules/power/fusion/core/core_control.dm @@ -21,7 +21,7 @@ /obj/machinery/computer/fusion_core_control/attack_ai(mob/user) attack_hand(user) -/obj/machinery/computer/fusion_core_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/fusion_core_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) interact(user) diff --git a/code/modules/power/fusion/core/core_field.dm b/code/modules/power/fusion/core/core_field.dm index 35daf97aa660..ec9c2ab34109 100644 --- a/code/modules/power/fusion/core/core_field.dm +++ b/code/modules/power/fusion/core/core_field.dm @@ -26,7 +26,6 @@ GLOBAL_VAR_INIT(max_fusion_air_heat, INFINITY) var/obj/machinery/power/fusion_core/owned_core var/list/dormant_reactant_quantities = list() - var/list/particle_catchers = list() var/list/ignore_types @@ -60,67 +59,24 @@ GLOBAL_VAR_INIT(max_fusion_air_heat, INFINITY) if(!owned_core) qdel(src) id_tag = owned_core.id_tag - //create the gimmicky things to handle field collisions - var/obj/effect/fusion_particle_catcher/catcher - - catcher = new (locate(src.x,src.y,src.z)) - catcher.parent = src - catcher.SetSize(1) - particle_catchers.Add(catcher) - - catcher = new (locate(src.x-1,src.y,src.z)) - catcher.parent = src - catcher.SetSize(3) - particle_catchers.Add(catcher) - catcher = new (locate(src.x+1,src.y,src.z)) - catcher.parent = src - catcher.SetSize(3) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y+1,src.z)) - catcher.parent = src - catcher.SetSize(3) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y-1,src.z)) - catcher.parent = src - catcher.SetSize(3) - particle_catchers.Add(catcher) - - catcher = new (locate(src.x-2,src.y,src.z)) - catcher.parent = src - catcher.SetSize(5) - particle_catchers.Add(catcher) - catcher = new (locate(src.x+2,src.y,src.z)) - catcher.parent = src - catcher.SetSize(5) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y+2,src.z)) - catcher.parent = src - catcher.SetSize(5) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y-2,src.z)) - catcher.parent = src - catcher.SetSize(5) - particle_catchers.Add(catcher) - - catcher = new (locate(src.x-3,src.y,src.z)) - catcher.parent = src - catcher.SetSize(7) - particle_catchers.Add(catcher) - catcher = new (locate(src.x+3,src.y,src.z)) - catcher.parent = src - catcher.SetSize(7) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y+3,src.z)) - catcher.parent = src - catcher.SetSize(7) - particle_catchers.Add(catcher) - catcher = new (locate(src.x,src.y-3,src.z)) - catcher.parent = src - catcher.SetSize(7) - particle_catchers.Add(catcher) START_PROCESSING(SSobj, src) +/obj/effect/fusion_em_field/bullet_act(obj/projectile/proj, impact_flags, def_zone, efficiency) + if(!(proj.projectile_type & PROJECTILE_TYPE_BEAM)) + return ..() + AddEnergy(proj.damage) + update_icon() + impact_flags |= PROJECTILE_IMPACT_DELETE + return ..() + +/obj/effect/fusion_em_field/CanAllowThrough(atom/movable/mover, turf/target) + if(istype(mover, /obj/projectile)) + var/obj/projectile/proj = mover + if(proj.projectile_type & PROJECTILE_TYPE_BEAM) + return FALSE + return TRUE + /obj/effect/fusion_em_field/process(delta_time) //make sure the field generator is still intact if(!owned_core || QDELETED(owned_core)) @@ -340,8 +296,20 @@ GLOBAL_VAR_INIT(max_fusion_air_heat, INFINITY) tick_instability += rand(30,50) AM.emp_act(empsev) -/obj/effect/fusion_em_field/proc/change_size(var/newsize = 1) - var/changed = 0 +/** + * Immediately change the field's size. + * + * * This is the radius of the field. + * * This will change `locs` of this field, and its bounds! + */ +/obj/effect/fusion_em_field/proc/change_size(new_size = 1) + ASSERT(!((new_size - 1) % 2)) + ASSERT(new_size <= 13) + + if(new_size == size) + return FALSE + + //! LEGACY var/static/list/size_to_icon = list( "3" = 'icons/effects/96x96.dmi', "5" = 'icons/effects/160x160.dmi', @@ -351,19 +319,27 @@ GLOBAL_VAR_INIT(max_fusion_air_heat, INFINITY) "13" = 'icons/effects/416x416.dmi' ) - if( ((newsize-1)%2==0) && (newsize<=13) ) - icon = 'icons/obj/machines/power/fusion.dmi' - if(newsize>1) - icon = size_to_icon["[newsize]"] - icon_state = "emfield_s[newsize]" - pixel_x = ((newsize-1) * -16) * PIXEL_MULTIPLIER - pixel_y = ((newsize-1) * -16) * PIXEL_MULTIPLIER - size = newsize - changed = newsize + icon = 'icons/obj/machines/power/fusion.dmi' - for(var/obj/effect/fusion_particle_catcher/catcher in particle_catchers) - catcher.UpdateSize() - return changed + if(new_size>1) + icon = size_to_icon["[new_size]"] + icon_state = "emfield_s[new_size]" + //! END + + size = new_size + + // this will shift locs! + var/new_additional_radius = (new_size - 1) / 2 + bound_x = -new_additional_radius * WORLD_ICON_SIZE + bound_y = -new_additional_radius * WORLD_ICON_SIZE + bound_width = new_additional_radius * 2 * WORLD_ICON_SIZE + WORLD_ICON_SIZE + bound_height = new_additional_radius * 2 * WORLD_ICON_SIZE + WORLD_ICON_SIZE + + // shift visuals + pixel_x = -new_additional_radius * WORLD_ICON_SIZE + pixel_y = -new_additional_radius * WORLD_ICON_SIZE + + return TRUE //the !!fun!! part /obj/effect/fusion_em_field/proc/React() @@ -480,18 +456,12 @@ GLOBAL_VAR_INIT(max_fusion_air_heat, INFINITY) /obj/effect/fusion_em_field/Destroy() set_light(0) RadiateAll() - for(var/obj/effect/fusion_particle_catcher/catcher in particle_catchers) - qdel(catcher) if(owned_core) owned_core.owned_field = null owned_core = null STOP_PROCESSING(SSobj, src) . = ..() -/obj/effect/fusion_em_field/bullet_act(var/obj/projectile/Proj) - AddEnergy(Proj.damage) - update_icon() - return 0 //All procs below this point are called in _core.dm, starting at line 41. //Stability monitoring. Gives radio annoucements if field stability is below 80% /obj/effect/fusion_em_field/proc/stability_monitor() diff --git a/code/modules/power/fusion/fuel_assembly/fuel_control.dm b/code/modules/power/fusion/fuel_assembly/fuel_control.dm index 668c1ee7d7cf..dc3b35787ad8 100644 --- a/code/modules/power/fusion/fuel_assembly/fuel_control.dm +++ b/code/modules/power/fusion/fuel_assembly/fuel_control.dm @@ -10,7 +10,7 @@ /obj/machinery/computer/fusion_fuel_control/attack_ai(mob/user) attack_hand(user) -/obj/machinery/computer/fusion_fuel_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/fusion_fuel_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) interact(user) diff --git a/code/modules/power/fusion/fuel_assembly/fuel_injector.dm b/code/modules/power/fusion/fuel_assembly/fuel_injector.dm index 99856dd63b44..53951245d996 100644 --- a/code/modules/power/fusion/fuel_assembly/fuel_injector.dm +++ b/code/modules/power/fusion/fuel_assembly/fuel_injector.dm @@ -79,7 +79,7 @@ var/list/fuel_injectors = list() return ..() -/obj/machinery/fusion_fuel_injector/attack_hand(mob/user, list/params) +/obj/machinery/fusion_fuel_injector/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(injecting) to_chat(user, "Shut \the [src] off before playing with the fuel rod!") diff --git a/code/modules/power/fusion/fusion_particle_catcher.dm b/code/modules/power/fusion/fusion_particle_catcher.dm deleted file mode 100644 index ea8ae497ff57..000000000000 --- a/code/modules/power/fusion/fusion_particle_catcher.dm +++ /dev/null @@ -1,43 +0,0 @@ -/obj/effect/fusion_particle_catcher - icon = 'icons/effects/effects.dmi' - density = 1 - anchored = 1 - invisibility = 101 - var/obj/effect/fusion_em_field/parent - var/mysize = 0 - - light_color = COLOR_BLUE - -/obj/effect/fusion_particle_catcher/Destroy() - . =..() - parent.particle_catchers -= src - parent = null - -/obj/effect/fusion_particle_catcher/proc/SetSize(var/newsize) - name = "collector [newsize]" - mysize = newsize - UpdateSize() - -/obj/effect/fusion_particle_catcher/proc/AddParticles(var/name, var/quantity = 1) - if(parent && parent.size >= mysize) - parent.AddParticles(name, quantity) - return 1 - return 0 - -/obj/effect/fusion_particle_catcher/proc/UpdateSize() - if(parent.size >= mysize) - density = 1 - name = "collector [mysize] ON" - else - density = 0 - name = "collector [mysize] OFF" - -/obj/effect/fusion_particle_catcher/bullet_act(var/obj/projectile/Proj) - parent.AddEnergy(Proj.damage) - update_icon() - return 0 - -/obj/effect/fusion_particle_catcher/CanAllowThrough(atom/movable/mover, turf/target) - if(istype(mover, /obj/effect/accelerated_particle) || istype(mover, /obj/projectile/beam)) - return !density - return TRUE diff --git a/code/modules/power/fusion/gyrotron/gyrotron_control.dm b/code/modules/power/fusion/gyrotron/gyrotron_control.dm index 8cff0d855e12..d51841206cdb 100644 --- a/code/modules/power/fusion/gyrotron/gyrotron_control.dm +++ b/code/modules/power/fusion/gyrotron/gyrotron_control.dm @@ -11,7 +11,7 @@ /obj/machinery/computer/gyrotron_control/attack_ai(var/mob/user) attack_hand(user) -/obj/machinery/computer/gyrotron_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/gyrotron_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) interact(user) diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 5315b5560e40..30b91be24149 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -188,7 +188,7 @@ GLOBAL_LIST_EMPTY(all_turbines) else ..() -/obj/machinery/power/generator/attack_hand(mob/user, list/params) +/obj/machinery/power/generator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER) || !anchored) return diff --git a/code/modules/power/generator_type2.dm b/code/modules/power/generator_type2.dm index 1bf94294a438..bbfb70e642f0 100644 --- a/code/modules/power/generator_type2.dm +++ b/code/modules/power/generator_type2.dm @@ -94,7 +94,7 @@ interact(user) -/obj/machinery/power/generator_type2/attack_hand(mob/user, list/params) +/obj/machinery/power/generator_type2/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index d4bcc7eebf22..c602f9b35e71 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -79,7 +79,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) /obj/machinery/gravity_generator/part/get_status() return main_part?.get_status() -/obj/machinery/gravity_generator/part/attack_hand(mob/user, list/params) +/obj/machinery/gravity_generator/part/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return main_part.attack_hand(user) /obj/machinery/gravity_generator/part/set_broken() @@ -239,7 +239,7 @@ GLOBAL_LIST_EMPTY(gravity_generators) return return ..() -/obj/machinery/gravity_generator/main/attack_hand(mob/user, list/params) +/obj/machinery/gravity_generator/main/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return ui_interact(user) diff --git a/code/modules/power/grid_checker.dm b/code/modules/power/grid_checker.dm index 4da2807a9e53..9489bb531bf9 100644 --- a/code/modules/power/grid_checker.dm +++ b/code/modules/power/grid_checker.dm @@ -51,7 +51,7 @@ else if(istype(W, /obj/item/multitool) || W.is_wirecutter()) attack_hand(user) -/obj/machinery/power/grid_checker/attack_hand(mob/user, list/params) +/obj/machinery/power/grid_checker/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!user) return add_fingerprint(user) diff --git a/code/modules/power/lighting/lighting.dm b/code/modules/power/lighting/lighting.dm index 0d8144581aee..61515e6b5059 100644 --- a/code/modules/power/lighting/lighting.dm +++ b/code/modules/power/lighting/lighting.dm @@ -77,7 +77,7 @@ var/global/list/light_type_cache = list() else to_chat(user, "This casing doesn't support power cells for backup power.") -/obj/machinery/light_construct/attack_hand(mob/user, list/params) +/obj/machinery/light_construct/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return . // obj/machinery/attack_hand returns 1 if user can't use the machine @@ -861,7 +861,7 @@ var/global/list/light_type_cache = list() // attack with hand - remove tube/bulb // if hands aren't protected and the light is on, burn the player -/obj/machinery/light/attack_hand(mob/user, list/params) +/obj/machinery/light/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) @@ -907,7 +907,7 @@ var/global/list/light_type_cache = list() // create a light tube/bulb item and put it in the user's hand user.put_in_active_hand(remove_bulb()) //puts it in our active hand -/obj/machinery/light/flamp/attack_hand(mob/user, list/params) +/obj/machinery/light/flamp/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(lamp_shade) if(status == LIGHT_EMPTY) to_chat(user, "There is no [get_fitting_name()] in this light.") diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index c74746180773..6c1603b29ed7 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -42,7 +42,7 @@ /obj/machinery/power/powered() return 1 //doesn't require an external power source -/obj/machinery/power/port_gen/attack_hand(mob/user, list/params) +/obj/machinery/power/port_gen/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(!anchored) @@ -302,7 +302,7 @@ return return ..() -/obj/machinery/power/port_gen/pacman/attack_hand(mob/user, list/params) +/obj/machinery/power/port_gen/pacman/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if (!anchored) return @@ -607,13 +607,13 @@ tesla_zap(src, 5, power_gen * 50) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(explosion), get_turf(src), 2, 3, 4, 8), 100) // Not a normal explosion. -/obj/machinery/power/rtg/abductor/bullet_act(obj/projectile/Proj) +/obj/machinery/power/rtg/abductor/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) . = ..() - if(!going_kaboom && istype(Proj) && !Proj.nodamage && ((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE))) - log_and_message_admins("[ADMIN_LOOKUPFLW(Proj.firer)] triggered an Abductor Core explosion at [x],[y],[z] via projectile.") + if(!going_kaboom && istype(proj) && !proj.nodamage && ((proj.damage_type == BURN) || (proj.damage_type == BRUTE))) + log_and_message_admins("[ADMIN_LOOKUPFLW(proj.firer)] triggered an Abductor Core explosion at [x],[y],[z] via projectile.") asplod() -/obj/machinery/power/rtg/abductor/attack_hand(mob/user, list/params) +/obj/machinery/power/rtg/abductor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!istype(user) || (. = ..())) return diff --git a/code/modules/power/sensors/sensor_monitoring.dm b/code/modules/power/sensors/sensor_monitoring.dm index fc9f49505e39..85f33951033c 100644 --- a/code/modules/power/sensors/sensor_monitoring.dm +++ b/code/modules/power/sensors/sensor_monitoring.dm @@ -41,7 +41,7 @@ ..() // On user click opens the UI of this computer. -/obj/machinery/computer/power_monitor/attack_hand(mob/user, list/params) +/obj/machinery/computer/power_monitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 1e5de40b4b86..6fdfc767bb8b 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -48,7 +48,7 @@ rad_insulation = active? rad_insulation_active : rad_insulation_inactive -/obj/machinery/power/rad_collector/attack_hand(mob/user, list/params) +/obj/machinery/power/rad_collector/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(anchored) if(!src.locked) toggle_power() diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index ebbfc93fe055..1e957093359c 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -21,7 +21,7 @@ FG2.cleanup() . = ..() -/obj/machinery/containment_field/attack_hand(mob/user, list/params) +/obj/machinery/containment_field/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(get_dist(src, user) > 1) return 0 else diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 7da6c2e6c880..a62c8516fdc9 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -58,7 +58,7 @@ else icon_state = "emitter" -/obj/machinery/power/emitter/attack_hand(mob/user, list/params) +/obj/machinery/power/emitter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) src.add_fingerprint(user) activate(user) diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 2233e458f083..ebd211036a73 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -21,7 +21,7 @@ field_generator power level display anchored = 0 density = 1 use_power = USE_POWER_OFF - + armor = /datum/armor/object/heavy worth_intrinsic = 350 var/const/num_power_levels = 6 // Total number of power level icon has var/Varedit_start = 0 @@ -75,7 +75,7 @@ field_generator power level display return -/obj/machinery/field_generator/attack_hand(mob/user, list/params) +/obj/machinery/field_generator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(state == 2) if(get_dist(src, user) <= 1)//Need to actually touch the thing to turn it on if(src.active >= 1) @@ -152,23 +152,20 @@ field_generator power level display ..() return - /obj/machinery/field_generator/emp_act() return 0 -/obj/machinery/field_generator/bullet_act(var/obj/projectile/Proj) - if(istype(Proj, /obj/projectile/beam)) - power += Proj.damage * EMITTER_DAMAGE_POWER_TRANSFER +/obj/machinery/field_generator/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(istype(proj, /obj/projectile/beam)) + power += proj.damage * EMITTER_DAMAGE_POWER_TRANSFER update_icon() - return 0 - + return PROJECTILE_IMPACT_DELETE + return ..() /obj/machinery/field_generator/Destroy() src.cleanup() . = ..() - - /obj/machinery/field_generator/proc/turn_off() active = 0 warming_up = 0 diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm index 3c6aadbaf710..c19fd665569b 100644 --- a/code/modules/power/singularity/particle_accelerator/particle.dm +++ b/code/modules/power/singularity/particle_accelerator/particle.dm @@ -5,8 +5,11 @@ desc = "Small things moving very fast." icon = 'icons/obj/machines/particle_accelerator2.dmi' icon_state = "particle1"//Need a new icon for this - anchored = 1 - density = 1 + anchored = TRUE + density = TRUE + generic_canpass = FALSE + plane = MOB_PLANE + layer = ABOVE_MOB_LAYER var/movement_range = 10 var/energy = 10 //energy in eV var/mega_energy = 0 //energy in MeV @@ -18,6 +21,10 @@ var/turf/source var/movetotarget = 1 +/obj/effect/accelerated_particle/CanPassThrough(atom/blocker, turf/target, blocker_opinion) + SHOULD_CALL_PARENT(FALSE) + return TRUE + /obj/effect/accelerated_particle/weak icon_state = "particle0" movement_range = 8 @@ -39,18 +46,44 @@ energy = -20 /obj/effect/accelerated_particle/Initialize(mapload, dir = SOUTH) + setDir(dir) . = ..() - src.loc = loc - src.setDir(dir) - INVOKE_ASYNC(src, PROC_REF(move), 1) + START_PROCESSING(SSprocess_5fps, src) + +/obj/effect/accelerated_particle/Destroy() + STOP_PROCESSING(SSprocess_5fps, src) + return ..() + +/obj/effect/accelerated_particle/process(delta_time) + var/old_z = z + var/turf/where_to = get_step(src, dir) + if(!where_to) + qdel(src) + return + if(!Move(where_to)) + if(!QDELETED(src)) + qdel(src) + return + else + return PROCESS_KILL + // being deleted changes Z so that's also an implicit qdeleted() check. + if(z != old_z) + if(!QDELETED(src)) + qdel(src) + return + else + return PROCESS_KILL /obj/effect/accelerated_particle/Moved() . = ..() if(!isturf(loc)) return - for(var/atom/movable/AM as anything in loc.contents) + for(var/atom/movable/AM as anything in loc) do_the_funny(AM) + if(QDELETED(src)) + return +// todo: particle_accelerator_act() or something /obj/effect/accelerated_particle/proc/do_the_funny(atom/A) if (A) if(ismob(A)) @@ -58,24 +91,14 @@ if((istype(A,/obj/machinery/the_singularitygen))||(istype(A,/obj/singularity/))||(istype(A, /obj/machinery/particle_smasher))) A:energy += energy //R-UST port - else if(istype(A,/obj/machinery/power/fusion_core)) - var/obj/machinery/power/fusion_core/collided_core = A - if(particle_type && particle_type != "neutron") - if(collided_core.AddParticles(particle_type, 1 + additional_particles)) - collided_core.owned_field.plasma_temperature += mega_energy - collided_core.owned_field.energy += energy - loc = null - else if(istype(A, /obj/effect/fusion_particle_catcher)) - var/obj/effect/fusion_particle_catcher/PC = A + if(istype(A, /obj/effect/fusion_em_field)) + var/obj/effect/fusion_em_field/field = A if(particle_type && particle_type != "neutron") - if(PC.parent.owned_core.AddParticles(particle_type, 1 + additional_particles)) - PC.parent.plasma_temperature += mega_energy - PC.parent.energy += energy - loc = null - - -/obj/effect/accelerated_particle/legacy_ex_act(severity) - qdel(src) + if(field.owned_core.AddParticles(particle_type, 1 + additional_particles)) + field.plasma_temperature += mega_energy + field.energy += energy + qdel(src) + return /obj/effect/accelerated_particle/singularity_act() return @@ -84,26 +107,3 @@ if(!istype(M)) return M.afflict_radiation(energy * 5, TRUE) - -/obj/effect/accelerated_particle/proc/move(var/lag) - var/turf/new_target - if(target) - if(movetotarget) - new_target = get_step_towards(src, target) - if(get_dist(src,new_target) < 1) - movetotarget = 0 - else - new_target = get_step_away(src, source) - else - new_target = get_step(src, dir) - if(new_target) - forceMove(new_target) - else - qdel(src) - return - movement_range-- - if(movement_range <= 0) - qdel(src) - else - sleep(lag) - move(lag) diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index d87f82a0621b..51efce1bc5fd 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -34,7 +34,7 @@ wires = null return ..() -/obj/machinery/particle_accelerator/control_box/attack_hand(mob/user, list/params) +/obj/machinery/particle_accelerator/control_box/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(construction_state >= 3) interact(user) else if(construction_state == 2) // Wires exposed diff --git a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm index a98c670f46a3..fb7500fcf428 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm @@ -10,6 +10,7 @@ anchored = 0 density = 1 use_power = USE_POWER_OFF + armor = /datum/armor/object/heavy var/image/material_layer // Holds the image used for the filled overlay. var/image/material_glow // Holds the image used for the glow overlay. @@ -127,11 +128,12 @@ else set_light(0, 0, "#FFFFFF") -/obj/machinery/particle_smasher/bullet_act(var/obj/projectile/Proj) - if(istype(Proj, /obj/projectile/beam)) - if(Proj.damage >= 50) +/obj/machinery/particle_smasher/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(istype(proj, /obj/projectile/beam)) + if(proj.damage >= 50) TryCraft() - return 0 + return PROJECTILE_IMPACT_DELETE + return ..() /obj/machinery/particle_smasher/process(delta_time) if(!src.anchored) // Rapidly loses focus. diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 0b12ef9396ea..00b55157c46f 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -46,7 +46,7 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity) STOP_PROCESSING(SSobj, src) return ..() -/obj/singularity/attack_hand(mob/user, list/params) +/obj/singularity/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) consume(user) return 1 @@ -64,8 +64,8 @@ GLOBAL_LIST_BOILERPLATE(all_singularities, /obj/singularity) if(3) energy -= round(((energy+1)/4),1) -/obj/singularity/bullet_act(obj/projectile/P) - return 0 //Will there be an impact? Who knows. Will we see it? No. +/obj/singularity/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + return PROJECTILE_IMPACT_DELETE /obj/singularity/Bump(atom/A) consume(A) diff --git a/code/modules/power/smes/smes.dm b/code/modules/power/smes/smes.dm index 9d2082dd458d..bde57b6dd623 100644 --- a/code/modules/power/smes/smes.dm +++ b/code/modules/power/smes/smes.dm @@ -241,7 +241,7 @@ GLOBAL_LIST_EMPTY(smeses) add_hiddenprint(user) ui_interact(user) -/obj/machinery/power/smes/attack_hand(mob/user, list/params) +/obj/machinery/power/smes/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) ui_interact(user) diff --git a/code/modules/power/smes/smes_construction.dm b/code/modules/power/smes/smes_construction.dm index c60612be15ac..2dde0f3ad5b6 100644 --- a/code/modules/power/smes/smes_construction.dm +++ b/code/modules/power/smes/smes_construction.dm @@ -111,7 +111,7 @@ // Proc: attack_hand() // Parameters: None // Description: Opens the UI as usual, and if cover is removed opens the wiring panel. -/obj/machinery/power/smes/buildable/attack_hand(mob/user, list/params) +/obj/machinery/power/smes/buildable/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() if(open_hatch) wires.Interact(usr) diff --git a/code/modules/power/solar/solar_control.dm b/code/modules/power/solar/solar_control.dm index cd503e9daa74..d2738102c702 100644 --- a/code/modules/power/solar/solar_control.dm +++ b/code/modules/power/solar/solar_control.dm @@ -116,7 +116,7 @@ add_overlay(image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir))) return -/obj/machinery/power/solar_control/attack_hand(mob/user, list/params) +/obj/machinery/power/solar_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!..()) interact(user) diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 599a8ed31d96..19854c1cd771 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -361,25 +361,26 @@ return 1 - -/obj/machinery/power/supermatter/bullet_act(var/obj/projectile/Proj) +/obj/machinery/power/supermatter/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) var/turf/L = loc - if(!istype(L)) // We don't run process() when we are in space - return 0 // This stops people from being able to really power up the supermatter - // Then bring it inside to explode instantly upon landing on a valid turf. + // We don't run process() when we are in space + // This stops people from being able to really power up the supermatter + // Then bring it inside to explode instantly upon landing on a valid turf. + if(!istype(L)) + return PROJECTILE_IMPACT_DELETE var/added_energy var/added_damage - var/proj_damage = Proj.get_structure_damage() - if(istype(Proj, /obj/projectile/beam)) + var/proj_damage = proj.get_structure_damage() + if(istype(proj, /obj/projectile/beam)) added_energy = proj_damage * config_bullet_energy * CHARGING_FACTOR / POWER_FACTOR power += added_energy else added_damage = proj_damage * config_bullet_energy damage += added_damage if(added_energy || added_damage) - investigate_log("Hit by \"[Proj.name]\". +[added_energy] Energy, +[added_damage] Damage.", INVESTIGATE_SUPERMATTER) - return 0 + investigate_log("Hit by \"[proj.name]\". +[added_energy] Energy, +[added_damage] Damage.", INVESTIGATE_SUPERMATTER) + return PROJECTILE_IMPACT_DELETE /obj/machinery/power/supermatter/attack_robot(mob/user as mob) if(Adjacent(user)) @@ -391,7 +392,7 @@ /obj/machinery/power/supermatter/attack_ai(mob/user as mob) ui_interact(user) -/obj/machinery/power/supermatter/attack_hand(mob/user, list/params) +/obj/machinery/power/supermatter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/datum/gender/TU = GLOB.gender_datums[user.get_visible_gender()] user.visible_message("\The [user] reaches out and touches \the [src], inducing a resonance... [TU.his] body starts to glow and bursts into flames before flashing into ash.",\ "You reach out and touch \the [src]. Everything starts burning and all you can hear is ringing. Your last thought is \"That was not a wise decision.\"",\ @@ -465,20 +466,25 @@ Consume(W) -/obj/machinery/power/supermatter/Bumped(atom/AM as mob|obj) - if(istype(AM, /obj/effect)) - return - if(istype(AM, /mob/living)) - var/mob/living/M = AM - var/datum/gender/T = GLOB.gender_datums[M.get_visible_gender()] - AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... [T.his] 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.\"",\ - "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.") - else if(!grav_pulling) //To prevent spam, detonating supermatter does not indicate non-mobs being destroyed - AM.visible_message("\The [AM] smacks into \the [src] and rapidly flashes to ash.",\ - "You hear a loud crack as you are washed with a wave of heat.") - - Consume(AM) +/obj/machinery/power/supermatter/Bumped(atom/movable/bumped_atom) + . = ..() + var/their_loc = bumped_atom.loc + spawn(0) + if(their_loc != bumped_atom.loc) + return + if(istype(bumped_atom, /obj/effect)) + return + if(istype(bumped_atom, /mob/living)) + var/mob/living/M = bumped_atom + var/datum/gender/T = GLOB.gender_datums[M.get_visible_gender()] + bumped_atom.visible_message("\The [bumped_atom] slams into \the [src] inducing a resonance... [T.his] 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.\"",\ + "You hear an uneartly ringing, then what sounds like a shrilling kettle as you are washed with a wave of heat.") + else if(!grav_pulling) //To prevent spam, detonating supermatter does not indicate non-mobs being destroyed + bumped_atom.visible_message("\The [bumped_atom] smacks into \the [src] and rapidly flashes to ash.",\ + "You hear a loud crack as you are washed with a wave of heat.") + + Consume(bumped_atom) /obj/machinery/power/supermatter/proc/Consume(var/mob/living/user) // todo: rework the fucking supermatter so we don't need this diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index 0121fcb4dc42..ee0741ed89ae 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -59,7 +59,7 @@ return wires.Interact(user) return ..() -/obj/machinery/power/tesla_coil/attack_hand(mob/user, list/params) +/obj/machinery/power/tesla_coil/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user)) return ..() @@ -125,7 +125,7 @@ return return ..() -/obj/machinery/power/grounding_rod/attack_hand(mob/user, list/params) +/obj/machinery/power/grounding_rod/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_GRAB && user_buckle_mob(user.pulling, user)) return ..() diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index e8d7410e77e0..4d52ad696e82 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -273,7 +273,7 @@ updateDialog() -/obj/machinery/power/turbine/attack_hand(mob/user, list/params) +/obj/machinery/power/turbine/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return src.interact(user) @@ -340,7 +340,7 @@ id = new_ident return -/obj/machinery/computer/turbine_computer/attack_hand(mob/user, list/params) +/obj/machinery/computer/turbine_computer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return src.interact(user) diff --git a/code/modules/projectiles/README.md b/code/modules/projectiles/README.md new file mode 100644 index 000000000000..845e6a2075b1 --- /dev/null +++ b/code/modules/projectiles/README.md @@ -0,0 +1,8 @@ +# Projectiles + +The projectiles module contains the following and the systems that support them: + +* Guns +* Ammunition +* Magazines +* Projectiles diff --git a/code/modules/projectiles/ammunition/ammo_caliber.dm b/code/modules/projectiles/ammunition/ammo_caliber.dm index 4bc9635dff49..a16391347c3c 100644 --- a/code/modules/projectiles/ammunition/ammo_caliber.dm +++ b/code/modules/projectiles/ammunition/ammo_caliber.dm @@ -1,5 +1,5 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 silicons *// +//* Copyright (c) 2024 Citadel Station Developers *// GLOBAL_LIST_INIT(calibers, init_calibers()) diff --git a/code/modules/projectiles/ammunition/calibers/normal/a5_7mm.dm b/code/modules/projectiles/ammunition/calibers/normal/a5_7mm.dm index 567dbf5fece8..ad7e3d669142 100644 --- a/code/modules/projectiles/ammunition/calibers/normal/a5_7mm.dm +++ b/code/modules/projectiles/ammunition/calibers/normal/a5_7mm.dm @@ -33,6 +33,7 @@ /obj/item/ammo_magazine/a5_7mm ammo_caliber = /datum/ammo_caliber/a5_7mm + ammo_preload = /obj/item/ammo_casing/a5_7mm /obj/item/ammo_magazine/a5_7mm/nt_les name = "magazine (5.7x28mm)" diff --git a/code/modules/projectiles/ammunition/calibers/special/dart.dm b/code/modules/projectiles/ammunition/calibers/special/dart.dm index a52d0c1939dc..a4772c10cd30 100644 --- a/code/modules/projectiles/ammunition/calibers/special/dart.dm +++ b/code/modules/projectiles/ammunition/calibers/special/dart.dm @@ -6,12 +6,13 @@ /obj/item/ammo_casing/dart/chemdart name = "chemical dart" desc = "A casing containing a small hardened, hollow dart." - icon_state = "dartcasing" + icon_state = "dart" caliber = /datum/ammo_caliber/dart projectile_type = /obj/projectile/bullet/chemdart /obj/item/ammo_casing/dart/chemdart/small name = "short chemical dart" + icon_state = "dartsmall" desc = "A casing containing a small hardened, hollow dart." projectile_type = /obj/projectile/bullet/chemdart/small @@ -21,6 +22,7 @@ name = "dart cartridge" desc = "A rack of hollow darts." + icon = 'icons/modules/projectiles/magazines/darts.dmi' icon_state = "darts-5" base_icon_state = "darts" rendering_system = GUN_RENDERING_STATES @@ -30,7 +32,7 @@ origin_tech = list(TECH_MATERIAL = 2) magazine_type = MAGAZINE_TYPE_NORMAL ammo_caliber = /datum/ammo_caliber/dart - ammo_type = /obj/item/ammo_casing/dart/chemdart + ammo_preload = /obj/item/ammo_casing/dart/chemdart ammo_max = 5 /obj/item/ammo_magazine/chemdart/small @@ -43,7 +45,7 @@ rendering_count = 3 origin_tech = list(TECH_MATERIAL = 2) - ammo_type = /obj/item/ammo_casing/dart/chemdart/small + ammo_preload = /obj/item/ammo_casing/dart/chemdart/small ammo_max = 3 //* Projectiles *// @@ -61,8 +63,13 @@ . = ..() create_reagents(reagent_amount) -/obj/projectile/bullet/chemdart/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) - if(blocked < 2 && isliving(target)) +/obj/projectile/bullet/chemdart/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if((. & PROJECTILE_IMPACT_BLOCKED) || (efficiency < 0.95)) + return + if(isliving(target)) var/mob/living/L = target if(L.can_inject(target_zone=def_zone)) reagents.trans_to_mob(L, reagent_amount, CHEM_INJECT) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 76512e496447..8fb6cf408a30 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -55,6 +55,15 @@ zoomdevicename = "scope" inhand_default_type = INHAND_DEFAULT_ICON_GUNS + //* Accuracy, Dispersion, Instability *// + + /// entirely disable baymiss on fired projectiles + /// + /// * this is a default value; set to null by default to have the projectile's say. + var/accuracy_disabled = null + + // legacy below // + var/burst = 1 var/fire_delay = 6 //delay after shooting before the gun can be used again var/burst_delay = 2 //delay between shots, if firing in bursts @@ -321,9 +330,10 @@ if(!istype(A)) return ..() if(user.a_intent == INTENT_HARM) //point blank shooting - if (A == user && user.zone_sel.selecting == O_MOUTH && !mouthshoot) - handle_suicide(user) - return + // todo: disabled for now + // if (A == user && user.zone_sel.selecting == O_MOUTH && !mouthshoot) + // handle_suicide(user) + // return var/mob/living/L = user if(user && user.client && istype(L) && L.aiming && L.aiming.active && L.aiming.aiming_at != A && A != user) PreFire(A,user) //They're using the new gun system, locate what they're aiming at. @@ -501,7 +511,7 @@ var/acc = burst_accuracy[min(i, burst_accuracy.len)] var/disp = dispersion[min(i, dispersion.len)] - P.accuracy = accuracy + acc + P.accuracy_overall_modify *= 1 + acc / 100 P.dispersion = disp P.shot_from = src.name @@ -662,22 +672,24 @@ disp_mod += one_handed_penalty*0.5 //dispersion per point of two-handedness //Accuracy modifiers - P.accuracy = accuracy + acc_mod - P.dispersion = disp_mod + if(!isnull(accuracy_disabled)) + P.accuracy_disabled = accuracy_disabled - P.accuracy -= user.get_accuracy_penalty() + P.accuracy_overall_modify *= 1 + (acc_mod / 100) + P.accuracy_overall_modify *= 1 - (user.get_accuracy_penalty() / 100) + P.dispersion = disp_mod //accuracy bonus from aiming if (aim_targets && (target in aim_targets)) //If you aim at someone beforehead, it'll hit more often. //Kinda balanced by fact you need like 2 seconds to aim //As opposed to no-delay pew pew - P.accuracy += 30 + P.accuracy_overall_modify *= 1.3 // Some modifiers make it harder or easier to hit things. for(var/datum/modifier/M in user.modifiers) if(!isnull(M.accuracy)) - P.accuracy += M.accuracy + P.accuracy_overall_modify += 1 + (M.accuracy / 100) if(!isnull(M.accuracy_dispersion)) P.dispersion = max(P.dispersion + M.accuracy_dispersion, 0) @@ -717,44 +729,45 @@ else playsound(src, shot_sound, 50, 1) +// todo: rework all this this is fucking dumb //Suicide handling. -/obj/item/gun/var/mouthshoot = 0 //To stop people from suiciding twice... >.> - -/obj/item/gun/proc/handle_suicide(mob/living/user) - if(!ishuman(user)) - return - var/mob/living/carbon/human/M = user - - mouthshoot = 1 - M.visible_message("[user] sticks their gun in their mouth, ready to pull the trigger...") - if(!do_after(user, 40)) - M.visible_message("[user] decided life was worth living") - mouthshoot = 0 - return - var/obj/projectile/in_chamber = consume_next_projectile() - if (istype(in_chamber)) - user.visible_message("[user] pulls the trigger.") - play_fire_sound(M, in_chamber) - if(istype(in_chamber, /obj/projectile/beam/lasertag)) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - return - - in_chamber.on_hit(M) - if(in_chamber.damage_type != HALLOSS && !in_chamber.nodamage) - log_and_message_admins("[key_name(user)] commited suicide using \a [src]") - user.apply_damage(in_chamber.damage*2.5, in_chamber.damage_type, "head", used_weapon = "Point blank shot in the mouth with \a [in_chamber]", sharp=1) - user.death() - else if(in_chamber.damage_type == HALLOSS) - to_chat(user, "Ow...") - user.apply_effect(110,AGONY,0) - qdel(in_chamber) - mouthshoot = 0 - return - else - handle_click_empty(user) - mouthshoot = 0 - return +// /obj/item/gun/var/mouthshoot = 0 //To stop people from suiciding twice... >.> + +// /obj/item/gun/proc/handle_suicide(mob/living/user) +// if(!ishuman(user)) +// return +// var/mob/living/carbon/human/M = user + +// mouthshoot = 1 +// M.visible_message("[user] sticks their gun in their mouth, ready to pull the trigger...") +// if(!do_after(user, 40)) +// M.visible_message("[user] decided life was worth living") +// mouthshoot = 0 +// return +// var/obj/projectile/in_chamber = consume_next_projectile() +// if (istype(in_chamber)) +// user.visible_message("[user] pulls the trigger.") +// play_fire_sound(M, in_chamber) +// if(istype(in_chamber, /obj/projectile/beam/lasertag)) +// user.show_message("You feel rather silly, trying to commit suicide with a toy.") +// mouthshoot = 0 +// return + +// in_chamber.on_hit(M) +// if(in_chamber.damage_type != HALLOSS && !in_chamber.nodamage) +// log_and_message_admins("[key_name(user)] commited suicide using \a [src]") +// user.apply_damage(in_chamber.damage*2.5, in_chamber.damage_type, "head", used_weapon = "Point blank shot in the mouth with \a [in_chamber]", sharp=1) +// user.death() +// else if(in_chamber.damage_type == HALLOSS) +// to_chat(user, "Ow...") +// user.apply_effect(110,AGONY,0) +// qdel(in_chamber) +// mouthshoot = 0 +// return +// else +// handle_click_empty(user) +// mouthshoot = 0 +// return /obj/item/gun/proc/toggle_scope(var/zoom_amount=2.0) //looking through a scope limits your periphereal vision diff --git a/code/modules/projectiles/gun_item_renderer.dm b/code/modules/projectiles/gun_item_renderer.dm index 3ee4b5652857..d7b57cc54155 100644 --- a/code/modules/projectiles/gun_item_renderer.dm +++ b/code/modules/projectiles/gun_item_renderer.dm @@ -1,6 +1,6 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station developers. *// +//* Copyright (c) 2024 Citadel Station Developers *// /** * gun render system diff --git a/code/modules/projectiles/gun_mob_renderer.dm b/code/modules/projectiles/gun_mob_renderer.dm index fb98d935d003..5bbce3dc96b3 100644 --- a/code/modules/projectiles/gun_mob_renderer.dm +++ b/code/modules/projectiles/gun_mob_renderer.dm @@ -1,6 +1,6 @@ //* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station developers. *// +//* Copyright (c) 2024 Citadel Station Developers *// /** * gun render system diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index 583256d45132..122b6cf69724 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -292,7 +292,7 @@ unload_ammo(user) update_icon() -/obj/item/gun/ballistic/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload_ammo(user, allow_dump=0) else diff --git a/code/modules/projectiles/guns/ballistic/microbattery/medigun_cells.dm b/code/modules/projectiles/guns/ballistic/microbattery/medigun_cells.dm index 389ba521281a..caeac99fe5e7 100644 --- a/code/modules/projectiles/guns/ballistic/microbattery/medigun_cells.dm +++ b/code/modules/projectiles/guns/ballistic/microbattery/medigun_cells.dm @@ -20,7 +20,12 @@ tracer_type = /obj/effect/projectile/tracer/medigun impact_type = /obj/effect/projectile/impact/medigun -/obj/projectile/beam/medical_cell/on_hit(var/mob/living/carbon/human/target) //what does it do when it hits someone? +/obj/projectile/beam/medical_cell/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(ishuman(target)) + on_hit_legacy(target) + +/obj/projectile/beam/medical_cell/proc/on_hit_legacy(var/mob/living/carbon/human/target) //what does it do when it hits someone? return /obj/item/ammo_casing/microbattery/medical/brute @@ -29,7 +34,7 @@ type_name = "BRUTE" projectile_type = /obj/projectile/beam/medical_cell/brute -/obj/projectile/beam/medical_cell/brute/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/brute/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-10) else @@ -41,7 +46,7 @@ type_name = "BURN" projectile_type = /obj/projectile/beam/medical_cell/burn -/obj/projectile/beam/medical_cell/burn/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/burn/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustFireLoss(-10) else @@ -53,7 +58,7 @@ type_name = "STABILIZE" projectile_type = /obj/projectile/beam/medical_cell/stabilize -/obj/projectile/beam/medical_cell/stabilize/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/stabilize/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustOxyLoss(-30) for(var/name in list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO)) @@ -81,7 +86,7 @@ type_name = "TOXIN" projectile_type = /obj/projectile/beam/medical_cell/toxin -/obj/projectile/beam/medical_cell/toxin/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/toxin/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustToxLoss(-10) else @@ -93,7 +98,7 @@ type_name = "OMNI" projectile_type = /obj/projectile/beam/medical_cell/omni -/obj/projectile/beam/medical_cell/omni/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/omni/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-2.5) target.adjustFireLoss(-2.5) @@ -108,7 +113,7 @@ type_name = "ANTIRAD" projectile_type = /obj/projectile/beam/medical_cell/antirad -/obj/projectile/beam/medical_cell/antirad/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/antirad/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustToxLoss(-2.5) target.cure_radiation(RAD_MOB_CURE_STRENGTH_MEDIGUN) @@ -121,7 +126,7 @@ type_name = "BRUTE-II" projectile_type = /obj/projectile/beam/medical_cell/brute2 -/obj/projectile/beam/medical_cell/brute2/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/brute2/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-20) else @@ -133,7 +138,7 @@ type_name = "BURN-II" projectile_type = /obj/projectile/beam/medical_cell/burn2 -/obj/projectile/beam/medical_cell/burn2/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/burn2/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustFireLoss(-20) else @@ -145,7 +150,7 @@ type_name = "STABILIZE-II" projectile_type = /obj/projectile/beam/medical_cell/stabilize2 -/obj/projectile/beam/medical_cell/stabilize2/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/stabilize2/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustOxyLoss(-200) for(var/name in list(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_ARM, BP_R_ARM, BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_GROIN, BP_TORSO)) @@ -168,7 +173,7 @@ type_name = "OMNI-II" projectile_type = /obj/projectile/beam/medical_cell/omni2 -/obj/projectile/beam/medical_cell/omni2/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/omni2/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-5) target.adjustFireLoss(-5) @@ -183,7 +188,7 @@ type_name = "TOXIN-II" projectile_type = /obj/projectile/beam/medical_cell/toxin2 -/obj/projectile/beam/medical_cell/toxin2/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/toxin2/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustToxLoss(-20) else @@ -195,7 +200,7 @@ type_name = "HASTE" projectile_type = /obj/projectile/beam/medical_cell/haste -/obj/projectile/beam/medical_cell/haste/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/haste/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.add_modifier(/datum/modifier/medigunhaste, 20 SECONDS) else @@ -215,7 +220,7 @@ type_name = "RESIST" projectile_type = /obj/projectile/beam/medical_cell/resist -/obj/projectile/beam/medical_cell/resist/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/resist/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.add_modifier(/datum/modifier/resistance, 20 SECONDS) else @@ -235,7 +240,7 @@ type_name = "CORPSE MEND" projectile_type = /obj/projectile/beam/medical_cell/corpse_mend -/obj/projectile/beam/medical_cell/corpse_mend/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/corpse_mend/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) if(target.stat == DEAD) target.adjustBruteLoss(-50) @@ -251,7 +256,7 @@ type_name = "BRUTE-III" projectile_type = /obj/projectile/beam/medical_cell/brute3 -/obj/projectile/beam/medical_cell/brute3/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/brute3/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-40) else @@ -263,7 +268,7 @@ type_name = "BURN-III" projectile_type = /obj/projectile/beam/medical_cell/burn3 -/obj/projectile/beam/medical_cell/burn3/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/burn3/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustFireLoss(-40) else @@ -275,7 +280,7 @@ type_name = "TOXIN-III" projectile_type = /obj/projectile/beam/medical_cell/toxin3 -/obj/projectile/beam/medical_cell/toxin3/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/toxin3/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustToxLoss(-40) else @@ -287,7 +292,7 @@ type_name = "OMNI-III" projectile_type = /obj/projectile/beam/medical_cell/omni3 -/obj/projectile/beam/medical_cell/omni3/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/omni3/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.adjustBruteLoss(-10) target.adjustFireLoss(-10) @@ -303,7 +308,7 @@ type_name = "SHRINK" projectile_type = /obj/projectile/beam/medical_cell/shrink -/obj/projectile/beam/medical_cell/shrink/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/shrink/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.resize(0.5) target.show_message("The beam fires into your body, changing your size!") @@ -317,7 +322,7 @@ type_name = "GROW" projectile_type = /obj/projectile/beam/medical_cell/grow -/obj/projectile/beam/medical_cell/grow/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/grow/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.resize(2.0) target.show_message("The beam fires into your body, changing your size!") @@ -331,7 +336,7 @@ type_name = "NORMALSIZE" projectile_type = /obj/projectile/beam/medical_cell/normalsize -/obj/projectile/beam/medical_cell/normalsize/on_hit(var/mob/living/carbon/human/target) +/obj/projectile/beam/medical_cell/normalsize/on_hit_legacy(var/mob/living/carbon/human/target) if(istype(target, /mob/living/carbon/human)) target.resize(1) target.show_message("The beam fires into your body, changing your size!") diff --git a/code/modules/projectiles/guns/ballistic/microbattery/revolver_cells.dm b/code/modules/projectiles/guns/ballistic/microbattery/revolver_cells.dm index c7ee2819ed9c..e43caa1981ce 100644 --- a/code/modules/projectiles/guns/ballistic/microbattery/revolver_cells.dm +++ b/code/modules/projectiles/guns/ballistic/microbattery/revolver_cells.dm @@ -40,9 +40,6 @@ damage = 2 agony = 20 pellets = 6 //number of pellets - range_step = 2 //projectile will lose a fragment each time it travels this distance. Can be a non-integer. - base_spread = 90 //lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone - spread_step = 10 embed_chance = 0 sharp = 0 damage_flag = ARMOR_MELEE @@ -67,14 +64,17 @@ sharp = 0 damage_flag = ARMOR_MELEE -/obj/projectile/bullet/stripper/on_hit(var/atom/stripped) - if(ishuman(stripped)) - var/mob/living/carbon/human/H = stripped +/obj/projectile/bullet/stripper/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + + if(ishuman(target)) + var/mob/living/carbon/human/H = target if(!H.permit_stripped) return H.drop_slots_to_ground(list(SLOT_ID_SUIT, SLOT_ID_UNIFORM, SLOT_ID_BACK, SLOT_ID_SHOES, SLOT_ID_GLOVES)) //Hats can stay! Most other things fall off with removing these. - ..() /obj/item/ammo_casing/microbattery/combat/final name = "\'Hydra\' microbattery - FINAL OPTION" @@ -94,7 +94,11 @@ tracer_type = /obj/effect/projectile/tracer/laser_omni impact_type = /obj/effect/projectile/impact/laser_omni -/obj/projectile/beam/final_option/on_hit(var/atom/impacted) +/obj/projectile/beam/final_option/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(isliving(impacted)) var/mob/living/L = impacted if(L.mind) @@ -104,5 +108,3 @@ nif = H.nif SStranscore.m_backup(L.mind,nif,one_time = TRUE) L.gib() - - ..() diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 33e3694cab93..89db582917b5 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -155,7 +155,7 @@ ..() load_ammo(A, user) -/obj/item/gun/energy/attack_hand(mob/user, list/params) +/obj/item/gun/energy/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload_ammo(user) else diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index a5d8cbf30dfa..ef599f88a7ca 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -223,7 +223,7 @@ kinetic_gun = null return ..() -/obj/projectile/kinetic/Bump(atom/target) +/obj/projectile/kinetic/pre_impact(atom/target, impact_flags, def_zone) if(kinetic_gun) var/list/mods = kinetic_gun.get_modkits() for(var/obj/item/ka_modkit/M in mods) @@ -234,20 +234,15 @@ pressure_decrease_active = TRUE return ..() -/obj/projectile/kinetic/projectile_attack_mob(mob/living/target_mob, distance, miss_modifier) - if(!pressure_decrease_active && !lavaland_environment_check(get_turf(src))) - name = "weakened [name]" - damage = damage * pressure_decrease - pressure_decrease_active = TRUE - return ..() - /obj/projectile/kinetic/legacy_on_range() strike_thing() ..() -/obj/projectile/kinetic/on_hit(atom/target) - strike_thing(target) +/obj/projectile/kinetic/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + strike_thing(target) /obj/projectile/kinetic/proc/strike_thing(atom/target) if(!pressure_decrease_active && !lavaland_environment_check(get_turf(src))) @@ -363,17 +358,6 @@ /obj/item/ka_modkit/range/modify_projectile(obj/projectile/kinetic/K) K.range += modifier * WORLD_ICON_SIZE - -//Damage -/obj/item/ka_modkit/damage - name = "damage increase" - desc = "Increases the damage of kinetic accelerator when installed." - modifier = 10 - -/obj/item/ka_modkit/damage/modify_projectile(obj/projectile/kinetic/K) - K.damage += modifier - - //Cooldown /obj/item/ka_modkit/cooldown name = "cooldown decrease" @@ -406,6 +390,20 @@ minebot_exclusive = TRUE +//Capacity +/obj/item/ka_modkit/capacity + name = "capacity increase" + desc = "A cutdown accelerator frame that increases mod capacity while reducing damage. Not compatible with minebots." + modifier = -6 + cost = -15 + maximum_of_type = 2 + minebot_upgrade = FALSE + denied_type = /obj/item/ka_modkit/capacity + +/obj/item/ka_modkit/capacity/modify_projectile(obj/projectile/kinetic/K) + K.damage += modifier + + //AoE blasts /obj/item/ka_modkit/aoe modifier = 0 diff --git a/code/modules/projectiles/guns/energy/particle.dm b/code/modules/projectiles/guns/energy/particle.dm index 910bedde7903..52184f2956c7 100644 --- a/code/modules/projectiles/guns/energy/particle.dm +++ b/code/modules/projectiles/guns/energy/particle.dm @@ -187,9 +187,9 @@ light_power = 1 light_color = "#CCFFFF" -/turf/simulated/mineral/bullet_act(var/obj/projectile/Proj) - if(istype(Proj, /obj/projectile/bullet/particle)) - if(prob(Proj.damage)) +/turf/simulated/mineral/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(istype(proj, /obj/projectile/bullet/particle)) + if(prob(proj.damage)) GetDrilled() - ..() diff --git a/code/modules/projectiles/guns/energy/sizegun_vr.dm b/code/modules/projectiles/guns/energy/sizegun_vr.dm index a96008be2e08..73b690bbd4cc 100644 --- a/code/modules/projectiles/guns/energy/sizegun_vr.dm +++ b/code/modules/projectiles/guns/energy/sizegun_vr.dm @@ -73,7 +73,11 @@ tracer_type = /obj/effect/projectile/tracer/xray impact_type = /obj/effect/projectile/impact/xray -/obj/projectile/beam/sizelaser/on_hit(var/atom/target) +/obj/projectile/beam/sizelaser/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + var/mob/living/M = target if(!M.permit_sizegun) M.visible_message("[src] has no effect on [M].") @@ -87,18 +91,13 @@ var/mob/living/H = M H.resize(set_size, TRUE) H.updateicon() - else - return 1 - /obj/projectile/beam/sizelaser/shrink set_size = 0.5 //50% of current size - /obj/projectile/beam/sizelaser/grow set_size = 2.0 //200% of current size - /obj/item/gun/energy/stripper//Because it can be fun name = "stripper gun" desc = "A gun designed to remove unnessary layers from people. For external use only!" diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 4286b0cd921d..b117377439ea 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -428,7 +428,7 @@ icon_state = "ermitter_gun" item_state = "pulse" projectile_type = /obj/projectile/beam/emitter - fire_delay = 10 + fire_delay = 2 SECONDS charge_cost = 900 cell_type = /obj/item/cell accept_cell_type = /obj/item/cell diff --git a/code/modules/projectiles/guns/launcher.dm b/code/modules/projectiles/guns/launcher.dm index f95cee55f07d..a752f382bd73 100644 --- a/code/modules/projectiles/guns/launcher.dm +++ b/code/modules/projectiles/guns/launcher.dm @@ -14,11 +14,6 @@ /obj/item/gun/launcher/can_hit(var/mob/living/target as mob, var/mob/living/user as mob) return 1 -//Override this to avoid a runtime with suicide handling. -/obj/item/gun/launcher/handle_suicide(mob/living/user) - to_chat(user, "Shooting yourself with \a [src] is pretty tricky. You can't seem to manage it.") - return - /obj/item/gun/launcher/proc/update_release_force(obj/projectile) return 0 diff --git a/code/modules/projectiles/guns/launcher/grenade_launcher.dm b/code/modules/projectiles/guns/launcher/grenade_launcher.dm index 747c7eb52156..319caebd3244 100644 --- a/code/modules/projectiles/guns/launcher/grenade_launcher.dm +++ b/code/modules/projectiles/guns/launcher/grenade_launcher.dm @@ -77,7 +77,7 @@ else ..() -/obj/item/gun/launcher/grenade/attack_hand(mob/user, list/params) +/obj/item/gun/launcher/grenade/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload(user) else diff --git a/code/modules/projectiles/guns/launcher/pneumatic.dm b/code/modules/projectiles/guns/launcher/pneumatic.dm index 9d43fcbb3fac..cd73e8f1bfb8 100644 --- a/code/modules/projectiles/guns/launcher/pneumatic.dm +++ b/code/modules/projectiles/guns/launcher/pneumatic.dm @@ -61,7 +61,7 @@ else to_chat(user, "There is nothing to remove in \the [src].") -/obj/item/gun/launcher/pneumatic/attack_hand(mob/user, list/params) +/obj/item/gun/launcher/pneumatic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload_hopper(user) else diff --git a/code/modules/projectiles/guns/launcher/syringe_gun.dm b/code/modules/projectiles/guns/launcher/syringe_gun.dm index 9393ff5c7233..4461b6a2c85e 100644 --- a/code/modules/projectiles/guns/launcher/syringe_gun.dm +++ b/code/modules/projectiles/guns/launcher/syringe_gun.dm @@ -108,7 +108,7 @@ next = darts[1] add_fingerprint(user) -/obj/item/gun/launcher/syringe/attack_hand(mob/user, list/params) +/obj/item/gun/launcher/syringe/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(!darts.len) to_chat(user, "[src] is empty.") diff --git a/code/modules/projectiles/guns/legacy_vr_guns/crestrose.dm b/code/modules/projectiles/guns/legacy_vr_guns/crestrose.dm index e96a67f93eb3..322951fb7dd5 100644 --- a/code/modules/projectiles/guns/legacy_vr_guns/crestrose.dm +++ b/code/modules/projectiles/guns/legacy_vr_guns/crestrose.dm @@ -31,10 +31,10 @@ update_icon() update_held_icon() - -/obj/item/gun/ballistic/automatic/fluff/crestrose/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(default_parry_check(user, attacker, damage_source) && prob(50)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 +// todo: uhh we can fix it later maybe idfk +// /obj/item/gun/ballistic/automatic/fluff/crestrose/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") +// if(default_parry_check(user, attacker, damage_source) && prob(50)) +// user.visible_message("\The [user] parries [attack_text] with \the [src]!") +// playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) +// return 1 +// return 0 diff --git a/code/modules/projectiles/guns/legacy_vr_guns/pummeler.dm b/code/modules/projectiles/guns/legacy_vr_guns/pummeler.dm index 1c4283e0b106..85a97302a6fe 100644 --- a/code/modules/projectiles/guns/legacy_vr_guns/pummeler.dm +++ b/code/modules/projectiles/guns/legacy_vr_guns/pummeler.dm @@ -36,13 +36,14 @@ vacuum_traversal = 0 range = WORLD_ICON_SIZE * 6 //Scary name, but just deletes the projectile after this range -/obj/projectile/pummel/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/pummel/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) var/mob/living/L = target var/throwdir = get_dir(firer,L) - if(prob(40) && !blocked) + if(prob(40) && (efficiency >= 0.9)) L.afflict_stun(20 * 1) L.Confuse(1) L.throw_at_old(get_edge_target_turf(L, throwdir), rand(3,6), 10) - - return 1 diff --git a/code/modules/projectiles/guns/legacy_vr_guns/sickshot.dm b/code/modules/projectiles/guns/legacy_vr_guns/sickshot.dm index efa27a577727..72b43ca83f12 100644 --- a/code/modules/projectiles/guns/legacy_vr_guns/sickshot.dm +++ b/code/modules/projectiles/guns/legacy_vr_guns/sickshot.dm @@ -34,7 +34,11 @@ vacuum_traversal = 0 range = WORLD_ICON_SIZE * 5 //Scary name, but just deletes the projectile after this range -/obj/projectile/sickshot/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/sickshot/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(isliving(target)) var/mob/living/L = target if(prob(20)) @@ -44,5 +48,3 @@ var/mob/living/carbon/human/H = target H.vomit() H.Confuse(2) - - return 1 diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index 90a5b2d2af9f..2cb005d1b736 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -27,9 +27,6 @@ icon_state = "staffofhealing" item_state = "staffofhealing" -/obj/item/gun/magic/staff/healing/handle_suicide() //Stops people trying to commit suicide to heal themselves - return - /* /obj/item/gun/magic/staff/chaos name = "staff of chaos" @@ -98,12 +95,12 @@ 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 +// /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/magnetic/bore.dm b/code/modules/projectiles/guns/magnetic/bore.dm index f59f7d47c8be..f6bf34c5d9d1 100644 --- a/code/modules/projectiles/guns/magnetic/bore.dm +++ b/code/modules/projectiles/guns/magnetic/bore.dm @@ -48,7 +48,7 @@ if(mat_storage) . += image(icon, "[icon_state]_loaded") -/obj/item/gun/magnetic/matfed/attack_hand(mob/user, list/params) // It doesn't keep a loaded item inside. +/obj/item/gun/magnetic/matfed/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) var/obj/item/removing diff --git a/code/modules/projectiles/guns/magnetic/magnetic.dm b/code/modules/projectiles/guns/magnetic/magnetic.dm index 6a85481a0084..8dffab58d8e4 100644 --- a/code/modules/projectiles/guns/magnetic/magnetic.dm +++ b/code/modules/projectiles/guns/magnetic/magnetic.dm @@ -150,7 +150,7 @@ return . = ..() -/obj/item/gun/magnetic/attack_hand(mob/user, list/params) +/obj/item/gun/magnetic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) var/obj/item/removing diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index e892f40b9857..f8b7dcbd6c23 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -166,7 +166,7 @@ else ..() -/obj/item/gun/ballistic/automatic/z8/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/automatic/z8/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && use_launcher) launcher.unload(user) else @@ -255,7 +255,7 @@ else return ..() //once closed, behave like normal -/obj/item/gun/ballistic/automatic/lmg/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/automatic/lmg/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!cover_open && user.get_inactive_held_item() == src) toggle_cover(user) //open the cover else @@ -735,10 +735,6 @@ . = ..() icon_state = (ammo_magazine)? "toy_smg" : "toy_smg-empty" -/obj/item/gun/ballistic/automatic/advanced_smg/foam/handle_suicide(mob/living/user) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - /obj/item/gun/ballistic/automatic/advanced_smg/foam/blue icon_state = "toy_smg_blue" @@ -767,10 +763,6 @@ else icon_state = "toy_c20r" -/obj/item/gun/ballistic/automatic/c20r/foam/handle_suicide(mob/living/user) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - //Foam LMG /obj/item/gun/ballistic/automatic/lmg/foam name = "toy light machine gun" @@ -795,8 +787,3 @@ /obj/item/gun/ballistic/automatic/lmg/foam/update_icon() . = ..() update_held_icon() - -/obj/item/gun/ballistic/automatic/lmg/foam/handle_suicide(mob/living/user) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - return diff --git a/code/modules/projectiles/guns/projectile/boltaction.dm b/code/modules/projectiles/guns/projectile/boltaction.dm index afe3233f7326..1106d7ea4a3b 100644 --- a/code/modules/projectiles/guns/projectile/boltaction.dm +++ b/code/modules/projectiles/guns/projectile/boltaction.dm @@ -50,7 +50,7 @@ // Stole hacky terrible code from doublebarrel shotgun. -Spades /obj/item/gun/ballistic/shotgun/pump/rifle/ceremonial/attackby(var/obj/item/A as obj, mob/user as mob) - if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) + if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/transforming/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) to_chat(user, "You begin to shorten the barrel and stock of \the [src].") if(loaded.len) afterattack(user, user) @@ -90,7 +90,7 @@ holy = TRUE /obj/item/gun/ballistic/shotgun/pump/rifle/lever/attackby(var/obj/item/A as obj, mob/user as mob) - if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) + if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/transforming/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) to_chat(user, "You begin to shorten the barrel and stock of \the [src].") if(loaded.len) afterattack(user, user) @@ -134,7 +134,7 @@ holy = TRUE /obj/item/gun/ballistic/shotgun/pump/rifle/lever/vintage/attackby(var/obj/item/A as obj, mob/user as mob) - if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) + if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/transforming/energy) || istype(A, /obj/item/pickaxe/plasmacutter) && w_class != WEIGHT_CLASS_NORMAL) to_chat(user, "You begin to shorten the barrel and stock of \the [src].") if(loaded.len) afterattack(user, user) diff --git a/code/modules/projectiles/guns/projectile/bow.dm b/code/modules/projectiles/guns/projectile/bow.dm index ec2f2da81eb1..b430fb5dceff 100644 --- a/code/modules/projectiles/guns/projectile/bow.dm +++ b/code/modules/projectiles/guns/projectile/bow.dm @@ -54,7 +54,7 @@ else return -/obj/item/gun/ballistic/bow/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/bow/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload_ammo(user, allow_dump=0) else diff --git a/code/modules/projectiles/guns/projectile/contender.dm b/code/modules/projectiles/guns/projectile/contender.dm index bd61be4f2683..0f3b1290d361 100644 --- a/code/modules/projectiles/guns/projectile/contender.dm +++ b/code/modules/projectiles/guns/projectile/contender.dm @@ -129,7 +129,7 @@ to_chat(user, "\The [src] is completely inoperable!") handle_click_empty() -/obj/item/gun/ballistic/contender/pipegun/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/contender/pipegun/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && destroyed) to_chat(user, "\The [src]'s chamber is too warped to extract the casing!") return diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index 08476e331c76..b0ea5a040444 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -74,7 +74,7 @@ icon_state = "colt-taj" /*//apart of reskins that have two sprites, touching may result in frustration and breaks -/obj/item/gun/ballistic/colt/detective/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/colt/detective/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!unique_reskin && loc == user) reskin_gun(user) return @@ -273,7 +273,7 @@ to_chat(user, "\The [src] is completely inoperable!") handle_click_empty() -/obj/item/gun/ballistic/pirate/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/pirate/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && destroyed) to_chat(user, "\The [src]'s chamber is too warped to extract the casing!") return @@ -403,7 +403,7 @@ else ..() -/obj/item/gun/ballistic/konigin/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/konigin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && use_shotgun) shotgun.unload_ammo(user) else @@ -537,11 +537,6 @@ allowed_magazines = list(/obj/item/ammo_magazine/foam/pistol) fire_sound = 'sound/items/syringeproj.ogg' -/obj/item/gun/ballistic/pistol/foam/handle_suicide(mob/living/user) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - return - /obj/item/gun/ballistic/pistol/foam/blue icon_state = "toy_pistol_blue" diff --git a/code/modules/projectiles/guns/projectile/rocket.dm b/code/modules/projectiles/guns/projectile/rocket.dm index ff201c946ef5..12dac8984e58 100644 --- a/code/modules/projectiles/guns/projectile/rocket.dm +++ b/code/modules/projectiles/guns/projectile/rocket.dm @@ -45,7 +45,7 @@ else ..() -/obj/item/gun/ballistic/rocket/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/rocket/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) unload(user) else @@ -86,7 +86,7 @@ to_chat(user, "You cannot reload the [src]!") return -/obj/item/gun/ballistic/rocket/collapsible/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/rocket/collapsible/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) to_chat(user, "You cannot unload the [src]'s munition!") return @@ -167,7 +167,7 @@ to_chat(user, "\The [src] is completely inoperable!") handle_click_empty() -/obj/item/gun/ballistic/rocket/tyrmalin/attack_hand(mob/user, list/params) +/obj/item/gun/ballistic/rocket/tyrmalin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && destroyed) to_chat(user, "\The [src]'s chamber is too warped to extract the casing!") return diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index 3aa3ab634014..96bd6b3fbd2f 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -187,7 +187,7 @@ //this is largely hacky and bad :( -Pete /obj/item/gun/ballistic/shotgun/doublebarrel/attackby(var/obj/item/A as obj, mob/user as mob) - if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/energy) || istype(A, /obj/item/pickaxe/plasmacutter)) + if(istype(A, /obj/item/surgical/circular_saw) || istype(A, /obj/item/melee/transforming/energy) || istype(A, /obj/item/pickaxe/plasmacutter)) to_chat(user, "You begin to shorten the barrel of \the [src].") if(loaded.len) var/burstsetting = burst @@ -350,11 +350,6 @@ one_handed_penalty = 5 fire_sound = 'sound/items/syringeproj.ogg' -/obj/item/gun/ballistic/shotgun/pump/foam/handle_suicide(mob/living/user) - user.show_message("You feel rather silly, trying to commit suicide with a toy.") - mouthshoot = 0 - return - /obj/item/gun/ballistic/shotgun/pump/foam/pump(mob/M as mob) playsound(M, action_sound, 60, 1) diff --git a/code/modules/projectiles/guns/vox.dm b/code/modules/projectiles/guns/vox.dm index 53f3c262bc7a..ca21ee38d086 100644 --- a/code/modules/projectiles/guns/vox.dm +++ b/code/modules/projectiles/guns/vox.dm @@ -151,8 +151,11 @@ /obj/projectile/sonic/strong damage = 45 -/obj/projectile/sonic/strong/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/sonic/strong/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ismob(target)) + var/mob/M = target var/throwdir = get_dir(firer,target) - target.throw_at_old(get_edge_target_turf(target, throwdir), rand(1,6), 10) - return 1 + M.throw_at_old(get_edge_target_turf(target, throwdir), rand(1,6), 10) diff --git a/code/modules/projectiles/projectile-tracing.dm b/code/modules/projectiles/projectile-tracing.dm deleted file mode 100644 index dc6144a7735b..000000000000 --- a/code/modules/projectiles/projectile-tracing.dm +++ /dev/null @@ -1,51 +0,0 @@ - -//* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station developers. *// - -/obj/projectile/trace - atom_flags = ATOM_ABSTRACT | ATOM_NONWORLD - invisibility = INVISIBILITY_ABSTRACT - hitscan = TRUE - has_tracer = FALSE - damage = 0 - nodamage = TRUE - /// did we manage to hit the given target? - var/could_hit_target = FALSE - /// delete on hitting target? - var/del_on_success = FALSE - /// do we check opacity? - var/check_opacity = FALSE - -/obj/projectile/trace/Bump(atom/A) - if(A == original) - could_hit_target = TRUE - if(del_on_success) - qdel(src) - return - return ..() - -/obj/projectile/trace/projectile_attack_mob() - return - -/obj/projectile/trace/Moved() - . = ..() - if(check_opacity && isturf(loc)) - // *sigh* // - var/turf/T = loc - if(T.has_opaque_atom) - qdel(src) - -/obj/projectile/trace/proc/prepare_trace(atom/target, pass_flags = ATOM_PASS_GLASS | ATOM_PASS_GRILLE | ATOM_PASS_TABLE, check_opacity) - src.pass_flags = pass_flags - src.original = target - src.check_opacity = check_opacity - src.range = max(src.range, (get_dist(src, target) + 1) * WORLD_ICON_SIZE) - -/** - * Simple trace to see if we could hit a given target - */ -/obj/projectile/trace/proc/simple_trace(atom/target, angle) - prepare_trace(target) - fire(angle) - del_on_success = TRUE - return could_hit_target diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm deleted file mode 100644 index 823dbbb39735..000000000000 --- a/code/modules/projectiles/projectile.dm +++ /dev/null @@ -1,1317 +0,0 @@ -/** - * ## Physics Specifications - * - * We track physics as absolute pixel on a tile, not byond's pixel x/y - * thus the first pixel at bottom left of tile is 1, 1 - * and the last pixel at top right is 32, 32 (for a world icon size of 32 pixels) - * - * We cross over to the next tile at above 32, for up/right, - * and to the last tile at below 1, for bottom/left. - * - * The code might handle it based on how it's implemented, - * but as long as the error is 1 pixel or below, it's not a big deal. - * - * The reason we're so accurate (1 pixel/below is pretty insanely strict) is - * so players have the projectile act like what the screen says it should be like; - * hence why projectiles can realistically path across corners based on their 'hitbox center'. - */ -/obj/projectile - name = "projectile" - icon = 'icons/obj/projectiles.dmi' - icon_state = "bullet" - density = FALSE - anchored = TRUE - integrity_flags = INTEGRITY_INDESTRUCTIBLE | INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF - pass_flags = ATOM_PASS_TABLE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - depth_level = INFINITY // nothing should be passing over us from depth - - //* Physics - Configuration *// - - /// speed, in pixels per second - var/speed = 25 * WORLD_ICON_SIZE - /// are we a hitscan projectile? - var/hitscan = FALSE - /// angle, in degrees **clockwise of north** - var/angle - /// max distance in pixels - /// - /// * please set this to a multiple of [WORLD_ICON_SIZE] so we scale with tile size. - var/range = WORLD_ICON_SIZE * 50 - // todo: lifespan - - //* Physics - Homing *// - /// Are we homing in on something? - var/homing = FALSE - /// current homing target - var/atom/homing_target - /// angle per second - /// - /// * this is smoother the less time between SSprojectiles fires - var/homing_turn_speed = 100 - /// rand(min, max) for inaccuracy offsets - var/homing_inaccuracy_min = 0 - /// rand(min, max) for inaccuracy offsets - var/homing_inaccuracy_max = 0 - /// pixels; added to the real location of target so we're not exactly on-point - var/homing_offset_x = 0 - /// pixels; added to the real location of target so we're not exactly on-point - var/homing_offset_y = 0 - - //* Physics - Tracers *// - - /// tracer /datum/point's - var/list/tracer_vertices - /// first point is a muzzle effect - var/tracer_muzzle_flash - /// last point is an impact - var/tracer_impact_effect - /// default tracer duration - var/tracer_duration = 5 - - //* Physics - State *// - - /// paused? if so, we completely get passed over during processing - var/paused = FALSE - /// currently hitscanning - var/hitscanning = FALSE - /// a flag to prevent movement hooks from resetting our physics on a forced movement - var/trajectory_ignore_forcemove = FALSE - /// cached value: move this much x for this much distance - /// basically, dx / distance - var/calculated_dx - /// cached value: move this much y for this much distance - /// basically, dy / distance - var/calculated_dy - /// cached sign of dx; 1, -1, or 0 - var/calculated_sdx - /// cached sign of dy; 1, -1, or 0 - var/calculated_sdy - /// our current pixel location on turf - /// byond pixel_x rounds, and we don't want that - /// - /// * at below 0 or at equals to WORLD_ICON_SIZE, we move to the next turf - var/current_px - /// our current pixel location on turf - /// byond pixel_y rounds, and we don't want that - /// - /// * at below 0 or at equals to WORLD_ICON_SIZE, we move to the next turf - var/current_py - /// the pixel location we're moving to, or the [current_px] after this iteration step - /// - /// * used so stuff like hitscan deflections work based on the actual raycasted collision step, and not the prior step. - var/next_px - /// the pixel location we're moving to, or the [current_px] after this iteration step - /// - /// * used so stuff like hitscan deflections work based on the actual raycasted collision step, and not the prior step. - var/next_py - /// used to track if we got kicked forwards after calling Move() - var/trajectory_kick_forwards = 0 - /// to avoid going too fast when kicked forwards by a mirror, if we overshoot the pixels we're - /// supposed to move this gets set to penalize the next move with a weird algorithm - /// that i won't bother explaining - var/trajectory_penalty_applied = 0 - /// currently travelled distance in pixels - var/distance_travelled - /// if we get forcemoved, this gets reset to 0 as a trip - /// this way, we know exactly how far we moved - var/distance_travelled_this_iteration - /// where the physics loop and/or some other thing moving us is trying to move to - /// used to determine where to draw hitscan tracers - // todo: this being here is kinda a symptom that things are handled weirdly but whatever - // optimally physics loop should handle tracking for stuff like animations, not require on hit processing to check turfs - var/turf/trajectory_moving_to - - //Fired processing vars - var/fired = FALSE //Have we been fired yet - var/ignore_source_check = FALSE - - var/original_angle = 0 //Angle at firing - var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle - var/spread = 0 //amount (in degrees) of projectile spread - animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy - var/ricochets = 0 - var/ricochets_max = 2 - var/ricochet_chance = 30 - - //Hitscan - /// do we have a tracer? if not we completely ignore hitscan logic - var/has_tracer = TRUE - var/tracer_type - var/muzzle_type - var/impact_type - - var/miss_sounds - var/ricochet_sounds - var/list/impact_sounds //for different categories, IMPACT_MEAT etc - - //Fancy hitscan lighting effects! - var/hitscan_light_intensity = 1.5 - var/hitscan_light_range = 0.75 - var/hitscan_light_color_override - var/muzzle_flash_intensity = 3 - var/muzzle_flash_range = 1.5 - var/muzzle_flash_color_override - var/impact_light_intensity = 3 - var/impact_light_range = 2 - var/impact_light_color_override - - //Targetting - var/yo = null - var/xo = null - var/atom/original = null // the original target clicked - var/turf/starting = null // the projectile's starting turf - var/list/permutated = list() // we've passed through these atoms, don't try to hit them again - var/p_x = 16 - var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center - - var/def_zone = "" //Aiming at - var/mob/firer = null//Who shot it - var/silenced = 0 //Attack message - var/shot_from = "" // name of the object which shot us - - var/accuracy = 0 - var/dispersion = 0.0 - - // Sub-munitions. Basically, multi-projectile shotgun, rather than pellets. - var/use_submunitions = FALSE - var/only_submunitions = FALSE // Will the projectile delete itself after firing the submunitions? - var/list/submunitions = list() // Assoc list of the paths of any submunitions, and how many they are. [projectilepath] = [projectilecount]. - var/submunition_spread_max = 30 // Divided by 10 to get the percentile dispersion. - var/submunition_spread_min = 5 // Above. - /// randomize spread? if so, evenly space between 0 and max on each side. - var/submunition_constant_spread = FALSE - var/force_max_submunition_spread = FALSE // Do we just force the maximum? - var/spread_submunition_damage = FALSE // Do we assign damage to our sub projectiles based on our main projectile damage? - - //? Damage - default handling - /// damage amount - var/damage = 10 - /// damage tier - goes hand in hand with [damage_armor] - var/damage_tier = BULLET_TIER_DEFAULT - /// todo: legacy - BRUTE, BURN, TOX, OXY, CLONE, HALLOSS, ELECTROCUTE, BIOACID are the only things that should be in here - var/damage_type = BRUTE - /// armor flag for damage - goes hand in hand with [damage_tier] - var/damage_flag = ARMOR_BULLET - /// damage mode - see [code/__DEFINES/combat/damage.dm] - var/damage_mode = NONE - - var/SA_bonus_damage = 0 // Some bullets inflict extra damage on simple animals. - var/SA_vulnerability = null // What kind of simple animal the above bonus damage should be applied to. Set to null to apply to all SAs. - var/nodamage = 0 //Determines if the projectile will skip any damage inflictions - var/taser_effect = 0 //If set then the projectile will apply it's agony damage using stun_effect_act() to mobs it hits, and other damage will be ignored - var/projectile_type = /obj/projectile - var/penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate() - //Effects - var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 maybe later for burst of fire around the impact point. - Mech - var/flammability = 0 //Amount of fire stacks to add for the above. - var/combustion = TRUE //Does this set off flammable objects on fire/hit? - var/stun = 0 - var/weaken = 0 - var/paralyze = 0 - var/irradiate = 0 - var/stutter = 0 - var/eyeblur = 0 - var/drowsy = 0 - var/agony = 0 - var/reflected = 0 // This should be set to 1 if reflected by any means, to prevent infinite reflections. - var/modifier_type_to_apply = null // If set, will apply a modifier to mobs that are hit by this projectile. - var/modifier_duration = null // How long the above modifier should last for. Leave null to be permanent. - var/excavation_amount = 0 // How much, if anything, it drills from a mineral turf. - /// If this projectile is holy. Silver bullets, etc. Currently no effects. - var/holy = FALSE - - // Antimagic - /// Should we check for antimagic effects? - var/antimagic_check = FALSE - /// Antimagic charges to use, if any - var/antimagic_charges_used = 0 - /// Multiplier for damage if antimagic is on the target - var/antimagic_damage_factor = 0 - - var/embed_chance = 0 //Base chance for a projectile to embed - - var/fire_sound = 'sound/weapons/Gunshot_old.ogg' // Can be overriden in gun.dm's fire_sound var. It can also be null but I don't know why you'd ever want to do that. -Ace - - // todo: currently unimplemneted - var/vacuum_traversal = TRUE //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum. - - var/no_attack_log = FALSE - -/obj/projectile/Destroy() - // stop processing - STOP_PROCESSING(SSprojectiles, src) - // cleanup - cleanup_hitscan_tracers() - return ..() - -/obj/projectile/proc/legacy_on_range() //if we want there to be effects when they reach the end of their range - finalize_hitscan_tracers(impact_effect = FALSE, kick_forwards = 8) - qdel(src) - -/obj/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a projectile is hit by it. - if(AM.is_incorporeal()) - return - ..() - if(isliving(AM) && !check_pass_flags(ATOM_PASS_MOB)) - var/mob/living/L = AM - if(can_hit_target(L, permutated, (AM == original))) - Bump(AM) - -/obj/projectile/forceMove(atom/target) - var/is_a_jump = isturf(target) != isturf(loc) || target.z != z || !trajectory_ignore_forcemove - if(is_a_jump) - record_hitscan_end() - render_hitscan_tracers() - . = ..() - if(!.) - stack_trace("projectile forcemove failed; please do not try to forcemove projectiles to invalid locations!") - distance_travelled_this_iteration = 0 - if(!trajectory_ignore_forcemove) - reset_physics_to_turf() - if(is_a_jump) - record_hitscan_start() - -/obj/projectile/proc/fire(set_angle_to, atom/direct_target) - if(only_submunitions) // refactor projectiles whwen holy shit this is awful lmao - // todo: this should make a muzzle flash - qdel(src) - return - //If no angle needs to resolve it from xo/yo! - if(direct_target) - direct_target.bullet_act(src, def_zone) - // todo: this should make a muzzle flash - qdel(src) - return - if(isnum(set_angle_to)) - set_angle(set_angle_to) - - // setup physics - setup_physics() - - var/turf/starting = get_turf(src) - if(isnull(angle)) //Try to resolve through offsets if there's no angle set. - if(isnull(xo) || isnull(yo)) - stack_trace("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!") - qdel(src) - return - var/turf/target = locate(clamp(starting + xo, 1, world.maxx), clamp(starting + yo, 1, world.maxy), starting.z) - set_angle(get_visual_angle(src, target)) - if(dispersion) - set_angle(angle + rand(-dispersion, dispersion)) - original_angle = angle - forceMove(starting) - permutated = list() - fired = TRUE - // kickstart - if(hitscan) - physics_hitscan() - else - START_PROCESSING(SSprojectiles, src) - physics_iteration(WORLD_ICON_SIZE, SSprojectiles.wait) - -/obj/projectile/Move(atom/newloc, dir = NONE) - . = ..() - if(.) - if(fired && can_hit_target(original, permutated, TRUE)) - Bump(original) - -//Spread is FORCED! -/obj/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0) - var/turf/curloc = get_turf(source) - var/turf/targloc = get_turf(target) - - if(istype(source, /atom/movable)) - var/atom/movable/MT = source - if(MT.locs && MT.locs.len) // Multi tile! - for(var/turf/T in MT.locs) - if(get_dist(T, target) < get_turf(curloc)) - curloc = get_turf(T) - - trajectory_ignore_forcemove = TRUE - forceMove(get_turf(source)) - trajectory_ignore_forcemove = FALSE - starting = curloc - original = target - if(targloc || !params) - yo = targloc.y - curloc.y - xo = targloc.x - curloc.x - set_angle(get_visual_angle(src, targloc) + spread) - - if(isliving(source) && params) - var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params) - p_x = calculated[2] - p_y = calculated[3] - - set_angle(calculated[1] + spread) - else if(targloc) - yo = targloc.y - curloc.y - xo = targloc.x - curloc.x - set_angle(get_visual_angle(src, targloc) + spread) - else - stack_trace("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!") - qdel(src) - -/proc/calculate_projectile_angle_and_pixel_offsets(mob/user, params) - var/list/mouse_control = params2list(params) - var/p_x = 0 - var/p_y = 0 - var/angle = 0 - if(mouse_control["icon-x"]) - p_x = text2num(mouse_control["icon-x"]) - if(mouse_control["icon-y"]) - p_y = text2num(mouse_control["icon-y"]) - if(mouse_control["screen-loc"]) - //Split screen-loc up into X+Pixel_X and Y+Pixel_Y - var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",") - - //Split X+Pixel_X up into list(X, Pixel_X) - var/list/screen_loc_X = splittext(screen_loc_params[1],":") - - //Split Y+Pixel_Y up into list(Y, Pixel_Y) - var/list/screen_loc_Y = splittext(screen_loc_params[2],":") - var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32 - var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32 - - //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average. - var/screenviewX = user.client.current_viewport_width * world.icon_size - var/screenviewY = user.client.current_viewport_height * world.icon_size - - var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x - var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y - angle = arctan(y - oy, x - ox) - return list(angle, p_x, p_y) - -/obj/projectile/proc/redirect(x, y, starting, source) - old_style_target(locate(x, y, z), starting? get_turf(starting) : get_turf(source)) - -/obj/projectile/proc/old_style_target(atom/target, atom/source) - if(!source) - source = get_turf(src) - starting = get_turf(source) - original = target - set_angle(get_visual_angle(source, target)) - -/obj/projectile/proc/vol_by_damage() - if(damage) - return clamp((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 - else - return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume. - -//Returns true if the target atom is on our current turf and above the right layer -//If direct target is true it's the originally clicked target. -/obj/projectile/proc/can_hit_target(atom/target, list/passthrough, direct_target = FALSE, ignore_loc = FALSE) - if(QDELETED(target)) - return FALSE - if(!ignore_source_check && firer) - var/mob/M = firer - if((target == firer) || ((target == firer.loc) && istype(firer.loc, /obj/vehicle/sealed/mecha)) || (target in firer.buckled_mobs) || (istype(M) && (M.buckled == target))) - return FALSE - if(!ignore_loc && (loc != target.loc)) - return FALSE - if(target in passthrough) - return FALSE - if(target.density) //This thing blocks projectiles, hit it regardless of layer/mob stuns/etc. - return TRUE - if(!isliving(target)) - if(direct_target) - return TRUE - if(target.layer < PROJECTILE_HIT_THRESHOLD_LAYER) - return FALSE - else - var/mob/living/L = target - if(!direct_target) - if(!L.density) - return FALSE - return TRUE - -/obj/projectile/Bump(atom/A) - if(A in permutated) - trajectory_ignore_forcemove = TRUE - forceMove(get_turf(A)) - trajectory_ignore_forcemove = FALSE - return FALSE - if(firer && !reflected) - if(A == firer || (A == firer.loc && istype(A, /obj/vehicle/sealed/mecha))) //cannot shoot yourself or your mech - trajectory_ignore_forcemove = TRUE - forceMove(get_turf(A)) - trajectory_ignore_forcemove = FALSE - return FALSE - - var/distance = get_dist(starting, get_turf(src)) - var/turf/target_turf = get_turf(A) - var/passthrough = FALSE - - if(ismob(A)) - var/mob/M = A - if(istype(A, /mob/living)) - //if they have a neck grab on someone, that person gets hit instead - var/obj/item/grab/G = locate() in M - if(G && G.state >= GRAB_NECK) - if(G.affecting.stat == DEAD) - var/shield_chance = min(80, (30 * (M.mob_size / 10))) //Small mobs have a harder time keeping a dead body as a shield than a human-sized one. Unathi would have an easier job, if they are made to be SIZE_LARGE in the future. -Mech - if(prob(shield_chance)) - visible_message("\The [M] uses [G.affecting] as a shield!") - if(Bump(G.affecting)) - return - else - visible_message("\The [M] tries to use [G.affecting] as a shield, but fails!") - else - visible_message("\The [M] uses [G.affecting] as a shield!") - if(Bump(G.affecting)) - return //If Bump() returns 0 (keep going) then we continue on to attack M. - - passthrough = !projectile_attack_mob(M, distance) - else - passthrough = 1 //so ghosts don't stop bullets - else - passthrough = (A.bullet_act(src, def_zone) == PROJECTILE_CONTINUE) //backwards compatibility - if(isturf(A)) - for(var/obj/O in A) - O.bullet_act(src) - for(var/mob/living/M in A) - projectile_attack_mob(M, distance) - - //penetrating projectiles can pass through things that otherwise would not let them - if(!passthrough && penetrating > 0) - if(check_penetrate(A)) - passthrough = TRUE - penetrating-- - - if(passthrough) - trajectory_ignore_forcemove = TRUE - forceMove(target_turf) - permutated.Add(A) - trajectory_ignore_forcemove = FALSE - return FALSE - - if(A) - on_impact(A) - - if(hitscanning) - if(trajectory_moving_to) - // create tracers - var/datum/point/visual_impact_point = get_intersection_point(trajectory_moving_to) - // kick it forwards a bit - visual_impact_point.shift_in_projectile_angle(angle, 2) - // draw - finalize_hitscan_tracers(visual_impact_point, impact_effect = TRUE) - else - finalize_hitscan_tracers(impact_effect = TRUE, kick_forwards = 32) - - qdel(src) - return TRUE - -//TODO: make it so this is called more reliably, instead of sometimes by bullet_act() and sometimes not -/obj/projectile/proc/on_hit(atom/target, blocked = 0, def_zone) - if(blocked >= 100) - return 0//Full block - if(!isliving(target)) - return 0 -// if(isanimal(target)) return 0 - var/mob/living/L = target - L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) // add in AGONY! - if(modifier_type_to_apply) - L.add_modifier(modifier_type_to_apply, modifier_duration) - return 1 - -//called when the projectile stops flying because it Bump'd with something -/obj/projectile/proc/on_impact(atom/A) - if(damage && damage_type == BURN) - var/turf/T = get_turf(A) - if(T) - T.hotspot_expose(700, 5) - -//Checks if the projectile is eligible for embedding. Not that it necessarily will. -/obj/projectile/proc/can_embed() - //embed must be enabled and damage type must be brute - if(embed_chance == 0 || damage_type != BRUTE) - return 0 - return 1 - -/obj/projectile/proc/get_structure_damage() - if(damage_type == BRUTE || damage_type == BURN) - return damage - return 0 - -//return 1 if the projectile should be allowed to pass through after all, 0 if not. -/obj/projectile/proc/check_penetrate(atom/A) - return 1 - -/obj/projectile/proc/check_fire(atom/target as mob, mob/living/user as mob) //Checks if you can hit them or not. - check_trajectory(target, user, pass_flags, atom_flags) - -/obj/projectile/CanAllowThrough() - . = ..() - return TRUE - -//Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying. -/obj/projectile/proc/projectile_attack_mob(mob/living/target_mob, distance, miss_modifier = 0) - if(!istype(target_mob)) - return - - //roll to-hit - miss_modifier = max(15*(distance-2) - accuracy + miss_modifier + target_mob.get_evasion(), 0) - var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, miss_modifier, ranged_attack=(distance > 1 || original != target_mob)) //if the projectile hits a target we weren't originally aiming at then retain the chance to miss - - var/result = PROJECTILE_FORCE_MISS - if(hit_zone) - def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part - result = target_mob.bullet_act(src, def_zone) - - if(result == PROJECTILE_FORCE_MISS) - if(!silenced) - visible_message("\The [src] misses [target_mob] narrowly!") - playsound(target_mob.loc, pick(miss_sounds), 60, 1) - return FALSE - - //hit messages - if(silenced) - to_chat(target_mob, "You've been hit in the [parse_zone(def_zone)] by \the [src]!") - else - visible_message("\The [target_mob] is hit by \the [src] in the [parse_zone(def_zone)]!")//X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter - - //admin logs - if(!no_attack_log) - if(istype(firer, /mob) && istype(target_mob)) - add_attack_logs(firer,target_mob,"Shot with \a [src.type] projectile") - - //sometimes bullet_act() will want the projectile to continue flying - if (result == PROJECTILE_CONTINUE) - return FALSE - - return TRUE - -/** - * i hate everything - * - * todo: refactor guns - * projectiles - * and everything else - * - * i am losing my fucking mind - * this shouldn't have to fucking exist because the ammo casing and/or gun should be doing it - * and submunitions SHOULDNT BE HANDLED HERE!! - */ -/obj/projectile/proc/launch_projectile_common(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) - original = target - def_zone = check_zone(target_zone) - firer = user - - if(use_submunitions && submunitions.len) - var/temp_min_spread = 0 - if(force_max_submunition_spread) - temp_min_spread = submunition_spread_max - else - temp_min_spread = submunition_spread_min - - var/damage_override = null - - if(spread_submunition_damage) - damage_override = damage - if(nodamage) - damage_override = 0 - - var/projectile_count = 0 - - for(var/proj in submunitions) - projectile_count += submunitions[proj] - - damage_override = round(damage_override / max(1, projectile_count)) - - for(var/path in submunitions) - var/amt = submunitions[path] - for(var/count in 1 to amt) - var/obj/projectile/SM = new path(get_turf(loc)) - SM.shot_from = shot_from - SM.silenced = silenced - if(!isnull(damage_override)) - SM.damage = damage_override - if(submunition_constant_spread) - SM.dispersion = 0 - var/calculated = angle + round((count / amt - 0.5) * submunition_spread_max, 1) - SM.launch_projectile(target, target_zone, user, params, calculated) - else - SM.dispersion = rand(temp_min_spread, submunition_spread_max) / 10 - SM.launch_projectile(target, target_zone, user, params, angle_override) - -/obj/projectile/proc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) - var/direct_target - if(get_turf(target) == get_turf(src)) - direct_target = target - - preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread) - launch_projectile_common(target, target_zone, user, params, angle_override, forced_spread) - return fire(angle_override, direct_target) - -//called to launch a projectile from a gun -/obj/projectile/proc/launch_from_gun(atom/target, target_zone, mob/user, params, angle_override, forced_spread, obj/item/gun/launcher) - - shot_from = launcher.name - silenced = launcher.silenced - - return launch_projectile(target, target_zone, user, params, angle_override, forced_spread) - -/obj/projectile/proc/launch_projectile_from_turf(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) - var/direct_target - if(get_turf(target) == get_turf(src)) - direct_target = target - - preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread) - launch_projectile_common(target, target_zone, user, params, angle_override, forced_spread) - return fire(angle_override, direct_target) - -/** - * Standard proc to determine damage when impacting something. This does not affect the special damage variables/effect variables, only damage and damtype. - * May or may not be called before/after armor calculations. - * - * @params - * - target The atom hit - * - * @return Damage to apply to target. - */ -/obj/projectile/proc/run_damage_vulnerability(atom/target) - var/final_damage = damage - if(isliving(target)) - var/mob/living/L = target - if(issimple(target)) - var/mob/living/simple_mob/SM = L - if(SM.mob_class & SA_vulnerability) - final_damage += SA_bonus_damage - if(L.anti_magic_check(TRUE, TRUE, antimagic_charges_used, FALSE)) - final_damage *= antimagic_damage_factor - return final_damage - -/** - * Probably isn't needed but saves me the time and I can regex this later: - * Gets the final `damage` that should be used on something - */ -/obj/projectile/proc/get_final_damage(atom/target) - return run_damage_vulnerability(target) - -//* Hitscan Visuals *// - -/** - * returns a /datum/point based on where we currently are - */ -/obj/projectile/proc/get_tracer_point() - RETURN_TYPE(/datum/point) - var/datum/point/point = new - if(trajectory_moving_to) - // we're in move. use next px/py to respect 1. kick forwards 2. deflections - point.x = (trajectory_moving_to.x - 1) * WORLD_ICON_SIZE + next_px - point.y = (trajectory_moving_to.y - 1) * WORLD_ICON_SIZE + next_py - else - point.x = (x - 1) * WORLD_ICON_SIZE + current_px - point.y = (y - 1) * WORLD_ICON_SIZE + current_py - point.z = z - return point - -/** - * * returns a /datum/point based on where we'll be when we loosely intersect a tile - * * returns null if we'll never intersect it - * * returns our current point if we're already loosely intersecting it - * * loosely intersecting means that we are level with the tile in either x or y. - */ -/obj/projectile/proc/get_intersection_point(turf/colliding) - RETURN_TYPE(/datum/point) - ASSERT(!isnull(angle)) - - // calculate hwere we are - var/our_x = (x - 1) * WORLD_ICON_SIZE + current_px - var/our_y = (y - 1) * WORLD_ICON_SIZE + current_py - - // calculate how far we have to go to touch their closest x / y axis - var/d_to_reach_x - var/d_to_reach_y - - if(colliding.x != x) - switch(calculated_sdx) - if(0) - return - if(1) - if(colliding.x < x) - return - d_to_reach_x = (((colliding.x - 1) * WORLD_ICON_SIZE + 0.5) - our_x) / calculated_dx - if(-1) - if(colliding.x > x) - return - d_to_reach_x = (((colliding.x - 0) * WORLD_ICON_SIZE + 0.5) - our_x) / calculated_dx - else - d_to_reach_x = 0 - - if(colliding.y != y) - switch(calculated_sdy) - if(0) - return - if(1) - if(colliding.y < y) - return - d_to_reach_y = (((colliding.y - 1) * WORLD_ICON_SIZE + 0.5) - our_y) / calculated_dy - if(-1) - if(colliding.y > y) - return - d_to_reach_y = (((colliding.y - 0) * WORLD_ICON_SIZE + 0.5) - our_y) / calculated_dy - else - d_to_reach_y = 0 - - var/needed_distance = max(d_to_reach_x, d_to_reach_y) - - // calculate if we'll actually be touching the tile once we go that far - var/future_x = our_x + needed_distance * calculated_dx - var/future_y = our_y + needed_distance * calculated_dy - // let's be slightly lenient and do 1 instead of 0.5 - if(future_x < (colliding.x - 1) * WORLD_ICON_SIZE && future_x > (colliding.x) * WORLD_ICON_SIZE + 1 && \ - future_y < (colliding.y - 1) * WORLD_ICON_SIZE && future_y > (colliding.y) * WORLD_ICON_SIZE + 1) - return // not gonna happen - - // make the point based on how far we need to go - var/datum/point/point = new - point.x = future_x - point.y = future_y - point.z = z - return point - -/** - * records the start of a hitscan - * - * this can edit the point passed in! - */ -/obj/projectile/proc/record_hitscan_start(datum/point/point, muzzle_marker, kick_forwards) - if(!hitscanning) - return - if(isnull(point)) - point = get_tracer_point() - tracer_vertices = list(point) - tracer_muzzle_flash = muzzle_marker - - // kick forwards - point.shift_in_projectile_angle(angle, kick_forwards) - -/** - * ends the hitscan tracer - * - * this can edit the point passed in! - */ -/obj/projectile/proc/record_hitscan_end(datum/point/point, impact_marker, kick_forwards) - if(!hitscanning) - return - if(isnull(point)) - point = get_tracer_point() - tracer_vertices += point - tracer_impact_effect = impact_marker - - // kick forwards - point.shift_in_projectile_angle(angle, kick_forwards) - -/** - * records a deflection (change in angle, aka generate new tracer) - */ -/obj/projectile/proc/record_hitscan_deflection(datum/point/point) - if(!hitscanning) - return - if(isnull(point)) - point = get_tracer_point() - // there's no way you need more than 25 - // if this is hit, fix your shit, don't bump this up; there's absolutely no reason for example, - // to simulate reflectors working !!25!! times. - if(length(tracer_vertices) >= 25) - CRASH("tried to add more than 25 vertices to a hitscan tracer") - tracer_vertices += point - -/obj/projectile/proc/render_hitscan_tracers(duration = tracer_duration) - // don't stay too long - ASSERT(duration >= 0 && duration <= 10 SECONDS) - // check everything - if(!has_tracer || !duration || !length(tracer_vertices)) - return - var/list/atom/movable/beam_components = list() - - // muzzle - if(muzzle_type && tracer_muzzle_flash) - var/datum/point/starting = tracer_vertices[1] - var/atom/movable/muzzle_effect = starting.instantiate_movable_with_unmanaged_offsets(muzzle_type) - if(muzzle_effect) - // turn it - var/matrix/muzzle_transform = matrix() - muzzle_transform.Turn(original_angle) - muzzle_effect.transform = muzzle_transform - muzzle_effect.color = color - muzzle_effect.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color) - // add to list - beam_components += muzzle_effect - // impact - if(impact_type && tracer_impact_effect) - var/datum/point/starting = tracer_vertices[length(tracer_vertices)] - var/atom/movable/impact_effect = starting.instantiate_movable_with_unmanaged_offsets(impact_type) - if(impact_effect) - // turn it - var/matrix/impact_transform = matrix() - impact_transform.Turn(angle) - impact_effect.transform = impact_transform - impact_effect.color = color - impact_effect.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color) - // add to list - beam_components += impact_effect - // path tracers - if(tracer_type) - var/tempref = "\ref[src]" - for(var/i in 1 to length(tracer_vertices) - 1) - var/j = i + 1 - var/datum/point/first_point = tracer_vertices[i] - var/datum/point/second_point = tracer_vertices[j] - generate_tracer_between_points(first_point, second_point, beam_components, tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref) - - QDEL_LIST_IN(beam_components, duration) - - -/obj/projectile/proc/cleanup_hitscan_tracers() - tracer_vertices=null - -/obj/projectile/proc/finalize_hitscan_tracers(datum/point/end_point, impact_effect, kick_forwards) - // if end wasn't recorded yet and we're still on a turf, record end - if(isnull(tracer_impact_effect) && loc) - record_hitscan_end(end_point, impact_marker = impact_effect, kick_forwards = kick_forwards) - // render & cleanup - render_hitscan_tracers() - cleanup_hitscan_tracers() - -//* Physics - Configuration *// - -/** - * sets our angle - */ -/obj/projectile/proc/set_angle(new_angle) - angle = new_angle - - // update sprite - if(!nondirectional_sprite) - var/matrix/M = new - M.Turn(angle) - transform = M - - // update trajectory - calculated_dx = sin(new_angle) - calculated_dy = cos(new_angle) - calculated_sdx = calculated_dx == 0? 0 : (calculated_dx > 0? 1 : -1) - calculated_sdy = calculated_dy == 0? 0 : (calculated_dy > 0? 1 : -1) - - // record our tracer's change - if(hitscanning) - record_hitscan_deflection() - -/** - * sets our speed in pixels per decisecond - */ -/obj/projectile/proc/set_speed(new_speed) - speed = clamp(new_speed, 1, WORLD_ICON_SIZE * 100) - -/** - * sets our angle and speed - */ -/obj/projectile/proc/set_velocity(new_angle, new_speed) - // this is so this can be micro-optimized later but for once i'm not going to do it early for no reason - set_speed(new_speed) - set_angle(new_angle) - -/** - * todo: this is somewhat mildly terrible - */ -/obj/projectile/proc/set_homing_target(atom/A) - if(!A || (!isturf(A) && !isturf(A.loc))) - return FALSE - homing = TRUE - homing_target = A - homing_offset_x = rand(homing_inaccuracy_min, homing_inaccuracy_max) - homing_offset_y = rand(homing_inaccuracy_min, homing_inaccuracy_max) - if(prob(50)) - homing_offset_x = -homing_offset_x - if(prob(50)) - homing_offset_y = -homing_offset_y - -/** - * initializes physics vars - */ -/obj/projectile/proc/setup_physics() - distance_travelled = 0 - -/** - * called after an unhandled forcemove is detected, or other event - * that should reset our on-turf state - */ -/obj/projectile/proc/reset_physics_to_turf() - // we use this because we can center larger than 32x32 projectiles - // without disrupting physics this way - // - // we add by (WORLD_ICON_SIZE / 2) because - // pixel_x / pixel_y starts at center, - // - current_px = pixel_x - base_pixel_x + (WORLD_ICON_SIZE / 2) - current_py = pixel_y - base_pixel_y + (WORLD_ICON_SIZE / 2) - // interrupt active move logic - trajectory_moving_to = null - -//* Physics - Processing *// - -/obj/projectile/process(delta_time) - if(paused) - return - physics_iteration(min(10 * WORLD_ICON_SIZE, delta_time * speed * SSprojectiles.global_projectile_speed_multiplier), delta_time) - -/** - * immediately processes hitscan - */ -/obj/projectile/proc/physics_hitscan(safety = 250, resuming) - // setup - if(!resuming) - hitscanning = TRUE - record_hitscan_start(muzzle_marker = TRUE, kick_forwards = 16) - - // just move as many times as we can - while(!QDELETED(src) && loc) - // check safety - safety-- - if(safety <= 0) - // if you're here, you shouldn't be. do not bump safety up, fix whatever - // you're doing because no one should be making projectiles go more than 250 - // tiles in a single life. - stack_trace("projectile hit iteration limit for hitscan") - break - - // move forwards by 1 tile length - distance_travelled += physics_step(WORLD_ICON_SIZE) - // if we're being yanked, yield - if(movable_flags & MOVABLE_IN_MOVED_YANK) - spawn(0) - physics_hitscan(safety, TRUE) - return - - // see if we're done - if(distance_travelled >= range) - legacy_on_range() - break - - hitscanning = FALSE - -/** - * ticks forwards a number of pixels - * - * todo: potential lazy animate support for performance, as we honestly don't need to animate at full fps if the server's above 20fps - * - * * delta_tiem is in deciseconds, not seconds. - */ -/obj/projectile/proc/physics_iteration(pixels, delta_time, additional_animation_length) - // setup iteration - var/safety = 15 - var/pixels_remaining = pixels - distance_travelled_this_iteration = 0 - - // apply penalty - var/penalizing = clamp(trajectory_penalty_applied, 0, pixels_remaining) - pixels_remaining -= penalizing - trajectory_penalty_applied -= penalizing - - // clamp to max distance - pixels_remaining = min(pixels_remaining, range - distance_travelled) - - // move as many times as we need to - // - // * break if we're loc = null (by deletion or otherwise) - // * break if we get paused - while(pixels_remaining > 0) - // check safety - safety-- - if(safety <= 0) - CRASH("ran out of safety! what happened?") - - // move - var/pixels_moved = physics_step(pixels_remaining) - distance_travelled += pixels_moved - distance_travelled_this_iteration += pixels_moved - pixels_remaining -= pixels_moved - // we're being yanked, yield - if(movable_flags & MOVABLE_IN_MOVED_YANK) - spawn(0) - physics_iteration(pixels_remaining, delta_time, distance_travelled_this_iteration) - return - if(!loc || paused) - break - - // penalize next one if we were kicked forwards forcefully too far - trajectory_penalty_applied = max(0, -pixels_remaining) - - // if we don't have a loc anymore just bail - if(!loc) - return - - // if we're at max range - if(distance_travelled >= range) - // todo: egh - legacy_on_range() - if(QDELETED(src)) - return - - // process homing - physics_tick_homing(delta_time) - - // perform animations - // we assume at this point any deflections that should have happened, has happened, - // so we just do a naive animate based on our current loc and pixel x/y - // - // todo: animation needs to take into account angle changes, - // but that's expensive as shit so uh lol - // - // the reason we use distance_travelled_this_iteration is so if something disappears - // by forceMove or whatnot, - // we won't have it bounce from its previous location to the new one as it's not going - // to be accurate anymore - // - // so instead, as of right now, we backtrack via how much we know we moved. - var/final_px = base_pixel_x + current_px - (WORLD_ICON_SIZE / 2) - var/final_py = base_pixel_y + current_py - (WORLD_ICON_SIZE / 2) - var/anim_dist = distance_travelled_this_iteration + additional_animation_length - pixel_x = final_px - (anim_dist * sin(angle)) - pixel_y = final_py - (anim_dist * cos(angle)) - - animate( - src, - delta_time, - flags = ANIMATION_END_NOW, - pixel_x = final_px, - pixel_y = final_py, - ) - -/** - * based on but exactly http://www.cs.yorku.ca/~amana/research/grid.pdf - * - * move into the next tile, or the specified number of pixels, - * whichever is less pixels moved - * - * this will modify our current_px/current_py as necessary - * - * @return pixels moved - */ -/obj/projectile/proc/physics_step(limit) - // distance to move in our angle to get to next turf for horizontal and vertical - var/d_next_horizontal = \ - (calculated_sdx? ((calculated_sdx > 0? (WORLD_ICON_SIZE + 0.5) - current_px : -current_px + 0.5) / calculated_dx) : INFINITY) - var/d_next_vertical = \ - (calculated_sdy? ((calculated_sdy > 0? (WORLD_ICON_SIZE + 0.5) - current_py : -current_py + 0.5) / calculated_dy) : INFINITY) - var/turf/move_to_target - - /** - * explanation on why current and next are done: - * - * projectiles track their pixel x/y on turf, not absolute pixel x/y from edge of map - * this is done to make it simpler to reason about, but is not necessarily the most simple - * or efficient way to do things. - * - * part of the problems with this approach is that Move() is not infallible. the projectile can be blocked. - * if we immediately set current pixel x/y, if the projectile is intercepted by a Bump, we now dont' know the 'real' - * position of the projectile because it's out of sync with where it should be - * - * now, things that require math operations on it don't know the actual location of the projectile until this proc - * rolls it back - * - * so instead, we never touch current px/py until the move is known to be successful, then we set it - * to the stored next px/py - * - * this way, things accessing can mutate our state freely without worrying about needing to handle rollbacks - * - * this entire system however adds overhead - * if we want to not have overhead, we'll need to rewrite hit processing and have it so moves are fully illegal to fail - * but doing that is literally not possible because anything can reject a move for any reason whatsoever - * and we cannot control that, so, instead, we make projectiles track in absolute pixel x/y coordinates from edge of map - * - * that way, we don't even need to care about where the .loc is, we just know where the projectile is supposed to be by - * knowing where it isn't, and by taking the change in its pixels the projectile controller can tell the projectile - * where to go- - * - * (all shitposting aside, this is for future work; it works right now and we have an API to do set angle, kick forwards, etc) - * (so i'm not going to touch this more because it's 4 AM and honestly this entire raycaster is already far less overhead) - * (than the old system of a 16-loop of brute forced 2 pixel increments) - */ - - if(d_next_horizontal == d_next_vertical) - // we're diagonal - if(d_next_horizontal <= limit) - move_to_target = locate(x + calculated_sdx, y + calculated_sdy, z) - . = d_next_horizontal - if(!move_to_target) - // we hit the world edge and weren't transit; time to get deleted. - if(hitscanning) - finalize_hitscan_tracers(impact_effect = FALSE) - qdel(src) - return - next_px = calculated_sdx > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) - next_py = calculated_sdy > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) - else if(d_next_horizontal < d_next_vertical) - // closer is to move left/right - if(d_next_horizontal <= limit) - move_to_target = locate(x + calculated_sdx, y, z) - . = d_next_horizontal - if(!move_to_target) - // we hit the world edge and weren't transit; time to get deleted. - if(hitscanning) - finalize_hitscan_tracers(impact_effect = FALSE) - qdel(src) - return - next_px = calculated_sdx > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) - next_py = current_py + d_next_horizontal * calculated_dy - else if(d_next_vertical < d_next_horizontal) - // closer is to move up/down - if(d_next_vertical <= limit) - move_to_target = locate(x, y + calculated_sdy, z) - . = d_next_vertical - if(!move_to_target) - // we hit the world edge and weren't transit; time to get deleted. - if(hitscanning) - finalize_hitscan_tracers(impact_effect = FALSE) - qdel(src) - return - next_px = current_px + d_next_vertical * calculated_dx - next_py = calculated_sdy > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) - - // if we need to move - if(move_to_target) - var/atom/old_loc = loc - trajectory_moving_to = move_to_target - if(!Move(move_to_target) && ((loc != move_to_target) || !trajectory_moving_to)) - // if we don't successfully move, don't change anything, we didn't move. - . = 0 - if(loc == old_loc) - stack_trace("projectile failed to move, but is still on turf instead of deleted or relocated.") - qdel(src) // bye - else - // only do these if we successfully move, or somehow end up in that turf anyways - if(trajectory_kick_forwards) - . += trajectory_kick_forwards - trajectory_kick_forwards = 0 - current_px = next_px - current_py = next_py - #ifdef CF_PROJECTILE_RAYCAST_VISUALS - new /atom/movable/render/projectile_raycast(move_to_target, current_px, current_py, "#77ff77") - #endif - trajectory_moving_to = null - else - // not moving to another tile, so, just move on current tile - if(trajectory_kick_forwards) - trajectory_kick_forwards = 0 - stack_trace("how did something kick us forwards when we didn't even move?") - . = limit - current_px += limit * calculated_dx - current_py += limit * calculated_dy - next_px = current_px - next_py = current_py - #ifdef CF_PROJECTILE_RAYCAST_VISUALS - new /atom/movable/render/projectile_raycast(loc, current_px, current_py, "#ff3333") - #endif - -#ifdef CF_PROJECTILE_RAYCAST_VISUALS -GLOBAL_VAR_INIT(projectile_raycast_debug_visual_delay, 2 SECONDS) - -/atom/movable/render/projectile_raycast - plane = OBJ_PLANE - icon = 'icons/system/color_32x32.dmi' - icon_state = "white-pixel" - -/** - * px, py are absolute pixel coordinates on the tile, not pixel_x / pixel_y of this renderer! - */ -/atom/movable/render/projectile_raycast/Initialize(mapload, px, py, color) - src.pixel_x = px - 1 - src.pixel_y = py - 1 - src.color = color - . = ..() - QDEL_IN(src, GLOB.projectile_raycast_debug_visual_delay) -#endif - -/** - * immediately, without processing, kicks us forward a number of pixels - * - * since we immediately cross over into a turf when entering, - * things like mirrors/reflectors will immediately set angle - * - * it looks ugly and is pretty bad to just reflect off the edge of a turf so said things can - * call this proc to kick us forwards by a bit - */ -/obj/projectile/proc/physics_kick_forwards(pixels) - trajectory_kick_forwards += pixels - next_px += pixels * calculated_dx - next_py += pixels * calculated_dy - -/** - * only works during non-hitscan - * - * this is called once per tick - * homing is smoother the higher fps the server / SSprojectiles runs at - * - * todo: this is somewhat mildly terrible - * todo: this has absolutely no arc/animation support; this is bad - */ -/obj/projectile/proc/physics_tick_homing(delta_time) - if(!homing) - return FALSE - // checks if they're 1. on a turf, 2. on our z - // todo: should we add support for tracking something even if it leaves a turf? - if(homing_target?.z != z) - // bye bye! - return FALSE - // todo: this assumes single-tile objects. at some point, we should upgrade this to be unnecessarily expensive and always center-mass. - var/dx = (homing_target.x - src.x) * WORLD_ICON_SIZE + (0 - current_px) + homing_offset_x - var/dy = (homing_target.y - src.y) * WORLD_ICON_SIZE + (0 - current_py) + homing_offset_y - // say it with me, arctan() - // is CCW of east if (dx, dy) - // and CW of north if (dy, dx) - // where dx and dy is distance in x/y pixels from us to them. - - var/nudge_towards = closer_angle_difference(arctan(dy, dx)) - var/max_turn_speed = homing_turn_speed * delta_time - - set_angle(angle + clamp(nudge_towards, -max_turn_speed, max_turn_speed)) - -//* Physics - Querying *// - -/** - * predict what turf we'll be in after going forwards a certain amount of pixels - * - * doesn't actually sim; so this will go through walls/obstacles! - * - * * if we go out of bounds, we will return null; this doesn't level-wrap - */ -/obj/projectile/proc/physics_predicted_turf_after_iteration(pixels) - // -1 at the end if 0, because: - // - // -32 is go back 1 tile and be at the 1st pixel (as 0 is going back) - // 0 is go back 1 tile and be at the 32nd pixel. - var/incremented_px = (current_px + pixels * calculated_dx) || - 1 - var/incremented_py = (current_py + pixels * calculated_dy) || - 1 - - var/incremented_tx = floor(incremented_px / 32) - var/incremented_ty = floor(incremented_py / 32) - - return locate(x + incremented_tx, y + incremented_ty, z) - -/** - * predict what turfs we'll hit, excluding the current turf, after going forwards - * a certain amount of pixels - * - * doesn't actually sim; so this will go through walls/obstacles! - */ -/obj/projectile/proc/physics_predicted_turfs_during_iteration(pixels) - return pixel_physics_raycast(loc, current_px, current_py, angle, pixels) - -//* Targeting *// - -/** - * Checks if something is a valid target when directly clicked. - */ -/obj/projectile/proc/is_valid_target(atom/target) - if(isobj(target)) - var/obj/O = target - return O.obj_flags & OBJ_RANGE_TARGETABLE - else if(isliving(target)) - return TRUE - else if(isturf(target)) - return target.density - return FALSE diff --git a/code/modules/projectiles/projectile/README.md b/code/modules/projectiles/projectile/README.md new file mode 100644 index 000000000000..187e857cfe43 --- /dev/null +++ b/code/modules/projectiles/projectile/README.md @@ -0,0 +1,6 @@ +# /obj/projectile + +The base type of physics-simulated raycasting flying projectiles. + +Used for when you need precision, as throwing is very imprecise. + diff --git a/code/modules/projectiles/projectile/arc.dm b/code/modules/projectiles/projectile/arc.dm index f0a0a86cbe89..8277bd2bfa5f 100644 --- a/code/modules/projectiles/projectile/arc.dm +++ b/code/modules/projectiles/projectile/arc.dm @@ -10,6 +10,7 @@ name = "arcing shot" icon_state = "fireball" // WIP movement_type = MOVEMENT_UNSTOPPABLE + impact_ground_on_expiry = TRUE plane = ABOVE_PLANE // Since projectiles are 'in the air', they might visually overlap mobs while in flight, so the projectile needs to be above their plane. speed = 10 * WORLD_ICON_SIZE var/fired_dir = null // Which direction was the projectile fired towards. Needed to invert the projectile turning based on if facing left or right. @@ -17,8 +18,6 @@ var/visual_y_offset = -16 // Adjusts how high the projectile and its shadow start, visually. This is so the projectile and shadow align with the center of the tile. var/obj/effect/projectile_shadow/shadow = null // Visual indicator for the projectile's 'true' position. Needed due to being bound to two dimensions in reality. -/obj/projectile/arc/Bump() - return /obj/projectile/arc/Initialize(mapload) shadow = new(get_turf(src)) @@ -28,7 +27,6 @@ QDEL_NULL(shadow) return ..() - /obj/projectile/arc/proc/calculate_initial_pixel_distance(atom/user, atom/target) var/datum/point/A = new(user) var/datum/point/B = new(target) @@ -44,12 +42,6 @@ var/datum/point/starting_point = new(starting) return pixel_length_between_points(current_point, starting_point) -/obj/projectile/arc/legacy_on_range() - if(loc) - on_impact(loc) - return ..() - - /obj/projectile/arc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) fired_dir = get_dir(user, target) // Used to determine if the projectile should turn in the air. distance_to_fly = calculate_initial_pixel_distance(user, target) // Calculates how many pixels to travel before hitting the ground. @@ -93,7 +85,6 @@ shadow.pixel_x = pixel_x shadow.pixel_y = pixel_y + visual_y_offset - /obj/effect/projectile_shadow name = "shadow" desc = "You better avoid the thing coming down!" @@ -102,64 +93,13 @@ anchored = TRUE animate_movement = 0 // Just like the projectile it's following. -////////////// -// Subtypes -////////////// - -// This is a test projectile in the sense that its testing the code to make sure it works, -// as opposed to a 'can I hit this thing' projectile. -/obj/projectile/arc/test/on_impact(turf/T) - new /obj/effect/explosion(T) - T.color = "#FF0000" - -// Generic, Hivebot related -/obj/projectile/arc/blue_energy - name = "energy missile" - icon_state = "force_missile" - damage = 15 - damage_type = BURN - -/obj/projectile/arc/blue_energy/on_impact(turf/T) - for(var/mob/living/L in T) - projectile_attack_mob(L) // Everything on the turf it lands gets hit. - -// Fragmentation arc shot -/obj/projectile/arc/fragmentation - name = "fragmentation shot" - icon_state = "shell" - var/list/fragment_types = list( - /obj/projectile/bullet/pellet/fragment, /obj/projectile/bullet/pellet/fragment, \ - /obj/projectile/bullet/pellet/fragment, /obj/projectile/bullet/pellet/fragment/strong - ) - var/fragment_amount = 63 // Same as a grenade. - var/spread_range = 7 - -/obj/projectile/arc/fragmentation/on_impact(turf/T) - fragmentate(T, fragment_amount, spread_range, fragment_types) - -/obj/projectile/arc/fragmentation/mortar - icon_state = "mortar" - fragment_amount = 10 - spread_range = 3 - -// EMP arc shot -/obj/projectile/arc/emp_blast - name = "emp blast" - icon_state = "bluespace" - -/obj/projectile/arc/emp_blast/on_impact(turf/T) - empulse(T, 2, 4, 7, 10) // Normal EMP grenade. - -/obj/projectile/arc/emp_blast/weak/on_impact(turf/T) - empulse(T, 1, 2, 3, 4) // Sec EMP grenade. - -// Radiation arc shot -/obj/projectile/arc/radioactive - name = "radiation blast" - icon_state = "green_pellet" - icon_scale_x = 2 - icon_scale_y = 2 - var/rad_power = RAD_INTENSITY_PROJ_ARC - -/obj/projectile/arc/radioactive/on_impact(turf/T) - radiation_pulse(T, rad_power) +//* We do not hit normally. *// + +/obj/projectile/arc/scan_crossed_atom(atom/movable/target) + return + +/obj/projectile/arc/scan_moved_turf(turf/tile) + return + +/obj/projectile/arc/impact_loop(turf/was_moving_onto, atom/bumped) + return diff --git a/code/modules/projectiles/projectile/helpers.dm b/code/modules/projectiles/projectile/helpers.dm new file mode 100644 index 000000000000..3469a09b6a67 --- /dev/null +++ b/code/modules/projectiles/projectile/helpers.dm @@ -0,0 +1,68 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * Creates a shrapnel explosion + * + * todo: should this be in a single file with procs like explosion(), emp_pulse(), and other blast-like effects? + * todo: /pellet shouldn't be /bullet/pellet + * todo: there has to be a better way to set vars on every cloud than passing them in as args + * + * @params + * * total_fragments - total pellets. this is divided by turfs within the given radius and rounded up. + * * radius - radius to target. fragments are spread into every turf in the range, making one pellet cloud projectile + * * fragment_types - either a type, or a list of types to pick from. weighted lists are not allowed for speed reasons. all types must be /obj/projectile/bullet/pellet. + * * source - (optional) actual source; used to detect if something is on the ground or not + * * shot_from_name - (legacy - pending reconsideration) what we were shot from + * * firer - (legacy - pending reconsideration) what to set firer to + */ +/turf/proc/shrapnel_explosion(total_fragments, radius, fragment_types = /obj/projectile/bullet/pellet/fragment, atom/movable/source, shot_from_name, firer) + SHOULD_NOT_OVERRIDE(TRUE) + shrapnel_explosion_impl(total_fragments, radius, fragment_types, source, shot_from_name, firer) + +/turf/proc/shrapnel_explosion_impl(total_fragments, radius, fragment_types, atom/movable/source, shot_from_name, firer) + SHOULD_NOT_OVERRIDE(TRUE) + if(radius > 8) + // remember that for radius 8 we're making 32 projectiles already + CRASH("attempted radius [radius]; this is too large.") + // todo: verify getcircle() behavior is what we want + // todo: the answer is probably no, as this doesn't work near map edges. + var/list/target_turfs = getcircle(src, radius) + // round up + var/fragments_per_projectile = ceil(total_fragments / length(target_turfs)) + // make shrapnel clouds + . = list() + for(var/turf/T in target_turfs) + var/fragment_type = islist(fragment_types) ? pick(fragment_types) : fragment_types + var/obj/projectile/bullet/pellet/pellet_cloud = new fragment_type(src) + pellet_cloud.pellets = fragments_per_projectile + pellet_cloud.shot_from = shot_from_name + pellet_cloud.firer = firer + pellet_cloud.fire(arctan(T.y - y, T.x - x), null, TRUE) + . += pellet_cloud + +/** + * make a shrapnel explosion + */ +/obj/proc/shrapnel_explosion(total_fragments, radius, fragment_types) + var/turf/turf = get_turf(src) + if(!turf) + return + var/list/obj/projectile/bullet/pellet/pellet_clouds = turf.shrapnel_explosion(total_fragments, radius, fragment_types, src, name, src) + // hit things in our turf + for(var/mob/living/victim in turf) + for(var/obj/projectile/bullet/pellet/pellet_cloud as anything in pellet_clouds) + if(QDELETED(pellet_cloud)) + continue + // they're laying on us, o h n o + if(victim.lying && (loc == turf)) + if(prob(90)) + pellet_cloud.impact(victim) + // they're not laying down but they're holding the source and standing + else if(!victim.lying && victim.is_holding(src)) + if(prob(25)) + pellet_cloud.impact(victim) + // they are either holding us while laying down or just on the turf without holding us + else + if(prob(15)) + pellet_cloud.impact(victim) diff --git a/code/modules/projectiles/projectile/pellets.dm b/code/modules/projectiles/projectile/pellets.dm deleted file mode 100644 index 61e9b45cda0c..000000000000 --- a/code/modules/projectiles/projectile/pellets.dm +++ /dev/null @@ -1,118 +0,0 @@ - -//For projectiles that actually represent clouds of projectiles -/obj/projectile/bullet/pellet - name = "shrapnel" //'shrapnel' sounds more dangerous (i.e. cooler) than 'pellet' - damage = 20 - //icon_state = "bullet" //TODO: would be nice to have it's own icon state - var/pellets = 4 //number of pellets - var/range_step = 2 //projectile will lose a fragment each time it travels this distance. Can be a non-integer. - var/base_spread = 90 //lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone - var/spread_step = 10 //higher means the pellets spread more across body parts with distance - -/obj/projectile/bullet/pellet/proc/get_pellets(var/distance) - var/pellet_loss = round((distance - 1)/range_step) //pellets lost due to distance - return max(pellets - pellet_loss, 1) - -/obj/projectile/bullet/pellet/projectile_attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier) - if (pellets < 0) return 1 - - var/total_pellets = get_pellets(distance) - var/spread = max(base_spread - (spread_step*distance), 0) - - //shrapnel explosions miss prone mobs with a chance that increases with distance - var/prone_chance = 0 - if(!base_spread) - prone_chance = max(spread_step*(distance - 2), 0) - - var/hits = 0 - for (var/i in 1 to total_pellets) - if(target_mob.lying && target_mob != original && prob(prone_chance)) - continue - - //pellet hits spread out across different zones, but 'aim at' the targeted zone with higher probability - //whether the pellet actually hits the def_zone or a different zone should still be determined by the parent using get_zone_with_miss_chance(). - var/old_zone = def_zone - def_zone = ran_zone(def_zone, spread) - if (..()) hits++ - def_zone = old_zone //restore the original zone the projectile was aimed at - - pellets -= hits //each hit reduces the number of pellets left - if (hits >= total_pellets || pellets <= 0) - return 1 - return 0 - -/obj/projectile/bullet/pellet/get_structure_damage() - var/distance = get_dist(loc, starting) - return ..() * get_pellets(distance) - -/obj/projectile/bullet/pellet/Move() - . = ..() - - //If this is a shrapnel explosion, allow mobs that are prone to get hit, too - if(. && !base_spread && isturf(loc)) - for(var/mob/living/M in loc) - if(M.lying || !M.CanPass(src, loc)) //Bump if lying or if we would normally Bump. - if(Bump(M)) //Bump will make sure we don't hit a mob multiple times - return - -//Explosive grenade projectile, borrowed from fragmentation grenade code. -/obj/projectile/bullet/pellet/fragment - damage = 10 - armor_penetration = 30 - range_step = 2 //projectiles lose a fragment each time they travel this distance. Can be a non-integer. - - base_spread = 0 //causes it to be treated as a shrapnel explosion instead of cone - spread_step = 20 - - silenced = 1 //embedding messages are still produced so it's kind of weird when enabled. - no_attack_log = 1 - muzzle_type = null - -/obj/projectile/bullet/pellet/fragment/strong - damage = 15 - armor_penetration = 20 - -/obj/projectile/bullet/pellet/fragment/weak - damage = 4 - armor_penetration = 40 - -/obj/projectile/bullet/pellet/fragment/rubber - name = "stingball shrapnel" - damage = 3 - agony = 8 - sharp = FALSE - edge = FALSE - damage_flag = ARMOR_MELEE - -/obj/projectile/bullet/pellet/fragment/rubber/strong - damage = 8 - agony = 16 - -// Tank rupture fragments -/obj/projectile/bullet/pellet/fragment/tank - name = "metal fragment" - damage = 9 //Big chunks flying off. - range_step = 2 //controls damage falloff with distance. projectiles lose a "pellet" each time they travel this distance. Can be a non-integer. - - base_spread = 0 //causes it to be treated as a shrapnel explosion instead of cone - spread_step = 20 - - armor_penetration = 20 - - silenced = 1 - no_attack_log = 1 - muzzle_type = null - pellets = 3 - -/obj/projectile/bullet/pellet/fragment/tank/small - name = "small metal fragment" - damage = 6 - armor_penetration = 5 - pellets = 5 - -/obj/projectile/bullet/pellet/fragment/tank/big - name = "large metal fragment" - damage = 17 - armor_penetration = 10 - range_step = 5 //controls damage falloff with distance. projectiles lose a "pellet" each time they travel this distance. Can be a non-integer. - pellets = 1 diff --git a/code/modules/projectiles/projectile/projectile-hitscan_visuals.dm b/code/modules/projectiles/projectile/projectile-hitscan_visuals.dm new file mode 100644 index 000000000000..f9a370eee631 --- /dev/null +++ b/code/modules/projectiles/projectile/projectile-hitscan_visuals.dm @@ -0,0 +1,195 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Hitscan Visuals *// + +/** + * returns a /datum/point based on where we currently are + */ +/obj/projectile/proc/get_tracer_point() + RETURN_TYPE(/datum/point) + var/datum/point/point = new + if(trajectory_moving_to) + // we're in move. use next px/py to respect 1. kick forwards 2. deflections + point.x = (trajectory_moving_to.x - 1) * WORLD_ICON_SIZE + next_px + point.y = (trajectory_moving_to.y - 1) * WORLD_ICON_SIZE + next_py + else + point.x = (x - 1) * WORLD_ICON_SIZE + current_px + point.y = (y - 1) * WORLD_ICON_SIZE + current_py + point.z = z + return point + +/** + * * returns a /datum/point based on where we'll be when we loosely intersect a tile + * * returns null if we'll never intersect it + * * returns our current point if we're already loosely intersecting it + * * loosely intersecting means that we are level with the tile in either x or y. + */ +/obj/projectile/proc/get_intersection_point(turf/colliding) + RETURN_TYPE(/datum/point) + ASSERT(!isnull(angle)) + + // calculate where we are + var/our_x + var/our_y + if(trajectory_moving_to) + // we're in move. use next px/py to respect 1. kick forwards 2. deflections + our_x = (trajectory_moving_to.x - 1) * WORLD_ICON_SIZE + next_px + our_y = (trajectory_moving_to.y - 1) * WORLD_ICON_SIZE + next_py + else + our_x = (x - 1) * WORLD_ICON_SIZE + current_px + our_y = (y - 1) * WORLD_ICON_SIZE + current_py + + // calculate how far we have to go to touch their closest x / y axis + var/d_to_reach_x + var/d_to_reach_y + + if(colliding.x != x) + switch(calculated_sdx) + if(0) + return + if(1) + if(colliding.x < x) + return + d_to_reach_x = (((colliding.x - 1) * WORLD_ICON_SIZE + 0.5) - our_x) / calculated_dx + if(-1) + if(colliding.x > x) + return + d_to_reach_x = (((colliding.x - 0) * WORLD_ICON_SIZE + 0.5) - our_x) / calculated_dx + else + d_to_reach_x = 0 + + if(colliding.y != y) + switch(calculated_sdy) + if(0) + return + if(1) + if(colliding.y < y) + return + d_to_reach_y = (((colliding.y - 1) * WORLD_ICON_SIZE + 0.5) - our_y) / calculated_dy + if(-1) + if(colliding.y > y) + return + d_to_reach_y = (((colliding.y - 0) * WORLD_ICON_SIZE + 0.5) - our_y) / calculated_dy + else + d_to_reach_y = 0 + + var/needed_distance = max(d_to_reach_x, d_to_reach_y) + + // calculate if we'll actually be touching the tile once we go that far + var/future_x = our_x + needed_distance * calculated_dx + var/future_y = our_y + needed_distance * calculated_dy + // let's be slightly lenient and do 1 instead of 0.5 + if(future_x < (colliding.x - 1) * WORLD_ICON_SIZE && future_x > (colliding.x) * WORLD_ICON_SIZE + 1 && \ + future_y < (colliding.y - 1) * WORLD_ICON_SIZE && future_y > (colliding.y) * WORLD_ICON_SIZE + 1) + return // not gonna happen + + // make the point based on how far we need to go + var/datum/point/point = new + point.x = future_x + point.y = future_y + point.z = z + return point + +/** + * records the start of a hitscan + * + * this can edit the point passed in! + */ +/obj/projectile/proc/record_hitscan_start(datum/point/point, muzzle_marker, kick_forwards) + if(!hitscanning) + return + if(isnull(point)) + point = get_tracer_point() + tracer_vertices = list(point) + tracer_muzzle_flash = muzzle_marker + + // kick forwards + point.shift_in_projectile_angle(angle, kick_forwards) + +/** + * ends the hitscan tracer + * + * this can edit the point passed in! + */ +/obj/projectile/proc/record_hitscan_end(datum/point/point, impact_marker, kick_forwards) + if(!hitscanning) + return + if(isnull(point)) + point = get_tracer_point() + tracer_vertices += point + tracer_impact_effect = impact_marker + + // kick forwards + point.shift_in_projectile_angle(angle, kick_forwards) + +/** + * records a deflection (change in angle, aka generate new tracer) + */ +/obj/projectile/proc/record_hitscan_deflection(datum/point/point) + if(!hitscanning) + return + if(isnull(point)) + point = get_tracer_point() + // there's no way you need more than 25 + // if this is hit, fix your shit, don't bump this up; there's absolutely no reason for example, + // to simulate reflectors working !!25!! times. + if(length(tracer_vertices) >= 25) + CRASH("tried to add more than 25 vertices to a hitscan tracer") + tracer_vertices += point + +/obj/projectile/proc/render_hitscan_tracers(duration = tracer_duration) + // don't stay too long + ASSERT(duration >= 0 && duration <= 10 SECONDS) + // check everything + if(!has_tracer || !duration || !length(tracer_vertices)) + return + var/list/atom/movable/beam_components = list() + + // muzzle + if(muzzle_type && tracer_muzzle_flash) + var/datum/point/starting = tracer_vertices[1] + var/atom/movable/muzzle_effect = starting.instantiate_movable_with_unmanaged_offsets(muzzle_type) + if(muzzle_effect) + // turn it + var/matrix/muzzle_transform = matrix() + muzzle_transform.Turn(original_angle) + muzzle_effect.transform = muzzle_transform + muzzle_effect.color = color + muzzle_effect.set_light(muzzle_flash_range, muzzle_flash_intensity, muzzle_flash_color_override? muzzle_flash_color_override : color) + // add to list + beam_components += muzzle_effect + // impact + if(impact_type && tracer_impact_effect) + var/datum/point/starting = tracer_vertices[length(tracer_vertices)] + var/atom/movable/impact_effect = starting.instantiate_movable_with_unmanaged_offsets(impact_type) + if(impact_effect) + // turn it + var/matrix/impact_transform = matrix() + impact_transform.Turn(angle) + impact_effect.transform = impact_transform + impact_effect.color = color + impact_effect.set_light(impact_light_range, impact_light_intensity, impact_light_color_override? impact_light_color_override : color) + // add to list + beam_components += impact_effect + // path tracers + if(tracer_type) + var/tempref = "\ref[src]" + for(var/i in 1 to length(tracer_vertices) - 1) + var/j = i + 1 + var/datum/point/first_point = tracer_vertices[i] + var/datum/point/second_point = tracer_vertices[j] + generate_tracer_between_points(first_point, second_point, beam_components, tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity, tempref) + + QDEL_LIST_IN(beam_components, duration) + +/obj/projectile/proc/cleanup_hitscan_tracers() + tracer_vertices = null + +/obj/projectile/proc/finalize_hitscan_tracers(datum/point/end_point, impact_effect, kick_forwards) + // if end wasn't recorded yet and we're still on a turf, record end + if(isnull(tracer_impact_effect) && loc) + record_hitscan_end(end_point, impact_marker = impact_effect, kick_forwards = kick_forwards) + // render & cleanup + render_hitscan_tracers() + cleanup_hitscan_tracers() diff --git a/code/modules/projectiles/projectile/projectile-physics.dm b/code/modules/projectiles/projectile/projectile-physics.dm new file mode 100644 index 000000000000..ed446daf0ff0 --- /dev/null +++ b/code/modules/projectiles/projectile/projectile-physics.dm @@ -0,0 +1,434 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Physics - Configuration *// + +/** + * sets our angle + */ +/obj/projectile/proc/set_angle(new_angle) + angle = new_angle + + // update sprite + if(!nondirectional_sprite) + var/matrix/M = new + M.Turn(angle) + transform = M + + // update trajectory + calculated_dx = sin(new_angle) + calculated_dy = cos(new_angle) + calculated_sdx = calculated_dx == 0? 0 : (calculated_dx > 0? 1 : -1) + calculated_sdy = calculated_dy == 0? 0 : (calculated_dy > 0? 1 : -1) + + var/normalized_to_first_quadrant = MODULUS_F(new_angle, 90) + angle_chebyshev_divisor = cos(normalized_to_first_quadrant >= 45? (90 - normalized_to_first_quadrant) : normalized_to_first_quadrant) + + // record our tracer's change + if(hitscanning) + record_hitscan_deflection() + +/** + * sets our speed in pixels per decisecond + */ +/obj/projectile/proc/set_speed(new_speed) + speed = clamp(new_speed, 1, WORLD_ICON_SIZE * 100) + +/** + * sets our angle and speed + */ +/obj/projectile/proc/set_velocity(new_angle, new_speed) + // this is so this can be micro-optimized later but for once i'm not going to do it early for no reason + set_speed(new_speed) + set_angle(new_angle) + +/** + * todo: this is somewhat mildly terrible + */ +/obj/projectile/proc/set_homing_target(atom/A) + if(!A || (!isturf(A) && !isturf(A.loc))) + return FALSE + homing = TRUE + homing_target = A + homing_offset_x = rand(homing_inaccuracy_min, homing_inaccuracy_max) + homing_offset_y = rand(homing_inaccuracy_min, homing_inaccuracy_max) + if(prob(50)) + homing_offset_x = -homing_offset_x + if(prob(50)) + homing_offset_y = -homing_offset_y + +/** + * initializes physics vars + */ +/obj/projectile/proc/setup_physics() + distance_travelled = 0 + +/** + * called after an unhandled forcemove is detected, or other event + * that should reset our on-turf state + */ +/obj/projectile/proc/reset_physics_to_turf() + // we use this because we can center larger than 32x32 projectiles + // without disrupting physics this way + // + // we add by (WORLD_ICON_SIZE / 2) because + // pixel_x / pixel_y starts at center, + // + current_px = pixel_x - base_pixel_x + (WORLD_ICON_SIZE / 2) + current_py = pixel_y - base_pixel_y + (WORLD_ICON_SIZE / 2) + // interrupt active move logic + trajectory_moving_to = null + +//* Physics - Processing *// + +/obj/projectile/process(delta_time) + if(paused) + return + physics_iteration(min(10 * WORLD_ICON_SIZE, delta_time * speed * SSprojectiles.global_projectile_speed_multiplier), delta_time) + +/** + * immediately processes hitscan + */ +/obj/projectile/proc/physics_hitscan(safety = 250, resuming) + // setup + if(!resuming) + hitscanning = TRUE + record_hitscan_start(muzzle_marker = TRUE, kick_forwards = 16) + + // just move as many times as we can + while(!QDELETED(src) && loc) + // check safety + safety-- + if(safety <= 0) + // if you're here, you shouldn't be. do not bump safety up, fix whatever + // you're doing because no one should be making projectiles go more than 250 + // tiles in a single life. + stack_trace("projectile hit iteration limit for hitscan") + break + + // move forwards by 1 tile length + var/pixels_moved = physics_step(WORLD_ICON_SIZE) + distance_travelled += pixels_moved + // if we're being yanked, yield + if(movable_flags & MOVABLE_IN_MOVED_YANK) + spawn(0) + physics_hitscan(safety, TRUE) + return + + // see if we're done + if(distance_travelled >= range) + legacy_on_range() + break + + hitscanning = FALSE + +/** + * ticks forwards a number of pixels + * + * todo: potential lazy animate support for performance, as we honestly don't need to animate at full fps if the server's above 20fps + * + * * delta_tiem is in deciseconds, not seconds. + */ +/obj/projectile/proc/physics_iteration(pixels, delta_time, additional_animation_length) + // setup iteration + var/safety = 15 + var/pixels_remaining = pixels + distance_travelled_this_iteration = 0 + + // apply penalty + var/penalizing = clamp(trajectory_penalty_applied, 0, pixels_remaining) + pixels_remaining -= penalizing + trajectory_penalty_applied -= penalizing + + // clamp to max distance + pixels_remaining = min(pixels_remaining, range - distance_travelled) + + // move as many times as we need to + // + // * break if we're loc = null (by deletion or otherwise) + // * break if we get paused + while(pixels_remaining > 0) + // check safety + safety-- + if(safety <= 0) + CRASH("ran out of safety! what happened?") + + // move + var/pixels_moved = physics_step(pixels_remaining) + distance_travelled += pixels_moved + distance_travelled_this_iteration += pixels_moved + pixels_remaining -= pixels_moved + // we're being yanked, yield + if(movable_flags & MOVABLE_IN_MOVED_YANK) + spawn(0) + physics_iteration(pixels_remaining, delta_time, distance_travelled_this_iteration) + return + // this is also a catch-all for deletion + if(!loc || paused) + break + + // penalize next one if we were kicked forwards forcefully too far + trajectory_penalty_applied = max(0, -pixels_remaining) + + // if we don't have a loc anymore just bail + if(!loc) + return + + // if we're at max range + if(distance_travelled >= range) + // todo: egh + legacy_on_range() + if(QDELETED(src)) + return + + // process homing + physics_tick_homing(delta_time) + + // perform animations + // we assume at this point any deflections that should have happened, has happened, + // so we just do a naive animate based on our current loc and pixel x/y + // + // todo: animation needs to take into account angle changes, + // but that's expensive as shit so uh lol + // + // the reason we use distance_travelled_this_iteration is so if something disappears + // by forceMove or whatnot, + // we won't have it bounce from its previous location to the new one as it's not going + // to be accurate anymore + // + // so instead, as of right now, we backtrack via how much we know we moved. + var/final_px = base_pixel_x + current_px - (WORLD_ICON_SIZE / 2) + var/final_py = base_pixel_y + current_py - (WORLD_ICON_SIZE / 2) + var/anim_dist = distance_travelled_this_iteration + additional_animation_length + pixel_x = final_px - (anim_dist * sin(angle)) + pixel_y = final_py - (anim_dist * cos(angle)) + + animate( + src, + delta_time, + flags = ANIMATION_END_NOW, + pixel_x = final_px, + pixel_y = final_py, + ) + +/** + * based on but exactly http://www.cs.yorku.ca/~amana/research/grid.pdf + * + * move into the next tile, or the specified number of pixels, + * whichever is less pixels moved + * + * this will modify our current_px/current_py as necessary + * + * @return pixels moved + */ +/obj/projectile/proc/physics_step(limit) + // distance to move in our angle to get to next turf for horizontal and vertical + var/d_next_horizontal = \ + (calculated_sdx? ((calculated_sdx > 0? (WORLD_ICON_SIZE + 0.5) - current_px : -current_px + 0.5) / calculated_dx) : INFINITY) + var/d_next_vertical = \ + (calculated_sdy? ((calculated_sdy > 0? (WORLD_ICON_SIZE + 0.5) - current_py : -current_py + 0.5) / calculated_dy) : INFINITY) + var/turf/move_to_target + + /** + * explanation on why current and next are done: + * + * projectiles track their pixel x/y on turf, not absolute pixel x/y from edge of map + * this is done to make it simpler to reason about, but is not necessarily the most simple + * or efficient way to do things. + * + * part of the problems with this approach is that Move() is not infallible. the projectile can be blocked. + * if we immediately set current pixel x/y, if the projectile is intercepted by a Bump, we now dont' know the 'real' + * position of the projectile because it's out of sync with where it should be + * + * now, things that require math operations on it don't know the actual location of the projectile until this proc + * rolls it back + * + * so instead, we never touch current px/py until the move is known to be successful, then we set it + * to the stored next px/py + * + * this way, things accessing can mutate our state freely without worrying about needing to handle rollbacks + * + * this entire system however adds overhead + * if we want to not have overhead, we'll need to rewrite hit processing and have it so moves are fully illegal to fail + * but doing that is literally not possible because anything can reject a move for any reason whatsoever + * and we cannot control that, so, instead, we make projectiles track in absolute pixel x/y coordinates from edge of map + * + * that way, we don't even need to care about where the .loc is, we just know where the projectile is supposed to be by + * knowing where it isn't, and by taking the change in its pixels the projectile controller can tell the projectile + * where to go- + * + * (all shitposting aside, this is for future work; it works right now and we have an API to do set angle, kick forwards, etc) + * (so i'm not going to touch this more because it's 4 AM and honestly this entire raycaster is already far less overhead) + * (than the old system of a 16-loop of brute forced 2 pixel increments) + */ + + if(d_next_horizontal == d_next_vertical) + // we're diagonal + if(d_next_horizontal <= limit) + move_to_target = locate(x + calculated_sdx, y + calculated_sdy, z) + . = d_next_horizontal + if(!move_to_target) + // we hit the world edge and weren't transit; time to get deleted. + if(hitscanning) + finalize_hitscan_tracers(impact_effect = FALSE) + qdel(src) + return + next_px = calculated_sdx > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) + next_py = calculated_sdy > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) + else if(d_next_horizontal < d_next_vertical) + // closer is to move left/right + if(d_next_horizontal <= limit) + move_to_target = locate(x + calculated_sdx, y, z) + . = d_next_horizontal + if(!move_to_target) + // we hit the world edge and weren't transit; time to get deleted. + if(hitscanning) + finalize_hitscan_tracers(impact_effect = FALSE) + qdel(src) + return + next_px = calculated_sdx > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) + next_py = current_py + d_next_horizontal * calculated_dy + else if(d_next_vertical < d_next_horizontal) + // closer is to move up/down + if(d_next_vertical <= limit) + move_to_target = locate(x, y + calculated_sdy, z) + . = d_next_vertical + if(!move_to_target) + // we hit the world edge and weren't transit; time to get deleted. + if(hitscanning) + finalize_hitscan_tracers(impact_effect = FALSE) + qdel(src) + return + next_px = current_px + d_next_vertical * calculated_dx + next_py = calculated_sdy > 0? 0.5 : (WORLD_ICON_SIZE + 0.5) + + // if we need to move + if(move_to_target) + var/atom/old_loc = loc + trajectory_moving_to = move_to_target + // mark next distance so impact processing can work + next_distance = distance_travelled + . + if(!Move(move_to_target) && ((loc != move_to_target) || !trajectory_moving_to)) + // if we don't successfully move, don't change anything, we didn't move. + . = 0 + if(loc == old_loc) + stack_trace("projectile failed to move, but is still on turf instead of deleted or relocated.") + qdel(src) // bye + else + // only do these if we successfully move, or somehow end up in that turf anyways + if(trajectory_kick_forwards) + . += trajectory_kick_forwards + trajectory_kick_forwards = 0 + current_px = next_px + current_py = next_py + #ifdef CF_PROJECTILE_RAYCAST_VISUALS + new /atom/movable/render/projectile_raycast(move_to_target, current_px, current_py, "#77ff77") + #endif + trajectory_moving_to = null + else + // not moving to another tile, so, just move on current tile + if(trajectory_kick_forwards) + trajectory_kick_forwards = 0 + stack_trace("how did something kick us forwards when we didn't even move?") + . = limit + current_px += limit * calculated_dx + current_py += limit * calculated_dy + next_px = current_px + next_py = current_py + #ifdef CF_PROJECTILE_RAYCAST_VISUALS + new /atom/movable/render/projectile_raycast(loc, current_px, current_py, "#ff3333") + #endif + +#ifdef CF_PROJECTILE_RAYCAST_VISUALS +GLOBAL_VAR_INIT(projectile_raycast_debug_visual_delay, 2 SECONDS) + +/atom/movable/render/projectile_raycast + plane = OBJ_PLANE + icon = 'icons/system/color_32x32.dmi' + icon_state = "white-pixel" + +/** + * px, py are absolute pixel coordinates on the tile, not pixel_x / pixel_y of this renderer! + */ +/atom/movable/render/projectile_raycast/Initialize(mapload, px, py, color) + src.pixel_x = px - 1 + src.pixel_y = py - 1 + src.color = color + . = ..() + QDEL_IN(src, GLOB.projectile_raycast_debug_visual_delay) +#endif + +/** + * immediately, without processing, kicks us forward a number of pixels + * + * since we immediately cross over into a turf when entering, + * things like mirrors/reflectors will immediately set angle + * + * it looks ugly and is pretty bad to just reflect off the edge of a turf so said things can + * call this proc to kick us forwards by a bit + */ +/obj/projectile/proc/physics_kick_forwards(pixels) + trajectory_kick_forwards += pixels + next_px += pixels * calculated_dx + next_py += pixels * calculated_dy + +/** + * only works during non-hitscan + * + * this is called once per tick + * homing is smoother the higher fps the server / SSprojectiles runs at + * + * todo: this is somewhat mildly terrible + * todo: this has absolutely no arc/animation support; this is bad + */ +/obj/projectile/proc/physics_tick_homing(delta_time) + if(!homing) + return FALSE + // checks if they're 1. on a turf, 2. on our z + // todo: should we add support for tracking something even if it leaves a turf? + if(homing_target?.z != z) + // bye bye! + return FALSE + // todo: this assumes single-tile objects. at some point, we should upgrade this to be unnecessarily expensive and always center-mass. + var/dx = (homing_target.x - src.x) * WORLD_ICON_SIZE + (0 - current_px) + homing_offset_x + var/dy = (homing_target.y - src.y) * WORLD_ICON_SIZE + (0 - current_py) + homing_offset_y + // say it with me, arctan() + // is CCW of east if (dx, dy) + // and CW of north if (dy, dx) + // where dx and dy is distance in x/y pixels from us to them. + + var/nudge_towards = closer_angle_difference(arctan(dy, dx)) + var/max_turn_speed = homing_turn_speed * delta_time + + set_angle(angle + clamp(nudge_towards, -max_turn_speed, max_turn_speed)) + +//* Physics - Querying *// + +/** + * predict what turf we'll be in after going forwards a certain amount of pixels + * + * doesn't actually sim; so this will go through walls/obstacles! + * + * * if we go out of bounds, we will return null; this doesn't level-wrap + */ +/obj/projectile/proc/physics_predicted_turf_after_iteration(pixels) + // -1 at the end if 0, because: + // + // -32 is go back 1 tile and be at the 1st pixel (as 0 is going back) + // 0 is go back 1 tile and be at the 32nd pixel. + var/incremented_px = (current_px + pixels * calculated_dx) || - 1 + var/incremented_py = (current_py + pixels * calculated_dy) || - 1 + + var/incremented_tx = floor(incremented_px / 32) + var/incremented_ty = floor(incremented_py / 32) + + return locate(x + incremented_tx, y + incremented_ty, z) + +/** + * predict what turfs we'll hit, excluding the current turf, after going forwards + * a certain amount of pixels + * + * doesn't actually sim; so this will go through walls/obstacles! + */ +/obj/projectile/proc/physics_predicted_turfs_during_iteration(pixels) + return pixel_physics_raycast(loc, current_px, current_py, angle, pixels) diff --git a/code/modules/projectiles/projectile/projectile-tracing.dm b/code/modules/projectiles/projectile/projectile-tracing.dm new file mode 100644 index 000000000000..7c4b4d10014f --- /dev/null +++ b/code/modules/projectiles/projectile/projectile-tracing.dm @@ -0,0 +1,68 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station developers. *// + +/obj/projectile/trace + atom_flags = ATOM_ABSTRACT | ATOM_NONWORLD + invisibility = INVISIBILITY_ABSTRACT + hitscan = TRUE + has_tracer = FALSE + damage = 0 + nodamage = TRUE + projectile_type = PROJECTILE_TYPE_TRACE + + /// did we manage to hit the given target? + var/could_hit_target = FALSE + /// delete on hitting target? + var/del_on_success = TRUE + /// do we check opacity? + var/check_opacity = FALSE + /// do we only care about opacity, and not pass flags or anything else? + var/only_opacity = FALSE + /// do we only need to reach their turf? + var/require_turf_only = FALSE + /// target turf, if we only require reaching their turf + var/turf/require_turf_cached + +/obj/projectile/trace/CanPassThrough(atom/blocker, turf/target, blocker_opinion) + if(only_opacity && !blocker.opacity) + return TRUE + return ..() + +/obj/projectile/trace/pre_impact(atom/target, impact_flags, def_zone) + if(target == original_target) + could_hit_target = TRUE + if(del_on_success) + return PROJECTILE_IMPACT_DELETE + if(get_turf(target) == require_turf_cached) + could_hit_target = TRUE + if(del_on_success) + return PROJECTILE_IMPACT_DELETE + . = ..() + // tracers only count as 'can move across' if pre_impact() says we should phase/pierce. + if(. & PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH) + return PROJECTILE_IMPACT_PASSTHROUGH + else + return PROJECTILE_IMPACT_DELETE + +/obj/projectile/trace/Moved() + . = ..() + if(QDELETED(src)) + return + if(require_turf_cached == loc) + could_hit_target = TRUE + if(del_on_success) + qdel(src) + return + if(check_opacity && isturf(loc)) + // *sigh* // + var/turf/T = loc + if(T.has_opaque_atom) + qdel(src) + +/** + * always call this before firing. + */ +/obj/projectile/trace/proc/prepare_trace(atom/target) + src.original_target = target + if(require_turf_only) + src.require_turf_cached = get_turf(target) diff --git a/code/modules/projectiles/projectile/projectile.dm b/code/modules/projectiles/projectile/projectile.dm new file mode 100644 index 000000000000..dd6fc8e9bab8 --- /dev/null +++ b/code/modules/projectiles/projectile/projectile.dm @@ -0,0 +1,1180 @@ +/** + * ## Physics Specifications + * + * We track physics as absolute pixel on a tile, not byond's pixel x/y + * thus the first pixel at bottom left of tile is 1, 1 + * and the last pixel at top right is 32, 32 (for a world icon size of 32 pixels) + * + * We cross over to the next tile at above 32, for up/right, + * and to the last tile at below 1, for bottom/left. + * + * The code might handle it based on how it's implemented, + * but as long as the error is 1 pixel or below, it's not a big deal. + * + * The reason we're so accurate (1 pixel/below is pretty insanely strict) is + * so players have the projectile act like what the screen says it should be like; + * hence why projectiles can realistically path across corners based on their 'hitbox center'. + */ +/obj/projectile + name = "projectile" + icon = 'icons/obj/projectiles.dmi' + icon_state = "bullet" + density = FALSE + anchored = TRUE + integrity_flags = INTEGRITY_INDESTRUCTIBLE | INTEGRITY_ACIDPROOF | INTEGRITY_FIREPROOF | INTEGRITY_LAVAPROOF + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + depth_level = INFINITY // nothing should be passing over us from depth + + //* Collision Handling *// + + /** PROJECTILE PIERCING + * WARNING: + * Projectile piercing MUST be done using these variables. + * Ordinary passflags will result in can_hit_target being false unless directly clicked on - similar to pass_flags_phase but without even going to process_hit. + * The two flag variables below both use pass flags. + * In the context of ATOM_PASS_THROWN, it means the projectile will ignore things that are currently "in the air" from a throw. + * + * Also, projectiles sense hits using Bump(), and then pierce them if necessary. + * They simply do not follow conventional movement rules. + * NEVER flag a projectile as PHASING movement type. + * If you so badly need to make one go through *everything*, override check_pierce() for your projectile to always return PROJECTILE_PIERCE_PHASE/HIT. + */ + /// The "usual" flags of pass_flags is used in that can_hit_target ignores these unless they're specifically targeted/clicked on. This behavior entirely bypasses process_hit if triggered, rather than phasing which uses prehit_pierce() to check. + pass_flags = ATOM_PASS_TABLE + /// we handle our own go through + generic_canpass = FALSE + /// If FALSE, allow us to hit something directly targeted/clicked/whatnot even if we're able to phase through it + var/phases_through_direct_target = FALSE + /// anything with these pass flags are automatically pierced + var/pass_flags_pierce = NONE + /// anything with these pass flags are automatically phased through + var/pass_flags_phase = NONE + /// number of times we've pierced something. Incremented BEFORE bullet_act and similar procs run! + var/pierces = 0 + /// What we already hit + /// initialized on fire() + var/list/impacted + + //* -- Combat - Accuracy -- *// + //* These are applied in additional to mob + //* evasion and miss handling! Projectiles should *// + //* generally be pretty accurate for that reason. *// + //* *// + //* All accuracy ranges use **manhattan** *// + //* distance, not euclidean! *// + + /// if enabled, projectile-side baymiss is entirely disabled + /// + /// * the target can still forcefully miss us, unfortunately, if it doesn't use the standard API + /// * this might be violently fixed in the future + var/accuracy_disabled = FALSE + /// perfect accuracy below this range (in pixels) + /// + /// * this means the projectile doesn't enforce inaccuracy; not the target! + /// * remember that even if a projectile clips a single pixel on a target turf, it hits. + /// * right now, accuracy is slightly more than it should be due to distance being ticked up post-impact. + var/accuracy_perfect_range = WORLD_ICON_SIZE * 7 + /// linear - accuracy outside of perfect range + /// + /// * [0, 100] inclusive as % + var/accuracy_drop_start = 100 + /// linear - hit % falloff per pixel + var/accuracy_drop_slope = 5 / WORLD_ICON_SIZE + /// linear - lowest possible accuracy to drop to + var/accuracy_drop_end = 20 + /// alter end result hit probability by this value + /// + /// todo: is this really the best way? + /// + /// * this is a multiplier for hit chance if less than 1 + /// * this is a divisor for miss chance if more than 1 + /// * 0.5 will turn a 80% hit to a 40% + /// * 2 will turn a 80% hit to a 90% + /// * 2 will turn a 40% hit to a 70% + var/accuracy_overall_modify = 1 + + //* Combat - Effects *// + + /// projectile effects + /// + /// * this is a static typelist on this typepath + /// * do not under any circumstances edit this + /// * this is /tmp because this should never change on a typepath + VAR_PROTECTED/tmp/list/base_projectile_effects + /// projectile effects + /// + /// * this is configured at runtime and can be edited + /// * this is non /tmp because this is infact serializable + VAR_PROTECTED/list/additional_projectile_effects + + //* Configuration *// + + /// Projectile type bitfield; set all that is relevant + var/projectile_type = NONE + /// Impact ground on expiry (range, or lifetime) + var/impact_ground_on_expiry = FALSE + + //* Physics - Configuration *// + + /// speed, in pixels per second + var/speed = 25 * WORLD_ICON_SIZE + /// are we a hitscan projectile? + var/hitscan = FALSE + /// angle, in degrees **clockwise of north** + var/angle + /// multiplier to distance travelled at the **current** [angle] to get it to chebyshev dist + var/angle_chebyshev_divisor + /// max distance in pixels + /// + /// * please set this to a multiple of [WORLD_ICON_SIZE] so we scale with tile size. + var/range = WORLD_ICON_SIZE * 50 + // todo: lifespan + + //* Physics - Homing *// + /// Are we homing in on something? + var/homing = FALSE + /// current homing target + var/atom/homing_target + /// angle per second + /// + /// * this is smoother the less time between SSprojectiles fires + var/homing_turn_speed = 100 + /// rand(min, max) for inaccuracy offsets + var/homing_inaccuracy_min = 0 + /// rand(min, max) for inaccuracy offsets + var/homing_inaccuracy_max = 0 + /// pixels; added to the real location of target so we're not exactly on-point + var/homing_offset_x = 0 + /// pixels; added to the real location of target so we're not exactly on-point + var/homing_offset_y = 0 + + //* Physics - Tracers *// + + /// tracer /datum/point's + var/list/tracer_vertices + /// first point is a muzzle effect + var/tracer_muzzle_flash + /// last point is an impact + var/tracer_impact_effect + /// default tracer duration + var/tracer_duration = 5 + + //* Physics - State *// + + /// paused? if so, we completely get passed over during processing + var/paused = FALSE + /// currently hitscanning + var/hitscanning = FALSE + /// a flag to prevent movement hooks from resetting our physics on a forced movement + var/trajectory_ignore_forcemove = FALSE + /// cached value: move this much x for this much distance + /// basically, dx / distance + var/calculated_dx + /// cached value: move this much y for this much distance + /// basically, dy / distance + var/calculated_dy + /// cached sign of dx; 1, -1, or 0 + var/calculated_sdx + /// cached sign of dy; 1, -1, or 0 + var/calculated_sdy + /// our current pixel location on turf + /// byond pixel_x rounds, and we don't want that + /// + /// * at below 0 or at equals to WORLD_ICON_SIZE, we move to the next turf + var/current_px + /// our current pixel location on turf + /// byond pixel_y rounds, and we don't want that + /// + /// * at below 0 or at equals to WORLD_ICON_SIZE, we move to the next turf + var/current_py + /// the pixel location we're moving to, or the [current_px] after this iteration step + /// + /// * used so stuff like hitscan deflections work based on the actual raycasted collision step, and not the prior step. + /// * only valid if [trajectory_moving_to] is set + var/next_px + /// the pixel location we're moving to, or the [current_px] after this iteration step + /// + /// * used so stuff like hitscan deflections work based on the actual raycasted collision step, and not the prior step. + /// * only valid if [trajectory_moving_to] is set + var/next_py + /// the pixel distance we'll have travelled after the current Move() + /// + /// * use this during impact processing or you'll be off by anywhere from 1 to 32 pixels. + /// * only valid if [trajectory_moving_to] is set + var/next_distance + /// used to track if we got kicked forwards after calling Move() + var/trajectory_kick_forwards = 0 + /// to avoid going too fast when kicked forwards by a mirror, if we overshoot the pixels we're + /// supposed to move this gets set to penalize the next move with a weird algorithm + /// that i won't bother explaining + var/trajectory_penalty_applied = 0 + /// currently travelled distance in pixels + var/distance_travelled + /// if we get forcemoved, this gets reset to 0 as a trip + /// this way, we know exactly how far we moved + var/distance_travelled_this_iteration + /// where the physics loop and/or some other thing moving us is trying to move to + /// used to determine where to draw hitscan tracers + // todo: this being here is kinda a symptom that things are handled weirdly but whatever + // optimally physics loop should handle tracking for stuff like animations, not require on hit processing to check turfs + var/turf/trajectory_moving_to + + //* Targeting *// + + /// Originally clicked target + var/atom/original_target + + //* legacy below *// + + //Fired processing vars + var/fired = FALSE //Have we been fired yet + + var/original_angle = 0 //Angle at firing + var/nondirectional_sprite = FALSE //Set TRUE to prevent projectiles from having their sprites rotated based on firing angle + var/spread = 0 //amount (in degrees) of projectile spread + animate_movement = 0 //Use SLIDE_STEPS in conjunction with legacy + var/ricochets = 0 + var/ricochets_max = 2 + var/ricochet_chance = 30 + + //Hitscan + /// do we have a tracer? if not we completely ignore hitscan logic + var/has_tracer = TRUE + var/tracer_type + var/muzzle_type + var/impact_type + + var/miss_sounds + var/ricochet_sounds + var/list/impact_sounds //for different categories, IMPACT_MEAT etc + + //Fancy hitscan lighting effects! + var/hitscan_light_intensity = 1.5 + var/hitscan_light_range = 0.75 + var/hitscan_light_color_override + var/muzzle_flash_intensity = 3 + var/muzzle_flash_range = 1.5 + var/muzzle_flash_color_override + var/impact_light_intensity = 3 + var/impact_light_range = 2 + var/impact_light_color_override + + //Targetting + var/yo = null + var/xo = null + var/turf/starting = null // the projectile's starting turf + var/p_x = 16 + var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center + + var/def_zone = BP_TORSO + var/mob/firer = null//Who shot it + var/silenced = 0 //Attack message + var/shot_from = "" // name of the object which shot us + + var/dispersion = 0.0 + + // Sub-munitions. Basically, multi-projectile shotgun, rather than pellets. + var/use_submunitions = FALSE + var/only_submunitions = FALSE // Will the projectile delete itself after firing the submunitions? + var/list/submunitions = list() // Assoc list of the paths of any submunitions, and how many they are. [projectilepath] = [projectilecount]. + var/submunition_spread_max = 30 // Divided by 10 to get the percentile dispersion. + var/submunition_spread_min = 5 // Above. + /// randomize spread? if so, evenly space between 0 and max on each side. + var/submunition_constant_spread = FALSE + var/force_max_submunition_spread = FALSE // Do we just force the maximum? + var/spread_submunition_damage = FALSE // Do we assign damage to our sub projectiles based on our main projectile damage? + + //? Damage - default handling + /// damage amount + var/damage = 10 + /// damage tier - goes hand in hand with [damage_armor] + var/damage_tier = BULLET_TIER_DEFAULT + /// todo: legacy - BRUTE, BURN, TOX, OXY, CLONE, HALLOSS, ELECTROCUTE, BIOACID are the only things that should be in here + var/damage_type = BRUTE + /// armor flag for damage - goes hand in hand with [damage_tier] + var/damage_flag = ARMOR_BULLET + /// damage mode - see [code/__DEFINES/combat/damage.dm] + var/damage_mode = NONE + + var/SA_bonus_damage = 0 // Some bullets inflict extra damage on simple animals. + var/SA_vulnerability = null // What kind of simple animal the above bonus damage should be applied to. Set to null to apply to all SAs. + var/nodamage = 0 //Determines if the projectile will skip any damage inflictions + var/taser_effect = 0 //If set then the projectile will apply it's agony damage using stun_effect_act() to mobs it hits, and other damage will be ignored + var/legacy_penetrating = 0 //If greater than zero, the projectile will pass through dense objects as specified by on_penetrate() + //Effects + var/incendiary = 0 //1 for ignite on hit, 2 for trail of fire. 3 maybe later for burst of fire around the impact point. - Mech + var/flammability = 0 //Amount of fire stacks to add for the above. + var/combustion = TRUE //Does this set off flammable objects on fire/hit? + var/stun = 0 + var/weaken = 0 + var/paralyze = 0 + var/irradiate = 0 + var/stutter = 0 + var/eyeblur = 0 + var/drowsy = 0 + var/agony = 0 + var/reflected = 0 // This should be set to 1 if reflected by any means, to prevent infinite reflections. + var/modifier_type_to_apply = null // If set, will apply a modifier to mobs that are hit by this projectile. + var/modifier_duration = null // How long the above modifier should last for. Leave null to be permanent. + var/excavation_amount = 0 // How much, if anything, it drills from a mineral turf. + /// If this projectile is holy. Silver bullets, etc. Currently no effects. + var/holy = FALSE + + // Antimagic + /// Should we check for antimagic effects? + var/antimagic_check = FALSE + /// Antimagic charges to use, if any + var/antimagic_charges_used = 0 + /// Multiplier for damage if antimagic is on the target + var/antimagic_damage_factor = 0 + + var/embed_chance = 0 //Base chance for a projectile to embed + + var/fire_sound = 'sound/weapons/Gunshot_old.ogg' // Can be overriden in gun.dm's fire_sound var. It can also be null but I don't know why you'd ever want to do that. -Ace + + // todo: currently unimplemneted + var/vacuum_traversal = TRUE //Determines if the projectile can exist in vacuum, if false, the projectile will be deleted if it enters vacuum. + + var/no_attack_log = FALSE + +/obj/projectile/Initialize(mapload) + if(islist(base_projectile_effects)) + base_projectile_effects = typelist(NAMEOF(src, base_projectile_effects), base_projectile_effects) + return ..() + +/obj/projectile/Destroy() + // stop processing + STOP_PROCESSING(SSprojectiles, src) + // cleanup + cleanup_hitscan_tracers() + return ..() + +/obj/projectile/proc/process_legacy_penetration(atom/A) + return TRUE + +/obj/projectile/proc/legacy_on_range() //if we want there to be effects when they reach the end of their range + finalize_hitscan_tracers(impact_effect = FALSE, kick_forwards = 8) + + for(var/datum/projectile_effect/effect as anything in base_projectile_effects) + if(effect.hook_lifetime) + effect.on_lifetime(src, impact_ground_on_expiry) + for(var/datum/projectile_effect/effect as anything in additional_projectile_effects) + if(effect.hook_lifetime) + effect.on_lifetime(src, impact_ground_on_expiry) + + if(impact_ground_on_expiry) + impact(loc, PROJECTILE_IMPACT_IS_EXPIRING) + + expire() + +/obj/projectile/proc/fire(set_angle_to, atom/direct_target, no_source_check) + if(only_submunitions) // refactor projectiles whwen holy shit this is awful lmao + // todo: this should make a muzzle flash + qdel(src) + return + + // setup impact checking + impacted = list() + // make sure firer is in it + if(firer && !no_source_check) + impacted[firer] = TRUE + if(ismob(firer)) + var/atom/buckle_iterating = firer.buckled + while(buckle_iterating) + if(impacted[buckle_iterating]) + CRASH("how did we loop in buckle iteration check?") + impacted[buckle_iterating] = TRUE + if(ismob(buckle_iterating)) + var/mob/cast_for_next = buckle_iterating + buckle_iterating = cast_for_next.buckled + else + break + // set angle if needed + if(isnum(set_angle_to)) + set_angle(set_angle_to) + // handle direct hit + if(direct_target) + if(direct_target.bullet_act(src, PROJECTILE_IMPACT_POINT_BLANK, def_zone) & PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH) + impacted[direct_target] = TRUE + else + // todo: this should make a muzzle flash + qdel(src) + return + // setup physics + setup_physics() + + // legacy below + var/turf/starting = get_turf(src) + if(isnull(angle)) //Try to resolve through offsets if there's no angle set. + if(isnull(xo) || isnull(yo)) + stack_trace("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!") + qdel(src) + return + var/turf/target = locate(clamp(starting + xo, 1, world.maxx), clamp(starting + yo, 1, world.maxy), starting.z) + set_angle(get_visual_angle(src, target)) + if(dispersion) + set_angle(angle + rand(-dispersion, dispersion)) + original_angle = angle + forceMove(starting) + fired = TRUE + // legacy aboev + + // start physics & kickstart movement + if(hitscan) + physics_hitscan() + else + START_PROCESSING(SSprojectiles, src) + physics_iteration(WORLD_ICON_SIZE, SSprojectiles.wait) + +//Spread is FORCED! +/obj/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0) + var/turf/curloc = get_turf(source) + var/turf/targloc = get_turf(target) + + if(istype(source, /atom/movable)) + var/atom/movable/MT = source + if(MT.locs && MT.locs.len) // Multi tile! + for(var/turf/T in MT.locs) + if(get_dist(T, target) < get_turf(curloc)) + curloc = get_turf(T) + + trajectory_ignore_forcemove = TRUE + forceMove(get_turf(source)) + trajectory_ignore_forcemove = FALSE + starting = curloc + original_target = target + if(targloc || !params) + yo = targloc.y - curloc.y + xo = targloc.x - curloc.x + set_angle(get_visual_angle(src, targloc) + spread) + + if(isliving(source) && params) + var/list/calculated = calculate_projectile_angle_and_pixel_offsets(source, params) + p_x = calculated[2] + p_y = calculated[3] + + set_angle(calculated[1] + spread) + else if(targloc) + yo = targloc.y - curloc.y + xo = targloc.x - curloc.x + set_angle(get_visual_angle(src, targloc) + spread) + else + stack_trace("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!") + qdel(src) + +/proc/calculate_projectile_angle_and_pixel_offsets(mob/user, params) + var/list/mouse_control = params2list(params) + var/p_x = 0 + var/p_y = 0 + var/angle = 0 + if(mouse_control["icon-x"]) + p_x = text2num(mouse_control["icon-x"]) + if(mouse_control["icon-y"]) + p_y = text2num(mouse_control["icon-y"]) + if(mouse_control["screen-loc"]) + //Split screen-loc up into X+Pixel_X and Y+Pixel_Y + var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",") + + //Split X+Pixel_X up into list(X, Pixel_X) + var/list/screen_loc_X = splittext(screen_loc_params[1],":") + + //Split Y+Pixel_Y up into list(Y, Pixel_Y) + var/list/screen_loc_Y = splittext(screen_loc_params[2],":") + var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32 + var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32 + + //Calculate the "resolution" of screen based on client's view and world's icon size. This will work if the user can view more tiles than average. + var/screenviewX = user.client.current_viewport_width * world.icon_size + var/screenviewY = user.client.current_viewport_height * world.icon_size + + var/ox = round(screenviewX/2) - user.client.pixel_x //"origin" x + var/oy = round(screenviewY/2) - user.client.pixel_y //"origin" y + angle = arctan(y - oy, x - ox) + return list(angle, p_x, p_y) + +/obj/projectile/proc/legacy_redirect(x, y, starting, source) + reflected = TRUE + old_style_target(locate(x, y, z), starting? get_turf(starting) : get_turf(source)) + +/obj/projectile/proc/old_style_target(atom/target, atom/source) + if(!source) + source = get_turf(src) + starting = get_turf(source) + original_target = target + set_angle(get_visual_angle(source, target)) + +/obj/projectile/proc/vol_by_damage() + if(damage) + return clamp((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 + else + return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume. + +//Checks if the projectile is eligible for embedding. Not that it necessarily will. +/obj/projectile/proc/can_embed() + //embed must be enabled and damage type must be brute + if(embed_chance == 0 || damage_type != BRUTE) + return 0 + return 1 + +/obj/projectile/proc/get_structure_damage() + if(damage_type == BRUTE || damage_type == BURN) + return damage + return 0 + +/obj/projectile/proc/check_fire(atom/target as mob, mob/living/user as mob) //Checks if you can hit them or not. + check_trajectory(target, user, pass_flags, atom_flags) + +/** + * i hate everything + * + * todo: refactor guns + * projectiles + * and everything else + * + * i am losing my fucking mind + * this shouldn't have to fucking exist because the ammo casing and/or gun should be doing it + * and submunitions SHOULDNT BE HANDLED HERE!! + */ +/obj/projectile/proc/launch_projectile_common(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) + original_target = target + def_zone = check_zone(target_zone) + firer = user + + if(use_submunitions && submunitions.len) + var/temp_min_spread = 0 + if(force_max_submunition_spread) + temp_min_spread = submunition_spread_max + else + temp_min_spread = submunition_spread_min + + var/damage_override = null + + if(spread_submunition_damage) + damage_override = damage + if(nodamage) + damage_override = 0 + + var/projectile_count = 0 + + for(var/proj in submunitions) + projectile_count += submunitions[proj] + + damage_override = round(damage_override / max(1, projectile_count)) + + for(var/path in submunitions) + var/amt = submunitions[path] + for(var/count in 1 to amt) + var/obj/projectile/SM = new path(get_turf(loc)) + SM.shot_from = shot_from + SM.silenced = silenced + if(!isnull(damage_override)) + SM.damage = damage_override + if(submunition_constant_spread) + SM.dispersion = 0 + var/calculated = angle + round((count / amt - 0.5) * submunition_spread_max, 1) + SM.launch_projectile(target, target_zone, user, params, calculated) + else + SM.dispersion = rand(temp_min_spread, submunition_spread_max) / 10 + SM.launch_projectile(target, target_zone, user, params, angle_override) + +/obj/projectile/proc/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) + var/direct_target + if(get_turf(target) == get_turf(src)) + direct_target = target + + preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread) + launch_projectile_common(target, target_zone, user, params, angle_override, forced_spread) + return fire(angle_override, direct_target) + +//called to launch a projectile from a gun +/obj/projectile/proc/launch_from_gun(atom/target, target_zone, mob/user, params, angle_override, forced_spread, obj/item/gun/launcher) + + shot_from = launcher.name + silenced = launcher.silenced + + return launch_projectile(target, target_zone, user, params, angle_override, forced_spread) + +/obj/projectile/proc/launch_projectile_from_turf(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) + var/direct_target + if(get_turf(target) == get_turf(src)) + direct_target = target + + preparePixelProjectile(target, user? user : get_turf(src), params, forced_spread) + launch_projectile_common(target, target_zone, user, params, angle_override, forced_spread) + return fire(angle_override, direct_target) + +/** + * Standard proc to determine damage when impacting something. This does not affect the special damage variables/effect variables, only damage and damtype. + * May or may not be called before/after armor calculations. + * + * @params + * - target The atom hit + * + * @return Damage to apply to target. + */ +/obj/projectile/proc/run_damage_vulnerability(atom/target) + var/final_damage = damage + if(isliving(target)) + var/mob/living/L = target + if(issimple(target)) + var/mob/living/simple_mob/SM = L + if(SM.mob_class & SA_vulnerability) + final_damage += SA_bonus_damage + if(L.anti_magic_check(TRUE, TRUE, antimagic_charges_used, FALSE)) + final_damage *= antimagic_damage_factor + return final_damage + +/** + * Probably isn't needed but saves me the time and I can regex this later: + * Gets the final `damage` that should be used on something + */ +/obj/projectile/proc/get_final_damage(atom/target) + return run_damage_vulnerability(target) + +// !legacy code above! + +//* Collision Handling *// + +/obj/projectile/CanAllowThrough() + SHOULD_CALL_PARENT(FALSE) + return TRUE + +/obj/projectile/CanPassThrough(atom/blocker, turf/target, blocker_opinion) + // performance + SHOULD_CALL_PARENT(FALSE) + // always can go through already impacted things + if(impacted[blocker]) + return TRUE + return blocker_opinion + +/obj/projectile/Crossed(atom/movable/AM) + ..() + scan_crossed_atom(AM) + +// todo: should we inline this? +/obj/projectile/proc/scan_crossed_atom(atom/movable/target) + if(!should_impact(target)) + return + impact(target) + +/obj/projectile/Bump(atom/A) + . = ..() + impact_loop(get_turf(A), A) + +/obj/projectile/forceMove(atom/target) + var/is_a_jump = isturf(target) != isturf(loc) || target.z != z || !trajectory_ignore_forcemove + if(is_a_jump) + record_hitscan_end() + render_hitscan_tracers() + . = ..() + if(!.) + stack_trace("projectile forcemove failed; please do not try to forcemove projectiles to invalid locations!") + distance_travelled_this_iteration = 0 + if(!trajectory_ignore_forcemove) + reset_physics_to_turf() + if(is_a_jump) + record_hitscan_start() + +/obj/projectile/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + . = ..() + // if we're being yanked, we'll get Moved() again + if(movable_flags & MOVABLE_IN_MOVED_YANK) + return + // not even fired yet + if(!fired) + return + // scan the turf for anything we need to hit + scan_moved_turf(loc) + // trigger effects + for(var/datum/projectile_effect/effect as anything in base_projectile_effects) + if(effect.hook_moved) + effect.on_moved(src, old_loc) + for(var/datum/projectile_effect/effect as anything in additional_projectile_effects) + if(effect.hook_moved) + effect.on_moved(src, old_loc) + +// todo: should we inline this? +/obj/projectile/proc/scan_moved_turf(turf/tile) + if(original_target?.loc != tile) + return + if(!should_impact(original_target)) + return + impact(original_target) + +/** + * checks if we're valid to hit a target + * + * this is called 'should' because it's not called in impact() + * this checks if we should try to impact when processing collisions + * this doesn't actually prevent us from having an impact call + */ +/obj/projectile/proc/should_impact(atom/target, is_colliding_us) + // 1. emulate the usual physics / cross + // remember that impact_loop() scans all atoms, not just the hit one. + if(impacted[target]) + return FALSE + if(QDELETED(target)) + return FALSE + // 1.5: legacy bullshit + if(target.is_incorporeal()) + return FALSE + // 2. are they the thing blocking us? + if(is_colliding_us) + return TRUE + // 3. process projectile things + if(target == original_target) + return TRUE + else if(!target.density || (target.pass_flags_self & pass_flags)) + return FALSE + else if(target.layer < PROJECTILE_HIT_THRESHOLD_LAYER) + return FALSE + return TRUE + +/** + * this strangely named proc is basically the hit processing loop + * + * "now why the hells would you do this" + * + * well, you see, turf movement handling doesn't support what we need to do, + * and for good reason. + * + * most of the time, turf movement handling is more than enough for any game use case. + * it is not nearly accurate/comprehensive enough for projectiles + * and we're not going to make it that, because that's a ton of overhead for everything else + * + * so instead, projectiles handle it themselves on a Bump(). + * + * when this happens, the projectile should hit everything that's going to collide it anyways + * in the turf, not just one thing; this way, hits are instant for a given collision. + * + * @params + * * was_moving_onto - the turf we were moving to + * * bumped - what bumped us? + */ +/obj/projectile/proc/impact_loop(turf/was_moving_onto, atom/bumped) + var/impact_return + // so unfortunately, only one border object is considered here + // why? + // because you see, for speed reasons we're not going to iterate once just to gather border. + // so we assume that 'bumped' is border, or it just doesn't happen. + + // make sure we're not inf looping + ASSERT(!(impacted[bumped])) + // see if we should impact + impact_return = impact(bumped) + if(!(impact_return & PROJECTILE_IMPACT_CONTINUE_LOOP)) + return + + // at this point we're technically safe to just move again because + // we processed bumped + // problem is, that's O(n^2) behavior + // we don't want to process just one bumped atom + // as turf/Enter() will loop through everything again + // + // we want to process all of them. + + // at this point you might ask + // why not use MOVEMENT_UNSTOPPABLE? + // if we did, we wouldn't have the border-prioritization we have + // that'd be bad as you'd be hit by a bullet behind a directional window + + // wow, projectiles are annoying to deal with + + // begin: main loop + + // first, targeted atom + if(original_target?.loc == was_moving_onto) + if(should_impact(original_target)) + impact_return = impact(bumped) + if(!(impact_return & PROJECTILE_IMPACT_CONTINUE_LOOP)) + return + + // then, mobs + for(var/mob/mob_target in was_moving_onto) + if(!should_impact(mob_target)) + continue + impact_return = impact(mob_target) + if(!(impact_return & PROJECTILE_IMPACT_CONTINUE_LOOP)) + return + + // then, objs + for(var/obj/obj_target in was_moving_onto) + if(!should_impact(obj_target)) + continue + impact_return = impact(obj_target) + if(!(impact_return & PROJECTILE_IMPACT_CONTINUE_LOOP)) + return + + // then, the turf + if(should_impact(was_moving_onto)) + impact_return = impact(was_moving_onto) + if(!(impact_return & PROJECTILE_IMPACT_CONTINUE_LOOP)) + return + + // if we passed everything and we're still going, + // we can safely move onto their turf again, and this time we should succeed. + if(trajectory_moving_to) + Move(trajectory_moving_to) + +//* Lifetime & Deletion *// + +// todo: on_lifetime() --> expire() + +/** + * Called to delete if: + * + * * ran out of range + * * hit something and shouldn't pass through + * + * @params + * * impacting - we're deleting from impact, rather than range + */ +/obj/projectile/proc/expire(impacting) + qdel(src) + +//* Impact Processing *// + +/** + * Called to perform an impact + * + * @params + * * target - thing being hit + * * impact_flags - impact flags to feed in + * * def_zone - zone to hit; otherwise this'll be calculated. + * + * @return resultant impact_flags + */ +/obj/projectile/proc/impact(atom/target, impact_flags, def_zone = src.def_zone || BP_TORSO) + SHOULD_NOT_OVERRIDE(TRUE) + + if(impacted[target]) + return impact_flags | PROJECTILE_IMPACT_PASSTHROUGH | PROJECTILE_IMPACT_DUPLICATE | PROJECTILE_IMPACT_CONTINUE_LOOP + impacted[target] = TRUE + var/where_we_were = loc + impact_flags = pre_impact(target, impact_flags, def_zone) + var/keep_going + + // priority 1: delete? + if(impact_flags & PROJECTILE_IMPACT_FLAGS_SHOULD_DELETE) + if(hitscanning) + finalize_hitscan_tracers() + qdel(src) + return impact_flags + // priority 2: should we hit? + if(impact_flags & PROJECTILE_IMPACT_FLAGS_SHOULD_NOT_HIT) + keep_going = TRUE + // phasing? + if(impact_flags & PROJECTILE_IMPACT_PHASE) + impact_flags = on_phase(target, impact_flags, def_zone) + // reflect? + else if(impact_flags & PROJECTILE_IMPACT_REFLECT) + impact_flags = on_reflect(target, impact_flags, def_zone) + // else, is passthrough. do nothing + else + impact_flags = target.bullet_act(src, impact_flags, def_zone, 1) + // did we pierce? + if(impact_flags & PROJECTILE_IMPACT_PIERCE) + keep_going = TRUE + impact_flags = on_pierce(target, impact_flags, def_zone) + // are we otherwise meant to keep going? + else if(impact_flags & PROJECTILE_IMPACT_FLAGS_SHOULD_GO_THROUGH) + keep_going = TRUE + + // did anything triggered up above trigger a delete? + if(impact_flags & PROJECTILE_IMPACT_FLAGS_SHOULD_DELETE) + if(hitscanning) + finalize_hitscan_tracers() + qdel(src) + return impact_flags + + // trigger projectile effects + if(base_projectile_effects || additional_projectile_effects) + // todo: can we move this to on_impact_new and have 'blocked' passed in? hrm. + for(var/datum/projectile_effect/effect as anything in base_projectile_effects) + if(effect.hook_impact) + impact_flags = effect.on_impact(src, target, impact_flags, def_zone) + for(var/datum/projectile_effect/effect as anything in additional_projectile_effects) + if(effect.hook_impact) + impact_flags = effect.on_impact(src, target, impact_flags, def_zone) + // did anything triggered up above trigger a delete? + if(impact_flags & PROJECTILE_IMPACT_FLAGS_SHOULD_DELETE) + if(hitscanning) + finalize_hitscan_tracers() + qdel(src) + return impact_flags + + // see if we should keep going or delete + if(keep_going) + if(loc == where_we_were) + // if we are supposed to keep going and we didn't get yanked, continue the impact loop. + impact_flags |= PROJECTILE_IMPACT_CONTINUE_LOOP + else + // or if we aren't supposed to keep going, delete. + if(hitscanning) + if(trajectory_moving_to) + // create tracers + var/datum/point/visual_impact_point = get_intersection_point(trajectory_moving_to) + // it's possible to not have an intersection point, if say, angle was being messed with mid-move + // this entire system is suboptimal design-wise but atleast it's fast. + if(visual_impact_point) + // kick it forwards a bit + visual_impact_point.shift_in_projectile_angle(angle, 2) + // draw + finalize_hitscan_tracers(visual_impact_point, impact_effect = TRUE) + else + finalize_hitscan_tracers(impact_effect = TRUE, kick_forwards = 32) + else + finalize_hitscan_tracers(impact_effect = TRUE, kick_forwards = 32) + expire(TRUE) + + return impact_flags + +/** + * Called at the start of impact. + * + * * Hooks to return flags / whatnot should happen here + * * You are allowed to edit the projectile here, but it is absolutely not recommended. + * + * @return new impact_flags + */ +/obj/projectile/proc/pre_impact(atom/target, impact_flags, def_zone) + if(target.pass_flags_self & pass_flags_phase) + return impact_flags | PROJECTILE_IMPACT_PHASE + if(target.pass_flags_self & pass_flags_pierce) + return impact_flags | PROJECTILE_IMPACT_PIERCE + return impact_flags + +/** + * Called after bullet_act() of the target. + * + * * Please take into account impact_flags. + * * Most impact flags returned are not re-checked for performance; pierce/phase calculations should be done in pre_impact(). + * * please see [/atom/proc/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args)] + * * Args at this point are no longer mutable after the ..() call. + * + * Things to keep in mind, if you ignore the above and didn't read bullet_act(): + * * Args are changed directly and passed up, but not passed back down. This means setting efficiency at base of /on_impact doesn't change a subtype's call + * * This also means you need to default efficiency to 1 if you have things acting on it, as it won't be propagated for you. + * * 'efficiency' is extremely powerful + * * impact_flags having PROJECTILE_IMPACT_DELETE is a good sign to delete and do nothing else. + * + * todo: add PROJECTILE_IMPACT_DELETE_AFTER as opposed to DELETE? so rest of effects can still run + * + * @return new impact_flags; only PROJECTILE_IMPACT_DELETE is rechecked. + */ +/obj/projectile/proc/on_impact(atom/target, impact_flags, def_zone, efficiency = 1) + //! legacy shit + var/blocked = clamp((1 - efficiency) * 100, 0, 100) + if(damage && damage_type == BURN) + var/turf/T = get_turf(target) + if(T) + T.hotspot_expose(700, 5) + if(isliving(target)) + var/mob/living/L = target + L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, agony, blocked, incendiary, flammability) + if(modifier_type_to_apply) + L.add_modifier(modifier_type_to_apply, modifier_duration) + //! end + return impact_flags + +/** + * called in bullet_act() to redirect our impact to another atom + * + * use like this: + * + * `return proj.impact_redirect(target, args)` + * + * * You **must** use this, not just `return target.bullet_act(arglist(args))` + * * This does book-keeping like adding the target to permutated, ensure the target can't be hit multiple times in a row, and more. + * * Don't be too funny about bullet_act_args, it's a directly passed in args list. Don't be stupid. + */ +/obj/projectile/proc/impact_redirect(atom/target, list/bullet_act_args) + if(impacted[target]) + return bullet_act_args[BULLET_ACT_ARG_FLAGS] | PROJECTILE_IMPACT_DUPLICATE + bullet_act_args[BULLET_ACT_ARG_FLAGS] |= PROJECTILE_IMPACT_INDIRECTED + return target.bullet_act(arglist(bullet_act_args)) + +/** + * phasing through + * + * * Most impact flags returned are not re-checked for performance; pierce/phase calculations should be done in pre_impact(). + * + * @return new impact flags; only PROJETILE_IMPACT_DELETE is rechecked. + */ +/obj/projectile/proc/on_phase(atom/target, impact_flags, def_zone) + return impact_flags + +/** + * reflected off of + * + * * Most impact flags returned are not re-checked for performance; pierce/phase calculations should be done in pre_impact(). + * + * @return new impact flags; only PROJETILE_IMPACT_DELETE is rechecked. + */ +/obj/projectile/proc/on_reflect(atom/target, impact_flags, def_zone) + return impact_flags + +/** + * piercing through + * + * * Most impact flags returned are not re-checked for performance; pierce/phase calculations should be done in pre_impact(). + * + * @return new impact flags; only PROJETILE_IMPACT_DELETE is rechecked. + */ +/obj/projectile/proc/on_pierce(atom/target, impact_flags, def_zone) + return impact_flags + +//* Impact Processing - Combat *// + +/** + * processes default hit probability for baymiss + * + * * This is called by things like /mob/living as needed; there is no default baymiss handling on /projectile anymore. + * * More than 100 target_opinion means that much % more than 100 of *not missing*. + * * e.g. 200 target_opinion makes a 75% inherent hit chance (25% miss chance) to 87.5% hit cahnce (12.5% miss chance) + * + * todo: 0 to 100 for accuracy might not be amazing; maybe allow negative values evasion-style? + * + * @params + * * target - what we're hitting + * * target_opinion - the return from processing hit chance on their side + * * distance - distance in pixels + * * impact_check - are we checking for impact? this way things like pellets can do their own rolls after 100% hitting + * + * @return hit probability as % in [0, 100]; > 100 is allowed. + */ +/obj/projectile/proc/process_accuracy(atom/target, target_opinion = 100, distance, impact_check) + if(isnull(distance)) + distance = (trajectory_moving_to ? next_distance : distance_travelled) * angle_chebyshev_divisor + if(accuracy_disabled) + return 100 + . = 100 + // perform accuracy curving + if(distance > accuracy_perfect_range) + . = accuracy_drop_start + var/extra_distance = distance - accuracy_perfect_range + var/drop_percent = extra_distance * accuracy_drop_slope + . = clamp(. - drop_percent, ., accuracy_drop_end) + if(accuracy_overall_modify != 1) + if(accuracy_overall_modify < 1) + // below 1: multiplier for hit chance + . *= accuracy_overall_modify + else + // above 1: divisor for miss chance + . = 100 - ((100 - .) / accuracy_overall_modify) + if(target_opinion < 100) + . *= (target_opinion / 100) + else if(target_opinion > 100) + . = 100 - ((100 - .) / (target_opinion / 100)) + +/** + * processes zone accuracy + * + * * this is here to override 'special' baymiss, like 'don't even hit this zone' systems. + * + * @params + * * target - what we're hitting + * * target_opinion - the return from processing hit zone on their side + * * distance - distance in pixels + * * impact_check - are we checking for impact? this way things like pellets can do their own processing + */ +/obj/projectile/proc/process_zone_miss(atom/target, target_opinion, distance, impact_check) + return target_opinion + +/** + * Applies the standard damage instance to an entity. + * + * @params + * * target - thing being hit + * * efficiency - 0 to 1+ - efficiency of hit, where 0% is full block + * * impact_flags - impact flags passed in + * * hit_zone - zone to hit + * + * @return BULLET_ACT_* flags to append into the calling bullet_act(). + */ +/obj/projectile/proc/inflict_impact_damage(atom/target, efficiency, impact_flags, hit_zone) + . = NONE + + //! LEGACY COMBAT CODE + // SHIM!!! + var/list/shieldcall_modified_args = target.check_damage_instance(damage, damage_type, damage_tier, damage_flag, damage_mode, ATTACK_TYPE_PROJECTILE, src, SHIELDCALL_FLAG_SECOND_CALL, hit_zone) + // todo: this handling very obviously should not be here + // dear lord this code is a dumpster fire + if(shieldcall_modified_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAGS_PIERCE_ATTACK) + . |= PROJECTILE_IMPACT_REFLECT + if(shieldcall_modified_args[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAGS_BLOCK_ATTACK) + return + // END + if(isliving(target)) + var/mob/living/L = target + //Armor + var/soaked = L.get_armor_soak(hit_zone, src.damage_flag, src.armor_penetration) + var/absorb = L.run_armor_check(hit_zone, src.damage_flag, src.armor_penetration) + var/proj_sharp = is_sharp(src) + var/proj_edge = has_edge(src) + var/final_damage = src.get_final_damage(target) * efficiency + + if ((proj_sharp || proj_edge) && (soaked >= round(src.damage*0.8))) + proj_sharp = 0 + proj_edge = 0 + + if ((proj_sharp || proj_edge) && prob(L.legacy_mob_armor(hit_zone, src.damage_flag))) + proj_sharp = 0 + proj_edge = 0 + + var/list/impact_sounds = islist(src.impact_sounds)? LAZYACCESS(src.impact_sounds, L.get_bullet_impact_effect_type(hit_zone)) : src.impact_sounds + if(length(impact_sounds)) + playsound(L, pick(impact_sounds), 75) + else if(!isnull(impact_sounds)) + playsound(L, impact_sounds, 75) + + //Stun Beams + if(src.taser_effect) + L.stun_effect_act(0, src.agony, hit_zone, src) + to_chat(L, "You have been hit by [src]!") + if(!src.nodamage) + L.apply_damage(final_damage, src.damage_type, hit_zone, absorb, soaked, 0, src, sharp=proj_sharp, edge=proj_edge) + return + + if(!src.nodamage) + L.apply_damage(final_damage, src.damage_type, hit_zone, absorb, soaked, 0, src, sharp=proj_sharp, edge=proj_edge) + //! END + + for(var/datum/projectile_effect/effect as anything in base_projectile_effects) + if(effect.hook_damage) + effect.on_damage(src, target, impact_flags, hit_zone, efficiency) + for(var/datum/projectile_effect/effect as anything in additional_projectile_effects) + if(effect.hook_damage) + effect.on_damage(src, target, impact_flags, hit_zone, efficiency) + + if(legacy_penetrating > 0) + if(process_legacy_penetration(target)) + . |= PROJECTILE_IMPACT_PIERCE | PROJECTILE_IMPACT_PASSTHROUGH + +/** + * wip algorithm to dampen a projectile when it pierces + * + * * entity - thing hit + * * force - nominal force to resist the damping; generally, projectiles at this lose a moderate chunk of energy, while 2x loses minimal, 0.5x loses a lot. + * * tier - effective armor tier of object; modulates actual energy lost + */ +/obj/projectile/proc/dampen_on_pierce_experimental(atom/entity, force, tier) + var/tdiff = damage_tier - tier + var/dmult = src.damage / force + var/malus = dmult >= 1 ? ((1 / dmult) ** tdiff * 10) : (10 * ((1 / dmult) / (1 + tdiff))) + src.damage = clamp(src.damage - malus, src.damage * 0.5, src.damage) + +//* Targeting *// + +/** + * Checks if something is a valid target when directly clicked. + */ +/obj/projectile/proc/is_valid_target(atom/target) + if(isobj(target)) + var/obj/O = target + return O.obj_flags & OBJ_RANGE_TARGETABLE + else if(isliving(target)) + return TRUE + else if(isturf(target)) + return target.density + return FALSE diff --git a/code/modules/projectiles/projectile/projectile_effect.dm b/code/modules/projectiles/projectile/projectile_effect.dm new file mode 100644 index 000000000000..cadfae340b97 --- /dev/null +++ b/code/modules/projectiles/projectile/projectile_effect.dm @@ -0,0 +1,51 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * projectile effects + * + * * can be anonymous type'd, so global cache exists for given projectile typepath, not here! + */ +/datum/projectile_effect + /// has impact effect + var/hook_impact = FALSE + /// has moved effect + var/hook_moved = FALSE + /// has on-range / lifetime expired effect + var/hook_lifetime = FALSE + /// has damage effect + var/hook_damage = FALSE + +/** + * * you'll note that [efficiency] is not a thing here + * * this is because this runs regardless of target's opinion + * * this means you should probably be careful and check impact_flags! + * + * @return new impact flags + */ +/datum/projectile_effect/proc/on_impact(obj/projectile/proj, atom/target, impact_flags, def_zone) + return impact_flags + +/** + * * you'll note that [efficiency] **is** a thing here + * * this only runs if we're able to damage the target + * + * @return new impact flags + */ +/datum/projectile_effect/proc/on_damage(obj/projectile/proj, atom/target, impact_flags, def_zone, efficiency) + return impact_flags + +/** + * Do not delete the projectile, the projectile does that. + * + * todo: add way to delete projectile so it doesn't drop stuff i guess for foam and whatnot + * + * @params + * * proj - the projectile + * * impact_ground_on_expiry - we should impact the ground on expiry; a projectile var, but relay'd in for override capability later + */ +/datum/projectile_effect/proc/on_lifetime(obj/projectile/proj, impact_ground_on_expiry) + return + +/datum/projectile_effect/proc/on_moved(obj/projectile/proj, atom/old_loc) + return diff --git a/code/modules/projectiles/projectile/animate.dm b/code/modules/projectiles/projectile/subtypes/animate.dm similarity index 100% rename from code/modules/projectiles/projectile/animate.dm rename to code/modules/projectiles/projectile/subtypes/animate.dm diff --git a/code/modules/projectiles/projectile/subtypes/arc.dm b/code/modules/projectiles/projectile/subtypes/arc.dm new file mode 100644 index 000000000000..7245b3001119 --- /dev/null +++ b/code/modules/projectiles/projectile/subtypes/arc.dm @@ -0,0 +1,82 @@ + +////////////// +// Subtypes +////////////// + +// This is a test projectile in the sense that its testing the code to make sure it works, +// as opposed to a 'can I hit this thing' projectile. +/obj/projectile/arc/test/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target + new /obj/effect/explosion(T) + T.color = "#FF0000" + +// Generic, Hivebot related +/obj/projectile/arc/blue_energy + name = "energy missile" + icon_state = "force_missile" + damage = 15 + damage_type = BURN + +/obj/projectile/arc/blue_energy/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target + for(var/mob/living/L in T) + impact(L) + +// Fragmentation arc shot +/obj/projectile/arc/fragmentation + name = "fragmentation shot" + icon_state = "shell" + var/list/fragment_types = list( + /obj/projectile/bullet/pellet/fragment, /obj/projectile/bullet/pellet/fragment, \ + /obj/projectile/bullet/pellet/fragment, /obj/projectile/bullet/pellet/fragment/strong + ) + var/fragment_amount = 63 // Same as a grenade. + var/spread_range = 7 + +/obj/projectile/arc/fragmentation/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target + T.shrapnel_explosion(fragment_amount, spread_range, fragment_types, src, name, firer) + +/obj/projectile/arc/fragmentation/mortar + icon_state = "mortar" + fragment_amount = 10 + spread_range = 3 + +// EMP arc shot +/obj/projectile/arc/emp_blast + name = "emp blast" + icon_state = "bluespace" + +/obj/projectile/arc/emp_blast/on_impact(atom/target, impact_flags, def_zone, efficiency) + var/turf/T = target + empulse(T, 2, 4, 7, 10) // Normal EMP grenade. + return impact_flags + +/obj/projectile/arc/emp_blast/weak/on_impact(atom/target, impact_flags, def_zone, efficiency) + var/turf/T = target + empulse(T, 1, 2, 3, 4) // Sec EMP grenade. + return impact_flags + +// Radiation arc shot +/obj/projectile/arc/radioactive + name = "radiation blast" + icon_state = "green_pellet" + icon_scale_x = 2 + icon_scale_y = 2 + var/rad_power = RAD_INTENSITY_PROJ_ARC + +/obj/projectile/arc/radioactive/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = target + radiation_pulse(T, rad_power) diff --git a/code/modules/projectiles/projectile/beam/beams.dm b/code/modules/projectiles/projectile/subtypes/beam/beams.dm similarity index 86% rename from code/modules/projectiles/projectile/beam/beams.dm rename to code/modules/projectiles/projectile/subtypes/beam/beams.dm index 47ed59a70088..617097a40deb 100644 --- a/code/modules/projectiles/projectile/beam/beams.dm +++ b/code/modules/projectiles/projectile/subtypes/beam/beams.dm @@ -6,6 +6,7 @@ damage = 40 damage_type = BURN damage_flag = ARMOR_LASER + projectile_type = PROJECTILE_TYPE_BEAM | PROJECTILE_TYPE_PHOTONIC eyeblur = 4 var/frequency = 1 hitscan = TRUE @@ -76,14 +77,14 @@ /obj/projectile/beam/heavylaser/cannon damage = 80 - armor_penetration = 50 + armor_penetration = 45 light_color = "#FF0D00" /obj/projectile/beam/xray name = "xray beam" icon_state = "xray" fire_sound = 'sound/weapons/eluger.ogg' - damage = 25 + damage = 30 armor_penetration = 50 light_color = "#00CC33" @@ -115,11 +116,24 @@ tracer_type = /obj/effect/projectile/tracer/laser_omni impact_type = /obj/effect/projectile/impact/laser_omni +/obj/projectile/beam/weak + name = "weak cyan beam" + icon_state = "cyan" + fire_sound = 'sound/weapons/Taser.ogg' + damage = 20 + light_color = "#74b1c2" + + muzzle_type = /obj/effect/projectile/muzzle/laser_omni + tracer_type = /obj/effect/projectile/tracer/laser_omni + impact_type = /obj/effect/projectile/impact/laser_omni + + /obj/projectile/beam/emitter name = "emitter beam" icon_state = "emitter" fire_sound = 'sound/weapons/emitter.ogg' - damage = 0 // The actual damage is computed in /code/modules/power/singularity/emitter.dm + damage = 40 + armor_penetration = 70 light_color = "#00CC33" excavation_amount = 70 // 3 shots to mine a turf @@ -131,7 +145,6 @@ name = "lasertag beam" damage = 0 eyeblur = 0 - no_attack_log = 1 damage_type = BURN damage_flag = ARMOR_LASER @@ -145,23 +158,27 @@ tracer_type = /obj/effect/projectile/tracer/laser_blue impact_type = /obj/effect/projectile/impact/laser_blue -/obj/projectile/beam/lasertag/blue/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/lasertag/blue/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ishuman(target)) var/mob/living/carbon/human/M = target if(istype(M.wear_suit, /obj/item/clothing/suit/redtag)) - M.afflict_paralyze(20 * 5) - return 1 + M.afflict_paralyze(1.5 SECONDS) /obj/projectile/beam/lasertag/red icon_state = "laser" light_color = "#FF0D00" -/obj/projectile/beam/lasertag/red/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/lasertag/red/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ishuman(target)) var/mob/living/carbon/human/M = target if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag)) - M.afflict_paralyze(20 * 5) - return 1 + M.afflict_paralyze(1.5 SECONDS) /obj/projectile/beam/lasertag/omni//A laser tag bolt that stuns EVERYONE icon_state = "omnilaser" @@ -171,12 +188,14 @@ tracer_type = /obj/effect/projectile/tracer/laser_omni impact_type = /obj/effect/projectile/impact/laser_omni -/obj/projectile/beam/lasertag/omni/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/lasertag/omni/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ishuman(target)) var/mob/living/carbon/human/M = target if((istype(M.wear_suit, /obj/item/clothing/suit/bluetag))||(istype(M.wear_suit, /obj/item/clothing/suit/redtag))) - M.afflict_paralyze(20 * 5) - return 1 + M.afflict_paralyze(1.5 SECONDS) /obj/projectile/beam/sniper name = "sniper beam" @@ -255,18 +274,18 @@ tracer_type = /obj/effect/projectile/tracer/laser_omni impact_type = /obj/effect/projectile/impact/laser_omni -/obj/projectile/beam/stun/disabler/on_hit(atom/target, blocked = 0, def_zone) - . = ..(target, blocked, def_zone) - - if(. && istype(target, /mob/living/silicon/robot) && prob(agony)) +/obj/projectile/beam/stun/disabler/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT)) + return + if(istype(target, /mob/living/silicon/robot) && prob(agony)) var/mob/living/silicon/robot/R = target var/drainamt = agony * (rand(5, 15) / 10) // 100 to 300 drain R.drain_energy(DYNAMIC_CELL_UNITS_TO_KJ(drainamt * 10)) if(istype(firer, /mob/living/silicon/robot)) // Mischevious sappers, the swarm drones are. var/mob/living/silicon/robot/A = firer - if(A.cell) - A.cell.give(drainamt * 2) + A.cell?.give(drainamt * 2) /obj/projectile/beam/shock name = "shock beam" diff --git a/code/modules/projectiles/projectile/beam/beams_vr.dm b/code/modules/projectiles/projectile/subtypes/beam/beams_vr.dm similarity index 84% rename from code/modules/projectiles/projectile/beam/beams_vr.dm rename to code/modules/projectiles/projectile/subtypes/beam/beams_vr.dm index 7e2e9e04484c..7e21f84b61cc 100644 --- a/code/modules/projectiles/projectile/beam/beams_vr.dm +++ b/code/modules/projectiles/projectile/subtypes/beam/beams_vr.dm @@ -26,9 +26,13 @@ tracer_type = /obj/effect/projectile/tracer/xray impact_type = /obj/effect/projectile/impact/xray -/obj/projectile/beam/energy_net/on_hit(var/atom/netted) - do_net(netted) - ..() +/obj/projectile/beam/energy_net/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(!ismob(target)) + return + do_net(target) /obj/projectile/beam/energy_net/proc/do_net(var/mob/M) var/obj/item/energy_net/net = new (get_turf(M)) @@ -47,7 +51,6 @@ icon_state = "healbeam" damage = 0 //stops it damaging walls nodamage = TRUE - no_attack_log = TRUE damage_type = BURN damage_flag = ARMOR_LASER light_color = "#80F5FF" @@ -58,7 +61,11 @@ tracer_type = /obj/effect/projectile/tracer/medigun impact_type = /obj/effect/projectile/impact/medigun -/obj/projectile/beam/medigun/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/medigun/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(istype(target, /mob/living/carbon/human)) var/mob/living/carbon/human/M = target if(M.health < M.maxHealth) @@ -74,4 +81,3 @@ M.adjustFireLoss(-15) M.adjustToxLoss(-5) M.adjustOxyLoss(-5) - return 1 diff --git a/code/modules/projectiles/projectile/beam/blaster.dm b/code/modules/projectiles/projectile/subtypes/beam/blaster.dm similarity index 100% rename from code/modules/projectiles/projectile/beam/blaster.dm rename to code/modules/projectiles/projectile/subtypes/beam/blaster.dm diff --git a/code/modules/projectiles/projectile/blob.dm b/code/modules/projectiles/projectile/subtypes/blob.dm similarity index 92% rename from code/modules/projectiles/projectile/blob.dm rename to code/modules/projectiles/projectile/subtypes/blob.dm index 6b643c160f2b..cbb0fae06578 100644 --- a/code/modules/projectiles/projectile/blob.dm +++ b/code/modules/projectiles/projectile/subtypes/blob.dm @@ -25,7 +25,11 @@ reagents = null ..() -/obj/projectile/energy/blob/on_impact(var/atom/A) +/obj/projectile/energy/blob/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(splatter) var/turf/location = get_turf(src) var/datum/effect_system/smoke_spread/chem/S = new /datum/effect_system/smoke_spread/chem @@ -34,7 +38,6 @@ playsound(location, 'sound/effects/slime_squish.ogg', 30, 1, -3) spawn(0) S.start() - ..() /obj/projectile/energy/blob/proc/ready_chemicals() if(reagents) diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/subtypes/bullets.dm similarity index 84% rename from code/modules/projectiles/projectile/bullets.dm rename to code/modules/projectiles/projectile/subtypes/bullets.dm index c6d6b9ef8960..11458f9cd56a 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/subtypes/bullets.dm @@ -8,7 +8,7 @@ damage_flag = ARMOR_BULLET embed_chance = 20 //Modified in the actual embed process, but this should keep embed chance about the same sharp = 1 - var/mob_passthrough_check = 0 + projectile_type = PROJECTILE_TYPE_KINETIC muzzle_type = /obj/effect/projectile/muzzle/bullet miss_sounds = list('sound/weapons/guns/miss1.ogg','sound/weapons/guns/miss2.ogg','sound/weapons/guns/miss3.ogg','sound/weapons/guns/miss4.ogg') @@ -16,37 +16,16 @@ 'sound/weapons/guns/ricochet3.ogg', 'sound/weapons/guns/ricochet4.ogg') impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_BULLET_MEAT, BULLET_IMPACT_METAL = SOUNDS_BULLET_METAL) -/obj/projectile/bullet/on_hit(var/atom/target, var/blocked = 0) - if (..(target, blocked)) - var/mob/living/L = target - shake_camera(L, 3, 2) - -/obj/projectile/bullet/projectile_attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier) - if(penetrating > 0 && damage > 20 && prob(damage)) - mob_passthrough_check = 1 - else - mob_passthrough_check = 0 - return ..() - -/obj/projectile/bullet/can_embed() - //prevent embedding if the projectile is passing through the mob - if(mob_passthrough_check) - return 0 - return ..() - -/obj/projectile/bullet/check_penetrate(var/atom/A) - if(!A || !A.density) return 1 //if whatever it was got destroyed when we hit it, then I guess we can just keep going - - if(istype(A, /obj/vehicle/sealed/mecha)) - return 1 //mecha have their own penetration handling - - if(ismob(A)) - if(!mob_passthrough_check) - return 0 - if(iscarbon(A)) - damage *= 0.7 //squishy mobs absorb KE - return 1 +/obj/projectile/bullet/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + var/mob/living/L = target + if(!istype(L)) + return + shake_camera(L, 3, 2) +/obj/projectile/bullet/process_legacy_penetration(atom/A) var/chance = damage if(istype(A, /turf/simulated/wall)) var/turf/simulated/wall/W = A @@ -57,14 +36,12 @@ if(D.glass) chance *= 2 else if(istype(A, /obj/structure/girder)) chance = 100 + else if(ismob(A)) + chance = damage >= 20 && prob(damage) - if(prob(chance)) - if(A.opacity) - //display a message so that people on the other side aren't so confused - A.visible_message("\The [src] pierces through \the [A]!") - return 1 - - return 0 + . = prob(chance) + if(.) + damage *= 0.7 /* short-casing projectiles, like the kind used in pistols or SMGs */ @@ -128,7 +105,7 @@ damage = 10 // high rof kinda fucked up lets be real agony = 10 // brute easily heals, agony not so much armor_penetration = 30 // reduces shield blockchance - accuracy = -20 // he do miss actually + accuracy_overall_modify = 0.8 // heehoo speed = 25 * WORLD_ICON_SIZE /obj/projectile/bullet/pistol/medium/ap/suppressor/turbo // spicy boys @@ -193,20 +170,15 @@ fire_sound = 'sound/weapons/weaponsounds_shotgunshot.ogg' damage = 13 pellets = 6 - range_step = 1 - spread_step = 10 + pellet_loss = 0.66 / WORLD_ICON_SIZE /obj/projectile/bullet/pellet/shotgun_improvised name = "shrapnel" - damage = 1 + damage = 4 pellets = 10 - range_step = 1 - spread_step = 10 /obj/projectile/bullet/pellet/shotgun/flak damage = 2 //The main weapon using these fires four at a time, usually with different destinations. Usually. - range_step = 2 - spread_step = 30 armor_penetration = 10 // This is my boomstick, @@ -218,8 +190,6 @@ SA_vulnerability = MOB_CLASS_DEMONIC | MOB_CLASS_ABERRATION embed_chance = -1 pellets = 6 - range_step = 1 - spread_step = 20 holy = TRUE /obj/projectile/bullet/shotgun/stake @@ -242,27 +212,31 @@ combustion = FALSE -/obj/projectile/bullet/shotgun/ion/on_hit(var/atom/target, var/blocked = 0) - ..() +/obj/projectile/bullet/shotgun/ion/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return empulse(target, 0, 0, 2, 0) //Only affects what it hits - return 1 + . |= PROJECTILE_IMPACT_DELETE //Frag shot /obj/projectile/bullet/shotgun/frag12 name ="frag12 slug" damage = 25 -/obj/projectile/bullet/shotgun/frag12/on_hit(atom/target, blocked = FALSE) - ..() +/obj/projectile/bullet/shotgun/frag12/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return explosion(target, -1, 0, 1) - return 1 + . |= PROJECTILE_IMPACT_DELETE /* "Rifle" rounds */ /obj/projectile/bullet/rifle fire_sound = 'sound/weapons/Gunshot_generic_rifle.ogg' armor_penetration = 15 - penetrating = 1 + legacy_penetrating = 1 /obj/projectile/bullet/rifle/a762 fire_sound = 'sound/weapons/weaponsounds_heavyrifleshot.ogg' @@ -286,7 +260,7 @@ /obj/projectile/bullet/rifle/a762/hp damage = 40 armor_penetration = -50 - penetrating = 0 + legacy_penetrating = 0 /obj/projectile/bullet/rifle/a762/hunter // Optimized for killing simple animals and not people, because Balance(tm) damage = 25 @@ -318,22 +292,28 @@ /obj/projectile/bullet/rifle/a556/hp damage = 35 armor_penetration = -50 - penetrating = 0 + legacy_penetrating = 0 /obj/projectile/bullet/rifle/a556/hunter damage = 15 SA_bonus_damage = 35 // 50 total on animals. SA_vulnerability = MOB_CLASS_ANIMAL -/obj/projectile/bullet/rifle/a12_7mm // 14.5×114mm is bigger than a .50 BMG round. +/obj/projectile/bullet/rifle/a12_7mm fire_sound = 'sound/weapons/Gunshot_cannon.ogg' // This is literally an anti-tank rifle caliber. It better sound like a fucking cannon. damage = 80 stun = 3 weaken = 3 - penetrating = 5 + legacy_penetrating = 5 armor_penetration = 80 hitscan = 1 //so the PTR isn't useless as a sniper weapon +/obj/projectile/bullet/mecha/a12mm_gauss //Mecha gauss rifle round. + fire_sound = 'sound/weapons/Gunshot_cannon.ogg' // This is literally an anti-tank rifle caliber. It better sound like a fucking cannon. + damage = 60 + legacy_penetrating = 1 + armor_penetration = 60 + /* Miscellaneous */ /obj/projectile/bullet/suffocationbullet//How does this even work? @@ -361,10 +341,12 @@ embed_chance = 0 edge = 1 -/obj/projectile/bullet/burstbullet/on_hit(var/atom/target, var/blocked = 0) - if(isturf(target)) - explosion(target, -1, 0, 2) - ..() +/obj/projectile/bullet/burstbullet/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + explosion(target, -1, 0, 2) + . |= PROJECTILE_IMPACT_DELETE /obj/projectile/bullet/burstbullet/service name = "charge bullet" @@ -376,10 +358,13 @@ SA_vulnerability = MOB_CLASS_DEMONIC | MOB_CLASS_ABERRATION holy = TRUE -/obj/projectile/bullet/burstbullet/service/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/bullet/burstbullet/service/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isturf(target)) explosion(target, 0, 1, 2) - ..() + return . | PROJECTILE_IMPACT_DELETE /* Black Powder */ @@ -399,8 +384,7 @@ /obj/projectile/bullet/pellet/blunderbuss //More Damage at close range greater falloff damage = 10 pellets = 8 - range_step = 0.5 //Very quick falloff - spread_step = 30 + pellet_loss = 1.5 / WORLD_ICON_SIZE /obj/projectile/bullet/pellet/blunderbuss/silver damage = 5 @@ -418,8 +402,6 @@ /obj/projectile/bullet/pellet/heavy_shotgun //I want this to use similar calcuations to blunderbuss shot for falloff. damage = 3 //Fires five pellets at a time. - range_step = 0.75 - spread_step = 30 armor_penetration = 10 /obj/projectile/bullet/pellet/heavy_shotgun/silver @@ -432,32 +414,33 @@ /obj/projectile/bullet/heavy_shotgun/grit name = "custom heavy slug" -/obj/projectile/bullet/heavy_shotgun/grit/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/bullet/heavy_shotgun/grit/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) var/mob/living/L = target var/throwdir = get_dir(firer,L) - if(prob(10) && !blocked) + if(prob(10) && (efficiency < 0.95)) L.afflict_stun(20 * 1) L.Confuse(1) L.throw_at_old(get_edge_target_turf(L, throwdir), rand(3,6), 10) - return 1 - /obj/projectile/bullet/pellet/heavy_shotgun/grit name = "heavy buckshot" - range_step = 1 -/obj/projectile/bullet/pellet/heavy_shotgun/grit/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/bullet/pellet/heavy_shotgun/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) var/mob/living/L = target var/throwdir = get_dir(firer,L) - if(prob(10) && !blocked) - L.afflict_stun(20 * 1) + if(prob(10) && (efficiency >= 0.9)) + L.afflict_stun(2 SECONDS) L.Confuse(1) L.throw_at_old(get_edge_target_turf(L, throwdir), rand(3,6), 10) - return 1 - /* Incendiary */ /obj/projectile/bullet/incendiary @@ -503,10 +486,14 @@ incendiary = 1 flammability = 4 armor_penetration = 40 - penetrating = 5 + legacy_penetrating = 5 combustion = TRUE -/obj/projectile/bullet/incendiary/caseless/on_hit(var/atom/movable/target, var/blocked = 0) +/obj/projectile/bullet/incendiary/caseless/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + // todo: burn this to the ground if(isliving(target)) var/mob/living/L = target L.adjustFireLoss(10) @@ -519,7 +506,7 @@ damage_type = BRUTE incendiary = 1 flammability = 4 - penetrating = 1 + legacy_penetrating = 1 combustion = TRUE diff --git a/code/modules/projectiles/projectile/bullets_vr.dm b/code/modules/projectiles/projectile/subtypes/bullets_vr.dm similarity index 100% rename from code/modules/projectiles/projectile/bullets_vr.dm rename to code/modules/projectiles/projectile/subtypes/bullets_vr.dm diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/subtypes/change.dm similarity index 93% rename from code/modules/projectiles/projectile/change.dm rename to code/modules/projectiles/projectile/subtypes/change.dm index a8a60f70ed98..187c92e96e7a 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/subtypes/change.dm @@ -8,8 +8,11 @@ combustion = FALSE -/obj/projectile/change/on_hit(var/atom/change) - wabbajack(change) +/obj/projectile/change/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + wabbajack(target) /obj/projectile/change/proc/wabbajack(var/mob/M) if(istype(M, /mob/living) && M.stat != DEAD) diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/subtypes/energy.dm similarity index 93% rename from code/modules/projectiles/projectile/energy.dm rename to code/modules/projectiles/projectile/subtypes/energy.dm index cc3b78328ffc..48717e3024a0 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/subtypes/energy.dm @@ -4,6 +4,7 @@ damage = 0 damage_type = BURN damage_flag = ARMOR_ENERGY + projectile_type = PROJECTILE_TYPE_ENERGY var/flash_strength = 10 //releases a burst of light on impact or after travelling a distance @@ -17,10 +18,11 @@ var/brightness = 7 var/light_colour = "#ffffff" -/obj/projectile/energy/flash/on_impact(var/atom/A) - var/turf/T = flash_range? src.loc : get_turf(A) - if(!istype(T)) return - +/obj/projectile/energy/flash/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(!isturf(target)) + return + var/turf/T = get_turf(target) //blind adjacent people for (var/mob/living/carbon/M in viewers(T, flash_range)) if(M.eyecheck() < 1) @@ -56,11 +58,9 @@ incendiary = 1 flammability = 2 -/obj/projectile/energy/flash/flare/on_impact(var/atom/A) +/obj/projectile/energy/flash/flare/on_impact(atom/target, impact_flags, def_zone, efficiency) light_colour = pick("#e58775", "#ffffff", "#90ff90", "#a09030") - - ..() //initial flash - + . = ..() //residual illumination new /obj/effect/particle_effect/smoke/illumination(src.loc, rand(190,240) SECONDS, 8, 3, light_colour) //same lighting power as flare @@ -206,9 +206,10 @@ to_chat(M, "Your ears start to ring!") M.update_icons() //Just to apply matrix transform for laying asap -/obj/projectile/energy/plasmastun/on_hit(var/atom/target) - bang(target) +/obj/projectile/energy/plasmastun/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + bang(target) + return . | PROJECTILE_IMPACT_DELETE /obj/projectile/energy/blue_pellet name = "suppressive pellet" diff --git a/code/modules/projectiles/projectile/energy_vr.dm b/code/modules/projectiles/projectile/subtypes/energy_vr.dm similarity index 100% rename from code/modules/projectiles/projectile/energy_vr.dm rename to code/modules/projectiles/projectile/subtypes/energy_vr.dm diff --git a/code/modules/projectiles/projectile/explosive.dm b/code/modules/projectiles/projectile/subtypes/explosive.dm similarity index 66% rename from code/modules/projectiles/projectile/explosive.dm rename to code/modules/projectiles/projectile/subtypes/explosive.dm index 05d9d1786811..8139b108fe42 100644 --- a/code/modules/projectiles/projectile/explosive.dm +++ b/code/modules/projectiles/projectile/subtypes/explosive.dm @@ -8,26 +8,16 @@ damage = 30 //Meaty whack. *Chuckles* movable_flags = MOVABLE_NO_THROW_SPIN | MOVABLE_NO_THROW_DAMAGE_SCALING | MOVABLE_NO_THROW_SPEED_SCALING -/obj/projectile/bullet/srmrocket/on_hit(atom/target, blocked=0) - ..() +/obj/projectile/bullet/srmrocket/on_impact(atom/target, impact_flags, def_zone, efficiency) if(!isliving(target)) //if the target isn't alive, so is a wall or something explosion(target, 0, 1, 2, 4) else explosion(target, 0, 0, 2, 4) - return 1 - + return PROJECTILE_IMPACT_DELETE /obj/projectile/bullet/srmrocket/weak //Used in the jury rigged one. damage = 10 -/obj/projectile/bullet/srmrocket/weak/on_hit(atom/target, blocked=0) - ..() +/obj/projectile/bullet/srmrocket/weak/on_impact(atom/target, impact_flags, def_zone, efficiency) explosion(target, 0, 0, 2, 4)//No need to have a question. - return 1 - -/*Old vars here for reference. - var/devastation = 0 - var/heavy_blast = 1 - var/light_blast = 2 - var/flash_blast = 4 -*/ + return PROJECTILE_IMPACT_DELETE diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/subtypes/force.dm similarity index 70% rename from code/modules/projectiles/projectile/force.dm rename to code/modules/projectiles/projectile/subtypes/force.dm index a95cefff5633..05e11a848f6f 100644 --- a/code/modules/projectiles/projectile/force.dm +++ b/code/modules/projectiles/projectile/subtypes/force.dm @@ -10,11 +10,12 @@ /obj/projectile/forcebolt/strong name = "force bolt" -/obj/projectile/forcebolt/on_hit(var/atom/movable/target, var/blocked = 0) - if(istype(target)) +/obj/projectile/forcebolt/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(ismovable(target)) + var/atom/movable/movable_target = target var/throwdir = get_dir(firer,target) - target.throw_at_old(get_edge_target_turf(target, throwdir),10,10) - return 1 + movable_target.throw_at_old(get_edge_target_turf(target, throwdir),10,10) /* /obj/projectile/forcebolt/strong/on_hit(var/atom/target, var/blocked = 0) diff --git a/code/modules/projectiles/projectile/hook.dm b/code/modules/projectiles/projectile/subtypes/hook.dm similarity index 95% rename from code/modules/projectiles/projectile/hook.dm rename to code/modules/projectiles/projectile/subtypes/hook.dm index 797063ec8ab8..bcf0837c6888 100644 --- a/code/modules/projectiles/projectile/hook.dm +++ b/code/modules/projectiles/projectile/subtypes/hook.dm @@ -58,12 +58,11 @@ ..() // Does the regular launching stuff. -/obj/projectile/energy/hook/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) - if(..()) - perform_intent_unique(target) - -/obj/projectile/energy/hook/on_impact(var/atom/A) - perform_intent_unique(get_turf(A)) +/obj/projectile/energy/hook/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + perform_intent_unique(target) /obj/projectile/energy/hook/proc/ranged_disarm(var/mob/living/carbon/human/H) if(istype(H)) @@ -116,8 +115,8 @@ else if(firer) var/obj/T - if(original in target.contents && istype(original, /obj)) - T = original + if(original_target.loc == target && istype(original_target, /obj)) + T = original_target var/list/possible_targets = list() for(var/obj/item/I in target.contents) diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/subtypes/magic.dm similarity index 94% rename from code/modules/projectiles/projectile/magic.dm rename to code/modules/projectiles/projectile/subtypes/magic.dm index 507121f0248b..7a1ae4faa0dd 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/subtypes/magic.dm @@ -36,8 +36,8 @@ /obj/item/ammo_casing/magic/honk projectile_type = /obj/projectile/bullet/honker -/obj/item/ammo_casing/magic/locker - projectile_type = /obj/projectile/magic/locker +// /obj/item/ammo_casing/magic/locker + // projectile_type = /obj/projectile/magic/locker //Spell book ammo casing /obj/item/ammo_casing/magic/book diff --git a/code/modules/projectiles/projectile/magnetic.dm b/code/modules/projectiles/projectile/subtypes/magnetic.dm similarity index 72% rename from code/modules/projectiles/projectile/magnetic.dm rename to code/modules/projectiles/projectile/subtypes/magnetic.dm index 1705a9c1c65b..0133907721bf 100644 --- a/code/modules/projectiles/projectile/magnetic.dm +++ b/code/modules/projectiles/projectile/subtypes/magnetic.dm @@ -6,7 +6,7 @@ damage = 65 stun = 1 weaken = 1 - penetrating = 5 + legacy_penetrating = 5 armor_penetration = 70 /obj/projectile/bullet/magnetic/slug @@ -53,7 +53,7 @@ agony = 50 incendiary = 1 flammability = 0 //Deuterium and Tritium are both held in water, but the object moving so quickly will ignite the target. - penetrating = 2 + legacy_penetrating = 2 embed_chance = 0 armor_penetration = 40 range = WORLD_ICON_SIZE * 20 @@ -63,7 +63,13 @@ var/detonate_mob = 0 //Will this fuelrod explode when it hits a mob? var/energetic_impact = 0 //Does this fuelrod cause a bright flash on impact with a mob? -/obj/projectile/bullet/magnetic/fuelrod/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) //Future-proofing. Special effects for impact. +/obj/projectile/bullet/magnetic/fuelrod/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(searing) + efficiency = max(efficiency, 1) + if(. & PROJECTILE_IMPACT_BLOCKED) + return + if(istype(target,/mob/living)) var/mob/living/V = target if(detonate_mob) @@ -81,28 +87,21 @@ M.afflict_stun(20 * 2) M.afflict_paralyze(20 * 10) - if(searing) - if(blocked) - blocked = 0 - - return ..(target, blocked, def_zone) - -/obj/projectile/bullet/magnetic/fuelrod/on_impact(var/atom/A) //Future-proofing, again. In the event new fuel rods are introduced, and have special effects for when they stop flying. + // what the fuck?? if(src.loc) if(detonate_travel && detonate_mob) visible_message("\The [src] shatters in a violent explosion!") - explosion(src.loc, 1, 1, 3, 4) + explosion(src.loc, 0, 1, 3, 4) else if(detonate_travel) visible_message("\The [src] explodes in a shower of embers!") - explosion(src.loc, -1, 1, 2, 3) - ..(A) + explosion(src.loc, 0, 1, 2, 3) /obj/projectile/bullet/magnetic/fuelrod/tritium icon_state = "fuel-tritium" damage = 100 //Much harder to get than tritium - needs mhydrogen flammability = -1 armor_penetration = 50 - penetrating = 3 + legacy_penetrating = 3 /obj/projectile/bullet/magnetic/fuelrod/phoron name = "blazing fuel rod" @@ -111,7 +110,7 @@ incendiary = 2 flammability = 2 armor_penetration = 60 - penetrating = 5 + legacy_penetrating = 5 irradiate = 20 detonate_mob = 1 @@ -123,7 +122,7 @@ flammability = 4 weaken = 2 armor_penetration = 100 - penetrating = 100 //Theoretically, this shouldn't stop flying for a while, unless someone lines it up with a wall or fires it into a mountain. + legacy_penetrating = 100 //Theoretically, this shouldn't stop flying for a while, unless someone lines it up with a wall or fires it into a mountain. irradiate = 120 range = WORLD_ICON_SIZE * 75 searing = 1 @@ -131,14 +130,16 @@ detonate_mob = 1 energetic_impact = 1 -/obj/projectile/bullet/magnetic/fuelrod/supermatter/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) //You cannot touch the supermatter without disentigrating. Assumedly, this is true for condensed rods of it flying at relativistic speeds. +/obj/projectile/bullet/magnetic/fuelrod/supermatter/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(istype(target,/turf/simulated/wall) || istype(target,/mob/living)) target.visible_message("The [src] burns a perfect hole through \the [target] with a blinding flash!") playsound(target.loc, 'sound/effects/teleport.ogg', 40, 0) - return ..(target, blocked, def_zone) -/obj/projectile/bullet/magnetic/fuelrod/supermatter/check_penetrate() - return 1 +/obj/projectile/bullet/magnetic/fuelrod/supermatter/pre_impact(atom/target, impact_flags, def_zone) + return ..() | PROJECTILE_IMPACT_PIERCE /obj/projectile/bullet/magnetic/bore name = "phorogenic blast" @@ -146,24 +147,26 @@ damage = 20 incendiary = 1 armor_penetration = 20 - penetrating = 0 + legacy_penetrating = 0 damage_flag = ARMOR_MELEE irradiate = 20 range = WORLD_ICON_SIZE * 6 -/obj/projectile/bullet/magnetic/bore/Bump(atom/A, forced=0) - if(istype(A, /turf/simulated/mineral)) - var/turf/simulated/mineral/MI = A - loc = get_turf(A) // Careful. - permutated.Add(A) +/obj/projectile/bullet/magnetic/bore/pre_impact(atom/target, impact_flags, def_zone) + if(istype(target, /turf/simulated/mineral)) + return PROJECTILE_IMPACT_PIERCE | impact_flags + return ..() + +/obj/projectile/bullet/magnetic/bore/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(istype(target, /turf/simulated/mineral)) + var/turf/simulated/mineral/MI = target MI.GetDrilled(TRUE) - return 0 - else if(istype(A, /turf/simulated/wall) || istype(A, /turf/simulated/shuttle/wall)) // Cause a loud, but relatively minor explosion on the wall it hits. - explosion(A, -1, -1, 1, 3) - qdel(src) - return 1 - else - ..() + else if(istype(target, /turf/simulated/wall) || istype(target, /turf/simulated/shuttle/wall)) + explosion(target, 0, 0, 1, 3) + . |= PROJECTILE_IMPACT_DELETE /obj/projectile/bullet/magnetic/bore/powerful name = "energetic phorogenic blast" @@ -171,7 +174,7 @@ damage = 30 incendiary = 2 armor_penetration = 20 - penetrating = 0 + legacy_penetrating = 0 damage_flag = ARMOR_MELEE irradiate = 20 range = WORLD_ICON_SIZE * 12 diff --git a/code/modules/projectiles/projectile/subtypes/pellet.dm b/code/modules/projectiles/projectile/subtypes/pellet.dm new file mode 100644 index 000000000000..cd9891e7f3cc --- /dev/null +++ b/code/modules/projectiles/projectile/subtypes/pellet.dm @@ -0,0 +1,153 @@ +// For projectiles that actually represent clouds of projectiles +// todo: message handling for this type is all over the place +// todo: target still makes message of being hit even if they weren't +// todo: just handle message in this file lmao +/obj/projectile/bullet/pellet + name = "shrapnel" //'shrapnel' sounds more dangerous (i.e. cooler) than 'pellet' + + damage = 20 + + /// number of pelelts + var/pellets = 4 + /// distance before pellets start falling off + var/pellet_loss_start = WORLD_ICON_SIZE * 2 + /// pellets lost per pixel moved + var/pellet_loss = 0.5 / WORLD_ICON_SIZE + /// last distance travelled + var/pellet_loss_last_distance = 0 + /// base spread chance to not hit center mass / the target limb + /// + /// * in 0 to 100 prob() value + var/pellet_zone_spread = 10 + /// min distance before spread + var/pellet_zone_spread_gain_threshold = 2 * WORLD_ICON_SIZE + /// spread chance per pixel to not hit center mass / target limb + /// + /// * in 0 to 100 prob() value + var/pellet_zone_spread_gain = 9 / WORLD_ICON_SIZE // complete spread after 2+10 tiles + +/obj/projectile/bullet/pellet/scan_moved_turf(turf/tile) + ..() + if(QDELETED(src)) + return + for(var/mob/victim in tile) + if(victim.atom_flags & (ATOM_NONWORLD | ATOM_ABSTRACT)) + continue + if(impacted[victim]) + continue + impact(victim) + +/obj/projectile/bullet/pellet/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + . = ..() + var/travelled = distance_travelled - pellet_loss_last_distance + pellet_loss_last_distance = distance_travelled + if(pellet_loss_start > 0) + var/reduction = min(pellet_loss_start, travelled) + travelled -= reduction + pellet_loss_start -= reduction + if(travelled <= 0) + return + pellets -= pellet_loss * travelled + +/obj/projectile/bullet/pellet/process_accuracy(atom/target, target_opinion, distance, impact_check) + if(impact_check) + return 100 + return ..() + +/obj/projectile/bullet/pellet/process_zone_miss(atom/target, target_opinion, distance, impact_check) + if(impact_check) + // if being hit, always direct to our def_zone + return def_zone + return ..() + +// todo: "you are hit by [number of] pellets in the chest" support. +/obj/projectile/bullet/pellet/inflict_impact_damage(atom/target, efficiency, impact_flags, hit_zone) + /** + * What's going on here: + * + * It's too expensive to simulate too many bullet_act()'s for no reason, but pellets + * aren't meant to hit one body part or even do one instance to every body part hit. + * + * Thus, we force process_damage_instance to do your dirty work as it's cheaper to run + * lower level shieldcalls/armorcalls than to simulate high level bullet hits. + */ + var/original_hit_zone = hit_zone + var/distance_penalty = distance_travelled > pellet_zone_spread_gain_threshold ? (distance_travelled * pellet_zone_spread_gain) : 0 + var/zone_true_chance = 100 - (pellet_zone_spread + distance_penalty) + var/pellet_hit_chance = 100 + . = NONE + if(isliving(target)) + var/mob/living/living_target = target + pellet_hit_chance = living_target.process_baymiss(src, impact_check = FALSE) + if(!target.density && (target != original_target)) + pellet_hit_chance *= 0.2 + var/hit_pellets = 0 + for(var/i in 1 to ceil(pellets)) + if(!prob(pellet_hit_chance)) + continue + // args is just a list ref, so we can do this (directly modify args) + hit_zone = ran_zone(def_zone, zone_true_chance) + . |= ..() + ++hit_pellets + pellets -= hit_pellets + hit_zone = original_hit_zone + if(pellets <= 0) + . |= PROJECTILE_IMPACT_DELETE + else + . |= PROJECTILE_IMPACT_PIERCE + +// todo: this is only needed because process_damage_instance isn't used for structured right now! +/obj/projectile/bullet/pellet/get_structure_damage() + return ..() * pellets + +//Explosive grenade projectile, borrowed from fragmentation grenade code. +/obj/projectile/bullet/pellet/fragment + damage = 10 + armor_penetration = 30 + + silenced = 1 //embedding messages are still produced so it's kind of weird when enabled. + muzzle_type = null + +/obj/projectile/bullet/pellet/fragment/strong + damage = 15 + armor_penetration = 20 + +/obj/projectile/bullet/pellet/fragment/weak + damage = 4 + armor_penetration = 40 + +/obj/projectile/bullet/pellet/fragment/rubber + name = "stingball shrapnel" + damage = 3 + agony = 8 + sharp = FALSE + edge = FALSE + damage_flag = ARMOR_MELEE + +/obj/projectile/bullet/pellet/fragment/rubber/strong + damage = 8 + agony = 16 + +// Tank rupture fragments +/obj/projectile/bullet/pellet/fragment/tank + name = "metal fragment" + damage = 9 //Big chunks flying off. + + armor_penetration = 20 + + silenced = 1 + muzzle_type = null + pellets = 3 + +/obj/projectile/bullet/pellet/fragment/tank/small + name = "small metal fragment" + damage = 6 + armor_penetration = 5 + pellets = 5 + +/obj/projectile/bullet/pellet/fragment/tank/big + name = "large metal fragment" + damage = 17 + armor_penetration = 10 + pellet_loss = 0.2 / WORLD_ICON_SIZE + pellets = 1 diff --git a/code/modules/projectiles/projectile/reusable.dm b/code/modules/projectiles/projectile/subtypes/reusable.dm similarity index 73% rename from code/modules/projectiles/projectile/reusable.dm rename to code/modules/projectiles/projectile/subtypes/reusable.dm index a2f5fabbb96d..4c2f5904ff82 100644 --- a/code/modules/projectiles/projectile/reusable.dm +++ b/code/modules/projectiles/projectile/subtypes/reusable.dm @@ -4,54 +4,19 @@ name = "reusable bullet" desc = "How do you even reuse a bullet?" var/ammo_type = /obj/item/ammo_casing/arrow - var/dropped = FALSE //var/fragile = FALSE //var/durable = FALSE //var/shattered = 0 //var/broken_type = null -/obj/projectile/bullet/reusable/on_hit(atom/target, blocked = FALSE) - . = ..() - handle_drop() - //handle_shatter() - -/obj/projectile/bullet/reusable/legacy_on_range() +/obj/projectile/bullet/reusable/expire(impacting) handle_drop() - ..() + return ..() /obj/projectile/bullet/reusable/proc/handle_drop() - if(!dropped) - var/turf/T = get_turf(src) - new ammo_type(T) - dropped = TRUE -/* - else - var/turf/T = get_turf(src) - new broken_type(T) - dropped = TRUE - -/obj/projectile/bullet/reusable/proc/handle_shatter() - if(fragile) - switch(rand(1,100)) - if(1 to 50) - src.shattered = 1 - if(31 to 100) - return - if(durable) - switch(rand(1,100)) - if(1 to 5) - src.shattered = 1 - if(6 to 100) - return - else - switch(rand(1,100)) - if(1 to 25) - src.shattered = 1 - if(16 to 100) - return - return -*/ + var/turf/T = get_turf(src) + new ammo_type(T) //Arrows /obj/projectile/bullet/reusable/arrow @@ -97,14 +62,20 @@ icon_state = "plunger" ammo_type = /obj/item/ammo_casing/arrow/plunger -/obj/projectile/bullet/reusable/plunger/on_hit(atom/hit_atom) +/obj/projectile/bullet/reusable/plunger/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() - var/mob/living/carbon/H = hit_atom + // use target abort as this is a target effect. + if(. & PROJECTILE_IMPACT_FLAGS_TARGET_ABORT) + return + var/mob/living/carbon/H = target + if(!istype(H)) + return var/obj/item/plunger/P if(!H.wear_mask) H.equip_to_slot_if_possible(P, SLOT_MASK) else handle_drop() + return . | PROJECTILE_IMPACT_DELETE //Foam Darts /obj/projectile/bullet/reusable/foam diff --git a/code/modules/projectiles/projectile/scatter.dm b/code/modules/projectiles/projectile/subtypes/scatter.dm similarity index 100% rename from code/modules/projectiles/projectile/scatter.dm rename to code/modules/projectiles/projectile/subtypes/scatter.dm diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/subtypes/special.dm similarity index 80% rename from code/modules/projectiles/projectile/special.dm rename to code/modules/projectiles/projectile/subtypes/special.dm index deadb73495d2..1846df5ce77f 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/subtypes/special.dm @@ -17,9 +17,12 @@ var/sev3_range = 1 var/sev4_range = 1 -/obj/projectile/ion/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/ion/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return empulse(target, sev1_range, sev2_range, sev3_range, sev4_range) - return 1 + return . | PROJECTILE_IMPACT_DELETE /obj/projectile/ion/small sev1_range = -1 @@ -41,9 +44,12 @@ sharp = 1 edge = 1 -/obj/projectile/bullet/gyro/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/bullet/gyro/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return explosion(target, -1, 0, 2) - ..() + return . | PROJECTILE_IMPACT_DELETE /obj/projectile/temp name = "freeze beam" @@ -61,8 +67,10 @@ combustion = FALSE -/obj/projectile/temp/on_hit(atom/target, blocked = FALSE) - ..() +/obj/projectile/temp/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) var/mob/living/L = target @@ -84,8 +92,6 @@ new_temperature = round(new_temperature * temp_factor) L.bodytemperature = new_temperature - return 1 - /obj/projectile/temp/hot name = "heat beam" target_temperature = 1000 @@ -133,7 +139,11 @@ combustion = FALSE -/obj/projectile/energy/floramut/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/energy/floramut/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + var/mob/living/M = target if(ishuman(target)) var/mob/living/carbon/human/H = M @@ -163,8 +173,6 @@ // for (var/mob/V in viewers(src)) // V.show_message("The radiation beam dissipates harmlessly through [M]", 3) M.show_message("The radiation beam dissipates harmlessly through your body.") - else - return 1 /obj/projectile/energy/floramut/gene name = "gamma somatoray" @@ -188,7 +196,10 @@ light_power = 0.5 light_color = "#FFFFFF" -/obj/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/energy/florayield/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return var/mob/M = target if(ishuman(target)) //These rays make plantmen fat. var/mob/living/carbon/human/H = M @@ -196,16 +207,15 @@ M.nutrition += 30 else if (istype(target, /mob/living/carbon/)) M.show_message("The radiation beam dissipates harmlessly through your body.") - else - return 1 - /obj/projectile/beam/mindflayer name = "flayer ray" - combustion = FALSE -/obj/projectile/beam/mindflayer/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/mindflayer/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ishuman(target)) var/mob/living/carbon/human/M = target M.Confuse(rand(5,8)) @@ -229,14 +239,16 @@ combustion = FALSE -/obj/projectile/bola/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/bola/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(ishuman(target)) var/mob/living/carbon/human/M = target var/obj/item/handcuffs/legcuffs/bola/B = new(src.loc) if(!B.place_legcuffs(M,firer)) - if(B) - qdel(B) - ..() + qdel(B) /obj/projectile/webball name = "ball of web" @@ -248,13 +260,16 @@ combustion = FALSE -/obj/projectile/webball/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/webball/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(isturf(target.loc)) var/obj/effect/spider/stickyweb/W = locate() in get_turf(target) if(!W && prob(75)) visible_message("\The [src] splatters a layer of web on \the [target]!") new /obj/effect/spider/stickyweb(target.loc) - ..() /obj/projectile/beam/tungsten name = "core of molten tungsten" @@ -272,7 +287,10 @@ tracer_type = /obj/effect/projectile/tungsten/tracer impact_type = /obj/effect/projectile/tungsten/impact -/obj/projectile/beam/tungsten/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/beam/tungsten/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_BLOCKED) + return if(isliving(target)) var/mob/living/L = target L.add_modifier(/datum/modifier/grievous_wounds, 30 SECONDS) @@ -316,17 +334,13 @@ else if(armor_special) target.visible_message("\The [src] slams into \the [target]'s [target_limb] with a low rumble!") - ..() - -/obj/projectile/beam/tungsten/on_impact(var/atom/A) - if(istype(A,/turf/simulated/shuttle/wall) || istype(A,/turf/simulated/wall) || (istype(A,/turf/simulated/mineral) && A.density) || istype(A,/obj/vehicle/sealed/mecha) || istype(A,/obj/machinery/door)) + if(istype(target,/turf/simulated/shuttle/wall) || istype(target,/turf/simulated/wall) || (istype(target,/turf/simulated/mineral) && target.density) || istype(target,/obj/vehicle/sealed/mecha) || istype(target,/obj/machinery/door)) var/blast_dir = src.dir - A.visible_message("\The [A] begins to glow!") + target.visible_message("\The [target] begins to glow!") spawn(2 SECONDS) - var/blastloc = get_step(A, blast_dir) + var/blastloc = get_step(target, blast_dir) if(blastloc) explosion(blastloc, -1, -1, 2, 3) - ..() /obj/projectile/bullet/honker damage = 0 @@ -384,19 +398,18 @@ light_range = 4 light_power = 3 light_color = "#00ccff" + var/heavy = FALSE -/obj/projectile/plasma/on_hit(var/atom/target, var/blocked = 0) - explosion(target, -1, 0, 1, 2) - ..() +/obj/projectile/plasma/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return -/obj/projectile/plasma/on_impact(var/atom/A) - if(istype(A,/turf/simulated/shuttle/wall) || istype(A,/turf/simulated/wall) || (istype(A,/turf/simulated/mineral) && A.density) || istype(A,/obj/vehicle/sealed/mecha) || istype(A,/obj/machinery/door)) - var/blast_dir = src.dir - A.visible_message("\The [A] is engulfed in roiling plasma!") - var/blastloc = get_step(A, blast_dir) - if(blastloc) - explosion(blastloc, -1, 0, 1, 2) - ..() + var/blast_dir = src.dir + target.visible_message("\The [target] is engulfed in roiling plasma!") + var/blastloc = get_step(target, blast_dir) + if(blastloc) + explosion(blastloc, -1, 0, heavy? 2 : 1, heavy? 3 : 2) /obj/projectile/plasma/hot name ="heavy plasma bolt" @@ -404,16 +417,4 @@ light_range = 5 light_power = 4 light_color = "#00ccff" - -/obj/projectile/plasma/hot/on_hit(var/atom/target, var/blocked = 0) - explosion(target, -1, 0, 2, 3) - ..() - -/obj/projectile/plasma/hot/on_impact(var/atom/A) - if(istype(A,/turf/simulated/shuttle/wall) || istype(A,/turf/simulated/wall) || (istype(A,/turf/simulated/mineral) && A.density) || istype(A,/obj/vehicle/sealed/mecha) || istype(A,/obj/machinery/door)) - var/blast_dir = src.dir - A.visible_message("\The [A] is engulfed in roiling plasma!") - var/blastloc = get_step(A, blast_dir) - if(blastloc) - explosion(blastloc, -1, 0, 2, 3) - ..() + heavy = TRUE diff --git a/code/modules/projectiles/projectile/trace.dm b/code/modules/projectiles/projectile/subtypes/trace.dm similarity index 94% rename from code/modules/projectiles/projectile/trace.dm rename to code/modules/projectiles/projectile/subtypes/trace.dm index a01e1574e40b..9c825bc3f757 100644 --- a/code/modules/projectiles/projectile/trace.dm +++ b/code/modules/projectiles/projectile/subtypes/trace.dm @@ -20,6 +20,7 @@ nodamage = TRUE damage = 0 has_tracer = FALSE + projectile_type = PROJECTILE_TYPE_TRACE var/list/hit = list() /obj/projectile/test/fire(angle, atom/direct_target) @@ -30,6 +31,3 @@ if(A != src) hit |= A return ..() - -/obj/projectile/test/projectile_attack_mob() - return diff --git a/code/modules/projectiles/unsorted/magic.dm b/code/modules/projectiles/unsorted/magic.dm index aa8735a1f020..690cd6a04e24 100644 --- a/code/modules/projectiles/unsorted/magic.dm +++ b/code/modules/projectiles/unsorted/magic.dm @@ -12,14 +12,17 @@ name = "bolt of death" icon_state = "pulse1_bl" -/obj/projectile/magic/death/on_hit(target, var/mob/living/L) +/obj/projectile/magic/death/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() - if(ismob(target)) - var/mob/M = target + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(isliving(target)) + var/mob/living/L = target if(L.anti_magic_check()) - M.visible_message("[src] vanishes on contact with [target]!") - return blocked - M.death(0) + L.visible_message("[src] vanishes on contact with [target]!") + . |= PROJECTILE_IMPACT_DELETE + return + L.death(0) /obj/projectile/magic/resurrection name = "bolt of resurrection" @@ -28,17 +31,21 @@ damage_type = OXY nodamage = 1 -/obj/projectile/magic/resurrection/on_hit(mob/living/carbon/target) +/obj/projectile/magic/resurrection/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(isliving(target)) - if(target.anti_magic_check()) - target.visible_message("[src] vanishes on contact with [target]!") - return blocked - if(target.revive(full_heal = TRUE)) - to_chat(target, "You rise with a start, you're alive!!!") - else if(target.stat != DEAD) - to_chat(target, "You feel great!") - target.rejuvenate(fix_missing = TRUE) + var/mob/living/L = target + if(L.anti_magic_check()) + L.visible_message("[src] vanishes on contact with [L]!") + . |= PROJECTILE_IMPACT_DELETE + return + if(L.revive(full_heal = TRUE)) + to_chat(L, "You rise with a start, you're alive!!!") + else if(L.stat != DEAD) + to_chat(L, "You feel great!") + L.rejuvenate(fix_missing = TRUE) /obj/projectile/magic/teleport name = "bolt of teleportation" @@ -49,13 +56,16 @@ var/inner_tele_radius = 0 var/outer_tele_radius = 6 -/obj/projectile/magic/teleport/on_hit(mob/target, var/mob/living/L) +/obj/projectile/magic/teleport/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ismob(target)) - var/mob/M = target + var/mob/L = target if(L.anti_magic_check()) - M.visible_message("[src] fizzles on contact with [target]!") - return blocked + L.visible_message("[src] fizzles on contact with [target]!") + . |= PROJECTILE_IMPACT_DELETE + return var/teleammount = 0 var/teleloc = target if(!isturf(target)) @@ -76,8 +86,10 @@ nodamage = 1 var/list/door_types = list(/obj/structure/simple_door/wood, /obj/structure/simple_door/iron, /obj/structure/simple_door/silver, /obj/structure/simple_door/gold, /obj/structure/simple_door/uranium, /obj/structure/simple_door/sandstone, /obj/structure/simple_door/phoron, /obj/structure/simple_door/diamond) -/obj/projectile/magic/door/on_hit(atom/target) +/obj/projectile/magic/door/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(istype(target, /obj/machinery/door)) OpenDoor(target) else @@ -291,6 +303,7 @@ if(owner) C.ChangeOwner(owner) */ + /obj/projectile/magic/spellblade name = "blade energy" icon_state = "lavastaff" @@ -299,14 +312,17 @@ sharp = TRUE magic = TRUE -/obj/projectile/magic/spellblade/on_hit(target, var/mob/living/L) +/obj/projectile/magic/spellblade/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(ismob(target)) - var/mob/M = target + var/mob/L = target if(L.anti_magic_check()) - M.visible_message("[src] vanishes on contact with [target]!") + L.visible_message("[src] vanishes on contact with [target]!") qdel(src) return - . = ..() /obj/projectile/magic/arcane_barrage name = "arcane bolt" @@ -318,16 +334,19 @@ magic = TRUE impact_sounds = 'sound/weapons/barragespellhit.ogg' -/obj/projectile/magic/arcane_barrage/on_hit(target, var/mob/living/L) +/obj/projectile/magic/arcane_barrage/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(ismob(target)) - var/mob/M = target + var/mob/L = target if(L.anti_magic_check()) - M.visible_message("[src] vanishes on contact with [target]!") + L.visible_message("[src] vanishes on contact with [target]!") qdel(src) return - . = ..() - +/* needs more work /obj/projectile/magic/locker name = "locker bolt" icon_state = "locker" @@ -352,21 +371,24 @@ return ..() */ -/obj/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) +/obj/projectile/magic/locker/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(!created) + var/obj/structure/closet/decay/C = new(src) C.update_icon() - created = TRUE - return ..() + if(locker_suck && !(target.atom_flags & (ATOM_ABSTRACT | ATOM_NONWORLD))) + if(ismovable(target)) + var/atom/movable/AM = target + AM.forceMove(src) /obj/projectile/magic/locker/Destroy() locker_suck = FALSE for(var/atom/movable/AM in contents) AM.forceMove(get_turf(src)) . = ..() +*/ /obj/structure/closet/decay breakout_time = 600 @@ -428,16 +450,17 @@ chain = caster.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY) ..() -/obj/projectile/magic/aoe/lightning/on_hit(target, var/mob/living/L) +/obj/projectile/magic/aoe/lightning/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ismob(target)) - var/mob/M = target + var/mob/L = target if(L.anti_magic_check()) - M.visible_message("[src] fizzles on contact with [target]!") - qdel(src) - return blocked + L.visible_message("[src] fizzles on contact with [target]!") + return . | PROJECTILE_IMPACT_DELETE tesla_zap(src, zap_range, zap_power) - qdel(src) + return . | PROJECTILE_IMPACT_DELETE /obj/projectile/magic/aoe/lightning/Destroy() qdel(chain) @@ -456,17 +479,21 @@ var/exp_flash = 3 var/exp_fire = 2 -/obj/projectile/magic/aoe/fireball/on_hit(target) +/obj/projectile/magic/aoe/fireball/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(ismob(target)) var/mob/living/M = target if(M.anti_magic_check()) visible_message("[src] vanishes into smoke on contact with [target]!") - return + return . | PROJECTILE_IMPACT_DELETE M.take_overall_damage(0,10) //between this 10 burn, the 10 brute, the explosion brute, and the onfire burn, your at about 65 damage if you stop drop and roll immediately var/turf/T = get_turf(target) explosion(T, -1, exp_heavy, exp_light, exp_flash, 0)//, flame_range = exp_fire) + return . | PROJECTILE_IMPACT_DELETE +/* requires IMPACT_DELETE_AFTER /obj/projectile/magic/aoe/fireball/infernal name = "infernal fireball" exp_heavy = -1 @@ -474,15 +501,14 @@ exp_flash = 4 exp_fire= 5 -/obj/projectile/magic/aoe/fireball/infernal/on_hit(target) +/obj/projectile/magic/aoe/fireball/infernal/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() - if(ismob(target)) - var/mob/living/M = target - if(M.anti_magic_check()) - return + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return var/turf/T = get_turf(target) for(var/i=0, i<50, i+=10) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(explosion), T, -1, exp_heavy, exp_light, exp_flash, FALSE, FALSE, exp_fire), i) +*/ /obj/projectile/magic/nuclear name = "\proper blazing manliness" @@ -491,7 +517,10 @@ var/mob/living/victim = null var/used = 0 -/obj/projectile/magic/nuclear/on_hit(target) +/obj/projectile/magic/nuclear/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(used) return if(ismob(target)) @@ -500,16 +529,12 @@ used = 1 visible_message("[victim] slams into [target] with explosive force!") explosion(src, 2, 3, 4, -1, TRUE, FALSE, 5) + return PROJECTILE_IMPACT_DELETE else used = 1 victim.take_overall_damage(30,30) explosion(src, -1, -1, -1, -1, FALSE, FALSE, 5) - return - -/obj/projectile/magic/nuclear/Destroy() - for(var/atom/movable/AM in contents) - AM.forceMove(get_turf(src)) - . = ..() + return PROJECTILE_IMPACT_DELETE //Spellcards @@ -533,8 +558,10 @@ damage = 4 var/fire_stacks = 4 -/obj/projectile/magic/spellcard/book/spark/on_hit(atom/target, blocked = FALSE) +/obj/projectile/magic/spellcard/book/spark/on_impact(atom/target, impact_flags, def_zone, efficiency) . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return var/mob/living/carbon/M = target if(ismob(target)) if(M.anti_magic_check()) diff --git a/code/modules/random_map/drop/drop_types.dm b/code/modules/random_map/drop/drop_types.dm index 9bd0d665fd50..c39f4ae18cc5 100644 --- a/code/modules/random_map/drop/drop_types.dm +++ b/code/modules/random_map/drop/drop_types.dm @@ -37,7 +37,7 @@ var/global/list/datum/supply_drop_loot/supply_drop /obj/item/storage/belt/security/tactical/bandolier, /obj/item/clothing/accessory/storage/black_drop_pouches, /obj/item/storage/backpack/dufflebag/sec, - /obj/item/shield/energy, + /obj/item/shield/transforming/energy, /obj/item/gun/energy/ionrifle, /obj/item/gun/energy/xray, /obj/item/storage/box/emps, @@ -58,7 +58,7 @@ var/global/list/datum/supply_drop_loot/supply_drop /obj/item/storage/belt/security/tactical/bandolier, /obj/item/clothing/accessory/storage/black_drop_pouches, /obj/item/storage/backpack/dufflebag/sec, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/storage/box/emps, /obj/item/storage/box/flashbangs, /obj/item/gun/ballistic/automatic/sts35, @@ -85,7 +85,7 @@ var/global/list/datum/supply_drop_loot/supply_drop /obj/item/gun/ballistic/automatic/bullpup, /obj/item/ammo_magazine/a7_62mm/ap, /obj/item/ammo_magazine/a7_62mm, - /obj/item/shield/energy, + /obj/item/shield/transforming/energy, /obj/item/grenade/explosive/frag, /obj/item/grenade/explosive/frag, /obj/item/grenade/smokebomb, @@ -107,7 +107,7 @@ var/global/list/datum/supply_drop_loot/supply_drop /obj/item/clothing/suit/armor/riot, /obj/item/clothing/gloves/arm_guard/riot, /obj/item/clothing/shoes/leg_guard/riot, - /obj/item/shield/riot/tele, + /obj/item/shield/transforming/telescopic, /obj/item/storage/box/flashbangs, /obj/item/storage/box/handcuffs, /obj/item/melee/baton, diff --git a/code/modules/random_map/drop/droppod_doors.dm b/code/modules/random_map/drop/droppod_doors.dm index e6b6c02f0724..619144a6e58b 100644 --- a/code/modules/random_map/drop/droppod_doors.dm +++ b/code/modules/random_map/drop/droppod_doors.dm @@ -23,7 +23,7 @@ /obj/structure/droppod_door/attack_generic(var/mob/user) attack_hand(user) -/obj/structure/droppod_door/attack_hand(mob/user, list/params) +/obj/structure/droppod_door/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(deploying) return to_chat(user, "You prime the explosive bolts. Better get clear!") sleep(30) diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 9855c529c98f..70a9ac9731a8 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -402,7 +402,7 @@ var/mob/living/L = target if(ishuman(L)) var/mob/living/carbon/human/H = L - if(H.check_shields(0, null, null, null, "the spray") == 1) //If they block the spray, it does nothing. + if(H.atom_shieldcall_handle_touch(null, SHIELDCALL_CONTACT_FLAG_NEUTRAL, SHIELDCALL_CONTACT_SPECIFIC_CHEMICAL_SPRAY) & SHIELDCALL_FLAGS_BLOCK_ATTACK) amount = 0 perm = L.reagent_permeability() return trans_to_mob(target, amount, CHEM_TOUCH, perm, copy) diff --git a/code/modules/reagents/chemistry/machinery.dm b/code/modules/reagents/chemistry/machinery.dm index dd4191774414..731bb44d72a0 100644 --- a/code/modules/reagents/chemistry/machinery.dm +++ b/code/modules/reagents/chemistry/machinery.dm @@ -160,7 +160,7 @@ if(AM in holdingitems) holdingitems -= AM -/obj/machinery/reagentgrinder/attack_hand(mob/user, list/params) +/obj/machinery/reagentgrinder/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) interact(user) /obj/machinery/reagentgrinder/interact(mob/user as mob) // The microwave Menu //I am reasonably certain that this is not a microwave diff --git a/code/modules/reagents/distilling/distilling.dm b/code/modules/reagents/distilling/distilling.dm index 04436f57fb1f..2384c3b21b59 100644 --- a/code/modules/reagents/distilling/distilling.dm +++ b/code/modules/reagents/distilling/distilling.dm @@ -107,7 +107,7 @@ ..() -/obj/machinery/portable_atmospherics/powered/reagent_distillery/attack_hand(mob/user, list/params) +/obj/machinery/portable_atmospherics/powered/reagent_distillery/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/list/options = list() options["examine"] = radial_examine options["use"] = radial_use diff --git a/code/modules/reagents/items/hypospray.dm b/code/modules/reagents/items/hypospray.dm index 8986f462573f..c3b990c67d25 100644 --- a/code/modules/reagents/items/hypospray.dm +++ b/code/modules/reagents/items/hypospray.dm @@ -68,7 +68,7 @@ if(HYPOSPRAY_MODE_SPRAY) . += "spray" -/obj/item/hypospray/attack_hand(mob/user, list/params) +/obj/item/hypospray/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.is_holding_inactive(src)) if(isnull(loaded)) user.action_feedback(SPAN_WARNING("[src] has no vial loaded."), src) diff --git a/code/modules/reagents/machinery/chem_master.dm b/code/modules/reagents/machinery/chem_master.dm index 691d9a521137..81d7a77327bd 100644 --- a/code/modules/reagents/machinery/chem_master.dm +++ b/code/modules/reagents/machinery/chem_master.dm @@ -148,7 +148,7 @@ pill_bottle = null return ..() -/obj/machinery/chem_master/attack_hand(mob/user, list/params) +/obj/machinery/chem_master/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & BROKEN) return user.set_machine(src) diff --git a/code/modules/reagents/machinery/reagent_dispenser/fuel.dm b/code/modules/reagents/machinery/reagent_dispenser/fuel.dm index f02c4724a39c..b4be4914556a 100644 --- a/code/modules/reagents/machinery/reagent_dispenser/fuel.dm +++ b/code/modules/reagents/machinery/reagent_dispenser/fuel.dm @@ -39,7 +39,7 @@ if(rig) . += "There is some kind of device rigged to the tank." -/obj/structure/reagent_dispensers/fueltank/attack_hand() +/obj/structure/reagent_dispensers/fueltank/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (rig) usr.visible_message("[usr] begins to detach [rig] from \the [src].", "You begin to detach [rig] from \the [src]") if(do_after(usr, 20)) @@ -84,13 +84,14 @@ return ..() -/obj/structure/reagent_dispensers/fueltank/bullet_act(var/obj/projectile/Proj) - if(Proj.get_structure_damage()) - if(istype(Proj.firer)) - message_admins("[key_name_admin(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) (JMP).") - log_game("[key_name(Proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).") +/obj/structure/reagent_dispensers/fueltank/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.get_structure_damage()) + if(istype(proj.firer)) + message_admins("[key_name_admin(proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]) (JMP).") + log_game("[key_name(proj.firer)] shot fueltank at [loc.loc.name] ([loc.x],[loc.y],[loc.z]).") - if(!istype(Proj ,/obj/projectile/beam/lasertag) && !istype(Proj ,/obj/projectile/beam/practice) ) + if(!istype(proj ,/obj/projectile/beam/lasertag) && !istype(proj ,/obj/projectile/beam/practice) ) explode() /obj/structure/reagent_dispensers/fueltank/legacy_ex_act() diff --git a/code/modules/reagents/machinery/reagent_dispenser/oil.dm b/code/modules/reagents/machinery/reagent_dispenser/oil.dm index 0ee657bc919d..e28b3074f4e1 100644 --- a/code/modules/reagents/machinery/reagent_dispenser/oil.dm +++ b/code/modules/reagents/machinery/reagent_dispenser/oil.dm @@ -21,8 +21,9 @@ ) starting_capacity = 5000 -/obj/structure/reagent_dispensers/cookingoil/bullet_act(var/obj/projectile/Proj) - if(Proj.get_structure_damage()) +/obj/structure/reagent_dispensers/cookingoil/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.get_structure_damage()) explode() /obj/structure/reagent_dispensers/cookingoil/legacy_ex_act() diff --git a/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm b/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm index 9926b6417d6d..95e3b73397d5 100644 --- a/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm +++ b/code/modules/reagents/machinery/reagent_dispenser/watercooler.dm @@ -122,7 +122,7 @@ to_chat(user, "There is already a cup dispenser there!") return -/obj/structure/reagent_dispensers/water_cooler/attack_hand(mob/user) +/obj/structure/reagent_dispensers/water_cooler/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(cups) new /obj/item/reagent_containers/food/drinks/sillycup(src.loc) cups-- diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 16fef9d00e2b..93760ce61848 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -172,7 +172,7 @@ . = ..() update_icon() -/obj/item/reagent_containers/glass/beaker/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/glass/beaker/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() update_icon() diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 57af42c64645..79621a907c05 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -93,7 +93,7 @@ volume = loaded_vial.volume reagents.maximum_volume = loaded_vial.reagents.maximum_volume -/obj/item/reagent_containers/hypospray/vial/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/hypospray/vial/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(loaded_vial) reagents.trans_to_holder(loaded_vial.reagents,volume) diff --git a/code/modules/reagents/reagent_containers/organic.dm b/code/modules/reagents/reagent_containers/organic.dm index 71e56dfe8d30..5e6f99728893 100644 --- a/code/modules/reagents/reagent_containers/organic.dm +++ b/code/modules/reagents/reagent_containers/organic.dm @@ -51,7 +51,7 @@ . = ..() update_icon() -/obj/item/reagent_containers/organic/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/organic/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() update_icon() diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 07a18cc86494..0d68bfa56c6f 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -47,7 +47,7 @@ return update_icon() -/obj/item/reagent_containers/syringe/attack_hand(mob/user, list/params) +/obj/item/reagent_containers/syringe/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ..() update_icon() @@ -119,7 +119,7 @@ B = T.take_blood(src,amount) drawing = 0 - if (B) + if (B && !(B in reagents.reagent_list)) reagents.reagent_list += B reagents.update_total() on_reagent_change() @@ -246,8 +246,10 @@ var/hit_area = affecting.name - if((user != target) && H.check_shields(7, src, user, "\the [src]")) - return + if(user != target) + var/list/shieldcall_results = target.run_mob_defense(7, attack_type = ATTACK_TYPE_MELEE, weapon = src, hit_zone = hit_area, clickchain = new /datum/event_args/actor/clickchain(user)) + if(shieldcall_results[SHIELDCALL_ARG_FLAGS] & SHIELDCALL_FLAG_ATTACK_BLOCKED) + return if (target != user && H.legacy_mob_armor(target_zone, "melee") > 5 && prob(50)) for(var/mob/O in viewers(world.view, user)) diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm deleted file mode 100644 index 885b1df83216..000000000000 --- a/code/modules/recycling/disposal.dm +++ /dev/null @@ -1,1496 +0,0 @@ -// Disposal bin -// Holds items for disposal into pipe system -// Draws air from turf, gradually charges internal reservoir -// Once full (~1 atm), uses air resv to flush items into the pipes -// Automatically recharges air (unless off), will flush when ready if pre-set -// Can hold items and human size things, no other draggables -// Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation -///kPa - assume the inside of a dispoal pipe is 1 atm, so that needs to be added. -#define SEND_PRESSURE (700 + ONE_ATMOSPHERE) -///L -#define PRESSURE_TANK_VOLUME 150 -///L/s - 4 m/s using a 15 cm by 15 cm inlet -#define PUMP_MAX_FLOW_RATE 90 -/obj/machinery/disposal - name = "disposal unit" - desc = "A pneumatic waste disposal unit." - icon = 'icons/obj/pipes/disposal.dmi' - icon_state = "disposal" - atom_colouration_system = FALSE - anchored = TRUE - density = TRUE - pass_flags_self = ATOM_PASS_OVERHEAD_THROW - var/datum/gas_mixture/air_contents // internal reservoir - var/mode = 1 // item mode 0=off 1=charging 2=charged - var/flush = FALSE // true if flush handle is pulled - var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk - var/flushing = FALSE // true if flushing in progress - var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush - var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush. - var/last_sound = 0 - active_power_usage = 2200 //the pneumatic pump power. 3 HP ~ 2200W - idle_power_usage = 100 - -// create a new disposal -// find the attached trunk (if present) and init gas resvr. -/obj/machinery/disposal/Initialize(mapload, newdir) - . = ..() - return INITIALIZE_HINT_LATELOAD - -/obj/machinery/disposal/LateInitialize() - . = ..() - trunk = locate() in src.loc - if(!trunk) - mode = 0 - flush = FALSE - else - trunk.linked = src // link the pipe trunk to self - - air_contents = new/datum/gas_mixture(PRESSURE_TANK_VOLUME) - update() - -/obj/machinery/disposal/Destroy() - eject() - if(trunk) - trunk.linked = null - return ..() - -// attack by item places it in to disposal -/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user) - if(user.a_intent != INTENT_HELP) - return ..() - - . = CLICKCHAIN_DO_NOT_PROPAGATE - - if(machine_stat & BROKEN || !I || !user) - return - - add_fingerprint(user, 0, I) - if(mode<=0) // It's off - if(I.is_screwdriver()) - if(contents.len > 0) - to_chat(user, "Eject the items first!") - return - if(mode==0) // It's off but still not unscrewed - mode=-1 // Set it to doubleoff l0l - playsound(src, I.tool_sound, 50, 1) - to_chat(user, "You remove the screws around the power connection.") - return - else if(mode==-1) - mode=0 - playsound(src, I.tool_sound, 50, 1) - to_chat(user, "You attach the screws around the power connection.") - return - else if(istype(I, /obj/item/weldingtool) && mode==-1) - if(contents.len > 0) - to_chat(user, "Eject the items first!") - return - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src, W.tool_sound, 100, 1) - to_chat(user, "You start slicing the floorweld off the disposal unit.") - - if(do_after(user,20 * W.tool_speed)) - if(!src || !W.isOn()) return - to_chat(user, "You sliced the floorweld off the disposal unit.") - var/obj/structure/disposalconstruct/C = new (src.loc) - src.transfer_fingerprints_to(C) - C.ptype = 6 // 6 = disposal unit - C.anchored = 1 - C.density = 1 - C.update() - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - - if(istype(I, /obj/item/storage/bag/trash)) - var/obj/item/storage/bag/trash/T = I - to_chat(user, "You empty the bag.") - for(var/obj/item/O in T.contents) - T.obj_storage.remove(O, src) - T.update_icon() - update() - return - - if(istype(I, /obj/item/material/ashtray)) - var/obj/item/material/ashtray/A = I - if(A.contents.len > 0) - user.visible_message("\The [user] empties \the [A.name] into [src].") - for(var/obj/item/O in A.contents) - O.forceMove(src) - A.update_icon() - update() - return - - var/obj/item/grab/G = I - if(istype(G)) // handle grabbed mob - if(ismob(G.affecting)) - var/mob/GM = G.affecting - for (var/mob/V in viewers(usr)) - V.show_message("[usr] starts putting [GM.name] into the disposal.", 3) - if(do_after(usr, 20)) - GM.forceMove(src) - GM.update_perspective() - for (var/mob/C in viewers(src)) - C.show_message("[GM.name] has been placed in \the [src] by [user].", 3) - qdel(G) - - add_attack_logs(user,GM,"Disposals dunked") - return - - if(!user.attempt_insert_item_for_installation(I, src)) - return - - to_chat(user, "You place \the [I] into \the [src].") - for(var/mob/M in viewers(src)) - if(M == user) - continue - M.show_message("[user.name] places \the [I] into \the [src].", 3) - - update() - -// mouse drop another mob or self -// -/obj/machinery/disposal/MouseDroppedOnLegacy(mob/target, mob/user) - if(!CHECK_MOBILITY(user, MOBILITY_CAN_USE) || !istype(target)) - return - if(target.buckled || get_dist(user, src) > 1 || get_dist(user, target) > 1) - return - - //animals cannot put mobs other than themselves into disposal - if(isanimal(user) && target != user) - return - - src.add_fingerprint(user) - var/target_loc = target.loc - var/msg - for (var/mob/V in viewers(usr)) - if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) - V.show_message("[usr] starts climbing into the disposal.", 3) - if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) - if(target.anchored) return - V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) - if(!do_after(usr, 20)) - return - if(target_loc != target.loc) - return - if(target == user && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) // if drop self, then climbed in - // must be awake, not stunned or whatever - msg = "[user.name] climbs into \the [src]." - to_chat(user, "You climb into \the [src].") - else if(target != user && !user.restrained() && !user.stat && CHECK_ALL_MOBILITY(user, MOBILITY_CAN_MOVE | MOBILITY_CAN_USE)) - msg = "[user.name] stuffs [target.name] into \the [src]!" - to_chat(user, "You stuff [target.name] into \the [src]!") - - add_attack_logs(user,target,"Disposals dunked") - else - return - target.forceMove(src) - target.update_perspective() - - for (var/mob/C in viewers(src)) - if(C == user) - continue - C.show_message(msg, 3) - - update() - return - -// attempt to move while inside -/obj/machinery/disposal/relaymove(mob/user as mob) - if(user.stat || src.flushing) - return - if(user.loc == src) - src.go_out(user) - return - -// leave the disposal -/obj/machinery/disposal/proc/go_out(mob/user) - user.forceMove(loc) - user.update_perspective() - update() - return - -// ai as human but can't flush -/obj/machinery/disposal/attack_ai(mob/user as mob) - interact(user, 1) - -// human interact with machine -/obj/machinery/disposal/attack_hand(mob/user, list/params) - - if(machine_stat & BROKEN) - return - - if(user && user.loc == src) - to_chat(user, "You cannot reach the controls from inside.") - return - - // Clumsy folks can only flush it. - if(user.IsAdvancedToolUser(1)) - interact(user, 0) - else - flush = !flush - update() - return - -// user interaction -/obj/machinery/disposal/interact(mob/user, var/ai=0) - - src.add_fingerprint(user) - if(machine_stat & BROKEN) - user.unset_machine() - return - - var/dat = "Waste Disposal UnitWaste Disposal Unit
" - - if(!ai) // AI can't pull flush handle - if(flush) - dat += "Disposal handle: Disengage Engaged" - else - dat += "Disposal handle: Disengaged Engage" - - dat += "

Eject contents
" - - if(mode <= 0) - dat += "Pump: Off On
" - else if(mode == 1) - dat += "Pump: Off On (pressurizing)
" - else - dat += "Pump: Off On (idle)
" - - var/per = 100* air_contents.return_pressure() / (SEND_PRESSURE) - - dat += "Pressure: [round(per, 1)]%
" - - - user.set_machine(src) - user << browse(dat, "window=disposal;size=360x170") - onclose(user, "disposal") - -// handle machine interaction - -/obj/machinery/disposal/Topic(href, href_list) - if(usr.loc == src) - to_chat(usr, "You cannot reach the controls from inside.") - return - - if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 - to_chat(usr, "The disposal units power is disabled.") - return - if(..()) - return - - if(machine_stat & BROKEN) - return - if(usr.stat || usr.restrained() || src.flushing) - return - - if(istype(src.loc, /turf)) - usr.set_machine(src) - - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=disposal") - return - - if(href_list["pump"]) - if(text2num(href_list["pump"])) - mode = 1 - else - mode = 0 - update() - - if(!isAI(usr)) - if(href_list["handle"]) - flush = text2num(href_list["handle"]) - update() - - if(href_list["eject"]) - eject() - else - usr << browse(null, "window=disposal") - usr.unset_machine() - return - return - -// eject the contents of the disposal unit -/obj/machinery/disposal/proc/eject() - for(var/atom/movable/AM in src) - AM.forceMove(src.loc) - AM.pipe_eject(0) - update() - -// update the icon & overlays to reflect mode & status -/obj/machinery/disposal/proc/update() - cut_overlays() - if(machine_stat & BROKEN) - icon_state = "disposal-broken" - mode = 0 - flush = 0 - return - - var/list/overlays_to_add = list() - - // flush handle - if(flush) - overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-handle") - - // only handle is shown if no power - if(machine_stat & NOPOWER || mode == -1) - add_overlay(overlays_to_add) - return - - // check for items in disposal - occupied light - if(contents.len > 0) - overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-full") - - // charging and ready light - if(mode == 1) - overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-charge") - else if(mode == 2) - overlays_to_add += image('icons/obj/pipes/disposal.dmi', "dispover-ready") - - add_overlay(overlays_to_add) - -// timed process -// charge the gas reservoir and perform flush if ready -/obj/machinery/disposal/process(delta_time) - if(!air_contents || (machine_stat & BROKEN)) // nothing can happen if broken - update_use_power(USE_POWER_OFF) - return - - flush_count++ - if( flush_count >= flush_every_ticks ) - if( contents.len ) - if(mode == 2) - spawn(0) - feedback_inc("disposal_auto_flush",1) - flush() - flush_count = 0 - - src.updateDialog() - - if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power - flush() - - if(mode != 1) //if off or ready, no need to charge - update_use_power(USE_POWER_IDLE) - else if(air_contents.return_pressure() >= SEND_PRESSURE) - mode = 2 //if full enough, switch to ready mode - update() - else - src.pressurize() //otherwise charge - -/obj/machinery/disposal/proc/pressurize() - if(machine_stat & NOPOWER) // won't charge if no power - update_use_power(USE_POWER_OFF) - return - - var/atom/L = loc // recharging from loc turf - var/datum/gas_mixture/env = L.return_air() - - var/power_draw = -1 - if(env && env.temperature > 0) - var/transfer_moles = (PUMP_MAX_FLOW_RATE/env.volume)*env.total_moles //group_multiplier is divided out here - power_draw = pump_gas(src, env, air_contents, transfer_moles, active_power_usage) - - if (power_draw > 0) - use_power(power_draw) - -// perform a flush -/obj/machinery/disposal/proc/flush() - - flushing = 1 - flick("[icon_state]-flush", src) - - var/wrapcheck = 0 - var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. - //Hacky test to get drones to mail themselves through disposals. - for(var/mob/living/silicon/robot/drone/D in src) - wrapcheck = 1 - - for(var/obj/item/smallDelivery/O in src) - wrapcheck = 1 - - if(wrapcheck == 1) - H.tomail = 1 - - - sleep(10) - if(last_sound < world.time + 1) - playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) - last_sound = world.time - sleep(5) // wait for animation to finish - - - H.init(src, air_contents) // copy the contents of disposer to holder - air_contents = new(PRESSURE_TANK_VOLUME) // new empty gas resv. - - H.start(src) // start the holder processing movement - flushing = 0 - // now reset disposal state - flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging - update() - return - - -// called when area power changes -/obj/machinery/disposal/power_change() - ..() // do default setting/reset of stat NOPOWER bit - update() // update icon - return - - -/// Called when holder is expelled from a disposal. -/// Should usually only occur if the pipe network is modified. -/obj/machinery/disposal/proc/expel(obj/structure/disposalholder/H) - - var/turf/target - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it - for(var/atom/movable/AM in H) - target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5)) - - AM.forceMove(src.loc) - AM.pipe_eject(0) - if(!istype(AM,/mob/living/silicon/robot/drone)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z - spawn(1) - if(AM) - AM.throw_at_old(target, 5, 1) - - H.vent_gas(loc) - qdel(H) - -/obj/machinery/disposal/throw_impacted(atom/movable/AM, datum/thrownthing/TT) - if(istype(AM, /obj/item) && !istype(AM, /obj/projectile)) - if(prob(75)) - AM.forceMove(src) - visible_message("\The [AM] lands in \the [src].") - return COMPONENT_THROW_HIT_TERMINATE - else - visible_message("\The [AM] bounces off of \the [src]'s rim!") - return ..() - return ..() - -/obj/machinery/disposal/CanAllowThrough(atom/movable/mover, turf/target) - if(istype(mover, /obj/projectile)) - return TRUE - return ..() - -/obj/machinery/disposal/wall - name = "inset disposal unit" - icon_state = "wall" - - density = FALSE - -/obj/machinery/disposal/wall/Initialize() - . = ..() - - spawn(1 SECOND) // Fixfix for weird interaction with buildmode or other late-spawning. - update() - -/obj/machinery/disposal/wall/update() - ..() - - switch(dir) - if(1) - pixel_x = 0 - pixel_y = -32 - if(2) - pixel_x = 0 - pixel_y = 32 - if(4) - pixel_x = -32 - pixel_y = 0 - if(8) - pixel_x = 32 - pixel_y = 0 - - -// virtual disposal object -// travels through pipes in lieu of actual items -// contents will be items flushed by the disposal -// this allows the gas flushed to be tracked - -/obj/structure/disposalholder - invisibility = 101 - var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point - var/active = 0 // true if the holder is moving, otherwise inactive - dir = 0 - var/count = 2048 //*** can travel 2048 steps before going inactive (in case of loops) - var/destinationTag = "" // changes if contains a delivery container - var/tomail = 0 //changes if contains wrapped package - var/hasmob = 0 //If it contains a mob - - var/partialTag = "" //set by a partial tagger the first time round, then put in destinationTag if it goes through again. - - -// initialize a holder from the contents of a disposal unit -/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/D, var/datum/gas_mixture/flush_gas) - gas = flush_gas// transfer gas resv. into holder object -- let's be explicit about the data this proc consumes, please. - - //Check for any living mobs trigger hasmob. - //hasmob effects whether the package goes to cargo or its tagged destination. - for(var/mob/living/M in D) - if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone)) - hasmob = 1 - - //Checks 1 contents level deep. This means that players can be sent through disposals... - //...but it should require a second person to open the package. (i.e. person inside a wrapped locker) - for(var/obj/O in D) - if(O.contents) - for(var/mob/living/M in O.contents) - if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone)) - hasmob = 1 - - // now everything inside the disposal gets put into the holder - // note AM since can contain mobs or objs - for(var/atom/movable/AM in D) - AM.forceMove(src) - if(istype(AM, /obj/structure/bigDelivery) && !hasmob) - var/obj/structure/bigDelivery/T = AM - src.destinationTag = T.sortTag - if(istype(AM, /obj/item/smallDelivery) && !hasmob) - var/obj/item/smallDelivery/T = AM - src.destinationTag = T.sortTag - //Drones can mail themselves through maint. - if(istype(AM, /mob/living/silicon/robot/drone)) - var/mob/living/silicon/robot/drone/drone = AM - src.destinationTag = drone.mail_destination - - -// start the movement process -// argument is the disposal unit the holder started in -/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/D) - if(!D.trunk) - D.expel(src) // no trunk connected, so expel immediately - return - - forceMove(D.trunk) - active = 1 - setDir(DOWN) - spawn(1) - move() // spawn off the movement process - - return - -// movement process, persists while holder is moving through pipes -/obj/structure/disposalholder/proc/move() - var/obj/structure/disposalpipe/last - // todo: while this is fucking awful? - while(active) - sleep(1) // was 1 - if(!loc) return // check if we got GC'd - - if(hasmob && prob(3)) - for(var/mob/living/H in src) - if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system, - H.take_overall_damage(20, 0, weapon_descriptor = "blunt trauma")//horribly maim any living creature jumping down disposals. c'est la vie - - var/obj/structure/disposalpipe/curr = loc - last = curr - curr = curr.transfer(src) - - if(!loc) return //side effects - - if(!curr) - last.expel(src, loc, dir) - - // - if(!(count--)) - active = 0 - return - - - -// find the turf which should contain the next pipe -/obj/structure/disposalholder/proc/nextloc() - return get_step(loc,dir) - -// find a matching pipe on a turf -/obj/structure/disposalholder/proc/findpipe(var/turf/T) - - if(!T) - return null - - var/fdir = turn(dir, 180) // flip the movement direction - for(var/obj/structure/disposalpipe/P in T) - if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir - return P - // if no matching pipe, return null - return null - -// merge two holder objects -// used when a a holder meets a stuck holder -/obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other) - for(var/atom/movable/AM in other) - AM.forceMove(src) // move everything in other holder to this one - if(ismob(AM)) - var/mob/M = AM - M.update_perspective() - - qdel(other) - - -/obj/structure/disposalholder/proc/settag(var/new_tag) - destinationTag = new_tag - -/obj/structure/disposalholder/proc/setpartialtag(var/new_tag) - if(partialTag == new_tag) - destinationTag = new_tag - partialTag = "" - else - partialTag = new_tag - - -// called when player tries to move while in a pipe -/obj/structure/disposalholder/relaymove(mob/user as mob) - - if(!istype(user,/mob/living)) - return - - var/mob/living/U = user - - if (U.stat || U.last_special <= world.time) - return - - U.last_special = world.time+100 - - if (src.loc) - for (var/mob/M in hearers(src.loc.loc)) - to_chat(M, "CLONG, clong!") - - playsound(src.loc, 'sound/effects/clang.ogg', 50, 0, 0) - -// called to vent all gas in holder to a location -/obj/structure/disposalholder/proc/vent_gas(var/atom/location) - location.assume_air(gas) // vent all gas to turf - return - -/obj/structure/disposalholder/Destroy() - QDEL_NULL(gas) - active = 0 - return ..() - -// Disposal pipes - -/obj/structure/disposalpipe - icon = 'icons/obj/pipes/disposal.dmi' - name = "disposal pipe" - desc = "An underfloor disposal pipe." - anchored = 1 - density = 0 - hides_underfloor = OBJ_UNDERFLOOR_ALWAYS - dir = 0 // dir will contain dominant direction for junction pipes - plane = TURF_PLANE - layer = DISPOSAL_LAYER // slightly lower than wires and other pipes. - - integrity = 100 - integrity_max = 100 - - #ifdef IN_MAP_EDITOR // Display disposal pipes etc. above walls in map editors. - alpha = 128 // Set for the benefit of mapping. - #endif - - /// Bitmask of pipe directions. - var/dpdir = 0 - var/sortType = "" - var/subtype = 0 - // new pipe, set the icon_state as on map - -/obj/structure/disposalpipe/Initialize(mapload, dir) - . = ..() - base_icon_state = icon_state - if(!isnull(dir)) - setDir(dir) - -// pipe is deleted -// ensure if holder is present, it is expelled -/obj/structure/disposalpipe/Destroy() - var/obj/structure/disposalholder/H = locate() in src - if(H) - // holder was present - H.active = 0 - var/turf/T = src.loc - if(T.density) - // deleting pipe is inside a dense turf (wall) - // this is unlikely, but just dump out everything into the turf in case - - for(var/atom/movable/AM in H) - AM.forceMove(T) - AM.pipe_eject(0) - qdel(H) - ..() - return - - // otherwise, do normal expel from turf - if(H) - expel(H, T, 0) - ..() - -// returns the direction of the next pipe object, given the entrance dir -// by default, returns the bitmask of remaining directions -/obj/structure/disposalpipe/proc/nextdir(var/fromdir) - return dpdir & (~turn(fromdir, 180)) - -// transfer the holder through this pipe segment -// overriden for special behaviour -// -/obj/structure/disposalpipe/proc/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir) - H.setDir(nextdir) - var/turf/T = H.nextloc() - var/obj/structure/disposalpipe/P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.forceMove(P) - else // if wasn't a pipe, then set loc to turf - H.forceMove(T) - return null - - return P - -// update actual icon_state depending on visibility -// if invisible, append "f" to icon_state to show faded version -// this will be revealed if a T-scanner is used -// if visible, use regular icon_state -/obj/structure/disposalpipe/proc/updateicon() -/* if(invisibility) //we hide things with alpha now, no need for transparent icons - icon_state = "[base_icon_state]f" - else - icon_state = base_icon_state*/ - icon_state = base_icon_state - return - - -// expel the held objects into a turf -// called when there is a break in the pipe -/obj/structure/disposalpipe/proc/expel(var/obj/structure/disposalholder/H, var/turf/T, var/direction) - if(!istype(H)) - return - - // Empty the holder if it is expelled into a dense turf. - // Leaving it intact and sitting in a wall is stupid. - if(T.density) - for(var/atom/movable/AM in H) - AM.loc = T - AM.pipe_eject(0) - qdel(H) - return - - - if(!T.is_plating() && istype(T,/turf/simulated/floor)) //intact floor, pop the tile - var/turf/simulated/floor/F = T - F.break_tile() - new /obj/item/stack/tile(H) // add to holder so it will be thrown with other stuff - - var/turf/target - if(direction) // direction is specified - if(istype(T, /turf/space)) // if ended in space, then range is unlimited - target = get_edge_target_turf(T, direction) - else // otherwise limit to 10 tiles - target = get_ranged_target_turf(T, direction, 10) - - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) - for(var/atom/movable/AM in H) - AM.forceMove(T) - AM.pipe_eject(direction) - spawn(1) - if(AM) - AM.throw_at_old(target, 100, 1) - H.vent_gas(T) - qdel(H) - - else // no specified direction, so throw in random direction - - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) - for(var/atom/movable/AM in H) - target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5)) - - AM.forceMove(T) - AM.pipe_eject(0) - spawn(1) - if(AM) - AM.throw_at_old(target, 5, 1) - - H.vent_gas(T) // all gas vent to turf - qdel(H) - -/obj/structure/disposalpipe/deconstructed(method) - . = ..() - src.invisibility = 101 // make invisible (since we won't delete the pipe immediately) - var/obj/structure/disposalholder/H = locate() in src - if(H) - // holder was present - H.active = 0 - var/turf/T = src.loc - if(T.density) - // broken pipe is inside a dense turf (wall) - // this is unlikely, but just dump out everything into the turf in case - - for(var/atom/movable/AM in H) - AM.forceMove(T) - AM.pipe_eject(0) - qdel(H) - return - - // otherwise, do normal expel from turf - if(H) - expel(H, T, 0) - return ..() - -/obj/structure/disposalpipe/drop_products(method, atom/where) - . = ..() - if(method != ATOM_DECONSTRUCT_DISASSEMBLED) - new /obj/structure/disposalpipe/broken(where, dir) - -//attack by item -//weldingtool: unfasten and convert to obj/disposalconstruct - -/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user) - - var/turf/T = src.loc - if(!T.is_plating()) - return // prevent interaction with T-scanner revealed pipes - src.add_fingerprint(user, 0, I) - if(istype(I, /obj/item/weldingtool)) - var/obj/item/weldingtool/W = I - - if(W.remove_fuel(0,user)) - playsound(src, W.tool_sound, 50, 1) - // check if anything changed over 2 seconds - var/turf/uloc = user.loc - var/atom/wloc = W.loc - to_chat(user, "Slicing the disposal pipe.") - sleep(30) - if(!W.isOn()) return - if(user.loc == uloc && wloc == W.loc) - welded() - else - to_chat(user, "You must stay still while welding the pipe.") - else - to_chat(user, "You need more welding fuel to cut the pipe.") - return - -// called when pipe is cut with welder -/obj/structure/disposalpipe/proc/welded() - - var/obj/structure/disposalconstruct/C = new (src.loc) - switch(base_icon_state) - if("pipe-s") - C.ptype = 0 - if("pipe-c") - C.ptype = 1 - if("pipe-j1") - C.ptype = 2 - if("pipe-j2") - C.ptype = 3 - if("pipe-y") - C.ptype = 4 - if("pipe-t") - C.ptype = 5 - if("pipe-j1s") - C.ptype = 9 - C.sortType = sortType - if("pipe-j2s") - C.ptype = 10 - C.sortType = sortType -///// Z-Level stuff - if("pipe-u") - C.ptype = 11 - if("pipe-d") - C.ptype = 12 -///// Z-Level stuff - if("pipe-tagger") - C.ptype = 13 - if("pipe-tagger-partial") - C.ptype = 14 - C.subtype = src.subtype - src.transfer_fingerprints_to(C) - C.setDir(dir) - C.density = 0 - C.anchored = 1 - C.update() - - qdel(src) - -// pipe is deleted -// ensure if holder is present, it is expelled -/obj/structure/disposalpipe/Destroy() - var/obj/structure/disposalholder/H = locate() in src - if(H) - // holder was present - H.active = 0 - var/turf/T = src.loc - if(T.density) - // deleting pipe is inside a dense turf (wall) - // this is unlikely, but just dump out everything into the turf in case - - for(var/atom/movable/AM in H) - AM.forceMove(T) - AM.pipe_eject(0) - qdel(H) - ..() - return - - // otherwise, do normal expel from turf - if(H) - expel(H, T, 0) - ..() - -// a straight or bent segment -/obj/structure/disposalpipe/segment - icon_state = "pipe-s" - -/obj/structure/disposalpipe/segment/New() - ..() - if(icon_state == "pipe-s") - dpdir = dir | turn(dir, 180) - else - dpdir = dir | turn(dir, -90) - return - -///// Z-Level stuff -/obj/structure/disposalpipe/up - icon_state = "pipe-u" - -/obj/structure/disposalpipe/up/New() - ..() - dpdir = dir - return - -/obj/structure/disposalpipe/up/nextdir(fromdir) - var/nextdir - if(fromdir == 11) - nextdir = dir - else - nextdir = 12 - return nextdir - -/obj/structure/disposalpipe/up/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir) - H.setDir(nextdir) - - var/turf/T - var/obj/structure/disposalpipe/P - - if(nextdir == 12) - T = get_vertical_step(src, UP) - if(!T) - H.forceMove(loc) - return - else - for(var/obj/structure/disposalpipe/down/F in T) - P = F - - else - T = get_step(src.loc, H.dir) - P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.forceMove(P) - else // if wasn't a pipe, then set loc to turf - H.forceMove(T) - return null - - return P - -/obj/structure/disposalpipe/down - icon_state = "pipe-d" - -/obj/structure/disposalpipe/down/New() - ..() - dpdir = dir - return - -/obj/structure/disposalpipe/down/nextdir(fromdir) - var/nextdir - if(fromdir == 12) - nextdir = dir - else - nextdir = 11 - return nextdir - -/obj/structure/disposalpipe/down/transfer(obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir) - H.dir = nextdir - - var/turf/T - var/obj/structure/disposalpipe/P - - if(nextdir == 11) - T = get_vertical_step(src, DOWN) - if(!T) - H.forceMove(src.loc) - return - else - for(var/obj/structure/disposalpipe/up/F in T) - P = F - - else - T = get_step(src.loc, H.dir) - P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.forceMove(P) - else // if wasn't a pipe, then set loc to turf - H.forceMove(T) - return null - - return P -///// Z-Level stuff - -/obj/structure/disposalpipe/junction/yjunction - icon_state = "pipe-y" - -//a three-way junction with dir being the dominant direction -/obj/structure/disposalpipe/junction - icon_state = "pipe-j1" - -/obj/structure/disposalpipe/junction/New() - ..() - if(icon_state == "pipe-j1") - dpdir = dir | turn(dir, -90) | turn(dir,180) - else if(icon_state == "pipe-j2") - dpdir = dir | turn(dir, 90) | turn(dir,180) - else // pipe-y - dpdir = dir | turn(dir,90) | turn(dir, -90) - return - - -// next direction to move -// if coming in from secondary dirs, then next is primary dir -// if coming in from primary dir, then next is equal chance of other dirs -/obj/structure/disposalpipe/junction/nextdir(var/fromdir) - var/flipdir = turn(fromdir, 180) - if(flipdir != dir) // came from secondary dir - return dir // so exit through primary - else // came from primary - // so need to choose either secondary exit - var/mask = ..(fromdir) - - // find a bit which is set - var/setbit = 0 - if(mask & NORTH) - setbit = NORTH - else if(mask & SOUTH) - setbit = SOUTH - else if(mask & EAST) - setbit = EAST - else - setbit = WEST - - if(prob(50)) // 50% chance to choose the found bit or the other one - return setbit - else - return mask & (~setbit) - -/obj/structure/disposalpipe/junction/flipped //for easier and cleaner mapping - icon_state = "pipe-j2" - -/obj/structure/disposalpipe/tagger - name = "package tagger" - icon_state = "pipe-tagger" - var/sort_tag = "" - var/partial = 0 - -/obj/structure/disposalpipe/tagger/proc/updatedesc() - desc = initial(desc) - if(sort_tag) - desc += "\nIt's tagging objects with the '[sort_tag]' tag." - -/obj/structure/disposalpipe/tagger/proc/updatename() - if(sort_tag) - name = "[initial(name)] ([sort_tag])" - else - name = initial(name) - -/obj/structure/disposalpipe/tagger/New() - . = ..() - dpdir = dir | turn(dir, 180) - if(sort_tag) GLOB.tagger_locations |= sort_tag - updatename() - updatedesc() - -/obj/structure/disposalpipe/tagger/attackby(obj/item/I, mob/user) - if(..()) - return - - if(istype(I, /obj/item/destTagger)) - var/obj/item/destTagger/O = I - - if(O.currTag)// Tag set - sort_tag = O.currTag - playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) - to_chat(user, "Changed tag to '[sort_tag]'.") - updatename() - updatedesc() - -/obj/structure/disposalpipe/tagger/transfer(obj/structure/disposalholder/H) - if(sort_tag) - if(partial) - H.setpartialtag(sort_tag) - else - H.settag(sort_tag) - return ..() - -/obj/structure/disposalpipe/tagger/partial //needs two passes to tag - name = "partial package tagger" - icon_state = "pipe-tagger-partial" - partial = 1 - -//a three-way junction that sorts objects -/obj/structure/disposalpipe/sortjunction - name = "sorting junction" - icon_state = "pipe-j1s" - desc = "An underfloor disposal pipe with a package sorting mechanism." - - var/posdir = 0 - var/negdir = 0 - var/sortdir = 0 - -/obj/structure/disposalpipe/sortjunction/proc/updatedesc() - desc = initial(desc) - if(sortType) - desc += "\nIt's filtering objects with the '[sortType]' tag." - -/obj/structure/disposalpipe/sortjunction/proc/updatename() - if(sortType) - name = "[initial(name)] ([sortType])" - else - name = initial(name) - -/obj/structure/disposalpipe/sortjunction/proc/updatedir() - posdir = dir - negdir = turn(posdir, 180) - - if(icon_state == "pipe-j1s") - sortdir = turn(posdir, -90) - else if(icon_state == "pipe-j2s") - sortdir = turn(posdir, 90) - - dpdir = sortdir | posdir | negdir - -/obj/structure/disposalpipe/sortjunction/New() - . = ..() - if(sortType) GLOB.tagger_locations |= sortType - - updatedir() - updatename() - updatedesc() - -/obj/structure/disposalpipe/sortjunction/attackby(obj/item/I, mob/user) - if(..()) - return - - if(istype(I, /obj/item/destTagger)) - var/obj/item/destTagger/O = I - - if(O.currTag)// Tag set - sortType = O.currTag - playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) - to_chat(user, "Changed filter to '[sortType]'.") - updatename() - updatedesc() - -/obj/structure/disposalpipe/sortjunction/proc/divert_check(checkTag) - return sortType == checkTag - -// next direction to move -// if coming in from negdir, then next is primary dir or sortdir -// if coming in from posdir, then flip around and go back to posdir -// if coming in from sortdir, go to posdir -/obj/structure/disposalpipe/sortjunction/nextdir(fromdir, sortTag) - if(fromdir != sortdir) // probably came from the negdir - if(divert_check(sortTag)) - return sortdir - else - return posdir - else // came from sortdir - // so go with the flow to positive direction - return posdir - -/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir, H.destinationTag) - H.setDir(nextdir) - var/turf/T = H.nextloc() - var/obj/structure/disposalpipe/P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.forceMove(P) - else // if wasn't a pipe, then set loc to turf - H.forceMove(T) - return null - - return P - -//a three-way junction that filters all wrapped and tagged items -/obj/structure/disposalpipe/sortjunction/wildcard - name = "wildcard sorting junction" - desc = "An underfloor disposal pipe which filters all wrapped and tagged items." - subtype = 1 - -/obj/structure/disposalpipe/sortjunction/wildcard/divert_check(checkTag) - return checkTag != "" - -//junction that filters all untagged items -/obj/structure/disposalpipe/sortjunction/untagged - name = "untagged sorting junction" - desc = "An underfloor disposal pipe which filters all untagged items." - subtype = 2 - -/obj/structure/disposalpipe/sortjunction/untagged/divert_check(checkTag) - return checkTag == "" - -/obj/structure/disposalpipe/sortjunction/flipped //for easier and cleaner mapping - icon_state = "pipe-j2s" - -/obj/structure/disposalpipe/sortjunction/wildcard/flipped - icon_state = "pipe-j2s" - -/obj/structure/disposalpipe/sortjunction/untagged/flipped - icon_state = "pipe-j2s" - -//a trunk joining to a disposal bin or outlet on the same turf -/obj/structure/disposalpipe/trunk - icon_state = "pipe-t" - var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet - -/obj/structure/disposalpipe/trunk/Initialize(mapload) - . = ..() - dpdir = dir - return INITIALIZE_HINT_LATELOAD - -/obj/structure/disposalpipe/trunk/LateInitialize() - . = ..() - getlinked() -/obj/structure/disposalpipe/trunk/proc/getlinked() - linked = null - var/obj/machinery/disposal/D = locate() in src.loc - if(D) - linked = D - if (!D.trunk) - D.trunk = src - - var/obj/structure/disposaloutlet/O = locate() in src.loc - if(O) - linked = O - return - - // Override attackby so we disallow trunkremoval when somethings ontop -/obj/structure/disposalpipe/trunk/attackby(obj/item/I, mob/user) - - //Disposal bins or chutes - /* - These shouldn't be required - var/obj/machinery/disposal/D = locate() in src.loc - if(D && D.anchored) - return - - //Disposal outlet - var/obj/structure/disposaloutlet/O = locate() in src.loc - if(O && O.anchored) - return - */ - - //Disposal constructors - var/obj/structure/disposalconstruct/C = locate() in src.loc - if(C && C.anchored) - return - - var/turf/T = src.loc - if(!T.is_plating()) - return // prevent interaction with T-scanner revealed pipes - src.add_fingerprint(user, 0, I) - if(istype(I, /obj/item/weldingtool)) - var/obj/item/weldingtool/W = I - - if(W.remove_fuel(0,user)) - playsound(src, W.tool_sound, 100, 1) - // check if anything changed over 2 seconds - var/turf/uloc = user.loc - var/atom/wloc = W.loc - to_chat(user, "Slicing the disposal pipe.") - sleep(30) - if(!W.isOn()) return - if(user.loc == uloc && wloc == W.loc) - welded() - else - to_chat(user, "You must stay still while welding the pipe.") - else - to_chat(user, "You need more welding fuel to cut the pipe.") - return - - // would transfer to next pipe segment, but we are in a trunk - // if not entering from disposal bin, - // transfer to linked object (outlet or bin) - -/obj/structure/disposalpipe/trunk/transfer(var/obj/structure/disposalholder/H) - - if(H.dir == DOWN) // we just entered from a disposer - return ..() // so do base transfer proc - // otherwise, go to the linked object - if(linked) - var/obj/structure/disposaloutlet/O = linked - if(istype(O) && (H)) - O.expel(H) // expel at outlet - else - var/obj/machinery/disposal/D = linked - if(H) - D.expel(H) // expel at disposal - else - if(H) - src.expel(H, src.loc, 0) // expel at turf - return null - - // nextdir - -/obj/structure/disposalpipe/trunk/nextdir(var/fromdir) - if(fromdir == DOWN) - return dir - else - return 0 - -// a broken pipe -/obj/structure/disposalpipe/broken - icon_state = "pipe-b" - dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes - // i.e. will be treated as an empty turf - desc = "A broken piece of disposal pipe." - -// called when welded -// for broken pipe, remove and turn into scrap -/obj/structure/disposalpipe/broken/welded() -// var/obj/item/scrap/S = new(src.loc) -// S.set_components(200,0,0) - qdel(src) - -// the disposal outlet machine - -/obj/structure/disposaloutlet - name = "disposal outlet" - desc = "An outlet for the pneumatic disposal system." - icon = 'icons/obj/pipes/disposal.dmi' - icon_state = "outlet" - density = 1 - anchored = 1 - var/active = 0 - var/turf/target // this will be where the output objects are 'thrown' to. - var/mode = 0 - -/obj/structure/disposaloutlet/LateInitialize() - target = get_ranged_target_turf(src, dir, 10) - var/obj/structure/disposalpipe/trunk/trunk = locate() in loc - if(trunk) - trunk.linked = src // link the pipe trunk to self - -// expel the contents of the holder object, then delete it -// called when the holder exits the outlet -/obj/structure/disposaloutlet/proc/expel(obj/structure/disposalholder/H) - - target = get_ranged_target_turf(src, dir, 10) - flick("outlet-open", src) - playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0) - sleep(20) //wait until correct animation frame - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - - if(H) - for(var/atom/movable/AM in H) - AM.forceMove(src.loc) - AM.pipe_eject(dir) - if(!istype(AM,/mob/living/silicon/robot/drone)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z - spawn(5) - AM.throw_at_old(target, 3, 1) - H.vent_gas(src.loc) - qdel(H) - - return - -/obj/structure/disposaloutlet/attackby(obj/item/I, mob/user) - if(!I || !user) - return - src.add_fingerprint(user, 0, I) - if(I.is_screwdriver()) - if(mode==0) - mode=1 - to_chat(user, "You remove the screws around the power connection.") - playsound(src, I.tool_sound, 50, 1) - return - else if(mode==1) - mode=0 - to_chat(user, "You attach the screws around the power connection.") - playsound(src, I.tool_sound, 50, 1) - return - else if(istype(I, /obj/item/weldingtool) && mode==1) - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src, W.tool_sound, 100, 1) - to_chat(user, "You start slicing the floorweld off the disposal outlet.") - if(do_after(user,20 * W.tool_speed)) - if(!src || !W.isOn()) return - to_chat(user, "You sliced the floorweld off the disposal outlet.") - var/obj/structure/disposalconstruct/C = new (src.loc) - src.transfer_fingerprints_to(C) - C.ptype = 7 // 7 = outlet - C.update() - C.anchored = 1 - C.density = 1 - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - -// called when movable is expelled from a disposal pipe or outlet -// by default does nothing, override for special behaviour -/atom/movable/proc/pipe_eject(direction) - return - -// check if mob has client, if so restore client view on eject -/mob/pipe_eject(direction) - update_perspective() - -/obj/effect/debris/cleanable/blood/gibs/pipe_eject(direction) - var/list/dirs - if(direction) - dirs = list( direction, turn(direction, -45), turn(direction, 45)) - else - dirs = GLOB.alldirs.Copy() - - src.streak(dirs) - -/obj/effect/debris/cleanable/blood/gibs/robot/pipe_eject(direction) - var/list/dirs - if(direction) - dirs = list( direction, turn(direction, -45), turn(direction, 45)) - else - dirs = GLOB.alldirs.Copy() - - src.streak(dirs) diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm deleted file mode 100644 index 8b793c7df654..000000000000 --- a/code/modules/recycling/sortingmachinery.dm +++ /dev/null @@ -1,469 +0,0 @@ -/obj/structure/bigDelivery - desc = "A big wrapped package." - name = "large parcel" - icon = 'icons/obj/storage.dmi' - icon_state = "deliverycloset" - var/obj/wrapped = null - density = 1 - var/sortTag = null - mouse_drag_pointer = MOUSE_ACTIVE_POINTER - var/examtext = null - var/nameset = 0 - var/label_y - var/label_x - var/tag_x - -/obj/structure/bigDelivery/attack_hand(mob/user, list/params) - unwrap() - -/obj/structure/bigDelivery/proc/unwrap() - // Destroy will drop our wrapped object on the turf, so let it. - qdel(src) - -/obj/structure/bigDelivery/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/destTagger)) - var/obj/item/destTagger/O = W - if(O.currTag) - if(src.sortTag != O.currTag) - to_chat(user, "You have labeled the destination as [O.currTag].") - if(!src.sortTag) - src.sortTag = O.currTag - update_icon() - else - src.sortTag = O.currTag - playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) - else - to_chat(user, "The package is already labeled for [O.currTag].") - else - to_chat(user, "You need to set a destination first!") - - else if(istype(W, /obj/item/pen)) - switch(alert("What would you like to alter?",,"Title","Description", "Cancel")) - if("Title") - var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN) - if(!str || !length(str)) - to_chat(user, " Invalid text.") - return - user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\ - "You title \the [src]: \"[str]\"",\ - "You hear someone scribbling a note.") - name = "[name] ([str])" - if(!examtext && !nameset) - nameset = 1 - update_icon() - else - nameset = 1 - if("Description") - var/str = sanitize(input(usr,"Label text?","Set label","")) - if(!str || !length(str)) - to_chat(user, "Invalid text.") - return - if(!examtext && !nameset) - examtext = str - update_icon() - else - examtext = str - user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\ - "You label \the [src]: \"[examtext]\"",\ - "You hear someone scribbling a note.") - return - -/obj/structure/bigDelivery/update_icon() - cut_overlays() - if(nameset || examtext) - var/image/I = new/image('icons/obj/storage.dmi',"delivery_label") - if(icon_state == "deliverycloset") - I.pixel_x = 2 - if(label_y == null) - label_y = rand(-6, 11) - I.pixel_y = label_y - else if(icon_state == "deliverycrate") - if(label_x == null) - label_x = rand(-8, 6) - I.pixel_x = label_x - I.pixel_y = -3 - add_overlay(I) - if(sortTag) - var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag") - if(icon_state == "deliverycloset") - if(tag_x == null) - tag_x = rand(-2, 3) - I.pixel_x = tag_x - I.pixel_y = 9 - else if(icon_state == "deliverycrate") - if(tag_x == null) - tag_x = rand(-8, 6) - I.pixel_x = tag_x - I.pixel_y = -3 - add_overlay(I) - -/obj/structure/bigDelivery/examine(mob/user, dist) - . = ..() - if(sortTag) - . += "It is labeled \"[sortTag]\"" - if(examtext) - . += "It has a note attached which reads, \"[examtext]\"" - -/obj/structure/bigDelivery/Destroy() - if(wrapped) //sometimes items can disappear. For example, bombs. --rastaf0 - wrapped.forceMove(get_turf(src)) - if(istype(wrapped, /obj/structure/closet)) - var/obj/structure/closet/O = wrapped - O.sealed = 0 - wrapped = null - var/turf/T = get_turf(src) - for(var/atom/movable/AM in contents) - AM.forceMove(T) - return ..() - -/obj/item/smallDelivery - desc = "A small wrapped package." - name = "small parcel" - icon = 'icons/obj/storage.dmi' - icon_state = "deliverycrate3" - drop_sound = 'sound/items/drop/cardboardbox.ogg' - pickup_sound = 'sound/items/pickup/cardboardbox.ogg' - var/obj/item/wrapped = null - var/sortTag = null - var/examtext = null - var/nameset = 0 - var/tag_x - -/obj/item/smallDelivery/attack_self(mob/user) - . = ..() - if(.) - return - if (src.wrapped) //sometimes items can disappear. For example, bombs. --rastaf0 - wrapped.loc = user.loc - if(ishuman(user)) - user.put_in_hands(wrapped) - else - wrapped.loc = get_turf(src) - - qdel(src) - return - -/obj/item/smallDelivery/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/destTagger)) - var/obj/item/destTagger/O = W - if(O.currTag) - if(src.sortTag != O.currTag) - to_chat(user, "You have labeled the destination as [O.currTag].") - if(!src.sortTag) - src.sortTag = O.currTag - update_icon() - else - src.sortTag = O.currTag - playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 1) - else - to_chat(user, "The package is already labeled for [O.currTag].") - else - to_chat(user, "You need to set a destination first!") - - else if(istype(W, /obj/item/pen)) - switch(alert("What would you like to alter?",,"Title","Description", "Cancel")) - if("Title") - var/str = sanitizeSafe(input(usr,"Label text?","Set label",""), MAX_NAME_LEN) - if(!str || !length(str)) - to_chat(user, " Invalid text.") - return - user.visible_message("\The [user] titles \the [src] with \a [W], marking down: \"[str]\"",\ - "You title \the [src]: \"[str]\"",\ - "You hear someone scribbling a note.") - name = "[name] ([str])" - if(!examtext && !nameset) - nameset = 1 - update_icon() - else - nameset = 1 - - if("Description") - var/str = sanitize(input(usr,"Label text?","Set label","")) - if(!str || !length(str)) - to_chat(user, "Invalid text.") - return - if(!examtext && !nameset) - examtext = str - update_icon() - else - examtext = str - user.visible_message("\The [user] labels \the [src] with \a [W], scribbling down: \"[examtext]\"",\ - "You label \the [src]: \"[examtext]\"",\ - "You hear someone scribbling a note.") - return - -/obj/item/smallDelivery/update_icon() - cut_overlays() - if((nameset || examtext) && icon_state != "deliverycrate1") - var/image/I = new/image('icons/obj/storage.dmi',"delivery_label") - if(icon_state == "deliverycrate5") - I.pixel_y = -1 - add_overlay(I) - if(sortTag) - var/image/I = new/image('icons/obj/storage.dmi',"delivery_tag") - switch(icon_state) - if("deliverycrate1") - I.pixel_y = -5 - if("deliverycrate2") - I.pixel_y = -2 - if("deliverycrate3") - I.pixel_y = 0 - if("deliverycrate4") - if(tag_x == null) - tag_x = rand(0,5) - I.pixel_x = tag_x - I.pixel_y = 3 - if("deliverycrate5") - I.pixel_y = -3 - add_overlay(I) - -/obj/item/smallDelivery/examine(mob/user, dist) - . = ..() - if(sortTag) - . += "It is labeled \"[sortTag]\"" - if(examtext) - . += "It has a note attached which reads, \"[examtext]\"" - -/obj/item/packageWrap - name = "package wrapper" - icon = 'icons/obj/items.dmi' - icon_state = "deliveryPaper" - w_class = WEIGHT_CLASS_NORMAL - var/amount = 25.0 - - -/obj/item/packageWrap/afterattack(atom/movable/target, mob/user, clickchain_flags, list/params) - if(!(clickchain_flags & CLICKCHAIN_HAS_PROXIMITY)) return - if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete - return - if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \ - || istype(target, /obj/item/gift) || istype(target, /obj/item/evidencebag)) - return - if(target.anchored) - return - if(target in user) - return - if(user in target) //no wrapping closets that you are inside - it's not physically possible - return - - user.attack_log += "\[[time_stamp()]\] Has used [name] on \ref[target]" - - - if (istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box))) - var/obj/item/O = target - if (src.amount > 1) - var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up! - if(!istype(O.loc, /turf)) - if(user.client) - user.client.screen -= O - P.wrapped = O - O.forceMove(P) - P.set_weight_class(O.w_class) - var/i = round(O.get_weight_class()) - if(i in list(1,2,3,4,5)) - P.icon_state = "deliverycrate[i]" - switch(i) - if(1) P.name = "tiny parcel" - if(3) P.name = "normal-sized parcel" - if(4) P.name = "large parcel" - if(5) P.name = "huge parcel" - if(i < 1) - P.icon_state = "deliverycrate1" - P.name = "tiny parcel" - if(i > 5) - P.icon_state = "deliverycrate5" - P.name = "huge parcel" - P.add_fingerprint(usr) - O.add_fingerprint(usr) - src.add_fingerprint(usr) - src.amount -= 1 - user.visible_message("\The [user] wraps \a [target] with \a [src].",\ - "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ - "You hear someone taping paper around a small object.") - else if (istype(target, /obj/structure/closet/crate)) - var/obj/structure/closet/crate/O = target - if (src.amount > 3 && !O.opened) - var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) - P.icon_state = "deliverycrate" - P.wrapped = O - O.loc = P - src.amount -= 3 - user.visible_message("\The [user] wraps \a [target] with \a [src].",\ - "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ - "You hear someone taping paper around a large object.") - else if(src.amount < 3) - to_chat(user, "You need more paper.") - else if (istype (target, /obj/structure/closet)) - var/obj/structure/closet/O = target - if (src.amount > 3 && !O.opened) - var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) - P.wrapped = O - O.sealed = 1 - O.loc = P - src.amount -= 3 - user.visible_message("\The [user] wraps \a [target] with \a [src].",\ - "You wrap \the [target], leaving [amount] units of paper on \the [src].",\ - "You hear someone taping paper around a large object.") - else if(src.amount < 3) - to_chat(user, "You need more paper.") - else - to_chat(user, "The object you are trying to wrap is unsuitable for the sorting machinery!") - if (src.amount <= 0) - new /obj/item/c_tube( src.loc ) - qdel(src) - return - return - -/obj/item/packageWrap/examine(mob/user, dist) - . = ..() - . += "There are [amount] units of package wrap left!" - -/obj/item/destTagger - name = "destination tagger" - desc = "Used to set the destination of properly wrapped packages." - icon = 'icons/obj/device.dmi' - icon_state = "dest_tagger" - var/currTag = 0 - - w_class = WEIGHT_CLASS_SMALL - item_state = "electronic" - slot_flags = SLOT_BELT - -/obj/item/destTagger/ui_state() - return GLOB.inventory_state - -/obj/item/destTagger/ui_interact(mob/user, datum/tgui/ui) - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "DestinationTagger", name) - ui.open() - -/obj/item/destTagger/ui_data(mob/user, datum/tgui/ui) - var/list/data = ..() - - data["currTag"] = currTag - data["taggerLocs"] = GLOB.tagger_locations - - return data - -/obj/item/destTagger/attack_self(mob/user) - . = ..() - if(.) - return - ui_interact(user) - -/obj/item/destTagger/ui_act(action, list/params, datum/tgui/ui) - if(..()) - return TRUE - add_fingerprint(usr) - switch(action) - if("set_tag") - var/new_tag = params["tag"] - if(!(new_tag in GLOB.tagger_locations)) - return FALSE - currTag = new_tag - . = TRUE - -/obj/machinery/disposal/deliveryChute - name = "Delivery chute" - desc = "A chute for big and small packages alike!" - density = 1 - icon_state = "intake" - - var/c_mode = 0 - -/obj/machinery/disposal/deliveryChute/Initialize(mapload, newdir) - . = ..() - spawn(5) - trunk = locate() in src.loc - if(trunk) - trunk.linked = src // link the pipe trunk to self - -/obj/machinery/disposal/deliveryChute/interact() - return - -/obj/machinery/disposal/deliveryChute/update() - return - -/obj/machinery/disposal/deliveryChute/Bumped(var/atom/movable/AM) //Go straight into the chute - if(istype(AM, /obj/projectile) || istype(AM, /obj/effect) || istype(AM, /obj/vehicle/sealed/mecha)) return - switch(dir) - if(NORTH) - if(AM.loc.y != src.loc.y+1) return - if(EAST) - if(AM.loc.x != src.loc.x+1) return - if(SOUTH) - if(AM.loc.y != src.loc.y-1) return - if(WEST) - if(AM.loc.x != src.loc.x-1) return - -// if(istype(AM, has_buckled_mobs()) return // I dont know what im doing @ktoma36 - - if(istype(AM, /obj)) - var/obj/O = AM - O.loc = src - else if(istype(AM, /mob)) - var/mob/M = AM - M.loc = src - src.flush() - -/obj/machinery/disposal/deliveryChute/flush() - flushing = 1 - flick("intake-closing", src) - var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. - air_contents = new() // new empty gas resv. - - sleep(10) - playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) - sleep(5) // wait for animation to finish - - H.init(src) // copy the contents of disposer to holder - - H.start(src) // start the holder processing movement - flushing = 0 - // now reset disposal state - flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging - update() - return - -/obj/machinery/disposal/deliveryChute/attackby(var/obj/item/I, var/mob/user) - if(!I || !user) - return - - if(I.is_screwdriver()) - if(c_mode==0) - c_mode=1 - playsound(src.loc, I.tool_sound, 50, 1) - to_chat(user, "You remove the screws around the power connection.") - return - else if(c_mode==1) - c_mode=0 - playsound(src.loc, I.tool_sound, 50, 1) - to_chat(user, "You attach the screws around the power connection.") - return - else if(istype(I, /obj/item/weldingtool) && c_mode==1) - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src.loc, W.tool_sound, 50, 1) - to_chat(user, "You start slicing the floorweld off the delivery chute.") - if(do_after(user,20 * W.tool_speed)) - if(!src || !W.isOn()) return - to_chat(user, "You sliced the floorweld off the delivery chute.") - var/obj/structure/disposalconstruct/C = new (src.loc) - C.ptype = 8 // 8 = Delivery chute - C.update() - C.anchored = 1 - C.density = 1 - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - -/obj/machinery/disposal/deliveryChute/Destroy() - if(trunk) - trunk.linked = null - ..() diff --git a/code/modules/research/designs/mechfab_designs.dm b/code/modules/research/designs/mechfab_designs.dm index 6ab674d5452f..7ae1195b9106 100644 --- a/code/modules/research/designs/mechfab_designs.dm +++ b/code/modules/research/designs/mechfab_designs.dm @@ -514,6 +514,16 @@ id = "mech_lmg" build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg +/datum/design/science/mecha/lmg_heavy + design_name = "AC 10" + id = "mech_lmg_heavy" + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg_heavy + +/datum/design/science/mecha/gauss_rifle + design_name = "Gauss Rifle" + id = "mech_gauss_rifle" + build_path = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/gauss_rifle + /datum/design/science/mecha/rigged_lmg design_name = "Jury-Rigged Machinegun" id = "mech_lmg-r" diff --git a/code/modules/research/designs/weapons.dm b/code/modules/research/designs/weapons.dm index 350c6ea1ae39..e966586ce3a4 100644 --- a/code/modules/research/designs/weapons.dm +++ b/code/modules/research/designs/weapons.dm @@ -331,14 +331,14 @@ id = "chargesword" req_tech = list(TECH_COMBAT = 6, TECH_MAGNET = 4, TECH_ENGINEERING = 5, TECH_ILLEGAL = 4, TECH_ARCANE = 1) materials_base = list(MAT_PLASTEEL = 3500, MAT_GLASS = 1000, MAT_LEAD = 2250, MAT_METALHYDROGEN = 500) - build_path = /obj/item/melee/energy/sword/charge + build_path = /obj/item/melee/transforming/energy/sword/charge /datum/design/science/weapon/melee/eaxe design_name = "Energy Axe" id = "chargeaxe" req_tech = list(TECH_COMBAT = 6, TECH_MAGNET = 5, TECH_ENGINEERING = 4, TECH_ILLEGAL = 4) materials_base = list(MAT_PLASTEEL = 3500, MAT_OSMIUM = 2000, MAT_LEAD = 2000, MAT_METALHYDROGEN = 500) - build_path = /obj/item/melee/energy/axe/charge + build_path = /obj/item/melee/transforming/energy/axe/charge /datum/design/science/weapon/grenade abstract_type = /datum/design/science/weapon/grenade diff --git a/code/modules/research/machinery/rdconsole.dm b/code/modules/research/machinery/rdconsole.dm index 5caf77516ca8..4aed6ff2db36 100644 --- a/code/modules/research/machinery/rdconsole.dm +++ b/code/modules/research/machinery/rdconsole.dm @@ -165,7 +165,7 @@ won't update every console in existence) but it's more of a hassle to do. Also, dat += "" return dat.Join() -/obj/machinery/computer/rdconsole/attack_hand(mob/user, list/params) +/obj/machinery/computer/rdconsole/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return ui_interact(user) diff --git a/code/modules/research/machinery/rdmachines.dm b/code/modules/research/machinery/rdmachines.dm index a0c8040fec25..5d31c7a76cf5 100644 --- a/code/modules/research/machinery/rdmachines.dm +++ b/code/modules/research/machinery/rdmachines.dm @@ -14,7 +14,7 @@ var/list/stored_materials = list() // Materials this machine can accept. var/list/hidden_materials = list() // Materials this machine will not display, unless it contains them. Must be in the materials list as well. -/obj/machinery/r_n_d/attack_hand(mob/user, list/params) +/obj/machinery/r_n_d/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.a_intent == INTENT_HARM) return ..() return diff --git a/code/modules/research/machinery/server.dm b/code/modules/research/machinery/server.dm index 987d9d65976b..734343e239b1 100644 --- a/code/modules/research/machinery/server.dm +++ b/code/modules/research/machinery/server.dm @@ -285,7 +285,7 @@ target.files.known_tech |= from.files.known_tech return TRUE -/obj/machinery/computer/rdservercontrol/attack_hand(mob/user, list/params) +/obj/machinery/computer/rdservercontrol/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN|NOPOWER)) return ui_interact(user) diff --git a/code/modules/resleeving/computers.dm b/code/modules/resleeving/computers.dm index a539e8c311a1..3101f93ae255 100644 --- a/code/modules/resleeving/computers.dm +++ b/code/modules/resleeving/computers.dm @@ -135,7 +135,7 @@ /obj/machinery/computer/transhuman/resleeving/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/computer/transhuman/resleeving/attack_hand(mob/user, list/params) +/obj/machinery/computer/transhuman/resleeving/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) add_fingerprint(user) diff --git a/code/modules/resleeving/infomorph.dm b/code/modules/resleeving/infomorph.dm index 13b11d6e4f93..10ddd357b16b 100644 --- a/code/modules/resleeving/infomorph.dm +++ b/code/modules/resleeving/infomorph.dm @@ -343,7 +343,7 @@ var/list/infomorph_emotions = list( if(stat != 2) close_up() return -/mob/living/silicon/infomorph/attack_hand(mob/user, list/params) +/mob/living/silicon/infomorph/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) visible_message("[user.name] boops [src] on the head.") close_up() diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm index 6eb21e0d84f8..145205f7d834 100644 --- a/code/modules/resleeving/machines.dm +++ b/code/modules/resleeving/machines.dm @@ -341,7 +341,7 @@ return 1 -/obj/machinery/transhuman/synthprinter/attack_hand(mob/user, list/params) +/obj/machinery/transhuman/synthprinter/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((busy == 0) || (machine_stat & NOPOWER)) return to_chat(user, "Current print cycle is [busy]% complete.") @@ -430,7 +430,7 @@ manip_rating += M.rating blur_amount = (48 - manip_rating * 8) -/obj/machinery/transhuman/resleever/attack_hand(mob/user, list/params) +/obj/machinery/transhuman/resleever/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) var/health_text = "" var/mind_text = "" diff --git a/code/modules/resleeving/mirror.dm b/code/modules/resleeving/mirror.dm index b68cb6791991..c084fa6aa17b 100644 --- a/code/modules/resleeving/mirror.dm +++ b/code/modules/resleeving/mirror.dm @@ -81,6 +81,10 @@ forceMove(MT) MT.imp = src +/obj/item/implant/mirror/surgically_remove(mob/living/carbon/human/target, obj/item/organ/external/chest/removing_from) + . = ..() + target.mirror = null + /obj/item/implant/mirror/positronic name = "Synthetic Mirror" desc = "An altered form of the common mirror designed to work with synthetic brains." @@ -182,7 +186,7 @@ imp = null update_icon() -/obj/item/mirrortool/attack_hand(mob/user as mob) +/obj/item/mirrortool/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) user.put_in_hands_or_drop(imp) imp = null diff --git a/code/modules/rogueminer_vr/roguemines_mobs.dm b/code/modules/rogueminer_vr/roguemines_mobs.dm index 14389fb38efe..25ecd8c57a95 100644 --- a/code/modules/rogueminer_vr/roguemines_mobs.dm +++ b/code/modules/rogueminer_vr/roguemines_mobs.dm @@ -1,23 +1,23 @@ /mob/living/simple_mob/animal/space/bats/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/space/carp/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/space/goose/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/wolf/space/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/space/carp/large/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/space/bear/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/vore/aggressive/corrupthound/space/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP /mob/living/simple_mob/animal/space/carp/large/huge/roguemines - faction = "roguemines" + iff_factions = MOB_IFF_FACTION_BIND_TO_MAP diff --git a/code/modules/rogueminer_vr/zone_console.dm b/code/modules/rogueminer_vr/zone_console.dm index 633bdd2b0232..4524216bb76c 100644 --- a/code/modules/rogueminer_vr/zone_console.dm +++ b/code/modules/rogueminer_vr/zone_console.dm @@ -32,7 +32,7 @@ /obj/machinery/computer/roguezones/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/computer/roguezones/attack_hand(mob/user, list/params) +/obj/machinery/computer/roguezones/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (BROKEN|NOPOWER)) return diff --git a/code/modules/rogueminer_vr/zonemaster.dm b/code/modules/rogueminer_vr/zonemaster.dm index 8bede43f5d87..aa9d20d5cc82 100644 --- a/code/modules/rogueminer_vr/zonemaster.dm +++ b/code/modules/rogueminer_vr/zonemaster.dm @@ -295,7 +295,7 @@ var/mobchoice = pickweight(rm_controller.mobs["tier[rm_controller.diffstep]"]) rm_controller.dbg("ZM(p): Picked [mobchoice] to spawn.") var/mob/living/newmob = new mobchoice(get_turf(SP)) - newmob.faction = "asteroid_belt" + newmob.set_iff_factions(MOB_IFF_FACTION_BIND_AUTO) spawned_mobs += newmob if(delay) sleep(delay) diff --git a/code/modules/security levels/keycard authentication.dm b/code/modules/security levels/keycard authentication.dm index 526f672973d5..1506cffaca37 100644 --- a/code/modules/security levels/keycard authentication.dm +++ b/code/modules/security levels/keycard authentication.dm @@ -68,7 +68,7 @@ if(machine_stat & NOPOWER) icon_state = "auth_off" -/obj/machinery/keycard_auth/attack_hand(mob/user, list/params) +/obj/machinery/keycard_auth/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.stat || machine_stat & (NOPOWER|BROKEN)) to_chat(user, "This device is not powered.") return diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index b6ae9691d5ed..191f4024fc1b 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -149,7 +149,7 @@ else check_delay-- -/obj/machinery/shieldgen/attack_hand(mob/user, list/params) +/obj/machinery/shieldgen/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(locked) to_chat(user, "The machine is locked, you are unable to use it.") return diff --git a/code/modules/shieldgen/energy_field.dm b/code/modules/shieldgen/energy_field.dm index 8d195c0f97f2..abb6b3f07911 100644 --- a/code/modules/shieldgen/energy_field.dm +++ b/code/modules/shieldgen/energy_field.dm @@ -58,11 +58,11 @@ user.do_attack_animation(src) user.setClickCooldown(user.get_attack_speed()) -/obj/effect/energy_field/inflict_atom_damage(damage, tier, flag, mode, attack_type, datum/weapon, gradual) +/obj/effect/energy_field/inflict_atom_damage(damage, damage_type, damage_tier, damage_flag, damage_mode, hit_zone, attack_type, datum/weapon) adjust_strength(damage / 20) return damage -/obj/effect/energy_field/attack_hand(mob/user, list/params) +/obj/effect/energy_field/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) impact_effect(3) // Harmless, but still produces the 'impact' effect. ..() diff --git a/code/modules/shieldgen/energy_shield.dm b/code/modules/shieldgen/energy_shield.dm index 4f3d459109f1..009e9bbb5b8e 100644 --- a/code/modules/shieldgen/energy_shield.dm +++ b/code/modules/shieldgen/energy_shield.dm @@ -162,7 +162,7 @@ animate(src, alpha = initial(alpha), time = 1 SECOND) // Just for fun -/obj/effect/shield/attack_hand(mob/user, list/params) +/obj/effect/shield/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) flash_adjacent_segments(3) /obj/effect/shield/proc/take_damage_legacy(var/damage, var/damtype, var/hitby) @@ -231,15 +231,15 @@ if(!disabled_for) take_damage_legacy(rand(10,15) / severity, SHIELD_DAMTYPE_PHYSICAL) - // Fire /obj/effect/shield/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(!disabled_for) take_damage_legacy(rand(5,10), SHIELD_DAMTYPE_HEAT) - // Projectiles -/obj/effect/shield/bullet_act(var/obj/projectile/proj) +/obj/effect/shield/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + impact_flags &= ~PROJECTILE_IMPACT_FLAGS_SHOULD_NOT_HIT + . = ..() if(proj.damage_type == BURN) take_damage_legacy(proj.get_structure_damage(), SHIELD_DAMTYPE_HEAT) else if (proj.damage_type == BRUTE) @@ -247,7 +247,6 @@ else //TODO - This will never happen because of get_structure_damage() only returning values for BRUTE and BURN damage types take_damage_legacy(proj.get_structure_damage(), SHIELD_DAMTYPE_EM) - // Attacks with hand tools. Blocked by Hyperkinetic flag. /obj/effect/shield/attackby(var/obj/item/I as obj, var/mob/user as mob) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) diff --git a/code/modules/shieldgen/handheld_defuser.dm b/code/modules/shieldgen/handheld_defuser.dm index 2155a0220615..ae075de65698 100644 --- a/code/modules/shieldgen/handheld_defuser.dm +++ b/code/modules/shieldgen/handheld_defuser.dm @@ -61,7 +61,7 @@ to_chat(user, "The charge meter reads [cell ? cell.percent() : 0]%") to_chat(user, "It is [enabled ? "enabled" : "disabled"].") -/obj/item/shield_diffuser/attack_hand(mob/user, list/params) +/obj/item/shield_diffuser/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src) if(cell) cell.update_icon() diff --git a/code/modules/shieldgen/sheldwallgen.dm b/code/modules/shieldgen/sheldwallgen.dm index d5cbc172a198..93aaed7bc407 100644 --- a/code/modules/shieldgen/sheldwallgen.dm +++ b/code/modules/shieldgen/sheldwallgen.dm @@ -26,7 +26,7 @@ var/max_stored_power = 50000 //50 kW use_power = USE_POWER_OFF //Draws directly from power net. Does not use APC power. -/obj/machinery/shieldwallgen/attack_hand(mob/user, list/params) +/obj/machinery/shieldwallgen/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(state != 1) to_chat(user, "The shield generator needs to be firmly secured to the floor first.") return 1 @@ -213,11 +213,9 @@ src.cleanup(8) ..() -/obj/machinery/shieldwallgen/bullet_act(var/obj/projectile/Proj) - storedpower -= 400 * Proj.get_structure_damage() - ..() - return - +/obj/machinery/shieldwallgen/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + storedpower -= 400 * proj.get_structure_damage() //////////////Containment Field START /obj/machinery/shieldwall @@ -258,7 +256,7 @@ update_nearby_tiles() ..() -/obj/machinery/shieldwall/attack_hand(mob/user, list/params) +/obj/machinery/shieldwall/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return @@ -277,18 +275,15 @@ else gen_secondary.storedpower -= power_usage - -/obj/machinery/shieldwall/bullet_act(var/obj/projectile/Proj) +/obj/machinery/shieldwall/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() if(needs_power) var/obj/machinery/shieldwallgen/G if(prob(50)) G = gen_primary else G = gen_secondary - G.storedpower -= 400 * Proj.get_structure_damage() - ..() - return - + G.storedpower -= 400 * proj.get_structure_damage() /obj/machinery/shieldwall/legacy_ex_act(severity) if(needs_power) diff --git a/code/modules/shieldgen/shield_capacitor.dm b/code/modules/shieldgen/shield_capacitor.dm index 1d6e73970352..739d6cf27ce5 100644 --- a/code/modules/shieldgen/shield_capacitor.dm +++ b/code/modules/shieldgen/shield_capacitor.dm @@ -67,7 +67,7 @@ else ..() -/obj/machinery/shield_capacitor/attack_hand(mob/user, list/params) +/obj/machinery/shield_capacitor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN)) return interact(user) diff --git a/code/modules/shieldgen/shield_diffuser.dm b/code/modules/shieldgen/shield_diffuser.dm index c3b2636463e6..67a4f842f17c 100644 --- a/code/modules/shieldgen/shield_diffuser.dm +++ b/code/modules/shieldgen/shield_diffuser.dm @@ -41,7 +41,7 @@ else icon_state = "fdiffuser_on" -/obj/machinery/shield_diffuser/attack_hand(mob/user, list/params) +/obj/machinery/shield_diffuser/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return if(alarm) diff --git a/code/modules/shieldgen/shield_gen.dm b/code/modules/shieldgen/shield_gen.dm index 1048bab6de77..4c9e84b79e00 100644 --- a/code/modules/shieldgen/shield_gen.dm +++ b/code/modules/shieldgen/shield_gen.dm @@ -94,7 +94,7 @@ /obj/machinery/shield_gen/attack_ai(user as mob) return src.attack_hand(user) -/obj/machinery/shield_gen/attack_hand(mob/user, list/params) +/obj/machinery/shield_gen/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (BROKEN)) return interact(user) diff --git a/code/modules/shieldgen/shield_generator.dm b/code/modules/shieldgen/shield_generator.dm index 65caacedfc4e..efcd593b77b5 100644 --- a/code/modules/shieldgen/shield_generator.dm +++ b/code/modules/shieldgen/shield_generator.dm @@ -431,7 +431,7 @@ return data -/obj/machinery/power/shield_generator/attack_hand(mob/user, list/params) +/obj/machinery/power/shield_generator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((. = ..())) return if(panel_open && Adjacent(user)) diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm index 9459cc25fba8..258d6f7d761c 100644 --- a/code/modules/shuttles/shuttle_console.dm +++ b/code/modules/shuttles/shuttle_console.dm @@ -11,7 +11,7 @@ var/skip_act = FALSE var/tgui_subtemplate = "ShuttleControlConsoleDefault" -/obj/machinery/computer/shuttle_control/attack_hand(mob/user, list/params) +/obj/machinery/computer/shuttle_control/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..(user)) return if(!allowed(user)) @@ -136,16 +136,6 @@ to_chat(user, "You short out the console's ID checking system. It's now available to everyone!") return 1 -/obj/machinery/computer/shuttle_control/bullet_act(var/obj/projectile/Proj) - visible_message("\The [Proj] ricochets off \the [src]!") - -/obj/machinery/computer/shuttle_control/legacy_ex_act() - return - -/obj/machinery/computer/shuttle_control/emp_act() - return - - GLOBAL_LIST_BOILERPLATE(papers_dockingcode, /obj/item/paper/dockingcodes) /hook/roundstart/proc/populate_dockingcodes() for(var/paper in GLOB.papers_dockingcode) diff --git a/code/modules/species/promethean/promethean_blob.dm b/code/modules/species/promethean/promethean_blob.dm index 510e07807926..835fe946bced 100644 --- a/code/modules/species/promethean/promethean_blob.dm +++ b/code/modules/species/promethean/promethean_blob.dm @@ -195,11 +195,10 @@ set_light(max(1,min(5,rad_glow/15)), max(1,min(10,rad_glow/25)), color) update_icon() -/mob/living/simple_mob/slime/promethean/bullet_act(obj/projectile/P) +/mob/living/simple_mob/slime/promethean/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(humanform) - return humanform.bullet_act(P) - else - return ..() + return proj.impact_redirect(humanform, args) + return ..() /mob/living/simple_mob/slime/promethean/death(gibbed, deathmessage = "rapidly loses cohesion, splattering across the ground...") if(humanform) diff --git a/code/modules/species/protean/protean_blob.dm b/code/modules/species/protean/protean_blob.dm index cf3f64c32e14..336209c011b4 100644 --- a/code/modules/species/protean/protean_blob.dm +++ b/code/modules/species/protean/protean_blob.dm @@ -9,7 +9,8 @@ icon_rest = "rest" icon_dead = "puddle" - faction = "neutral" + iff_factions = MOB_IFF_FACTION_NEUTRAL + maxHealth = 250 health = 250 say_list_type = /datum/say_list/protean_blob @@ -257,7 +258,7 @@ else return ..() -/mob/living/simple_mob/protean_blob/attack_hand(mob/user, list/params) +/mob/living/simple_mob/protean_blob/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) var/mob/living/L = user if(!istype(L)) return diff --git a/code/modules/species/shadekin/crew_shadekin_abilities.dm b/code/modules/species/shadekin/crew_shadekin_abilities.dm index 359b1eca0824..e6b878cb70a0 100644 --- a/code/modules/species/shadekin/crew_shadekin_abilities.dm +++ b/code/modules/species/shadekin/crew_shadekin_abilities.dm @@ -109,7 +109,7 @@ on_created_text = "You drag part of The Dark into realspace, enveloping yourself." on_expired_text = "You lose your grasp on The Dark and realspace reasserts itself." stacks = MODIFIER_STACK_EXTEND - var/mob/living/simple_mob/shadekin/my_kin + var/mob/living/carbon/human/my_kin /datum/modifier/crew_shadekin/create_shade/tick() if(my_kin.ability_flags & AB_PHASE_SHIFTED) diff --git a/code/modules/species/shadekin/shadekin.dm b/code/modules/species/shadekin/shadekin.dm index cd3b048c69f0..1de2c40e446a 100644 --- a/code/modules/species/shadekin/shadekin.dm +++ b/code/modules/species/shadekin/shadekin.dm @@ -13,13 +13,15 @@ SPRITE_ACCESSORY_SLOT_TAIL = /datum/sprite_accessory/tail/bodyset/shadekin, ) - //TODO: Something that's not wiki copypaste blurb = {" - Very little is known about these creatures. They appear to be largely mammalian in appearance. - Seemingly very rare to encounter, there have been widespread myths of these creatures the galaxy over, - but next to no verifiable evidence to their existence. However, they have recently been more verifiably - documented in the Virgo system, following a mining bombardment of Virgo 3. The crew of NSB Adephagia have - taken to calling these creatures 'Shadekin', and the name has generally stuck and spread. + Shadekin are rather unusual creatures, coming from the Azuel system. Their appearance is largely + mammalian, even though they aren't mammals. The official, formal name for the species is Lumelea, + but thanks to a period of difficulties when the Lumelea first met other species, the nickname + Shadekin was made popular by the galaxy's various species, and it stuck to this day as an + informal name. After a few hundred years of living side by side, it's by now widely known that + Shadekin culture revolves around tribes with various levels of technology, with some tribes + integrating into other cultures and cities, as well as some Shadekin leaving their tribe to + travel alone. NanoTrasen is one of the biggest employers of Shadekin. "} wikilink = "https://citadel-station.net/wikiRP/index.php?title=Race:_Shadekin" catalogue_data = list(/datum/category_item/catalogue/fauna/shadekin) diff --git a/code/modules/species/shadekin/shadekin_abilities.dm b/code/modules/species/shadekin/shadekin_abilities.dm index 79842dfb72b4..218cb4d4cd25 100644 --- a/code/modules/species/shadekin/shadekin_abilities.dm +++ b/code/modules/species/shadekin/shadekin_abilities.dm @@ -287,7 +287,7 @@ on_created_text = "You drag part of The Dark into realspace, enveloping yourself." on_expired_text = "You lose your grasp on The Dark and realspace reasserts itself." stacks = MODIFIER_STACK_EXTEND - var/mob/living/simple_mob/shadekin/my_kin + var/mob/living/carbon/human/my_kin /datum/modifier/shadekin/create_shade/tick() if(my_kin.ability_flags & AB_PHASE_SHIFTED) diff --git a/code/modules/species/shadekin/shadekin_blackeyed.dm b/code/modules/species/shadekin/shadekin_blackeyed.dm index 11152d27e9da..1890bcf5f497 100644 --- a/code/modules/species/shadekin/shadekin_blackeyed.dm +++ b/code/modules/species/shadekin/shadekin_blackeyed.dm @@ -14,14 +14,15 @@ SPRITE_ACCESSORY_SLOT_TAIL = /datum/sprite_accessory/tail/bodyset/shadekin, ) - //TODO: Something more fitting for black-eyes - //TODO: CIT ADDENDUM: since we're not really on the tether anymore we'll need a bullshit reason as to why they're around wherever we are. blurb = {" - Very little is known about these creatures. They appear to be largely mammalian in appearance. - Seemingly very rare to encounter, there have been widespread myths of these creatures the galaxy over, - but next to no verifiable evidence to their existence. However, they have recently been more verifiably - documented in the Virgo system, following a mining bombardment of Virgo 3. The crew of NSB Adephagia have - taken to calling these creatures 'Shadekin', and the name has generally stuck and spread. + Shadekin are rather unusual creatures, coming from the Azuel system. Their appearance is largely + mammalian, even though they aren't mammals. The official, formal name for the species is Lumelea, + but thanks to a period of difficulties when the Lumelea first met other species, the nickname + Shadekin was made popular by the galaxy's various species, and it stuck to this day as an + informal name. After a few hundred years of living side by side, it's by now widely known that + Shadekin culture revolves around tribes with various levels of technology, with some tribes + integrating into other cultures and cities, as well as some Shadekin leaving their tribe to + travel alone. NanoTrasen is one of the biggest employers of Shadekin. "} wikilink = "https://citadel-station.net/wikiRP/index.php?title=Race:_Shadekin" diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index a675e88c3243..49f83c734ec3 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -84,6 +84,13 @@ /// do we have a species statpanel? var/species_statpanel = FALSE + //* IFF *// + + /// Inherent IFF factions + /// + /// * This isn't the best way to do this, but it works. + var/list/iff_factions_inherent + //? Icons /// Normal icon set. var/icobase = 'icons/mob/species/human/body.dmi' @@ -545,6 +552,9 @@ for(var/datum/ability/ability as anything in abilities) ability.associate(H) + for(var/faction in iff_factions_inherent) + H.add_iff_faction(faction) + /** * called when we are removed from a mob */ @@ -572,6 +582,9 @@ for(var/datum/ability/ability as anything in abilities) ability.disassociate(H) + for(var/faction in iff_factions_inherent) + H.remove_iff_faction(faction) + /datum/species/proc/sanitize_species_name(var/name) return sanitizeName(name, MAX_NAME_LEN) diff --git a/code/modules/species/station/xenomorph_hybrids/hybrid_resin.dm b/code/modules/species/station/xenomorph_hybrids/hybrid_resin.dm index a4ca77512b9d..9af0aa0221b3 100644 --- a/code/modules/species/station/xenomorph_hybrids/hybrid_resin.dm +++ b/code/modules/species/station/xenomorph_hybrids/hybrid_resin.dm @@ -178,7 +178,7 @@ T.thermal_conductivity = initial(T.thermal_conductivity) ..() -/obj/structure/alien/hybrid_resin/attack_hand(mob/user, list/params) +/obj/structure/alien/hybrid_resin/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(iscarbon(user)) var/mob/living/carbon/C = user if(locate(/obj/item/organ/internal/xenos/hivenode) in C.internal_organs) diff --git a/code/modules/species/xenomorphs/alien_facehugger.dm b/code/modules/species/xenomorphs/alien_facehugger.dm index 6d3c82a512c3..5eb271e11ce5 100644 --- a/code/modules/species/xenomorphs/alien_facehugger.dm +++ b/code/modules/species/xenomorphs/alien_facehugger.dm @@ -30,7 +30,7 @@ var/const/MAX_ACTIVE_TIME = 400 var/strength = 5 var/attached = 0 -/obj/item/clothing/mask/facehugger/attack_hand(mob/user, list/params) +/obj/item/clothing/mask/facehugger/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((stat == CONSCIOUS && !sterile)) if(Attach(user)) return @@ -66,9 +66,9 @@ var/const/MAX_ACTIVE_TIME = 400 Die() return -/obj/item/clothing/mask/facehugger/bullet_act() +/obj/item/clothing/mask/facehugger/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() Die() - return /obj/item/clothing/mask/facehugger/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(exposed_temperature > T0C+80) @@ -315,7 +315,7 @@ var/const/MAX_ACTIVE_TIME = 400 var/strength = 5 var/attached = 0 -/mob/living/simple_mob/animal/space/alien/facehugger/attack_hand(mob/user, list/params) +/mob/living/simple_mob/animal/space/alien/facehugger/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((stat == CONSCIOUS && !sterile)) if(Attach(user)) @@ -351,7 +351,7 @@ var/const/MAX_ACTIVE_TIME = 400 Die() return -/mob/living/simple_mob/animal/space/alien/facehugger/bullet_act() +/mob/living/simple_mob/animal/space/alien/facehugger/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) Die() return diff --git a/code/modules/species/xenomorphs/alien_species.dm b/code/modules/species/xenomorphs/alien_species.dm index eb33fdd386b0..e8eaf1b9b056 100644 --- a/code/modules/species/xenomorphs/alien_species.dm +++ b/code/modules/species/xenomorphs/alien_species.dm @@ -85,6 +85,10 @@ BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/unseverable/xeno) ) + iff_factions_inherent = list( + MOB_IFF_FACTION_XENOMORPH, + ) + /datum/species/xenos/get_bodytype_legacy() return SPECIES_XENO diff --git a/code/modules/species/xenomorphs/xenomorphs.dm b/code/modules/species/xenomorphs/xenomorphs.dm index 2c44851e4fab..ce9a6e4f5481 100644 --- a/code/modules/species/xenomorphs/xenomorphs.dm +++ b/code/modules/species/xenomorphs/xenomorphs.dm @@ -10,22 +10,22 @@ /mob/living/carbon/human/xdrone species = /datum/species/xenos/drone h_style = "Bald" - faction = "xeno" + iff_factions = MOB_IFF_FACTION_XENOMORPH /mob/living/carbon/human/xsentinel species = /datum/species/xenos/sentinel h_style = "Bald" - faction = "xeno" + iff_factions = MOB_IFF_FACTION_XENOMORPH /mob/living/carbon/human/xhunter species = /datum/species/xenos/hunter h_style = "Bald" - faction = "xeno" + iff_factions = MOB_IFF_FACTION_XENOMORPH /mob/living/carbon/human/xqueen species = /datum/species/xenos/queen h_style = "Bald" - faction = "xeno" + iff_factions = MOB_IFF_FACTION_XENOMORPH // I feel like we should generalize/condense down all the various icon-rendering antag procs. /*---------------------------------------- diff --git a/code/modules/spells/spell_projectile.dm b/code/modules/spells/spell_projectile.dm index 60538b4ba7b1..8b3e0be5ea11 100644 --- a/code/modules/spells/spell_projectile.dm +++ b/code/modules/spells/spell_projectile.dm @@ -6,7 +6,6 @@ var/spell/targeted/projectile/carried - penetrating = 0 range = WORLD_ICON_SIZE * 10 //set by the duration of the spell var/proj_trail = 0 //if it leaves a trail @@ -47,10 +46,12 @@ prox_cast(carried.choose_prox_targets(user = carried.holder, spell_holder = src)) return 1 -/obj/projectile/spell_projectile/on_impact() +/obj/projectile/spell_projectile/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return if(loc && carried) prox_cast(carried.choose_prox_targets(user = carried.holder, spell_holder = src)) - return 1 /obj/projectile/spell_projectile/seeking name = "seeking spell" diff --git a/code/modules/spells/targeted/ethereal_jaunt.dm b/code/modules/spells/targeted/ethereal_jaunt.dm index 078cc2ac2ae4..d1cb091cd77a 100644 --- a/code/modules/spells/targeted/ethereal_jaunt.dm +++ b/code/modules/spells/targeted/ethereal_jaunt.dm @@ -105,5 +105,3 @@ /obj/effect/dummy/spell_jaunt/legacy_ex_act(blah) return -/obj/effect/dummy/spell_jaunt/bullet_act(blah) - return diff --git a/code/modules/sprite_accessories/markings/tesh.dm b/code/modules/sprite_accessories/markings/tesh.dm index b6aa4baac171..4a2ac81d4f70 100644 --- a/code/modules/sprite_accessories/markings/tesh.dm +++ b/code/modules/sprite_accessories/markings/tesh.dm @@ -9,6 +9,12 @@ id = "marking_teshari_heterochromia" icon_state = "teshi_heterochromia" body_parts = list(BP_HEAD) + +/datum/sprite_accessory/marking/tesh/teshi_eyes + name = "Teshari Eyes" + id = "marking_teshari_eyes" + icon_state = "teshi_eyes" + body_parts = list(BP_HEAD) /datum/sprite_accessory/marking/tesh/tesh_feathers name = "Teshari Feathers" diff --git a/code/modules/stockmarket/computer.dm b/code/modules/stockmarket/computer.dm index 666891005f5d..e30228b5a8cb 100644 --- a/code/modules/stockmarket/computer.dm +++ b/code/modules/stockmarket/computer.dm @@ -13,7 +13,7 @@ . = ..() logged_in = "Cargo Department" -/obj/machinery/computer/stockexchange/attack_hand(mob/user, list/params) +/obj/machinery/computer/stockexchange/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..(user)) return diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm index 55b3c517a8da..4624eae98cc1 100644 --- a/code/modules/surgery/generic.dm +++ b/code/modules/surgery/generic.dm @@ -76,7 +76,7 @@ /obj/item/surgical/scalpel/laser3 = 95, \ /obj/item/surgical/scalpel/laser2 = 85, \ /obj/item/surgical/scalpel/laser1 = 75, \ - /obj/item/melee/energy/sword = 5 + /obj/item/melee/transforming/energy/sword = 5 ) priority = 2 req_open = 0 diff --git a/code/modules/surgery/implant.dm b/code/modules/surgery/implant.dm index 4f5bff255204..e0f11fce24b9 100644 --- a/code/modules/surgery/implant.dm +++ b/code/modules/surgery/implant.dm @@ -158,6 +158,15 @@ // IMPLANT/ITEM REMOVAL SURGERY ////////////////////////////////////////////////////////////////// +/obj/item/proc/surgically_remove(mob/living/carbon/human/target, obj/item/organ/external/chest/removing_from) + removing_from.implants -= src + + target.update_hud_sec_implants() + + loc = get_turf(target) + add_blood(target) + update_icon() + /datum/surgery_step/cavity/implant_removal allowed_tools = list( /obj/item/surgical/hemostat = 100, \ @@ -190,49 +199,19 @@ /datum/surgery_step/cavity/implant_removal/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/chest/affected = target.get_organ(target_zone) - var/find_prob = 0 - if (affected.implants.len) - var/obj/item/obj = pick(affected.implants) - - if(istype(obj,/obj/item/implant)) - var/obj/item/implant/imp = obj - if (imp.islegal()) - find_prob +=60 - else - find_prob +=40 - else - find_prob +=50 - - if (prob(find_prob)) - user.visible_message("[user] takes something out of incision on [target]'s [affected.name] with \the [tool]!", \ - "You take [obj] out of incision on [target]'s [affected.name]s with \the [tool]!" ) - affected.implants -= obj - - target.update_hud_sec_implants() - - //Handle possessive brain borers. - if(istype(obj,/mob/living/simple_mob/animal/borer)) - var/mob/living/simple_mob/animal/borer/worm = obj - if(worm.controlling) - target.release_control() - worm.detatch() - worm.leave_host() - else - obj.loc = get_turf(target) - obj.add_blood(target) - obj.update_icon() - if(istype(obj,/obj/item/implant)) - var/obj/item/implant/imp = obj - imp.imp_in = null - imp.implanted = 0 - if(istype(obj, /obj/item/implant/mirror)) - target.mirror = null - else if(istype(tool,/obj/item/nif)){var/obj/item/nif/N = tool;N.unimplant(target)} - else - user.visible_message("[user] removes \the [tool] from [target]'s [affected.name].", \ - "There's something inside [target]'s [affected.name], but you just missed it this time." ) + var/obj/item/obj = input("What do you want to extract?") in affected.implants + + user.visible_message("[user] takes something out of incision on [target]'s [affected.name] with \the [tool]!", \ + "You take [obj] out of incision on [target]'s [affected.name]s with \the [tool]!" ) + + obj.surgically_remove(target, affected) + + if(istype(tool, /obj/item/nif)) + var/obj/item/nif/N = tool + N.unimplant(target) + else user.visible_message("[user] could not find anything inside [target]'s [affected.name], and pulls \the [tool] out.", \ "You could not find anything inside [target]'s [affected.name]." ) diff --git a/code/modules/telesci/hyper_pad.dm b/code/modules/telesci/hyper_pad.dm index cd5c759375cf..e3c129750dc6 100644 --- a/code/modules/telesci/hyper_pad.dm +++ b/code/modules/telesci/hyper_pad.dm @@ -56,7 +56,7 @@ if(primary) primary.attack_ghost(ghost) -/obj/machinery/hyperpad/centre/attack_hand(mob/user, list/params) +/obj/machinery/hyperpad/centre/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return @@ -77,7 +77,7 @@ src.add_fingerprint(user) startteleport(user) -/obj/machinery/hyperpad/attack_hand(mob/user, list/params) +/obj/machinery/hyperpad/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(primary) primary.attack_hand(user) diff --git a/code/modules/telesci/quantum_pad.dm b/code/modules/telesci/quantum_pad.dm index 3aabd75950bc..ee060a8bdfab 100644 --- a/code/modules/telesci/quantum_pad.dm +++ b/code/modules/telesci/quantum_pad.dm @@ -78,7 +78,7 @@ else icon_state = initial(icon_state) -/obj/machinery/power/quantumpad/attack_hand(mob/user, list/params) +/obj/machinery/power/quantumpad/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/telesci/telesci_computer.dm b/code/modules/telesci/telesci_computer.dm index 7445b287c5ca..3123dae91893 100644 --- a/code/modules/telesci/telesci_computer.dm +++ b/code/modules/telesci/telesci_computer.dm @@ -77,7 +77,7 @@ /obj/machinery/computer/telescience/attack_ai(mob/user) src.attack_hand(user) -/obj/machinery/computer/telescience/attack_hand(mob/user, list/params) +/obj/machinery/computer/telescience/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return nano_ui_interact(user) diff --git a/code/modules/tension/tension.dm b/code/modules/tension/tension.dm index 8886f777f6de..7c4fd2bff7db 100644 --- a/code/modules/tension/tension.dm +++ b/code/modules/tension/tension.dm @@ -69,7 +69,7 @@ if(incapacitated(INCAPACITATION_DISABLED)) return 0 // Can't currently hurt you if it's stunned. - var/friendly = threatened.faction == faction + var/friendly = threatened.shares_iff_faction(src) var/threat = guess_threat_level(threatened) diff --git a/code/modules/turbolift/turbolift_console.dm b/code/modules/turbolift/turbolift_console.dm index 782afd39a580..129ddc9d3bb2 100644 --- a/code/modules/turbolift/turbolift_console.dm +++ b/code/modules/turbolift/turbolift_console.dm @@ -39,7 +39,7 @@ /obj/structure/lift/attack_generic(var/mob/user) return attack_hand(user) -/obj/structure/lift/attack_hand(mob/user, list/params) +/obj/structure/lift/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return interact(user) /obj/structure/lift/interact(var/mob/user) diff --git a/code/modules/turbolift/turbolift_map.dm b/code/modules/turbolift/turbolift_map.dm index 0f0dd99deecb..c2609f274039 100644 --- a/code/modules/turbolift/turbolift_map.dm +++ b/code/modules/turbolift/turbolift_map.dm @@ -162,7 +162,7 @@ return // Update path appropriately if needed. - var/swap_to = /turf/simulated/open + var/swap_to = /turf/baseturf_bottom if(cz == uz) // Elevator. if(wall_type && (tx == ux || ty == uy || tx == ex || ty == ey) && !(tx >= door_x1 && tx <= door_x2 && ty >= door_y1 && ty <= door_y2)) swap_to = wall_type diff --git a/code/modules/vehicles/sealed.dm b/code/modules/vehicles/sealed.dm index 27b14622f94d..deaf039bbe40 100644 --- a/code/modules/vehicles/sealed.dm +++ b/code/modules/vehicles/sealed.dm @@ -176,7 +176,7 @@ * * silent - suppress user messages * * suppressed - suppress external messages */ -/obj/vehicle/sealed/proc/mob_exit(mob/exiting, datum/event_args/actor/actor, atom/new_loc, silent, suppressed) +/obj/vehicle/sealed/proc/mob_exit(mob/exiting, datum/event_args/actor/actor, atom/new_loc = drop_location(), silent, suppressed) var/old_control_flags = occupants[exiting] remove_occupant(exiting) exiting.forceMove(new_loc) diff --git a/code/modules/vehicles/sealed/mecha/equipment/weapons/ballistic/automatic.dm b/code/modules/vehicles/sealed/mecha/equipment/weapons/ballistic/automatic.dm index 7bc1b1c89d12..6686d75d011f 100644 --- a/code/modules/vehicles/sealed/mecha/equipment/weapons/ballistic/automatic.dm +++ b/code/modules/vehicles/sealed/mecha/equipment/weapons/ballistic/automatic.dm @@ -11,6 +11,30 @@ projectile_energy_cost = 20 fire_cooldown = 2 +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg_heavy + name = "\improper AC 10" + desc = "The original in anti-mech firepower, the standard Hephaestus Autocannon MK10 design fires AP slugs in order to damage other heavy armor suits. This does mean its rate between bursts is longer than most." + icon_state = "mecha_uac2" + equip_cooldown = 1 SECONDS + projectile = /obj/projectile/bullet/rifle/a762/ap + fire_sound = 'sound/weapons/Gunshot_deagle.ogg' + projectiles = 20 //Mag size + projectiles_per_shot = 2 + deviation = 0.3 + projectile_energy_cost = 60 + fire_cooldown = 2 + +/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/gauss_rifle + name = "\improper gauss rifle" + desc = "The current standard in non-laser, anti-armor firepower, this weapon is the same as those mounted on light tanks for their primary weapon. Fires a single nickle-iron slug at high speed. Requires a long charge time between shots. " + icon_state = "mecha_uac2-rig" + equip_cooldown = 3 SECONDS + projectile = /obj/projectile/bullet/mecha/a12mm_gauss + fire_sound = 'sound/weapons/Gunshot_cannon.ogg' + projectiles = 10 //Mag size + projectiles_per_shot = 1 + projectile_energy_cost = 100 + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg/rigged name = "jury-rigged machinegun" desc = "The cross between a jackhammer and a whole lot of zipguns." diff --git a/code/modules/vehicles/sealed/mecha/equipment/weapons/energy/pulse.dm b/code/modules/vehicles/sealed/mecha/equipment/weapons/energy/pulse.dm index c575a01e3aa6..b2de5f21e69a 100644 --- a/code/modules/vehicles/sealed/mecha/equipment/weapons/energy/pulse.dm +++ b/code/modules/vehicles/sealed/mecha/equipment/weapons/energy/pulse.dm @@ -11,11 +11,3 @@ /obj/projectile/beam/pulse/heavy name = "heavy pulse laser" icon_state = "pulse1_bl" - var/life = 20 - -/obj/projectile/beam/pulse/heavy/Bump(atom/A) - A.bullet_act(src, def_zone) - src.life -= 10 - if(life <= 0) - qdel(src) - return diff --git a/code/modules/vehicles/sealed/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/sealed/mecha/equipment/weapons/weapons.dm index 90966f171338..6e9b11c38847 100644 --- a/code/modules/vehicles/sealed/mecha/equipment/weapons/weapons.dm +++ b/code/modules/vehicles/sealed/mecha/equipment/weapons/weapons.dm @@ -83,11 +83,11 @@ if(!istype(P)) return - P.accuracy -= user.get_accuracy_penalty() + P.accuracy_overall_modify *= 1 - (user.get_accuracy_penalty() / 100) // Some modifiers make it harder or easier to hit things. for(var/datum/modifier/M in user.modifiers) if(!isnull(M.accuracy)) - P.accuracy += M.accuracy + P.accuracy_overall_modify *= 1 + (M.accuracy / 100) if(!isnull(M.accuracy_dispersion)) P.dispersion = max(P.dispersion + M.accuracy_dispersion, 0) diff --git a/code/modules/vehicles/sealed/mecha/mech_fabricator.dm b/code/modules/vehicles/sealed/mecha/mech_fabricator.dm index 1a6fe4513e80..f9c009710b78 100644 --- a/code/modules/vehicles/sealed/mecha/mech_fabricator.dm +++ b/code/modules/vehicles/sealed/mecha/mech_fabricator.dm @@ -482,7 +482,7 @@ immediate += /datum/asset_pack/spritesheet/materials return ..() -/obj/machinery/mecha_part_fabricator/attack_hand(mob/user, list/params) +/obj/machinery/mecha_part_fabricator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return if(!allowed(user)) diff --git a/code/modules/vehicles/sealed/mecha/mecha.dm b/code/modules/vehicles/sealed/mecha/mecha.dm index e06cb7c084a5..a7107c0848fa 100644 --- a/code/modules/vehicles/sealed/mecha/mecha.dm +++ b/code/modules/vehicles/sealed/mecha/mecha.dm @@ -304,16 +304,16 @@ src.legacy_eject_occupant() for(var/mob/M in src) //Be Extra Sure M.forceMove(get_turf(src)) - M.loc.Entered(M) if(M != src.occupant_legacy) step_rand(M) + for(var/atom/movable/A in src.cargo) A.forceMove(get_turf(src)) var/turf/T = get_turf(A) if(T) T.Entered(A) step_rand(A) - + cargo = list() if(prob(30)) explosion(get_turf(loc), 0, 0, 1, 3) @@ -624,7 +624,7 @@ return /obj/vehicle/sealed/mecha/proc/interface_action(obj/machinery/target) - if(istype(target, /obj/machinery/access_button)) + if(istype(target, /obj/machinery/access_button) || istype(target, /obj/machinery/button/remote/blast_door)) src.occupant_message("Interfacing with [target].") src.log_message("Interfaced with [target].") target.attack_hand(src.occupant_legacy) @@ -918,6 +918,7 @@ /////////////////////////////////// //ATM, the ignore_threshold is literally only used for the pulse rifles beams used mostly by deathsquads. +// todo: this is uh, not a check, this is a **roll**. /obj/vehicle/sealed/mecha/proc/check_for_internal_damage(var/list/possible_int_damage,var/ignore_threshold=null) if(!islist(possible_int_damage) || !length(possible_int_damage)) return if(prob(30)) @@ -960,16 +961,6 @@ occupant_message("Internal fire extinquished.") if(MECHA_INT_TANK_BREACH) occupant_message("Damaged internal tank has been sealed.") - return - - -//////////////////////////////////////// -//////// Health related procs //////// -//////////////////////////////////////// - -/obj/vehicle/sealed/mecha/bullet_act(obj/projectile/Proj) - . = ..() - /obj/vehicle/sealed/mecha/proc/take_damage_legacy(amount, type="brute") update_damage_alerts() @@ -1041,7 +1032,7 @@ qdel(src) return -/obj/vehicle/sealed/mecha/attack_hand(mob/user, list/params) +/obj/vehicle/sealed/mecha/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user == occupant_legacy) show_radial_occupant(user) return @@ -1158,17 +1149,16 @@ return -// todo: MAKE INFLICT_DAMAGE_INSTANCE() A THING ON HIT HANDLING PR!! -/obj/vehicle/sealed/mecha/bullet_act(var/obj/projectile/Proj) //wrapper - if(istype(Proj, /obj/projectile/test)) - var/obj/projectile/test/Test = Proj - Test.hit |= occupant_legacy // Register a hit on the occupant_legacy, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode. - return +/obj/vehicle/sealed/mecha/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + if(istype(proj, /obj/projectile/test)) + var/obj/projectile/test/Test = proj + Test.hit |= occupant_legacy // Register a hit on the occupant, for things like turrets, or in simple-mob cases stopping friendly fire in firing line mode. + return ..() - src.log_message("Hit by projectile. Type: [Proj.name]([Proj.damage_flag]).",1) - call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(Proj) //calls equipment - ..() - return + src.log_message("Hit by projectile. Type: [proj.name]([proj.damage_flag]).",1) + impact_flags |= call((proc_res["dynbulletdamage"]||src), "dynbulletdamage")(proj) //calls equipment + impact_flags |= PROJECTILE_IMPACT_SKIP_STANDARD_DAMAGE + return ..() /obj/vehicle/sealed/mecha/proc/dynbulletdamage(var/obj/projectile/Proj) var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] @@ -1210,7 +1200,7 @@ if(pass_damage < temp_damage_minimum)//too pathetic to really damage you. src.occupant_message("The armor deflects incoming projectile.") src.visible_message("The [src.name] armor deflects\the [Proj]") - return + return PROJECTILE_IMPACT_BLOCKED else if(Proj.armor_penetration < temp_minimum_penetration) //If you don't have enough pen, you won't do full damage src.occupant_message("\The [Proj] struggles to pierce \the [src] armor.") @@ -1230,25 +1220,21 @@ src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),ignore_threshold) //AP projectiles have a chance to cause additional damage - if(Proj.penetrating) - var/distance = get_dist(Proj.starting, get_turf(loc)) - var/hit_occupant = 1 //only allow the occupant_legacy to be hit once - for(var/i in 1 to min(Proj.penetrating, round(Proj.damage/15))) + if(Proj.legacy_penetrating) + var/hit_occupant = 1 //only allow the occupant to be hit once + for(var/i in 1 to min(Proj.legacy_penetrating, round(Proj.damage/15))) if(src.occupant_legacy && hit_occupant && prob(20)) - Proj.projectile_attack_mob(src.occupant_legacy, distance) + Proj.impact(occupant_legacy) hit_occupant = 0 else if(pass_damage > internal_damage_minimum) //Only decently painful attacks trigger a chance of mech damage. src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT), 1) - Proj.penetrating-- + Proj.legacy_penetrating-- if(prob(15)) break //give a chance to exit early - Proj.on_hit(src) //on_hit just returns if it's argument is not a living mob so does this actually do anything? - return - //This refer to whenever you are caught in an explosion. /obj/vehicle/sealed/mecha/legacy_ex_act(severity) var/obj/item/mecha_parts/component/armor/ArmC = internal_components[MECH_ARMOR] @@ -1267,19 +1253,13 @@ src.log_append_to_last("Armor saved, changing severity to [severity].") switch(severity) if(1.0) - src.take_damage_legacy(initial(src.integrity), "bomb") + src.take_damage_legacy(initial(src.integrity)/1.25, "bomb") if(2.0) - if (prob(30)) - src.take_damage_legacy(initial(src.integrity), "bomb") - else - src.take_damage_legacy(initial(src.integrity)/2, "bomb") - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + src.take_damage_legacy(initial(src.integrity)/2.5, "bomb") + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) if(3.0) - if (prob(5)) - qdel(src) - else - src.take_damage_legacy(initial(src.integrity)/5, "bomb") - src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) + src.take_damage_legacy(initial(src.integrity)/8, "bomb") + src.check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT),1) return /*Will fix later -Sieve @@ -2724,7 +2704,6 @@ // removing.mobility_flags = NONE removing.clear_alert("charge") removing.clear_alert("mech damage") - removing.reset_perspective() if(occupant_legacy == removing) occupant_legacy = null diff --git a/code/modules/vehicles/sealed/mecha/mecha_actions.dm b/code/modules/vehicles/sealed/mecha/mecha_actions.dm index 188179b6e542..a0ec46bdf318 100644 --- a/code/modules/vehicles/sealed/mecha/mecha_actions.dm +++ b/code/modules/vehicles/sealed/mecha/mecha_actions.dm @@ -68,7 +68,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.lights() - button_icon_state = "mech_lights_[chassis.lights ? "off" : "on"]" + button_icon_state = "mech_lights_[chassis.lights ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_toggle_internals @@ -80,7 +80,7 @@ if(.) return var/obj/vehicle/sealed/mecha/chassis = target - button_icon_state = "mech_internals_[chassis.use_internal_tank ? "off" : "on"]" + button_icon_state = "mech_internals_[chassis.use_internal_tank ? "on" : "off"]" update_buttons() chassis.internal_tank() @@ -105,7 +105,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.strafing() - button_icon_state = "mech_strafe_[chassis.strafing ? "off" : "on"]" + button_icon_state = "mech_strafe_[chassis.strafing ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_defence_mode @@ -118,7 +118,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.defence_mode() - button_icon_state = "mech_defense_mode_[chassis.defence_mode ? "off" : "on"]" + button_icon_state = "mech_defense_mode_[chassis.defence_mode ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_overload_mode @@ -131,7 +131,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.overload() - button_icon_state = "mech_overload_[chassis.overload ? "off" : "on"]" + button_icon_state = "mech_overload_[chassis.overload ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_smoke @@ -155,7 +155,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.zoom() - button_icon_state = "mech_zoom_[chassis.zoom ? "off" : "on"]" + button_icon_state = "mech_zoom_[chassis.zoom ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_toggle_thrusters @@ -168,7 +168,7 @@ return var/obj/vehicle/sealed/mecha/chassis = target chassis.thrusters() - button_icon_state = "mech_thrusters_[chassis.thrusters ? "off" : "on"]" + button_icon_state = "mech_thrusters_[chassis.thrusters ? "on" : "off"]" update_buttons() /datum/action/mecha/mech_cycle_equip //I'll be honest, i don't understand this part, buuuuuut it works! @@ -241,7 +241,7 @@ if(.) return var/obj/vehicle/sealed/mecha/chassis = target - button_icon_state = "mech_phasing_[chassis.phasing ? "off" : "on"]" + button_icon_state = "mech_phasing_[chassis.phasing ? "on" : "off"]" update_buttons() chassis.phasing() @@ -254,7 +254,7 @@ if(.) return var/obj/vehicle/sealed/mecha/chassis = target - button_icon_state = "mech_phasing_[chassis.cloaked ? "off" : "on"]" + button_icon_state = "mech_phasing_[chassis.cloaked ? "on" : "off"]" update_buttons() chassis.toggle_cloaking() diff --git a/code/modules/vehicles/sealed/mecha/mecha_control_console.dm b/code/modules/vehicles/sealed/mecha/mecha_control_console.dm index 2d1483ab79d0..b9c9ee11c8e4 100644 --- a/code/modules/vehicles/sealed/mecha/mecha_control_console.dm +++ b/code/modules/vehicles/sealed/mecha/mecha_control_console.dm @@ -13,7 +13,7 @@ /obj/machinery/computer/mecha/attack_ai(mob/user) return attack_hand(user) -/obj/machinery/computer/mecha/attack_hand(mob/user, list/params) +/obj/machinery/computer/mecha/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return ui_interact(user) diff --git a/code/modules/vehicles/sealed/mecha/mecha_parts.dm b/code/modules/vehicles/sealed/mecha/mecha_parts.dm index 652ff65aee8b..22f7f2c7407c 100644 --- a/code/modules/vehicles/sealed/mecha/mecha_parts.dm +++ b/code/modules/vehicles/sealed/mecha/mecha_parts.dm @@ -22,7 +22,7 @@ ..() return -/obj/item/mecha_parts/chassis/attack_hand(mob/user, list/params) +/obj/item/mecha_parts/chassis/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return //! Ripley @@ -467,7 +467,7 @@ ..() return -/obj/item/mecha_parts/fighter/chassis/attack_hand(mob/user, list/params) +/obj/item/mecha_parts/fighter/chassis/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return diff --git a/code/modules/vehicles/sealed/mecha/mecha_wreckage.dm b/code/modules/vehicles/sealed/mecha/mecha_wreckage.dm index 210ec44013fe..c7f2b5ee2392 100644 --- a/code/modules/vehicles/sealed/mecha/mecha_wreckage.dm +++ b/code/modules/vehicles/sealed/mecha/mecha_wreckage.dm @@ -20,16 +20,6 @@ crowbar_salvage = new return -/obj/effect/decal/mecha_wreckage/legacy_ex_act(severity) - if(severity < 2) - spawn - qdel(src) - return - -/obj/effect/decal/mecha_wreckage/bullet_act(var/obj/projectile/Proj) - return - - /obj/effect/decal/mecha_wreckage/attackby(obj/item/W as obj, mob/user as mob) if(istype(W, /obj/item/weldingtool)) var/obj/item/weldingtool/WT = W diff --git a/code/modules/vehicles/sealed/mecha/subtypes/combat/gorilla.dm b/code/modules/vehicles/sealed/mecha/subtypes/combat/gorilla.dm index 49381b860169..10e46a3ccd1c 100644 --- a/code/modules/vehicles/sealed/mecha/subtypes/combat/gorilla.dm +++ b/code/modules/vehicles/sealed/mecha/subtypes/combat/gorilla.dm @@ -128,9 +128,11 @@ icon_state = "shell" damage = 1000 // In order to 1-hit any other mech and royally fuck anyone unfortunate enough to get in the way. -/obj/projectile/bullet/cannon/on_hit(var/atom/target, var/blocked = 0) +/obj/projectile/bullet/cannon/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return explosion(target, 0, 0, 2, 4) - return 1 /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/cannon/weak name = "8.8 cm KwK 36" diff --git a/code/modules/vehicles/sealed/mecha/subtypes/micro/mecha_parts_vr.dm b/code/modules/vehicles/sealed/mecha/subtypes/micro/mecha_parts_vr.dm index 7c49970beadb..6b361fa23c4d 100644 --- a/code/modules/vehicles/sealed/mecha/subtypes/micro/mecha_parts_vr.dm +++ b/code/modules/vehicles/sealed/mecha/subtypes/micro/mecha_parts_vr.dm @@ -17,7 +17,7 @@ ..() return -/obj/item/mecha_parts/micro/chassis/attack_hand(mob/user, list/params) +/obj/item/mecha_parts/micro/chassis/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) return //Gopher diff --git a/code/modules/vehicles/sealed/mecha/subtypes/working/ripley.dm b/code/modules/vehicles/sealed/mecha/subtypes/working/ripley.dm index abbbddf2bf1e..4565776996a8 100644 --- a/code/modules/vehicles/sealed/mecha/subtypes/working/ripley.dm +++ b/code/modules/vehicles/sealed/mecha/subtypes/working/ripley.dm @@ -27,13 +27,6 @@ icon_scale_x = 1.2 icon_scale_y = 1.2 -/obj/vehicle/sealed/mecha/working/ripley/Destroy() - for(var/atom/movable/A in src.cargo) - A.forceMove(loc) - step_rand(A) - cargo.Cut() - ..() - /obj/vehicle/sealed/mecha/working/ripley/firefighter desc = "Standard APLU chassis was refitted with additional thermal protection and cistern." name = "APLU \"Firefighter\"" diff --git a/code/modules/vehicles_legacy/Securitrain_vr.dm b/code/modules/vehicles_legacy/Securitrain_vr.dm index 005f7ac2b577..81be49d1a6ba 100644 --- a/code/modules/vehicles_legacy/Securitrain_vr.dm +++ b/code/modules/vehicles_legacy/Securitrain_vr.dm @@ -101,12 +101,11 @@ ..() //cargo trains are open topped, so there is a chance the projectile will hit the mob ridding the train instead -/obj/vehicle_old/train/security/bullet_act(var/obj/projectile/Proj) +/obj/vehicle_old/train/security/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(has_buckled_mobs() && prob(70)) - var/mob/living/M = pick(buckled_mobs) - M.bullet_act(Proj) - return - ..() + var/mob/buckled = pick(buckled_mobs) + return proj.impact_redirect(buckled, args) + return ..() /obj/vehicle_old/train/security/update_icon() if(open) diff --git a/code/modules/vehicles_legacy/bike.dm b/code/modules/vehicles_legacy/bike.dm index 121c533d38f4..d977de210b81 100644 --- a/code/modules/vehicles_legacy/bike.dm +++ b/code/modules/vehicles_legacy/bike.dm @@ -120,7 +120,7 @@ return CLICKCHAIN_DO_NOT_PROPAGATE return CLICKCHAIN_DO_NOT_PROPAGATE -/obj/vehicle_old/bike/attack_hand(mob/user, list/params) +/obj/vehicle_old/bike/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user == load) unload(load, user) to_chat(user, "You unbuckle yourself from \the [src].") @@ -174,12 +174,11 @@ ..() -/obj/vehicle_old/bike/bullet_act(var/obj/projectile/Proj) +/obj/vehicle_old/bike/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(has_buckled_mobs() && prob(protection_percent)) var/mob/living/L = pick(buckled_mobs) - L.bullet_act(Proj) - return - ..() + return L.bullet_act(arglist(args)) + return ..() /obj/vehicle_old/bike/update_icon() cut_overlays() diff --git a/code/modules/vehicles_legacy/cargo_train.dm b/code/modules/vehicles_legacy/cargo_train.dm index 0f5c981c533d..084640716307 100644 --- a/code/modules/vehicles_legacy/cargo_train.dm +++ b/code/modules/vehicles_legacy/cargo_train.dm @@ -86,7 +86,7 @@ /* //cargo trains are open topped, so there is a chance the projectile will hit the mob ridding the train instead -/obj/vehicle_old/train/cargo/bullet_act(var/obj/projectile/Proj) +/obj/vehicle_old/train/cargo/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(has_buckled_mobs() && prob(70)) var/mob/living/L = pick(buckled_mobs) L.bullet_act(Proj) diff --git a/code/modules/vehicles_legacy/rover_vr.dm b/code/modules/vehicles_legacy/rover_vr.dm index cbc180b2767d..ef0305f9abb4 100644 --- a/code/modules/vehicles_legacy/rover_vr.dm +++ b/code/modules/vehicles_legacy/rover_vr.dm @@ -102,12 +102,11 @@ ..() //cargo trains are open topped, so there is a chance the projectile will hit the mob ridding the train instead -/obj/vehicle_old/train/rover/bullet_act(var/obj/projectile/Proj) +/obj/vehicle_old/train/rover/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(has_buckled_mobs() && prob(70)) - var/mob/living/M = pick(buckled_mobs) - M.bullet_act(Proj) - return - ..() + var/mob/buckled = pick(buckled_mobs) + return proj.impact_redirect(buckled, args) + return ..() /obj/vehicle_old/train/rover/update_icon() if(open) diff --git a/code/modules/vehicles_legacy/skateboard.dm b/code/modules/vehicles_legacy/skateboard.dm index 1b500e707aed..7b481283a238 100644 --- a/code/modules/vehicles_legacy/skateboard.dm +++ b/code/modules/vehicles_legacy/skateboard.dm @@ -47,7 +47,7 @@ return CLICKCHAIN_DO_NOT_PROPAGATE return CLICKCHAIN_DO_NOT_PROPAGATE -/obj/vehicle_old/skateboard/attack_hand(mob/user, list/params) +/obj/vehicle_old/skateboard/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user == load) unbuckle_mob(load, user) to_chat(user, "You unbuckle yourself from \the [src].") diff --git a/code/modules/vehicles_legacy/train.dm b/code/modules/vehicles_legacy/train.dm index 1cd8e8bab08f..a957a1c6776e 100644 --- a/code/modules/vehicles_legacy/train.dm +++ b/code/modules/vehicles_legacy/train.dm @@ -55,12 +55,11 @@ add_attack_logs(D,M,"Ran over with [src.name]") //trains are commonly open topped, so there is a chance the projectile will hit the mob riding the train instead -/obj/vehicle_old/train/bullet_act(var/obj/projectile/Proj) +/obj/vehicle_old/train/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(has_buckled_mobs() && prob(70)) - var/mob/living/L = pick(buckled_mobs) - L.bullet_act(Proj) - return - ..() + var/mob/buckled = pick(buckled_mobs) + return proj.impact_redirect(buckled, args) + return ..() /obj/vehicle_old/train/update_icon() if(open) @@ -110,7 +109,7 @@ return CLICKCHAIN_DO_NOT_PROPAGATE return CLICKCHAIN_DO_NOT_PROPAGATE -/obj/vehicle_old/train/attack_hand(mob/user, list/params) +/obj/vehicle_old/train/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.stat || user.restrained() || !Adjacent(user)) return 0 diff --git a/code/modules/vehicles_legacy/vehicle.dm b/code/modules/vehicles_legacy/vehicle.dm index 8e5b29152079..80de4b285178 100644 --- a/code/modules/vehicles_legacy/vehicle.dm +++ b/code/modules/vehicles_legacy/vehicle.dm @@ -13,6 +13,8 @@ anchored = 1 animate_movement=1 light_range = 3 + // todo: uses old integrity for now + integrity_flags = INTEGRITY_INDESTRUCTIBLE buckle_allowed = TRUE buckle_flags = BUCKLING_PASS_PROJECTILES_UPWARDS @@ -121,9 +123,9 @@ else ..() -/obj/vehicle_old/bullet_act(var/obj/projectile/Proj) - health -= Proj.get_structure_damage() - ..() +/obj/vehicle_old/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + health -= proj.get_structure_damage() healthcheck() /obj/vehicle_old/proc/adjust_health(amount) diff --git a/code/modules/virus2/curer.dm b/code/modules/virus2/curer.dm index 4263e11d7450..4735ccdf43ce 100644 --- a/code/modules/virus2/curer.dm +++ b/code/modules/virus2/curer.dm @@ -36,7 +36,7 @@ /obj/machinery/computer/curer/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/curer/attack_hand(mob/user, list/params) +/obj/machinery/computer/curer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return user.machine = src diff --git a/code/modules/virus2/diseasesplicer.dm b/code/modules/virus2/diseasesplicer.dm index b2a1e2ef5d2b..9d9771d2a389 100644 --- a/code/modules/virus2/diseasesplicer.dm +++ b/code/modules/virus2/diseasesplicer.dm @@ -38,7 +38,7 @@ /obj/machinery/computer/diseasesplicer/attack_ai(var/mob/user as mob) return src.attack_hand(user) -/obj/machinery/computer/diseasesplicer/attack_hand(mob/user, list/params) +/obj/machinery/computer/diseasesplicer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return TRUE ui_interact(user) diff --git a/code/modules/virus2/dishincubator.dm b/code/modules/virus2/dishincubator.dm index df4e6ab94357..eb32bee10fb0 100644 --- a/code/modules/virus2/dishincubator.dm +++ b/code/modules/virus2/dishincubator.dm @@ -45,7 +45,7 @@ src.attack_hand(user) -/obj/machinery/disease2/incubator/attack_hand(mob/user, list/params) +/obj/machinery/disease2/incubator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return nano_ui_interact(user) diff --git a/code/modules/virus2/isolator.dm b/code/modules/virus2/isolator.dm index ea48c01a5ede..80093b43806d 100644 --- a/code/modules/virus2/isolator.dm +++ b/code/modules/virus2/isolator.dm @@ -41,7 +41,7 @@ src.attack_hand(user) -/obj/machinery/disease2/isolator/attack_hand(mob/user, list/params) +/obj/machinery/disease2/isolator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(machine_stat & (NOPOWER|BROKEN)) return ui_interact(user) diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index 76b11ed93417..09f70bcacd6d 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -390,15 +390,10 @@ //You're in a belly! if(isbelly(loc)) var/obj/belly/B = loc - var/confirm = alert(src, "You're in a mob. Don't use this as a trick to get out of hostile animals. This is for escaping from preference-breaking and if you're otherwise unable to escape from endo (pred AFK for a long time).", "Confirmation", "Okay", "Cancel") - if(!confirm == "Okay" || loc != B) - return //Actual escaping absorbed = 0 //Make sure we're not absorbed muffled = 0 //Removes Muffling forceMove(get_turf(src)) //Just move me up to the turf, let's not cascade through bellies, there's been a problem, let's just leave. - for(var/mob/living/simple_mob/SA in range(10)) - SA.prey_excludes[src] = world.time log_and_message_admins("[key_name(src)] used the OOC escape button to get out of [key_name(B.owner)] ([B.owner ? "JMP" : "null"])") if(!ishuman(B.owner)) diff --git a/code/modules/vore/eating/simple_animal_vr.dm b/code/modules/vore/eating/simple_animal_vr.dm index 19726b5b3e4d..a4dfd5683531 100644 --- a/code/modules/vore/eating/simple_animal_vr.dm +++ b/code/modules/vore/eating/simple_animal_vr.dm @@ -1,7 +1,6 @@ ///////////////////// Simple Animal ///////////////////// /mob/living/simple_mob var/swallowTime = (3 SECONDS) //How long it takes to eat its prey in 1/10 of a second. The default is 3 seconds. - var/list/prey_excludes = list() //For excluding people from being eaten. // // Simple nom proc for if you get ckey'd into a simple_mob mob! Avoids grabs. @@ -23,81 +22,3 @@ return feed_grabbed_to_self(src,T) update_icon() - return - -/* - * Simple proc for animals to have their digestion toggled on/off externally - * Added as a verb in /mob/living/simple_mob/init_vore() if vore is enabled for this mob. - */ -/mob/living/simple_mob/proc/toggle_digestion() - set name = "Toggle Animal's Digestion" - set desc = "Enables digestion on this mob for 20 minutes." - set category = VERB_CATEGORY_OOC - set src in oview(1) - - var/mob/living/carbon/human/user = usr - if(!istype(user) || user.stat) return - - if(!istype(src.ai_holder, /datum/ai_holder/polaris)) - return - var/datum/ai_holder/polaris/ai_holder = src.ai_holder - - if(!vore_selected) - to_chat(user, "[src] isn't planning on eating anything much less digesting it.") - return - if(ai_holder.retaliate || (ai_holder.hostile && faction != user.faction)) - to_chat(user, "This predator isn't friendly, and doesn't give a shit about your opinions of it digesting you.") - return - if(vore_selected.digest_mode == DM_HOLD) - var/confirm = tgui_alert(user, "Enabling digestion on [name] will cause it to digest all stomach contents. Using this to break OOC prefs is against the rules. Digestion will reset after 20 minutes.", "Enabling [name]'s Digestion", list("Enable", "Cancel")) - if(confirm == "Enable") - vore_selected.digest_mode = DM_DIGEST - addtimer(VARSET_CALLBACK(vore_selected, digest_mode, vore_default_mode), 20 MINUTES) - else - var/confirm = tgui_alert(user, "This mob is currently set to process all stomach contents. Do you want to disable this?", "Disabling [name]'s Digestion", list("Disable", "Cancel")) - if(confirm == "Disable") - vore_selected.digest_mode = DM_HOLD - -// Added as a verb in /mob/living/simple_mob/init_vore() if vore is enabled for this mob. -/mob/living/simple_mob/proc/toggle_fancygurgle() - set name = "Toggle Animal's Gurgle sounds" - set desc = "Switches between Fancy and Classic sounds on this mob." - set category = VERB_CATEGORY_OOC - set src in oview(1) - - var/mob/living/user = usr //I mean, At least ghosts won't use it. - if(!istype(user) || user.stat) - return - if(!vore_selected) - to_chat(user, SPAN_WARNING("[src] isn't vore capable.")) - return - - vore_selected.fancy_vore = !vore_selected.fancy_vore - to_chat(user, "[src] is now using [vore_selected.fancy_vore ? "Fancy" : "Classic"] vore sounds.") - -/mob/living/simple_mob/attackby(var/obj/item/O, var/mob/user) - if(!istype(src.ai_holder, /datum/ai_holder/polaris)) - return ..() - var/datum/ai_holder/polaris/ai_holder = src.ai_holder - if (istype(O, /obj/item/newspaper) && !(ckey || (ai_holder.hostile && faction != user.faction)) && isturf(user.loc)) - if (ai_holder.retaliate && prob(vore_pounce_chance/2)) // This is a gamble! - user.afflict_paralyze(20 * 5) //They get tackled anyway whether they're edible or not. - user.visible_message("\the [user] swats \the [src] with \the [O] and promptly gets tackled!!") - if (will_eat(user)) - set_AI_busy(TRUE) - animal_nom(user) - update_icon() - set_AI_busy(FALSE) - else if (!ai_holder.target) // no using this to clear a retaliate mob's target - ai_holder.target = user //just because you're not tasty doesn't mean you get off the hook. A swat for a swat. - else - user.visible_message("\the [user] swats \the [src] with \the [O]!!") - release_vore_contents() - for(var/mob/living/L in living_mobs(0)) //add everyone on the tile to the do-not-eat list for a while - if(!(L in prey_excludes)) // Unless they're already on it, just to avoid fuckery. - prey_excludes += L - spawn(5 MINUTES) - if(src && L) - prey_excludes -= L - else - ..() diff --git a/code/modules/vore/fluffstuff/custom_items.dm b/code/modules/vore/fluffstuff/custom_items.dm index 0ac75f8d2924..2266c6708b9f 100644 --- a/code/modules/vore/fluffstuff/custom_items.dm +++ b/code/modules/vore/fluffstuff/custom_items.dm @@ -66,50 +66,6 @@ if(!parts) qdel(src) -/* -//JoanRisu:Joan Risu -/obj/item/flame/lighter/zippo/fluff/joan - name = "Federation Zippo Lighter" - desc = "A red zippo lighter with the United Federation Logo on it." - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "joanzip" - -//JoanRisu:Joan Risu -/obj/item/sword/fluff/joanaria - name = "Aria" - desc = "A beautifully crafted rapier owned by Joan Risu. It has a thin blade and is used for quick attacks." - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "joanaria" - icon_override = 'icons/vore/custom_items_vr.dmi' - item_state = "joanariamob" - origin_tech = "materials=7" - damage_force = 15 - sharp = 1 - edge = 1 - attack_sound = 'sound/weapons/bladeslice.ogg' - - -/obj/item/sword/fluff/joanaria/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - - if(default_parry_check(user, attacker, damage_source) && prob(75)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - -//joanrisu:Katarina Eine -/obj/item/material/knife/tacknife/combatknife/fluff/katarina - name = "tactical Knife" - desc = "A tactical knife with a small butterly engraved on the blade." -*/ - -/obj/item/material/knife/tacknife/combatknife/fluff/katarina/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - - if(default_parry_check(user, attacker, damage_source) && prob(75)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 //For General use /obj/item/sword/fluff/joanaria/scisword @@ -119,87 +75,6 @@ icon_state = "scisword" origin_tech = "materials=7" -/* -//john.wayne9392:Harmony Prechtl -/obj/item/twohanded/fireaxe/fluff/mjollnir - name = "Mjollnir" - desc = "Large hammer that looks like it can do a great deal of damage if properly used." - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "harmonymjollnir" - origin_tech = "materials=7" - attack_verb = list("attacked", "hammered", "smashed", "slammed", "crushed") - -//JoanRisu:Joan Risu -/obj/item/card/id/centcom/station/fluff/joanbadge - name = "Faded Badge" - desc = "A faded badge, backed with leather, that reads 'NT Security Force' across the front." - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "joanbadge" - registered_name = "Joan Risu" - assignment = "Centcom Officer" - - -/obj/item/card/id/centcom/station/fluff/joanbadge/attack_self(mob/user) - . = ..() - if(.) - return - if(isliving(user)) - user.visible_message("[user] flashes their golden security badge.\nIt reads:NT Security.","You display the faded badge.\nIt reads: NT Security.") - -/obj/item/card/id/centcom/station/fluff/joanbadge/attack(mob/living/carbon/human/M, mob/living/user) - if(isliving(user)) - user.visible_message("[user] invades [M]'s personal space, thrusting [src] into their face insistently.","You invade [M]'s personal space, thrusting [src] into their face insistently.") - -//JoanRisu:Joan Risu -/obj/item/pda/heads/hos/joanpda - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "pda-joan" - -//Vorrarkul:Lucina Dakarim -/obj/item/pda/heads/cmo/fluff/lucinapda - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "pda-lucina" - -//john.wayne9392:Harmony Prechtl -/obj/item/modkit_conversion/fluff/harmonyspace - name = "Harmony's captain space suit modkit" - desc = "A kit containing all the needed tools and parts to modify a Captain's hardsuit. It has green and yellow parts inside." - - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "harmony_kit" - - from_helmet = /obj/item/clothing/head/helmet/space/capspace - from_suit = /obj/item/clothing/suit/armor/captain - to_helmet = /obj/item/clothing/head/helmet/space/capspace/fluff/harmhelm - to_suit = /obj/item/clothing/suit/armor/captain/fluff/harmsuit - -//john.wayne9392:Harmony Prechtl -/obj/item/modkit_conversion/fluff/harmonysuit - name = "Harmony's captain suit modkit" - desc = "A sewing kit containing all the needed tools and fabric to modify a Captain's suit and hat. It has green and yellow fabrics inside." - - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "harmony_kit" - - from_helmet = /obj/item/clothing/head/caphat - from_suit = /obj/item/clothing/under/rank/captain - to_helmet = /obj/item/clothing/head/centhat/fluff/harmhat - to_suit = /obj/item/clothing/under/rank/captain/fluff/harmuniform - -//scree:Scree -/obj/item/modkit_conversion/fluff/screekit - name = "Scree's hardsuit modification kit" - desc = "A kit containing all the needed tools and parts to modify a hardsuit for a specific user. This one looks like it's fitted for a winged creature." - - icon = 'icons/vore/custom_items_vr.dmi' - icon_state = "modkit" - - from_helmet = /obj/item/clothing/head/helmet/space/void - from_suit = /obj/item/clothing/suit/space/void - to_helmet = /obj/item/clothing/head/helmet/space/void/engineering/hazmat/fluff/screehelm - to_suit = /obj/item/clothing/suit/space/void/engineering/hazmat/fluff/screespess -*/ - //General Use /obj/item/flag name = "Nanotrasen Banner" @@ -722,7 +597,7 @@ ..() -/obj/item/perfect_tele/attack_hand(mob/user, list/params) +/obj/item/perfect_tele/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(user.get_inactive_held_item() == src && power_source) to_chat(user,"You eject \the [power_source] from \the [src].") user.put_in_hands(power_source) @@ -984,7 +859,7 @@ tele_hand = null return ..() -/obj/item/perfect_tele_beacon/attack_hand(mob/user, list/params) +/obj/item/perfect_tele_beacon/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if((user.ckey != creator) && !(user.ckey in warned_users)) warned_users |= user.ckey var/choice = alert(user,"This device is a translocator beacon. Having it on your person may mean that anyone \ @@ -1254,6 +1129,7 @@ ..() //jacknoir413:Areax Third +// todo: check sprite, if it matches citmain just integrate this to citrp proper. /obj/item/melee/baton/fluff/stunstaff name = "Electrostaff" desc = "Six-foot long staff from dull, rugged metal, with two thin spikes protruding from each end. Small etching near to the middle of it reads 'Children Of Nyx Facilities: Product No. 12'." @@ -1270,6 +1146,10 @@ attack_verb = list("beaten") lightcolor = "#CC33FF" + passive_parry = /datum/passive_parry/melee{ + parry_chance_melee = 30; + } + //Two Handed var/wielded = 0 var/base_name = "stunstaff" @@ -1294,13 +1174,6 @@ update_icon() ..() -/obj/item/melee/baton/fluff/stunstaff/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") - if(wielded && default_parry_check(user, attacker, damage_source) && prob(30)) - user.visible_message("\The [user] parries [attack_text] with \the [src]!") - playsound(user.loc, 'sound/weapons/punchmiss.ogg', 50, 1) - return 1 - return 0 - /obj/item/melee/baton/fluff/stunstaff/update_icon() icon_state = "[base_icon][wielded][status]" item_state = icon_state diff --git a/code/modules/vore/weight/fitness_machines_vr.dm b/code/modules/vore/weight/fitness_machines_vr.dm index 1af486035e79..5e27217d991d 100644 --- a/code/modules/vore/weight/fitness_machines_vr.dm +++ b/code/modules/vore/weight/fitness_machines_vr.dm @@ -11,7 +11,7 @@ var/cooldown = 10 var/weightloss_power = 1 -/obj/machinery/fitness/attack_hand(mob/user, list/params) +/obj/machinery/fitness/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return @@ -68,7 +68,7 @@ playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) return -/obj/machinery/fitness/heavy/attack_hand(mob/user, list/params) +/obj/machinery/fitness/heavy/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(!anchored) to_chat(user, "For safety reasons, you are required to have this equipment wrenched down before using it!") return @@ -98,7 +98,7 @@ idle_power_usage = 0 active_power_usage = 0 -/obj/machinery/scale/attack_hand(mob/user, list/params) +/obj/machinery/scale/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) . = ..() if(.) return diff --git a/code/modules/xenoarcheaology/anomaly_container.dm b/code/modules/xenoarcheaology/anomaly_container.dm index fc2efc0ad1a6..85d76cf6521f 100644 --- a/code/modules/xenoarcheaology/anomaly_container.dm +++ b/code/modules/xenoarcheaology/anomaly_container.dm @@ -14,7 +14,7 @@ if(A) contain(A) -/obj/structure/anomaly_container/attack_hand(mob/user, list/params) +/obj/structure/anomaly_container/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) release() /obj/structure/anomaly_container/attack_robot(var/mob/user) diff --git a/code/modules/xenoarcheaology/artifacts/artifact.dm b/code/modules/xenoarcheaology/artifacts/artifact.dm index b29df3796257..54ffea27f32b 100644 --- a/code/modules/xenoarcheaology/artifacts/artifact.dm +++ b/code/modules/xenoarcheaology/artifacts/artifact.dm @@ -4,6 +4,7 @@ icon = 'icons/obj/xenoarchaeology.dmi' icon_state = "ano00" var/icon_num = 0 + integrity_flags = INTEGRITY_INDESTRUCTIBLE density = TRUE var/datum/artifact_effect/my_effect var/datum/artifact_effect/secondary_effect @@ -174,7 +175,7 @@ if(secondary_effect && secondary_effect.trigger == TRIGGER_NITRO && !secondary_effect.activated) secondary_effect.ToggleActivate(0) -/obj/machinery/artifact/attack_hand(mob/user, list/params) +/obj/machinery/artifact/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if (get_dist(user, src) > 1) to_chat(user, "You can't reach [src] from here.") return @@ -223,7 +224,7 @@ if(secondary_effect && secondary_effect.trigger == TRIGGER_TOXIN && prob(25)) secondary_effect.ToggleActivate(0) else if(istype(W,/obj/item/melee/baton) && W:status ||\ - istype(W,/obj/item/melee/energy) ||\ + istype(W,/obj/item/melee/transforming/energy) ||\ istype(W,/obj/item/melee/cultblade) ||\ istype(W,/obj/item/card/emag) ||\ istype(W,/obj/item/multitool)) @@ -274,16 +275,17 @@ to_chat(M, "You accidentally touch [src].") ..() -/obj/machinery/artifact/bullet_act(var/obj/projectile/P) - if(istype(P,/obj/projectile/bullet)) +/obj/machinery/artifact/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(istype(proj,/obj/projectile/bullet)) if(my_effect.trigger == TRIGGER_FORCE) my_effect.ToggleActivate() if(secondary_effect && secondary_effect.trigger == TRIGGER_FORCE && prob(25)) secondary_effect.ToggleActivate(0) - else if(istype(P,/obj/projectile/beam) ||\ - istype(P,/obj/projectile/ion) ||\ - istype(P,/obj/projectile/energy)) + else if(istype(proj,/obj/projectile/beam) ||\ + istype(proj,/obj/projectile/ion) ||\ + istype(proj,/obj/projectile/energy)) if(my_effect.trigger == TRIGGER_ENERGY) my_effect.ToggleActivate() if(secondary_effect && secondary_effect.trigger == TRIGGER_ENERGY && prob(25)) diff --git a/code/modules/xenoarcheaology/artifacts/gigadrill.dm b/code/modules/xenoarcheaology/artifacts/gigadrill.dm index 7f54c8337021..73a9c119aef5 100644 --- a/code/modules/xenoarcheaology/artifacts/gigadrill.dm +++ b/code/modules/xenoarcheaology/artifacts/gigadrill.dm @@ -9,7 +9,7 @@ density = 1 layer = ABOVE_JUNK_LAYER -/obj/machinery/giga_drill/attack_hand(mob/user, list/params) +/obj/machinery/giga_drill/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(active) active = 0 icon_state = "gigadrill" diff --git a/code/modules/xenoarcheaology/artifacts/replicator.dm b/code/modules/xenoarcheaology/artifacts/replicator.dm index 8b9f81e9e511..726c86594f69 100644 --- a/code/modules/xenoarcheaology/artifacts/replicator.dm +++ b/code/modules/xenoarcheaology/artifacts/replicator.dm @@ -138,7 +138,7 @@ last_process_time = world.time -/obj/machinery/replicator/attack_hand(mob/user, list/params) +/obj/machinery/replicator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/replicator/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/xenoarcheaology/finds/talking.dm b/code/modules/xenoarcheaology/finds/talking.dm index c6527c6dc84c..c3460957bd2c 100644 --- a/code/modules/xenoarcheaology/finds/talking.dm +++ b/code/modules/xenoarcheaology/finds/talking.dm @@ -114,8 +114,6 @@ continue //skip monkeys and leavers if (istype(M, /mob/new_player)) continue - if(M.stat == DEAD && M.get_preference_toggle(/datum/game_preference_toggle/observer/ghost_ears)) - listening|=M for(var/mob/M in listening) to_chat(M, "[icon2html(thing = holder_atom, target = M)] [holder_atom] reverberates, \"[msg]\"") diff --git a/code/modules/xenoarcheaology/tools/artifact_analyser.dm b/code/modules/xenoarcheaology/tools/artifact_analyser.dm index b10e28b38bf5..40bbe0f242a5 100644 --- a/code/modules/xenoarcheaology/tools/artifact_analyser.dm +++ b/code/modules/xenoarcheaology/tools/artifact_analyser.dm @@ -24,7 +24,7 @@ if(!owned_scanner) owned_scanner = locate(/obj/machinery/artifact_scanpad) in orange(1, src) -/obj/machinery/artifact_analyser/attack_hand(mob/user, list/params) +/obj/machinery/artifact_analyser/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (NOPOWER|BROKEN) || get_dist(src, user) > 1) return diff --git a/code/modules/xenoarcheaology/tools/artifact_harvester.dm b/code/modules/xenoarcheaology/tools/artifact_harvester.dm index b036b06a210d..61bd66103705 100644 --- a/code/modules/xenoarcheaology/tools/artifact_harvester.dm +++ b/code/modules/xenoarcheaology/tools/artifact_harvester.dm @@ -35,7 +35,7 @@ else return..() -/obj/machinery/artifact_harvester/attack_hand(mob/user, list/params) +/obj/machinery/artifact_harvester/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) add_fingerprint(user) if(machine_stat & (NOPOWER|BROKEN)) return diff --git a/code/modules/xenoarcheaology/tools/coolant_tank.dm b/code/modules/xenoarcheaology/tools/coolant_tank.dm index ba9ac92b9f42..feeeadb1fca8 100644 --- a/code/modules/xenoarcheaology/tools/coolant_tank.dm +++ b/code/modules/xenoarcheaology/tools/coolant_tank.dm @@ -9,9 +9,10 @@ . = ..() reagents.add_reagent("coolant", 1000) -/obj/structure/reagent_dispensers/coolanttank/bullet_act(var/obj/projectile/Proj) - if(Proj.get_structure_damage()) - if(!istype(Proj ,/obj/projectile/beam/lasertag) && !istype(Proj ,/obj/projectile/beam/practice) ) // TODO: make this not terrible +/obj/structure/reagent_dispensers/coolanttank/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) + . = ..() + if(proj.get_structure_damage()) + if(!istype(proj ,/obj/projectile/beam/lasertag) && !istype(proj ,/obj/projectile/beam/practice) ) // TODO: make this not terrible explode() /obj/structure/reagent_dispensers/coolanttank/legacy_ex_act() diff --git a/code/modules/xenoarcheaology/tools/geosample_scanner.dm b/code/modules/xenoarcheaology/tools/geosample_scanner.dm index 8fa28dece263..99d9a99d3095 100644 --- a/code/modules/xenoarcheaology/tools/geosample_scanner.dm +++ b/code/modules/xenoarcheaology/tools/geosample_scanner.dm @@ -115,7 +115,7 @@ if(total_purity && fresh_coolant) coolant_purity = total_purity / fresh_coolant -/obj/machinery/radiocarbon_spectrometer/attack_hand(mob/user, list/params) +/obj/machinery/radiocarbon_spectrometer/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) ui_interact(user) /obj/machinery/radiocarbon_spectrometer/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/modules/xenoarcheaology/tools/suspension_generator.dm b/code/modules/xenoarcheaology/tools/suspension_generator.dm index 555e3fb8b71b..9d94bddb67ca 100644 --- a/code/modules/xenoarcheaology/tools/suspension_generator.dm +++ b/code/modules/xenoarcheaology/tools/suspension_generator.dm @@ -35,7 +35,7 @@ else deactivate() -/obj/machinery/suspension_gen/attack_hand(mob/user, list/params) +/obj/machinery/suspension_gen/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(panel_open) if(cell) to_chat(user, SPAN_NOTICE("You remove [cell].")) diff --git a/code/modules/xenobio/items/slimepotions.dm b/code/modules/xenobio/items/slimepotions.dm index fcd47d828ed8..8e663c9f84e4 100644 --- a/code/modules/xenobio/items/slimepotions.dm +++ b/code/modules/xenobio/items/slimepotions.dm @@ -204,6 +204,7 @@ qdel(src) // Makes slimes not kill (most) humanoids but still fight spiders/carp/bears/etc. +// todo: this is janky as shit /obj/item/slimepotion/loyalty name = "slime loyalty agent" desc = "A potent chemical mix that makes an animal deeply loyal to the species of whoever applies this, and will attack threats to them." @@ -225,7 +226,7 @@ if(S.stat == DEAD) to_chat(user, SPAN_WARNING("The animal is dead!")) return - if(S.faction == user.faction) + if(S.shares_iff_faction(user)) to_chat(user, SPAN_WARNING("\The [S] is already loyal to your species!")) return if(!S.has_polaris_AI()) @@ -239,7 +240,7 @@ to_chat(user, SPAN_NOTICE("You feed \the [S] the agent. It will now try to murder things that want to murder you instead.")) to_chat(S, SPAN_NOTICE("\The [user] feeds you \the [src], and feel that the others will regard you as an outsider now.")) - S.faction = user.faction + S.copy_iff_factions() AI.lost_target() // So hostile things stop attacking people even if not hostile anymore. playsound(src, 'sound/effects/bubbles.ogg', 50, 1) qdel(src) diff --git a/code/modules/xenobio/items/weapons.dm b/code/modules/xenobio/items/weapons.dm index 243054319cad..e3a2c4b73cde 100644 --- a/code/modules/xenobio/items/weapons.dm +++ b/code/modules/xenobio/items/weapons.dm @@ -91,7 +91,11 @@ /obj/projectile/beam/stun/xeno/weak //Weaker variant for non-research equipment, turrets, or rapid fire types. agony = 3 -/obj/projectile/beam/stun/xeno/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) +/obj/projectile/beam/stun/xeno/on_impact(atom/target, impact_flags, def_zone, efficiency) + . = ..() + if(. & PROJECTILE_IMPACT_FLAGS_UNCONDITIONAL_ABORT) + return + if(istype(target, /mob/living)) var/mob/living/L = target if(L.mob_class & MOB_CLASS_SLIME) @@ -106,5 +110,3 @@ if(H.species && H.species.get_species_id() == SPECIES_ID_PROMETHEAN) if(agony == initial(agony)) // ?????? agony = round((14 * agony) - agony) //60-4 = 56, 56 / 4 = 14. Prior was flat 60 - agony of the beam to equate to 60. - - ..() diff --git a/code/modules/xenobio/machinery/processor.dm b/code/modules/xenobio/machinery/processor.dm index 600a748c01c6..3e0f9474ee46 100644 --- a/code/modules/xenobio/machinery/processor.dm +++ b/code/modules/xenobio/machinery/processor.dm @@ -29,7 +29,7 @@ . += SPAN_NOTICE("It looks slick enough to let slimes glide over it.") . += SPAN_BOLDNOTICE("The automatic intake switch is in the [auto_mode? "On" : "Off"] position.") -/obj/machinery/processor/attack_hand(mob/user, list/params) +/obj/machinery/processor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(processing) to_chat(user, SPAN_WARNING("The processor is in the process of processing!")) return diff --git a/code/modules/xenobio2/machinery/core_extractor.dm b/code/modules/xenobio2/machinery/core_extractor.dm index c7ae4f1dc3fd..7126bf529979 100644 --- a/code/modules/xenobio2/machinery/core_extractor.dm +++ b/code/modules/xenobio2/machinery/core_extractor.dm @@ -145,7 +145,7 @@ eject_slime() //Here lies the UI -/obj/machinery/slime/extractor/attack_hand(mob/user, list/params) +/obj/machinery/slime/extractor/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) interact(user) diff --git a/code/modules/xenobio2/machinery/gene_manipulators.dm b/code/modules/xenobio2/machinery/gene_manipulators.dm index 5599d09ff8d7..548fdfae7b89 100644 --- a/code/modules/xenobio2/machinery/gene_manipulators.dm +++ b/code/modules/xenobio2/machinery/gene_manipulators.dm @@ -59,7 +59,7 @@ /obj/machinery/xenobio/attack_ai(mob/user as mob) return attack_hand(user) -/obj/machinery/xenobio/attack_hand(mob/user, list/params) +/obj/machinery/xenobio/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) nano_ui_interact(user) /obj/machinery/xenobio/attackby(obj/item/W as obj, mob/user as mob) diff --git a/code/modules/xenobio2/machinery/injector_computer.dm b/code/modules/xenobio2/machinery/injector_computer.dm index da929dd1257a..2da0a6d0573c 100644 --- a/code/modules/xenobio2/machinery/injector_computer.dm +++ b/code/modules/xenobio2/machinery/injector_computer.dm @@ -23,7 +23,7 @@ injector.computer = null ..() -/obj/machinery/computer/xenobio2/attack_hand(mob/user, list/params) +/obj/machinery/computer/xenobio2/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(..()) return 1 nano_ui_interact(user) diff --git a/code/modules/xenobio2/machinery/slime_replicator.dm b/code/modules/xenobio2/machinery/slime_replicator.dm index edc019043cfa..a36c97634b01 100644 --- a/code/modules/xenobio2/machinery/slime_replicator.dm +++ b/code/modules/xenobio2/machinery/slime_replicator.dm @@ -99,7 +99,7 @@ eject_core() //Here lies the UI -/obj/machinery/slime/replicator/attack_hand(mob/user, list/params) +/obj/machinery/slime/replicator/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) user.set_machine(src) interact(user) diff --git a/code/modules/xenobio2/mob/xeno procs.dm b/code/modules/xenobio2/mob/xeno procs.dm index ad6846e281e0..99c7e66db44e 100644 --- a/code/modules/xenobio2/mob/xeno procs.dm +++ b/code/modules/xenobio2/mob/xeno procs.dm @@ -143,7 +143,7 @@ Divergence proc, used in mutation to make unique datums. /mob/living/simple_mob/xeno/proc/BuildReagentLists() return -/mob/living/simple_mob/xeno/bullet_act(var/obj/projectile/P) +/mob/living/simple_mob/xeno/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) //Shamelessly stolen from ablative armor. if((traitdat.traits[TRAIT_XENO_CHROMATIC]) && istype(P, /obj/projectile/beam)) visible_message(")\The beam reflects off of the [src]!") diff --git a/code/modules/xenobio2/mob/xeno.dm b/code/modules/xenobio2/mob/xeno.dm index 4f801e75df5f..6b9cef60afb4 100644 --- a/code/modules/xenobio2/mob/xeno.dm +++ b/code/modules/xenobio2/mob/xeno.dm @@ -92,7 +92,7 @@ Also includes Life and New if(!health) set_stat(DEAD) -/mob/living/simple_mob/xeno/bullet_act(var/obj/projectile/Proj) +/mob/living/simple_mob/xeno/on_bullet_act(obj/projectile/proj, impact_flags, list/bullet_act_args) if(istype(Proj, /obj/projectile/beam/stun/xeno)) var/obj/projectile/beam/stun/xeno/hit = Proj stasis += hit.stasisforce diff --git a/config/entries/game.txt b/config/entries/game.txt index 8f155dab9f77..b83f9f2e7cde 100644 --- a/config/entries/game.txt +++ b/config/entries/game.txt @@ -11,15 +11,9 @@ STARLIGHT ## Set to 0 to disable holidays (you monster) ALLOW_HOLIDAYS -## Engine submap probabilities -## Supermatter -ENGINE_SUBMAP EngineSubmap_SM 40 -## R-UST Fusion -ENGINE_SUBMAP EngineSubmap_RUST 40 -## Tesla -ENGINE_SUBMAP EngineSubmap_Tesla 20 -## Singulo -ENGINE_SUBMAP EngineSubmap_Singulo 20 +## Engine submap probabilities by name +# todo: add the rest once names are standardized +# ENGINE_SUBMAP ProcEngine_Triumph_RUST ## Alert Levels # ALERT_DESC_GREEN TEXT HERE diff --git a/icons/effects/defensive/README.md b/icons/effects/defensive/README.md new file mode 100644 index 000000000000..750cab6abc41 --- /dev/null +++ b/icons/effects/defensive/README.md @@ -0,0 +1,3 @@ +# Effects - Defensive + +Basically shields, parries, etc. diff --git a/icons/effects/defensive/main_parry.dmi b/icons/effects/defensive/main_parry.dmi new file mode 100644 index 000000000000..307ef5b4a7e8 Binary files /dev/null and b/icons/effects/defensive/main_parry.dmi differ diff --git a/icons/items/melee/basic.dmi b/icons/items/melee/basic.dmi new file mode 100644 index 000000000000..c1207c837d18 Binary files /dev/null and b/icons/items/melee/basic.dmi differ diff --git a/icons/items/melee/transforming.dmi b/icons/items/melee/transforming.dmi new file mode 100644 index 000000000000..0a1c652185cf Binary files /dev/null and b/icons/items/melee/transforming.dmi differ diff --git a/icons/items/shields/basic.dmi b/icons/items/shields/basic.dmi new file mode 100644 index 000000000000..6afb78610041 Binary files /dev/null and b/icons/items/shields/basic.dmi differ diff --git a/icons/items/shields/transforming.dmi b/icons/items/shields/transforming.dmi new file mode 100644 index 000000000000..56d1329a3486 Binary files /dev/null and b/icons/items/shields/transforming.dmi differ diff --git a/icons/mapping/helpers/electrochromatic_helper.dmi b/icons/mapping/helpers/electrochromatic_helper.dmi new file mode 100644 index 000000000000..13963d331259 Binary files /dev/null and b/icons/mapping/helpers/electrochromatic_helper.dmi differ diff --git a/icons/mob/124x124_enigma.dmi b/icons/mob/124x124_enigma.dmi new file mode 100644 index 000000000000..475110c4fdf0 Binary files /dev/null and b/icons/mob/124x124_enigma.dmi differ diff --git a/icons/mob/enigma.dmi b/icons/mob/enigma.dmi new file mode 100644 index 000000000000..689b6257c634 Binary files /dev/null and b/icons/mob/enigma.dmi differ diff --git a/icons/mob/items/lefthand_melee.dmi b/icons/mob/items/lefthand_melee.dmi index 9b9c5035fdf9..9d8c1daa0d54 100644 Binary files a/icons/mob/items/lefthand_melee.dmi and b/icons/mob/items/lefthand_melee.dmi differ diff --git a/icons/mob/items/righthand_melee.dmi b/icons/mob/items/righthand_melee.dmi index 9609132d0a7b..1cfcd5f622c5 100644 Binary files a/icons/mob/items/righthand_melee.dmi and b/icons/mob/items/righthand_melee.dmi differ diff --git a/icons/mob/sprite_accessories/markings/tesh_stuff.dmi b/icons/mob/sprite_accessories/markings/tesh_stuff.dmi index 9f642fdfe98d..bce2ee9eef75 100644 Binary files a/icons/mob/sprite_accessories/markings/tesh_stuff.dmi and b/icons/mob/sprite_accessories/markings/tesh_stuff.dmi differ diff --git a/icons/mob/sprite_accessories/markings/vox_stuff.dmi b/icons/mob/sprite_accessories/markings/vox_stuff.dmi index c1587e1ddbfd..62747f617e28 100644 Binary files a/icons/mob/sprite_accessories/markings/vox_stuff.dmi and b/icons/mob/sprite_accessories/markings/vox_stuff.dmi differ diff --git a/icons/modules/projectiles/casings/slim.dmi b/icons/modules/projectiles/casings/slim.dmi index 034c040811df..6a88fe907a21 100644 Binary files a/icons/modules/projectiles/casings/slim.dmi and b/icons/modules/projectiles/casings/slim.dmi differ diff --git a/icons/obj/weapons.dmi b/icons/obj/weapons.dmi index 1d4e812f6708..cda3aaf191cc 100644 Binary files a/icons/obj/weapons.dmi and b/icons/obj/weapons.dmi differ diff --git a/icons/obj/weapons_vr.dmi b/icons/obj/weapons_vr.dmi index efb220ac8ab6..1ca53b66f4c7 100644 Binary files a/icons/obj/weapons_vr.dmi and b/icons/obj/weapons_vr.dmi differ diff --git a/icons/obj/xenoarchaeology.dmi b/icons/obj/xenoarchaeology.dmi index 8f90645f6094..36e0634dba2a 100644 Binary files a/icons/obj/xenoarchaeology.dmi and b/icons/obj/xenoarchaeology.dmi differ diff --git a/maps/away_missions/140x140/listeningpost.dmm b/maps/away_missions/140x140/listeningpost.dmm index 1789fec7c818..b9abaf16d1bd 100644 --- a/maps/away_missions/140x140/listeningpost.dmm +++ b/maps/away_missions/140x140/listeningpost.dmm @@ -61,8 +61,7 @@ dir = 4 }, /mob/living/simple_mob/humanoid/merc{ - desc = "A weary looking syndicate operative."; - faction = "syndicate" + desc = "A weary looking syndicate operative." }, /turf/simulated/floor, /area/awaymission/listeningpost) diff --git a/maps/away_missions/140x140/zoo.dmm b/maps/away_missions/140x140/zoo.dmm index e4b346826209..e69738ee3168 100644 --- a/maps/away_missions/140x140/zoo.dmm +++ b/maps/away_missions/140x140/zoo.dmm @@ -73,7 +73,7 @@ /obj/item/stolenpackage, /obj/item/plastique, /obj/item/plastique, -/obj/item/melee/energy/sword/pirate, +/obj/item/melee/transforming/energy/sword/cutlass, /turf/space, /area/awaymission/zoo) "aj" = ( @@ -1539,7 +1539,7 @@ /area/awaymission/zoo/pirateship) "eB" = ( /mob/living/simple_mob/animal/space/alien{ - faction = "pirate" + iff_factions = "pirate" }, /turf/simulated/floor/plating, /area/awaymission/zoo/pirateship) @@ -2843,7 +2843,7 @@ "ik" = ( /mob/living/simple_mob/humanoid/merc/ranged{ desc = "He doesn't seem like he's looking for a fight."; - faction = "neutral"; + iff_factions = "neutral"; friendly = "hugs"; name = "Mercenary" }, @@ -3606,7 +3606,7 @@ /area/awaymission/zoo) "jT" = ( /mob/living/simple_mob/animal/passive/chicken{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -3620,7 +3620,7 @@ /area/awaymission/zoo) "jV" = ( /mob/living/simple_mob/animal/passive/dog/corgi/puppy{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -3847,7 +3847,7 @@ "kH" = ( /obj/structure/flora/ausbushes/ywflowers, /mob/living/simple_mob/animal/passive/bird/parrot/kea{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -3862,7 +3862,7 @@ "kK" = ( /obj/structure/flora/ausbushes/pointybush, /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -3933,7 +3933,7 @@ /area/awaymission/zoo) "kV" = ( /mob/living/simple_mob/animal/passive/cat{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/wood, /area/awaymission/zoo) @@ -3981,7 +3981,7 @@ /area/awaymission/zoo) "le" = ( /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -3992,7 +3992,7 @@ "lg" = ( /obj/structure/flora/ausbushes/fullgrass, /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -4003,14 +4003,14 @@ "li" = ( /obj/structure/flora/ausbushes/brflowers, /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) "lj" = ( /obj/structure/flora/ausbushes/ppflowers, /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar/bluegreen{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -4028,7 +4028,7 @@ /area/awaymission/zoo) "lm" = ( /mob/living/simple_mob/horse{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/wood, /area/awaymission/zoo) @@ -4167,14 +4167,14 @@ /area/awaymission/zoo) "lI" = ( /mob/living/simple_mob/animal/passive/bird/parrot/budgerigar{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) "lJ" = ( /obj/structure/flora/ausbushes/sunnybush, /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -4188,7 +4188,7 @@ /area/awaymission/zoo) "lM" = ( /mob/living/simple_mob/animal/passive/cow{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -4280,13 +4280,13 @@ /area/awaymission/zoo) "mb" = ( /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/yellowish{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) "mc" = ( /mob/living/simple_mob/animal/passive/bird/parrot/white_caique{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -4798,7 +4798,7 @@ /area/awaymission/zoo) "nt" = ( /mob/living/simple_mob/animal/passive/tindalos{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass0" @@ -5073,7 +5073,7 @@ /area/awaymission/zoo) "of" = ( /mob/living/simple_mob/vore/aggressive/frog{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass{ icon_state = "grass2" @@ -5104,7 +5104,7 @@ /area/awaymission/zoo) "oj" = ( /mob/living/simple_mob/animal/passive/tindalos{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass1" @@ -5173,7 +5173,7 @@ /area/awaymission/zoo) "ov" = ( /mob/living/simple_mob/animal/passive/dog/corgi{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/lino, /area/awaymission/zoo) @@ -5204,7 +5204,7 @@ "oA" = ( /obj/structure/flora/ausbushes/lavendergrass, /mob/living/simple_mob/animal/passive/cat{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass4" @@ -5212,7 +5212,7 @@ /area/awaymission/zoo) "oB" = ( /mob/living/simple_mob/vore/bee{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass{ icon_state = "hive"; @@ -5280,7 +5280,7 @@ /area/awaymission/zoo) "oL" = ( /mob/living/simple_mob/vore/aggressive/giant_snake{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass3" @@ -5336,7 +5336,7 @@ /area/awaymission/zoo) "oU" = ( /mob/living/simple_mob/animal/space/bats{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -5435,13 +5435,13 @@ /area/awaymission/zoo) "pg" = ( /mob/living/carbon/human/sharkm{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/water, /area/awaymission/zoo) "ph" = ( /mob/living/simple_mob/animal/passive/tindalos{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass2" @@ -5461,7 +5461,7 @@ /area/awaymission/zoo) "pk" = ( /mob/living/simple_mob/vore/aggressive/giant_snake{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -5475,7 +5475,7 @@ /area/awaymission/zoo) "pm" = ( /mob/living/simple_mob/animal/passive/cat{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass3" @@ -5568,7 +5568,7 @@ /area/awaymission/zoo) "pB" = ( /mob/living/simple_mob/animal/passive/tindalos{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -5588,7 +5588,7 @@ /area/awaymission/zoo) "pE" = ( /mob/living/simple_mob/animal/passive/cat{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass2" @@ -6005,7 +6005,7 @@ /area/awaymission/zoo) "qJ" = ( /mob/living/carbon/human/sparram{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -6044,7 +6044,7 @@ /area/awaymission/zoo) "qR" = ( /mob/living/simple_mob/animal/passive/mouse{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/plating, /area/awaymission/zoo) @@ -6092,7 +6092,7 @@ /area/awaymission/zoo) "rb" = ( /mob/living/carbon/human/neaera{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass{ icon_state = "grass2" @@ -6129,7 +6129,7 @@ /area/awaymission/zoo) "rf" = ( /mob/living/simple_mob/animal/sif/diyaab{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -6374,7 +6374,7 @@ /area/awaymission/zoo) "rL" = ( /mob/living/simple_mob/animal/space/alien{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -6434,7 +6434,7 @@ /area/awaymission/zoo) "rS" = ( /mob/living/simple_mob/animal/space/alien/drone{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -6475,7 +6475,7 @@ /area/awaymission/zoo) "rW" = ( /mob/living/simple_mob/animal/passive/penguin{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -6499,7 +6499,7 @@ /area/awaymission/zoo) "rZ" = ( /mob/living/simple_mob/animal/passive/snake{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass1" @@ -6515,7 +6515,7 @@ name = "rocky edge" }, /mob/living/simple_mob/animal/space/alien/drone{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -6535,28 +6535,28 @@ "sc" = ( /obj/effect/floor_decal/industrial/outline/yellow, /mob/living/simple_mob/vore/aggressive/mimic{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/tiled/neutral, /area/awaymission/zoo) "sd" = ( /obj/structure/flora/ausbushes/fullgrass, /mob/living/simple_mob/animal/passive/penguin{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) "se" = ( /obj/effect/floor_decal/asteroid, /mob/living/simple_mob/animal/space/bear{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "sf" = ( /obj/structure/flora/grass/both, /mob/living/simple_mob/animal/passive/penguin{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -6584,7 +6584,7 @@ /area/awaymission/zoo) "sl" = ( /mob/living/simple_mob/animal/space/carp{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/space, /area/awaymission/zoo) @@ -6622,7 +6622,7 @@ name = "rocky edge" }, /mob/living/simple_mob/animal/space/alien/drone{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -6646,7 +6646,7 @@ "sq" = ( /obj/effect/spider/stickyweb, /mob/living/simple_mob/animal/giant_spider/hunter{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6655,14 +6655,14 @@ icon_state = "cocoon_large2" }, /mob/living/simple_mob/animal/giant_spider/hunter{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "ss" = ( /obj/effect/spider/stickyweb, /mob/living/simple_mob/animal/giant_spider{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6744,7 +6744,7 @@ name = "rocky edge" }, /mob/living/simple_mob/animal/space/alien{ - faction = "zoo"; + iff_factions = "!bind-map"; name = "invisible alien hunter" }, /turf/simulated/floor/holofloor/desert{ @@ -6760,7 +6760,7 @@ icon_state = "cocoon3" }, /mob/living/simple_mob/animal/giant_spider/hunter{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6769,7 +6769,7 @@ icon_state = "cobweb1" }, /mob/living/simple_mob/animal/giant_spider/hunter{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6778,7 +6778,7 @@ icon_state = "cocoon2" }, /mob/living/simple_mob/animal/giant_spider/nurse{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6787,7 +6787,7 @@ icon_state = "cobweb2" }, /mob/living/simple_mob/animal/giant_spider{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6799,7 +6799,7 @@ icon_state = "cocoon_large1" }, /mob/living/simple_mob/animal/giant_spider{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -6812,19 +6812,19 @@ icon_state = "cocoon_large3" }, /mob/living/simple_mob/animal/giant_spider/hunter{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "sI" = ( /mob/living/simple_mob/animal/giant_spider{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "sJ" = ( /mob/living/simple_mob/animal/passive/bird/parrot/polly{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -6834,13 +6834,13 @@ /area/awaymission/zoo) "sL" = ( /mob/living/simple_mob/animal/space/bear{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "sM" = ( /mob/living/simple_mob/animal/space/alien{ - faction = "zoo"; + iff_factions = "!bind-map"; name = "invisible alien hunter" }, /turf/simulated/floor/holofloor/desert{ @@ -6859,7 +6859,7 @@ /area/awaymission/zoo) "sP" = ( /mob/living/simple_mob/animal/passive/snake{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass4" @@ -6946,7 +6946,7 @@ "ta" = ( /obj/effect/floor_decal/asteroid, /mob/living/simple_mob/animal/space/alien{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -7020,7 +7020,7 @@ /area/awaymission/zoo) "ti" = ( /mob/living/simple_mob/animal/passive/yithian{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/wood{ icon_state = "diona" @@ -7046,7 +7046,7 @@ "tn" = ( /obj/effect/floor_decal/asteroid, /mob/living/carbon/human/stok{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -7064,7 +7064,7 @@ /area/awaymission/zoo) "tq" = ( /mob/living/carbon/human/farwa{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -7074,7 +7074,7 @@ /area/awaymission/zoo) "ts" = ( /mob/living/carbon/human/stok{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -7103,7 +7103,7 @@ "tx" = ( /obj/structure/flora/ausbushes/sunnybush, /mob/living/simple_mob/animal/passive/yithian{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/wood{ icon_state = "diona" @@ -7123,7 +7123,7 @@ /area/awaymission/zoo) "tA" = ( /mob/living/simple_mob/creature{ - faction = "zoo"; + iff_factions = "!bind-map"; name = "redspace abomination" }, /turf/simulated/floor/cult, @@ -7220,7 +7220,7 @@ "tO" = ( /obj/effect/floor_decal/asteroid, /mob/living/simple_mob/vore/aggressive/dino{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -7308,7 +7308,7 @@ /area/awaymission/zoo) "tX" = ( /mob/living/simple_mob/vore/aggressive/dragon{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert{ icon_state = "asteroidplating"; @@ -7317,19 +7317,19 @@ /area/awaymission/zoo) "tY" = ( /mob/living/simple_mob/animal/passive/lizard{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "tZ" = ( /mob/living/simple_mob/tomato{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) "ua" = ( -/mob/living/simple_mob/fox{ - faction = "zoo" +/mob/living/simple_mob/animal/passive/fox{ + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -7350,7 +7350,7 @@ "uc" = ( /obj/effect/floor_decal/asteroid, /mob/living/simple_mob/vore/aggressive/deathclaw{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) @@ -7370,7 +7370,7 @@ /area/awaymission/zoo) "ue" = ( /mob/living/simple_mob/animal/space/goose{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/outdoors/beach, /area/awaymission/zoo) @@ -7418,46 +7418,46 @@ /area/awaymission/zoo) "ui" = ( /mob/living/simple_mob/vore/aggressive/deathclaw{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "uj" = ( /mob/living/simple_mob/slime/feral/dark_blue{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "uk" = ( /obj/structure/flora/ausbushes/sparsegrass, /mob/living/simple_mob/vore/aggressive/dino{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "ul" = ( /obj/effect/floor_decal/asteroid, /mob/living/simple_mob/animal/passive/lizard{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "um" = ( /obj/landmark/away, /mob/living/simple_mob/animal/space/goose{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/wood, /area/awaymission/zoo) "un" = ( /mob/living/simple_mob/animal/wolf{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) "uo" = ( /mob/living/simple_mob/animal/space/goose{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/wood, /area/awaymission/zoo) @@ -7538,7 +7538,7 @@ "uz" = ( /obj/effect/floor_decal/asteroid, /mob/living/carbon/human/sergallingm{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass2" @@ -7546,7 +7546,7 @@ /area/awaymission/zoo) "uA" = ( /mob/living/simple_mob/animal/space/bear/brown{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -7558,7 +7558,7 @@ /area/awaymission/zoo) "uC" = ( /mob/living/carbon/human/sergallingm{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass4" @@ -7578,7 +7578,7 @@ /area/awaymission/zoo) "uF" = ( /mob/living/simple_mob/animal/passive/dog/tamaskan{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) @@ -7615,7 +7615,7 @@ /area/awaymission/zoo) "uL" = ( /mob/living/carbon/human/sergallingm{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass1" @@ -7623,7 +7623,7 @@ /area/awaymission/zoo) "uM" = ( /mob/living/carbon/human/sergallingm{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass2" @@ -7632,7 +7632,7 @@ "uN" = ( /obj/structure/flora/ausbushes/genericbush, /mob/living/simple_mob/animal/space/bear/brown{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass, /area/awaymission/zoo) @@ -7736,14 +7736,14 @@ /area/awaymission/zoo) "uZ" = ( /mob/living/simple_mob/animal/space/carp/large{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/desert, /area/awaymission/zoo) "va" = ( /obj/structure/flora/ausbushes/fernybush, /mob/living/carbon/human/monkey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass1" @@ -7763,13 +7763,13 @@ /area/awaymission/zoo) "vd" = ( /mob/living/simple_mob/animal/passive/crab{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand, /area/awaymission/zoo) "ve" = ( /mob/living/carbon/human/monkey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass3" @@ -7777,7 +7777,7 @@ /area/awaymission/zoo) "vf" = ( /mob/living/simple_mob/vore/aggressive/panther{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/grass{ icon_state = "grass2" @@ -7845,7 +7845,7 @@ /area/awaymission/zoo) "vl" = ( /mob/living/simple_mob/animal/passive/crab{ - faction = "zoo"; + iff_factions = "!bind-map"; icon_dead = "evilcrab_dead"; icon_living = "evilcrab"; icon_state = "evilcrab" @@ -7861,13 +7861,13 @@ /area/awaymission/zoo) "vn" = ( /mob/living/simple_mob/animal/sif/shantak{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) "vo" = ( /mob/living/carbon/human/monkey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass2" @@ -7876,7 +7876,7 @@ "vp" = ( /mob/living/simple_mob/animal/sif/shantak{ color = ""; - faction = "zoo"; + iff_factions = "!bind-map"; health = 100; maxHealth = 100; name = "alpha shantak" @@ -7885,20 +7885,20 @@ /area/awaymission/zoo) "vq" = ( /mob/living/simple_mob/animal/space/tree{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) "vr" = ( /obj/structure/flora/bush, /mob/living/simple_mob/animal/sif/shantak{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/snow, /area/awaymission/zoo) "vs" = ( /mob/living/carbon/human/monkey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "dgrass0" @@ -7907,7 +7907,7 @@ "vt" = ( /obj/structure/flora/ausbushes/lavendergrass, /mob/living/carbon/human/monkey{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/holofloor/beach/sand{ icon_state = "fullgrass1" @@ -7949,7 +7949,7 @@ /area/awaymission/zoo) "vz" = ( /mob/living/simple_mob/animal/space/bear/polar{ - faction = "zoo" + iff_factions = "!bind-map" }, /turf/simulated/floor/outdoors/beach, /area/awaymission/zoo) diff --git a/maps/away_missions/archive/spacebattle.dmm b/maps/away_missions/archive/spacebattle.dmm index 48106f306f10..0c972979b977 100644 --- a/maps/away_missions/archive/spacebattle.dmm +++ b/maps/away_missions/archive/spacebattle.dmm @@ -270,7 +270,7 @@ /area/awaymission/spacebattle/syndicate3) "bw" = ( /obj/structure/table/reinforced, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/simulated/floor, /area/awaymission/spacebattle/syndicate1) "bx" = ( @@ -888,7 +888,7 @@ /turf/simulated/floor, /area/awaymission/spacebattle/cruiser) "eR" = ( -/obj/item/shield/energy, +/obj/item/shield/transforming/energy, /turf/simulated/floor, /area/awaymission/spacebattle/cruiser) "eT" = ( @@ -929,7 +929,7 @@ /area/awaymission/spacebattle/cruiser) "fa" = ( /obj/spawner/corpse/syndicatesoldier, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/effect/debris/cleanable/blood, /turf/simulated/floor, /area/awaymission/spacebattle/cruiser) diff --git a/maps/away_missions/archive/stationCollision.dmm b/maps/away_missions/archive/stationCollision.dmm index 74a28eeb4968..94520c133f68 100644 --- a/maps/away_missions/archive/stationCollision.dmm +++ b/maps/away_missions/archive/stationCollision.dmm @@ -2777,7 +2777,7 @@ /turf/simulated/floor, /area/awaymission/arrivalblock) "kN" = ( -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/item/clothing/shoes/syndigaloshes, /obj/item/clothing/under/syndicate, /obj/effect/decal/remains/human{ diff --git a/maps/generic/misc.dm b/maps/generic/misc.dm index 3af8d819c540..b511fd2712a1 100644 --- a/maps/generic/misc.dm +++ b/maps/generic/misc.dm @@ -68,7 +68,7 @@ /turf/simulated/floor/maglev/Entered(var/atom/movable/AM, var/atom/old_loc) if(isliving(AM) && prob(50)) track_zap(AM) -/turf/simulated/floor/maglev/attack_hand(mob/user, list/params) +/turf/simulated/floor/maglev/attack_hand(mob/user, datum/event_args/actor/clickchain/e_args) if(prob(75)) track_zap(user) /turf/simulated/floor/maglev/proc/track_zap(var/mob/living/user) diff --git a/maps/rift/levels/rift-02-underground2.dmm b/maps/rift/levels/rift-02-underground2.dmm index 31c630ddd71b..5f9d41e3c720 100644 --- a/maps/rift/levels/rift-02-underground2.dmm +++ b/maps/rift/levels/rift-02-underground2.dmm @@ -7891,6 +7891,9 @@ "CH" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/sun, +/obj/map_helper/electrochromatic_linker{ + id = "ce_office" + }, /turf/simulated/floor, /area/crew_quarters/heads/chief) "CI" = ( @@ -8485,6 +8488,9 @@ "ET" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/palebottlegreen, +/obj/map_helper/electrochromatic_linker{ + id = "chapel_office" + }, /turf/simulated/floor/plating, /area/chapel/office) "EU" = ( @@ -8963,6 +8969,9 @@ "Ha" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/palebottlegreen, +/obj/map_helper/electrochromatic_linker{ + id = "chapel_hall" + }, /turf/simulated/floor/plating, /area/chapel/main) "Hb" = ( diff --git a/maps/rift/levels/rift-04-surface1.dmm b/maps/rift/levels/rift-04-surface1.dmm index 3f2d4abc72c4..42bf056e1e34 100644 --- a/maps/rift/levels/rift-04-surface1.dmm +++ b/maps/rift/levels/rift-04-surface1.dmm @@ -873,6 +873,9 @@ "aFU" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "cmo_office" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/cmo) "aGd" = ( @@ -1702,7 +1705,8 @@ }, /obj/machinery/conveyor_switch{ pixel_y = 12; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 8 @@ -2136,7 +2140,8 @@ "bxV" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/structure/railing, /obj/structure/railing{ @@ -2258,7 +2263,8 @@ }, /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/structure/railing, /turf/simulated/floor/plating, @@ -2381,7 +2387,8 @@ "bFI" = ( /obj/structure/plasticflaps, /obj/machinery/conveyor{ - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/plating, /area/quartermaster/delivery) @@ -3142,6 +3149,9 @@ /area/hallway/primary/surfaceone) "cdW" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/map_helper/electrochromatic_linker{ + id = "iaa" + }, /turf/simulated/floor/plating, /area/lawoffice) "cec" = ( @@ -3286,6 +3296,9 @@ "clK" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/beastybrown, +/obj/map_helper/electrochromatic_linker{ + id = "QM_office" + }, /turf/simulated/floor/plating, /area/quartermaster/qm) "cmb" = ( @@ -3514,6 +3527,9 @@ "ctO" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "patient_room_b" + }, /turf/simulated/floor/plating, /area/medical/patient_a) "cuR" = ( @@ -3679,6 +3695,9 @@ "czx" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "exam_room" + }, /turf/simulated/floor/plating, /area/medical/exam_room) "czN" = ( @@ -6961,7 +6980,8 @@ pixel_x = -32 }, /obj/machinery/conveyor{ - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/plating, /area/quartermaster/delivery) @@ -7936,6 +7956,9 @@ "flS" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "psych_office" + }, /turf/simulated/floor/plating, /area/tether/surfacebase/medical/mentalhealth) "flT" = ( @@ -9100,7 +9123,8 @@ "gbL" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/structure/railing, /obj/machinery/camera/network/cargo, @@ -10301,7 +10325,8 @@ }, /obj/structure/disposalpipe/trunk, /obj/machinery/conveyor{ - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/plating, /area/quartermaster/delivery) @@ -10543,7 +10568,8 @@ dir = 1 }, /obj/machinery/conveyor{ - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/plating, /area/quartermaster/delivery) @@ -15997,6 +16023,9 @@ "kko" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "patient_room_a" + }, /turf/simulated/floor/plating, /area/medical/patient_b) "kku" = ( @@ -18431,7 +18460,8 @@ "lJv" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/machinery/door/blast/shutters{ density = 0; @@ -19447,10 +19477,6 @@ }, /turf/simulated/floor/tiled/monowhite, /area/medical/psych_ward) -"mpA" = ( -/obj/structure/undies_wardrobe, -/turf/simulated/wall/r_wall/prepainted, -/area/rift/surfacebase/outside/outside1) "mpD" = ( /obj/machinery/atmospherics/component/unary/outlet_injector{ id = "violet_protocol"; @@ -23284,6 +23310,9 @@ "oBN" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/darkred, +/obj/map_helper/electrochromatic_linker{ + id = "det_office" + }, /turf/simulated/floor/plating, /area/security/forensics) "oBO" = ( @@ -23926,7 +23955,8 @@ "oTu" = ( /obj/machinery/conveyor_switch{ pixel_y = 12; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/effect/floor_decal/industrial/warning{ dir = 5 @@ -28591,7 +28621,8 @@ "rEL" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/machinery/light{ dir = 8 @@ -30802,7 +30833,8 @@ "sMB" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/machinery/door/firedoor/glass, /obj/structure/plasticflaps/mining, @@ -31830,7 +31862,8 @@ dir = 4 }, /obj/machinery/conveyor{ - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/plating, /area/quartermaster/delivery) @@ -32223,7 +32256,8 @@ }, /obj/machinery/conveyor_switch/oneway{ convdir = -1; - tag = "mailroom" + tag = "mailroom"; + id = "DLBelt" }, /turf/simulated/floor/tiled/steel_grid, /area/quartermaster/delivery) @@ -33143,7 +33177,8 @@ "udl" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/structure/railing, /turf/simulated/floor/plating, @@ -34887,7 +34922,8 @@ "vaP" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /obj/structure/railing, /turf/simulated/floor/plating, @@ -36741,7 +36777,8 @@ "wjJ" = ( /obj/machinery/conveyor{ dir = 4; - tag = "cargoshuttle" + tag = "cargoshuttle"; + id = "CVLoad" }, /turf/simulated/floor/plating, /area/quartermaster/hallway) @@ -46117,7 +46154,7 @@ iFN iFN iFN iFN -mpA +vUA vUA vUA iFN diff --git a/maps/rift/levels/rift-05-surface2.dmm b/maps/rift/levels/rift-05-surface2.dmm index a62116cdac90..16dac551e5bf 100644 --- a/maps/rift/levels/rift-05-surface2.dmm +++ b/maps/rift/levels/rift-05-surface2.dmm @@ -2279,6 +2279,9 @@ spawn_grille = 0 }, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "operating_theatre_2" + }, /turf/simulated/floor/plating, /area/medical/surgery2) "bmY" = ( @@ -2749,6 +2752,9 @@ /obj/structure/window/reinforced/tinted/frosted{ dir = 8 }, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_r" + }, /turf/simulated/floor/tiled/red, /area/security/security_processing) "bBF" = ( @@ -4236,6 +4242,9 @@ /obj/structure/window/reinforced/tinted/frosted{ dir = 4 }, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_l" + }, /turf/simulated/floor/tiled/red, /area/security/security_processing) "cDU" = ( @@ -5753,6 +5762,9 @@ /obj/machinery/holosign/surgery{ id = "operating_theatre_2" }, +/obj/map_helper/electrochromatic_linker{ + id = "operating_theatre_2" + }, /obj/machinery/door/airlock/glass/medical/polarized{ id_tint = "operating_theatre_2"; name = "Operating Theatre 2"; @@ -6990,6 +7002,9 @@ /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/darkred, /obj/structure/cable/green, +/obj/map_helper/electrochromatic_linker{ + id = "Interr" + }, /turf/simulated/floor/plating, /area/security/interrogation) "etT" = ( @@ -7261,6 +7276,14 @@ }, /turf/simulated/floor/tiled/steel, /area/security/hallway) +"eCP" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "resleeving-tint" + }, +/turf/simulated/floor/plating, +/area/medical/resleeving) "eCU" = ( /obj/effect/floor_decal/industrial/outline/grey, /obj/random/maintenance/research, @@ -7636,6 +7659,9 @@ opacity = 0 }, /obj/effect/paint/purplegray, +/obj/map_helper/electrochromatic_linker{ + id = "robotics_inner" + }, /turf/simulated/floor/plating, /area/assembly/robotics) "eTD" = ( @@ -7677,6 +7703,9 @@ /obj/structure/window/reinforced/tinted/frosted{ dir = 4 }, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_l" + }, /turf/simulated/floor/tiled/red, /area/security/security_processing) "eUD" = ( @@ -9104,6 +9133,9 @@ /obj/structure/window/reinforced/tinted/frosted{ dir = 8 }, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_r" + }, /turf/simulated/floor/tiled/red, /area/security/security_processing) "fQU" = ( @@ -9162,6 +9194,9 @@ spawn_grille = 0 }, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "operating_theatre_1" + }, /turf/simulated/floor/plating, /area/medical/surgery) "fTu" = ( @@ -11266,6 +11301,9 @@ /obj/structure/disposalpipe/segment, /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "medbay_outer" + }, /turf/simulated/floor/plating, /area/medical/reception) "hlW" = ( @@ -14386,6 +14424,9 @@ opacity = 0 }, /obj/effect/paint/purplegray, +/obj/map_helper/electrochromatic_linker{ + id = "research_outer" + }, /turf/simulated/floor/plating, /area/rnd/research) "jdp" = ( @@ -16801,6 +16842,9 @@ /obj/structure/cable/green{ icon_state = "2-8" }, +/obj/map_helper/electrochromatic_linker{ + id = "Interr" + }, /turf/simulated/floor/plating, /area/security/interrogation) "kHg" = ( @@ -17651,6 +17695,14 @@ }, /turf/simulated/floor/wood, /area/crew_quarters/sleep/Dorm_3) +"ldw" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/effect/paint/darkred, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_r" + }, +/turf/simulated/floor/plating, +/area/security/security_processing) "ldL" = ( /obj/effect/floor_decal/borderfloor{ dir = 4 @@ -18544,6 +18596,9 @@ /area/maintenance/dormitory) "lIZ" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/map_helper/electrochromatic_linker{ + id = "research_inner" + }, /turf/simulated/floor/plating, /area/rnd/research) "lJq" = ( @@ -19571,6 +19626,14 @@ }, /turf/simulated/floor/wood, /area/tether/surfacebase/reading_room) +"moI" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/effect/paint/darkred, +/obj/map_helper/electrochromatic_linker{ + id = "sec_processing_l" + }, +/turf/simulated/floor/plating, +/area/security/security_processing) "moS" = ( /obj/structure/toilet{ pixel_y = 9 @@ -19693,6 +19756,9 @@ /obj/structure/cable/green{ icon_state = "0-2" }, +/obj/map_helper/electrochromatic_linker{ + id = "Interr" + }, /turf/simulated/floor/plating, /area/security/interrogation) "mqP" = ( @@ -22752,6 +22818,9 @@ opacity = 0 }, /obj/effect/paint/purplegray, +/obj/map_helper/electrochromatic_linker{ + id = "rd_office" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/hor) "ofP" = ( @@ -25336,6 +25405,9 @@ /obj/machinery/holosign/surgery{ id = "operating_theatre_1" }, +/obj/map_helper/electrochromatic_linker{ + id = "operating_theatre_1" + }, /obj/map_helper/access_helper/airlock/station/medical/surgery, /obj/machinery/door/airlock/glass/medical/polarized{ id_tint = "operating_theatre_1"; @@ -28985,6 +29057,9 @@ /area/crew_quarters/pool) "sdE" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, +/obj/map_helper/electrochromatic_linker{ + id = "sauna" + }, /turf/simulated/floor/plating, /area/triumph/surfacebase/sauna) "sen" = ( @@ -31443,6 +31518,9 @@ opacity = 0 }, /obj/effect/paint/purplegray, +/obj/map_helper/electrochromatic_linker{ + id = "robotics_outer" + }, /turf/simulated/floor/plating, /area/assembly/robotics) "tUr" = ( @@ -31631,6 +31709,9 @@ opacity = 0 }, /obj/effect/paint/darkred, +/obj/map_helper/electrochromatic_linker{ + id = "hos_office" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/hos) "tZW" = ( @@ -34470,6 +34551,9 @@ "vPP" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/babyblue, +/obj/map_helper/electrochromatic_linker{ + id = "medbay_outer" + }, /turf/simulated/floor/plating, /area/medical/reception) "vQg" = ( @@ -35298,6 +35382,9 @@ /obj/structure/cable/green{ icon_state = "4-8" }, +/obj/map_helper/electrochromatic_linker{ + id = "sauna" + }, /turf/simulated/floor/plating, /area/triumph/surfacebase/sauna) "wtF" = ( @@ -48633,8 +48720,8 @@ oLJ oLJ kid kid -oFb -oFb +eCP +eCP kid kid kid @@ -49218,7 +49305,7 @@ eYx gHr ixy odu -oFb +eCP jvX vjt wnW @@ -49412,7 +49499,7 @@ eYx iKu kRa lto -oFb +eCP eUN dyj wBH @@ -49606,7 +49693,7 @@ oUw dTk bor lto -oFb +eCP epz tQp xkt @@ -57752,7 +57839,7 @@ yga xNm bhS nuH -keC +moI pUO xQz owT @@ -57946,7 +58033,7 @@ myF pwT bhS nuH -keC +moI hGI ijR oZw @@ -58140,7 +58227,7 @@ mJx wXp bhS nuH -keC +moI eUi cCa fEm @@ -58722,7 +58809,7 @@ tZG tiu bhS nuH -keC +ldw fQG bBy bVn @@ -58916,7 +59003,7 @@ tZG mve bhS nuH -keC +ldw hGI ijR eTG @@ -59110,7 +59197,7 @@ iFX eCN ohv oQS -keC +ldw pUO mzx yaJ diff --git a/maps/rift/levels/rift-06-surface3.dmm b/maps/rift/levels/rift-06-surface3.dmm index 7806faa9b5ee..496bc980a809 100644 --- a/maps/rift/levels/rift-06-surface3.dmm +++ b/maps/rift/levels/rift-06-surface3.dmm @@ -2066,6 +2066,11 @@ "agL" = ( /obj/item/folder/blue, /obj/structure/table/wooden_reinforced, +/obj/machinery/button/windowtint/multitint{ + pixel_x = -8; + pixel_y = 7; + id = "pathfinder_office" + }, /turf/simulated/floor/tiled/monotile, /area/exploration/pathfinder_office) "agN" = ( @@ -19893,6 +19898,9 @@ opacity = 0 }, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "hop_office" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/hop) "fUe" = ( @@ -21063,6 +21071,9 @@ "igJ" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/violet, +/obj/map_helper/electrochromatic_linker{ + id = "pathfinder_office" + }, /turf/simulated/floor/plating, /area/exploration/pathfinder_office) "iio" = ( @@ -21188,6 +21199,9 @@ id = "hop_office" }, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "hop_office" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/hop) "iCv" = ( @@ -22196,6 +22210,9 @@ req_one_access = null }, /obj/map_helper/access_helper/airlock/station/command/bridge, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/tiled/dark, /area/bridge) "khK" = ( @@ -22509,6 +22526,9 @@ }, /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/plating, /area/bridge) "kQV" = ( @@ -23441,6 +23461,9 @@ req_one_access = null }, /obj/map_helper/access_helper/airlock/station/command/bridge, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/tiled/dark, /area/bridge) "mCH" = ( @@ -24438,6 +24461,9 @@ "oJN" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "blueshieldoffice" + }, /turf/simulated/floor/plating, /area/crew_quarters/heads/blueshield) "oLI" = ( @@ -24666,6 +24692,9 @@ req_one_access = null }, /obj/map_helper/access_helper/airlock/station/command/bridge, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/tiled/dark, /area/bridge) "pis" = ( @@ -25454,6 +25483,9 @@ opacity = 0 }, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/plating, /area/bridge) "qKs" = ( @@ -26545,6 +26577,11 @@ /obj/structure/flora/pottedplant/smallcactus{ pixel_y = 12 }, +/obj/machinery/button/windowtint/multitint{ + pixel_x = 9; + pixel_y = 8; + id = "blueshieldoffice" + }, /turf/simulated/floor/carpet/blue, /area/crew_quarters/heads/blueshield) "toU" = ( @@ -27118,6 +27155,9 @@ opacity = 0 }, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "bridge_tint" + }, /turf/simulated/floor/plating, /area/bridge) "uyX" = ( @@ -28302,6 +28342,9 @@ "wns" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "meeting_tint" + }, /turf/simulated/floor/plating, /area/bridge/bridge_hallway) "wnH" = ( @@ -28553,6 +28596,9 @@ "wTS" = ( /obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks, /obj/effect/paint/commandblue, +/obj/map_helper/electrochromatic_linker{ + id = "meeting_tint" + }, /turf/simulated/floor/plating, /area/bridge/meeting_room) "wUC" = ( diff --git a/maps/rift/levels/rift-11-orbital.dmm b/maps/rift/levels/rift-11-orbital.dmm index 8b39ef2f618d..fc113d7d0de8 100644 --- a/maps/rift/levels/rift-11-orbital.dmm +++ b/maps/rift/levels/rift-11-orbital.dmm @@ -312,6 +312,31 @@ }, /turf/simulated/floor/holofloor/tiled/dark, /area/holodeck/source_emptycourt) +"bg" = ( +/obj/structure/bed/chair/shuttle, +/obj/effect/floor_decal/borderfloor{ + dir = 9 + }, +/obj/effect/floor_decal/corner/lightgrey/border{ + dir = 9 + }, +/obj/effect/floor_decal/borderfloor/corner2{ + dir = 10 + }, +/obj/effect/floor_decal/corner/lightgrey/bordercorner2{ + dir = 10 + }, +/obj/structure/closet/hydrant{ + pixel_y = 32 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/shuttle/escape) +"bh" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_medical" + }, +/turf/simulated/floor, +/area/shuttle/escape) "bi" = ( /obj/structure/window/reinforced{ dir = 8 @@ -2268,7 +2293,7 @@ /obj/structure/table/reinforced, /obj/machinery/keycard_auth{ pixel_x = -24; - pixel_y = -8 + pixel_y = 10 }, /obj/item/storage/secure/briefcase, /obj/item/multitool, @@ -2310,7 +2335,7 @@ /obj/structure/table/reinforced, /obj/machinery/keycard_auth{ pixel_x = 24; - pixel_y = -8 + pixel_y = 11 }, /obj/item/folder/blue, /obj/item/radio{ @@ -2513,10 +2538,13 @@ /obj/structure/bed/chair/bay/comfy/black{ dir = 8 }, +/obj/structure/closet/walllocker/autolok_wall{ + pixel_y = 30; + pixel_x = -24 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "hY" = ( -/obj/structure/closet/walllocker/autolok_wall, /obj/structure/handrail{ dir = 1 }, @@ -2529,6 +2557,10 @@ /obj/structure/bed/chair/bay/comfy/black{ dir = 4 }, +/obj/structure/closet/walllocker/autolok_wall{ + pixel_y = 30; + pixel_x = 24 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "ic" = ( @@ -3306,6 +3338,7 @@ "kw" = ( /obj/structure/table/standard, /obj/item/material/ashtray/glass, +/obj/random/action_figure, /turf/simulated/floor/tiled/monotile, /area/shuttle/escape) "kx" = ( @@ -3602,6 +3635,10 @@ /obj/effect/floor_decal/corner/lightgrey/bordercorner2{ dir = 8 }, +/obj/structure/closet/hydrant{ + dir = 4; + pixel_x = -32 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "lm" = ( @@ -3692,6 +3729,10 @@ /obj/effect/floor_decal/corner/lightgrey/bordercorner2{ dir = 6 }, +/obj/structure/closet/hydrant{ + dir = 4; + pixel_x = 32 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "lr" = ( @@ -3982,13 +4023,17 @@ /area/shuttle/escape) "mp" = ( /obj/structure/table/glass, -/obj/random/action_figure, /obj/effect/floor_decal/borderfloor{ dir = 8 }, /obj/effect/floor_decal/corner/lightgrey/border{ dir = 8 }, +/obj/item/radio{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/radio, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "mq" = ( @@ -4167,6 +4212,9 @@ /obj/effect/floor_decal/corner/lightgrey/bordercorner2{ dir = 5 }, +/obj/machinery/vending/wallmed1/public{ + pixel_x = 26 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "mM" = ( @@ -4272,7 +4320,7 @@ name = "Shuttle Security"; req_one_access = null }, -/obj/map_helper/access_helper/airlock/station/security, +/obj/map_helper/access_helper/airlock/station/security/department, /turf/simulated/floor/tiled/monotile, /area/shuttle/escape) "mZ" = ( @@ -4280,7 +4328,7 @@ name = "Shuttle Medical"; req_one_access = null }, -/obj/map_helper/access_helper/airlock/station/medical, +/obj/map_helper/access_helper/airlock/station/medical/department, /turf/simulated/floor/tiled/monotile, /area/shuttle/escape) "na" = ( @@ -4306,7 +4354,9 @@ id = "emergency_shuttle_lockdown"; name = "Emergency Shuttle Blast Door" }, -/obj/spawner/window/full/firelocks, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_security" + }, /turf/simulated/floor/reinforced, /area/shuttle/escape) "nd" = ( @@ -4402,6 +4452,9 @@ "nr" = ( /obj/item/handcuffs, /obj/structure/table/standard, +/obj/structure/closet/hydrant{ + pixel_y = -26 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "ns" = ( @@ -4531,7 +4584,13 @@ /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "nM" = ( -/obj/spawner/window/full/firelocks, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_cockpit" + }, /turf/simulated/floor, /area/shuttle/escape) "nN" = ( @@ -4656,6 +4715,9 @@ dir = 1 }, /obj/structure/handrail, +/obj/structure/closet/hydrant{ + pixel_y = 32 + }, /turf/simulated/floor/tiled/steel_grid, /area/shuttle/escape) "og" = ( @@ -6222,7 +6284,7 @@ /obj/item/clothing/suit/armor/tdome/red, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor/dark, /area/tdome/tdome1) "tj" = ( @@ -6293,6 +6355,10 @@ /obj/item/grenade/chem_grenade/metalfoam, /turf/unsimulated/floor/dark, /area/centcom/specops) +"un" = ( +/obj/spawner/window/low_wall/reinforced/full/firelocks, +/turf/simulated/floor, +/area/shuttle/escape) "ur" = ( /obj/structure/cable{ icon_state = "1-2" @@ -6381,7 +6447,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/green, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor/dark, /area/tdome/tdome1) "vT" = ( @@ -6504,7 +6570,7 @@ /obj/item/clothing/suit/armor/tdome/green, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor/dark, /area/tdome/tdome1) "xw" = ( @@ -6819,6 +6885,15 @@ icon_state = "lino" }, /area/tdome/tdomeobserve) +"yJ" = ( +/obj/structure/bed/chair/shuttle{ + dir = 4 + }, +/obj/structure/closet/hydrant{ + pixel_y = -26 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/shuttle/escape) "yO" = ( /obj/structure/table/standard, /obj/machinery/computer/skills{ @@ -7676,6 +7751,12 @@ /obj/item/clothing/glasses/sunglasses/sechud/tactical, /turf/unsimulated/floor/dark, /area/centcom/specops) +"BW" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_cockpit" + }, +/turf/simulated/floor, +/area/shuttle/escape) "Cb" = ( /obj/machinery/lathe/autolathe{ desc = "Your typical Autolathe. It appears to have much more options than your regular one, however..."; @@ -7978,6 +8059,12 @@ }, /turf/simulated/floor/tiled/techfloor/grid, /area/centcom/specops/dock) +"Ey" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_security" + }, +/turf/simulated/floor, +/area/shuttle/escape) "EC" = ( /obj/machinery/vending/tool, /turf/unsimulated/floor/dark, @@ -8295,6 +8382,15 @@ /obj/item/storage/belt/security/tactical, /turf/unsimulated/floor/dark, /area/centcom/specops) +"Jr" = ( +/obj/machinery/atmospherics/pipe/simple/visible/fuel, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/obj/spawner/window/low_wall/reinforced/full/firelocks, +/turf/simulated/floor/reinforced, +/area/shuttle/escape) "Jx" = ( /obj/structure/table/rack/shelf/steel, /obj/item/storage/firstaid/combat, @@ -8425,6 +8521,25 @@ /obj/effect/shuttle_landmark/shuttle_initializer/specops, /turf/simulated/floor/tiled/dark, /area/shuttle/specops/general) +"Kv" = ( +/obj/structure/bed/chair/shuttle, +/obj/effect/floor_decal/borderfloor{ + dir = 5 + }, +/obj/effect/floor_decal/corner/lightgrey/border{ + dir = 5 + }, +/obj/effect/floor_decal/borderfloor/corner2{ + dir = 5 + }, +/obj/effect/floor_decal/corner/lightgrey/bordercorner2{ + dir = 5 + }, +/obj/structure/closet/hydrant{ + pixel_y = 32 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/shuttle/escape) "Ky" = ( /obj/item/radio/intercom{ dir = 1; @@ -9075,6 +9190,17 @@ "Rn" = ( /turf/unsimulated/wall, /area/centcom/specops/dock) +"Ro" = ( +/obj/machinery/atmospherics/pipe/simple/visible/fuel, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_medical" + }, +/turf/simulated/floor/reinforced, +/area/shuttle/escape) "Rs" = ( /obj/structure/cable{ icon_state = "1-4" @@ -9358,7 +9484,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/red, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor/dark, /area/tdome/tdome1) "Uk" = ( @@ -11580,7 +11706,7 @@ gx ji jP kt -nc +Jr kt kt kt @@ -11965,7 +12091,7 @@ gy gz nM gy -jk +bg jR rY ia @@ -12352,7 +12478,7 @@ gz gz hi hX -nM +BW jm ia ku @@ -12362,7 +12488,7 @@ lL gz gy mY -nM +Ey iE gz of @@ -12546,7 +12672,7 @@ nM ha hj hY -nM +BW jn jT jT @@ -12558,7 +12684,7 @@ mK ia nf ns -nM +un og oI px @@ -12934,7 +13060,7 @@ nM hc hl hY -nM +BW jo jV jV @@ -12946,7 +13072,7 @@ mL ia ng nu -nM +un oi oK gz @@ -13128,7 +13254,7 @@ gz gz hm ib -nM +BW jp ia kx @@ -13138,7 +13264,7 @@ lO gz gy mZ -nM +bh iE gz of @@ -13333,7 +13459,7 @@ gy mM ia Cx -Cx +yJ gy oj oM @@ -13517,7 +13643,7 @@ gy gz nM gy -jF +Kv jW rY ia @@ -13908,7 +14034,7 @@ gx js jY kt -nc +Jr kt kt kt @@ -14108,8 +14234,8 @@ lJ mr mP nb -nc -nc +Ro +Ro nb ok gx diff --git a/maps/sectors/frozen_140/levels/frozen_140.dmm b/maps/sectors/frozen_140/levels/frozen_140.dmm index c598c1ec59f2..d1fbb4504dff 100644 --- a/maps/sectors/frozen_140/levels/frozen_140.dmm +++ b/maps/sectors/frozen_140/levels/frozen_140.dmm @@ -535,7 +535,7 @@ /area/class_p/facility) "wQ" = ( /mob/living/simple_mob/animal/space/tree{ - faction = "frozen" + iff_factions = "frozen" }, /turf/simulated/floor/outdoors/snow/classp, /area/class_p/unexplored) diff --git a/maps/sectors/frozen_192/levels/frozen_192.dmm b/maps/sectors/frozen_192/levels/frozen_192.dmm index 96384fcd6485..f2d2b32fe180 100644 --- a/maps/sectors/frozen_192/levels/frozen_192.dmm +++ b/maps/sectors/frozen_192/levels/frozen_192.dmm @@ -242,7 +242,7 @@ /area/class_p/unexplored) "lW" = ( /mob/living/simple_mob/animal/space/tree{ - faction = "frozen" + iff_factions = "frozen" }, /turf/simulated/floor/outdoors/snow/classp, /area/class_p/unexplored) diff --git a/maps/sectors/piratebase_140/levels/piratebase_140.dmm b/maps/sectors/piratebase_140/levels/piratebase_140.dmm index e69c9fe43ed1..0b9aa0ae3387 100644 --- a/maps/sectors/piratebase_140/levels/piratebase_140.dmm +++ b/maps/sectors/piratebase_140/levels/piratebase_140.dmm @@ -469,7 +469,7 @@ /area/piratebase/halls) "vW" = ( /mob/living/simple_mob/mechanical/mecha/combat/marauder/mauler{ - faction = "voxpirate" + iff_factions = "voxpirate" }, /turf/simulated/floor/reinforced, /area/piratebase/halls) diff --git a/maps/sectors/piratebase_192/levels/piratebase_192.dmm b/maps/sectors/piratebase_192/levels/piratebase_192.dmm index adb8f29930a7..022055adef19 100644 --- a/maps/sectors/piratebase_192/levels/piratebase_192.dmm +++ b/maps/sectors/piratebase_192/levels/piratebase_192.dmm @@ -4410,7 +4410,7 @@ dir = 8 }, /mob/living/simple_mob/animal/giant_spider/pepper{ - faction = "pirate"; + iff_factions = "pirate"; name = "First Mate Lil Peppies" }, /turf/simulated/floor/wood, @@ -5314,14 +5314,14 @@ /area/piratebase/captain) "Wc" = ( /obj/structure/table/rack/shelf/steel, -/obj/item/melee/energy/sword/pirate{ +/obj/item/melee/transforming/energy/sword/cutlass{ pixel_x = 5 }, -/obj/item/melee/energy/sword/pirate{ +/obj/item/melee/transforming/energy/sword/cutlass{ pixel_x = 5; pixel_y = 5 }, -/obj/item/melee/energy/sword/pirate{ +/obj/item/melee/transforming/energy/sword/cutlass{ pixel_x = 5; pixel_y = 10 }, diff --git a/maps/sectors/tradeport_140/levels/tradeport_140.dmm b/maps/sectors/tradeport_140/levels/tradeport_140.dmm index 2684410d2157..04dd84918912 100644 --- a/maps/sectors/tradeport_140/levels/tradeport_140.dmm +++ b/maps/sectors/tradeport_140/levels/tradeport_140.dmm @@ -471,7 +471,7 @@ req_access = list(160) }, /obj/structure/table/marble, -/obj/item/melee/energy/hfmachete, +/obj/item/melee/transforming/hfmachete, /turf/simulated/floor/carpet/bcarpet, /area/tradeport/cyndishow) "bU" = ( diff --git a/maps/sectors/tradeport_192/levels/tradeport_192.dmm b/maps/sectors/tradeport_192/levels/tradeport_192.dmm index 596ac6f8df30..14ff915c4c12 100644 --- a/maps/sectors/tradeport_192/levels/tradeport_192.dmm +++ b/maps/sectors/tradeport_192/levels/tradeport_192.dmm @@ -3061,7 +3061,7 @@ req_access = list(160) }, /obj/structure/table/marble, -/obj/item/melee/energy/hfmachete, +/obj/item/melee/transforming/hfmachete, /turf/simulated/floor/carpet/bcarpet, /area/tradeport/cyndishow) "kD" = ( @@ -6033,18 +6033,8 @@ /turf/simulated/floor/tiled/dark, /area/shuttle/trade_ship/cockpit) "vA" = ( -/obj/machinery/atmospherics/component/unary/vent_pump{ - dir = 4; - external_pressure_bound = 0; - external_pressure_bound_default = 0; - frequency = null; - icon_state = "map_vent_in"; - initialize_directions = 1; - internal_pressure_bound = 4000; - internal_pressure_bound_default = 4000; - pressure_checks = 2; - pressure_checks_default = 2; - pump_direction = 0 +/obj/machinery/atmospherics/component/unary/vent_pump/siphon/on{ + dir = 4 }, /turf/simulated/floor/reinforced/airmix, /area/tradeport/atmospherics) @@ -14149,9 +14139,10 @@ /turf/simulated/floor/outdoors/beach/sand/desert/indoors, /area/tradeport/safarizoo) "YT" = ( -/obj/machinery/atmospherics/component/binary/pump/on{ +/obj/machinery/atmospherics/component/binary/pump/high_power/on{ dir = 1; - name = "Airmix to Distro" + name = "Air Supply"; + target_pressure = 1000 }, /turf/simulated/floor/plating, /area/tradeport/atmospherics) @@ -14294,14 +14285,14 @@ /turf/simulated/floor/wmarble, /area/tradeport/cafeteria) "Zu" = ( -/obj/machinery/atmospherics/component/binary/pump/on{ - dir = 4 - }, /obj/effect/floor_decal/industrial/warning{ dir = 8 }, /obj/effect/debris/cleanable/blood/gibs/robot/limb, /obj/effect/floor_decal/rust, +/obj/machinery/atmospherics/pipe/simple/visible/cyan{ + dir = 4 + }, /turf/simulated/floor/plating, /area/tradeport/atmospherics) "Zv" = ( diff --git a/maps/submaps/level_specific/class_h/covert_post.dmm b/maps/submaps/level_specific/class_h/covert_post.dmm index 2d5d8a9390ad..b9108d9ba56c 100644 --- a/maps/submaps/level_specific/class_h/covert_post.dmm +++ b/maps/submaps/level_specific/class_h/covert_post.dmm @@ -226,7 +226,7 @@ /area/class_h/POIs/covert_post) "V" = ( /mob/living/simple_mob/animal/passive/honkpet{ - faction = "clown"; + iff_factions = "clown"; name = "Hunkle" }, /turf/simulated/floor/tiled/white, diff --git a/maps/submaps/level_specific/debrisfield/derelict.dmm b/maps/submaps/level_specific/debrisfield/derelict.dmm index fd2f6abe714e..58cfe5408344 100644 --- a/maps/submaps/level_specific/debrisfield/derelict.dmm +++ b/maps/submaps/level_specific/debrisfield/derelict.dmm @@ -25,7 +25,7 @@ /area/debrisfield/derelict) "ap" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/asteroid_steel/airless, /area/debrisfield/derelict) @@ -82,7 +82,7 @@ /area/debrisfield/derelict) "aA" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/airless, /area/debrisfield/derelict) @@ -139,7 +139,7 @@ "aR" = ( /obj/mob_spawner/derelict/corrupt_maint_swarm, /mob/living/simple_mob/mechanical/corrupt_maint_drone{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/airless, /area/debrisfield/derelict) @@ -246,7 +246,7 @@ "bo" = ( /obj/mob_spawner/derelict/corrupt_maint_swarm, /mob/living/simple_mob/mechanical/corrupt_maint_drone{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/asteroid_steel/airless, /area/debrisfield/derelict) @@ -283,7 +283,7 @@ /area/debrisfield/derelict) "bu" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/white/airless, /area/debrisfield/derelict) diff --git a/maps/submaps/level_specific/debrisfield_vr/derelict.dmm b/maps/submaps/level_specific/debrisfield_vr/derelict.dmm index 29e0a3e15a2a..13c4ed44be15 100644 --- a/maps/submaps/level_specific/debrisfield_vr/derelict.dmm +++ b/maps/submaps/level_specific/debrisfield_vr/derelict.dmm @@ -292,7 +292,7 @@ /area/submap/debrisfield_vr/derelict/interior) "aZ" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/dark, /area/submap/debrisfield_vr/derelict/interior) @@ -356,7 +356,7 @@ /area/submap/debrisfield_vr/derelict/interior) "bn" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/steel_grid, /area/submap/debrisfield_vr/derelict/interior) @@ -406,7 +406,7 @@ "bx" = ( /obj/mob_spawner/derelict/corrupt_maint_swarm, /mob/living/simple_mob/mechanical/corrupt_maint_drone{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/steel_grid, /area/submap/debrisfield_vr/derelict/interior) @@ -546,7 +546,7 @@ "bX" = ( /obj/mob_spawner/derelict/corrupt_maint_swarm, /mob/living/simple_mob/mechanical/corrupt_maint_drone{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/dark, /area/submap/debrisfield_vr/derelict/interior) @@ -609,7 +609,7 @@ /area/submap/debrisfield_vr/derelict/interior) "ci" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "derelict" + iff_factions = "derelict" }, /turf/simulated/floor/tiled/white, /area/submap/debrisfield_vr/derelict/interior) diff --git a/maps/submaps/level_specific/debrisfield_vr/mining_drones.dmm b/maps/submaps/level_specific/debrisfield_vr/mining_drones.dmm index 27c88e83d9d7..006b3cc94f96 100644 --- a/maps/submaps/level_specific/debrisfield_vr/mining_drones.dmm +++ b/maps/submaps/level_specific/debrisfield_vr/mining_drones.dmm @@ -7,7 +7,7 @@ /area/space) "c" = ( /mob/living/simple_mob/mechanical/corrupt_maint_drone{ - faction = "poi_mining_drones" + iff_factions = "poi_mining_drones" }, /turf/simulated/floor/airless, /area/submap/debrisfield_vr/mining_outpost) diff --git a/maps/submaps/level_specific/underdark/abandonded_outpost.dmm b/maps/submaps/level_specific/underdark/abandonded_outpost.dmm index 81854381bf99..d6a54d376107 100644 --- a/maps/submaps/level_specific/underdark/abandonded_outpost.dmm +++ b/maps/submaps/level_specific/underdark/abandonded_outpost.dmm @@ -354,7 +354,7 @@ /area/mine/explored/underdark) "bB" = ( /mob/living/simple_mob/mechanical/infectionbot{ - faction = "underdark" + iff_factions = "underdark" }, /turf/simulated/floor/tiled/steel_dirty/virgo3b, /area/mine/explored/underdark) diff --git a/maps/submaps/level_specific/underdark/wolf_den.dmm b/maps/submaps/level_specific/underdark/wolf_den.dmm index 3d43dc486d23..6122e62fce0a 100644 --- a/maps/submaps/level_specific/underdark/wolf_den.dmm +++ b/maps/submaps/level_specific/underdark/wolf_den.dmm @@ -37,7 +37,7 @@ /area/mine/explored/underdark) "j" = ( /mob/living/simple_mob/otie/feral{ - faction = "underdark" + iff_factions = "underdark" }, /turf/simulated/mineral/floor/ignore_cavegen/virgo3b, /area/mine/explored/underdark) diff --git a/maps/submaps/level_specific/virgo2/Rockybase.dmm b/maps/submaps/level_specific/virgo2/Rockybase.dmm index f0642ac9f246..ef2d0f990489 100644 --- a/maps/submaps/level_specific/virgo2/Rockybase.dmm +++ b/maps/submaps/level_specific/virgo2/Rockybase.dmm @@ -24,7 +24,7 @@ /mob/living/simple_mob/mechanical/combat_drone{ ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting/threatening/returnhome; desc = "An automated combat drone with an aged apperance."; - faction = "syndicate"; + iff_factions = "syndicate"; maxbodytemp = 900 }, /turf/simulated/mineral/floor/ignore_mapgen/virgo2, @@ -114,7 +114,7 @@ /area/submap/virgo2/Rockybase) "ax" = ( /mob/living/bot/cleanbot{ - faction = "malf_drone" + iff_factions = "malf_drone" }, /turf/simulated/floor/tiled/techfloor/virgo2, /area/submap/virgo2/Rockybase) @@ -307,7 +307,7 @@ /area/submap/virgo2/Rockybase) "bf" = ( /mob/living/bot/farmbot{ - faction = "malf_drone" + iff_factions = "malf_drone" }, /turf/simulated/floor/tiled/techfloor/virgo2, /area/submap/virgo2/Rockybase) @@ -341,7 +341,7 @@ /mob/living/simple_mob/mechanical/combat_drone{ ai_holder_type = /datum/ai_holder/polaris/simple_mob/ranged/kiting/threatening/returnhome; desc = "An automated combat drone with an aged apperance."; - faction = "syndicate"; + iff_factions = "syndicate"; maxbodytemp = 900 }, /turf/simulated/floor/tiled/techfloor/virgo2, diff --git a/maps/submaps/plains/Oldhouse.dmm b/maps/submaps/plains/Oldhouse.dmm index df7623b7f23b..ee2c21772cef 100644 --- a/maps/submaps/plains/Oldhouse.dmm +++ b/maps/submaps/plains/Oldhouse.dmm @@ -183,7 +183,7 @@ attack_armor_pen = 100; attacktext = list("lightly bonked"); desc = "Furry and brown, this spider is so goddamn fat you're surprised it even moves around."; - faction = "neutral"; + iff_factions = "neutral"; health = 400; legacy_melee_damage_lower = 1; legacy_melee_damage_upper = 3; diff --git a/maps/submaps/plains/Oldhouse_vr.dmm b/maps/submaps/plains/Oldhouse_vr.dmm index 4c3de3e3c55e..089de09d6358 100644 --- a/maps/submaps/plains/Oldhouse_vr.dmm +++ b/maps/submaps/plains/Oldhouse_vr.dmm @@ -183,7 +183,7 @@ attack_armor_pen = 100; attacktext = list("lightly bonked"); desc = "Furry and brown, this spider is so goddamn fat you're surprised it even moves around."; - faction = "neutral"; + iff_factions = "neutral"; health = 400; maxHealth = 400; legacy_melee_damage_lower = 1; diff --git a/maps/submaps/wilderness/Rockybase.dmm b/maps/submaps/wilderness/Rockybase.dmm index 1571a38f7ae7..e7810469bd03 100644 --- a/maps/submaps/wilderness/Rockybase.dmm +++ b/maps/submaps/wilderness/Rockybase.dmm @@ -167,7 +167,7 @@ /area/submap/Rockybase) "aK" = ( /mob/living/bot/cleanbot{ - faction = "malf_drone" + iff_factions = "malf_drone" }, /turf/simulated/floor/tiled, /area/submap/Rockybase) @@ -435,7 +435,7 @@ dir = 8 }, /mob/living/bot/farmbot{ - faction = "malf_drone"; + iff_factions = "malf_drone"; name = "Mr. Weddleton" }, /turf/simulated/floor/tiled, @@ -790,7 +790,7 @@ /area/submap/Rockybase) "cL" = ( /mob/living/simple_mob/mechanical/mecha/combat/gygax/dark/advanced{ - faction = "malf_drone" + iff_factions = "malf_drone" }, /turf/simulated/floor/tiled, /area/submap/Rockybase) @@ -1035,7 +1035,7 @@ "dB" = ( /obj/effect/debris/cleanable/dirt, /mob/living/bot/medibot{ - faction = "malf_drone" + iff_factions = "malf_drone" }, /turf/simulated/floor/tiled, /area/submap/Rockybase) diff --git a/maps/templates/admin/dhael_centcom.dmm b/maps/templates/admin/dhael_centcom.dmm index 247c12e9fe5e..91ea585fafb8 100644 --- a/maps/templates/admin/dhael_centcom.dmm +++ b/maps/templates/admin/dhael_centcom.dmm @@ -229,17 +229,17 @@ /area/centcom/specops) "ay" = ( /obj/structure/table/rack, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/window/reinforced{ dir = 1 @@ -10688,17 +10688,17 @@ /area/centcom/specops) "GH" = ( /obj/structure/table/rack, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -11332,7 +11332,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/red, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -12059,7 +12059,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/green, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -12076,7 +12076,7 @@ /obj/item/clothing/suit/armor/tdome/green, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -13315,7 +13315,7 @@ /obj/item/clothing/suit/armor/tdome/red, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, diff --git a/maps/templates/admin/ert_base.dmm b/maps/templates/admin/ert_base.dmm index 96879d65dce4..5844bc5022bf 100644 --- a/maps/templates/admin/ert_base.dmm +++ b/maps/templates/admin/ert_base.dmm @@ -1357,16 +1357,16 @@ /area/shuttle/specops/centcom) "cL" = ( /obj/structure/table/rack/steel, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /turf/simulated/shuttle/floor/black, /area/shuttle/specops/centcom) "cM" = ( diff --git a/maps/templates/admin/kk_mercship.dmm b/maps/templates/admin/kk_mercship.dmm index 16b08549308f..f889b1fa0f65 100644 --- a/maps/templates/admin/kk_mercship.dmm +++ b/maps/templates/admin/kk_mercship.dmm @@ -9345,18 +9345,18 @@ /turf/simulated/floor/tiled/techmaint, /area/ship/manta/bridge) "VF" = ( -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/structure/table/steel_reinforced, /obj/machinery/atmospherics/component/unary/vent_scrubber/on{ dir = 4 diff --git a/maps/templates/admin/mercbase.dmm b/maps/templates/admin/mercbase.dmm index 59eb33bbafb5..bc8c94c333a0 100644 --- a/maps/templates/admin/mercbase.dmm +++ b/maps/templates/admin/mercbase.dmm @@ -489,18 +489,18 @@ /area/antag/antag_base) "aP" = ( /obj/structure/table/rack, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/machinery/recharger/wallcharger{ pixel_x = 5; pixel_y = 32 diff --git a/maps/templates/admin/skipjack.dmm b/maps/templates/admin/skipjack.dmm index caad2f925597..e757e7953c50 100644 --- a/maps/templates/admin/skipjack.dmm +++ b/maps/templates/admin/skipjack.dmm @@ -1099,7 +1099,7 @@ /area/shuttle/skipjack) "cK" = ( /obj/structure/table/rack, -/obj/item/melee/energy/sword/pirate, +/obj/item/melee/transforming/energy/sword/cutlass, /obj/item/clothing/suit/space/pirate, /obj/item/clothing/suit/space/pirate, /obj/item/tank/oxygen, diff --git a/maps/templates/admin/thunderdome.dmm b/maps/templates/admin/thunderdome.dmm index 31f85fdaa411..8cb99cbba953 100644 --- a/maps/templates/admin/thunderdome.dmm +++ b/maps/templates/admin/thunderdome.dmm @@ -26,7 +26,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/red, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark"; dir = 5 @@ -43,7 +43,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/green, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark"; dir = 5 @@ -77,7 +77,7 @@ /obj/item/clothing/suit/armor/tdome/red, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark"; dir = 5 @@ -128,7 +128,7 @@ /obj/item/clothing/suit/armor/tdome/green, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark"; dir = 5 diff --git a/maps/templates/shuttles/overmaps/generic/cruiser.dmm b/maps/templates/shuttles/overmaps/generic/cruiser.dmm index 5472b593c6ca..071f0063aa0b 100644 --- a/maps/templates/shuttles/overmaps/generic/cruiser.dmm +++ b/maps/templates/shuttles/overmaps/generic/cruiser.dmm @@ -5404,16 +5404,16 @@ /obj/item/melee/baton/loaded, /obj/item/melee/baton/loaded, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/item/material/knife/tacknife/survival, /obj/item/material/knife/tacknife/survival, /obj/item/material/knife/tacknife/survival, diff --git a/maps/templates/shuttles/overmaps/generic/shelter_6.dmm b/maps/templates/shuttles/overmaps/generic/shelter_6.dmm index 242fc42c3824..e0a277ddfda5 100644 --- a/maps/templates/shuttles/overmaps/generic/shelter_6.dmm +++ b/maps/templates/shuttles/overmaps/generic/shelter_6.dmm @@ -65,14 +65,14 @@ /obj/item/material/knife/machete, /obj/item/material/knife/machete, /obj/item/material/knife/machete, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/item/melee/baton/loaded, /obj/item/melee/baton/loaded, /obj/item/melee/baton/loaded, diff --git a/maps/tether/levels/plains.dmm b/maps/tether/levels/plains.dmm index a71bc8e6ad49..a60e9f1db8af 100644 --- a/maps/tether/levels/plains.dmm +++ b/maps/tether/levels/plains.dmm @@ -20,7 +20,7 @@ /turf/simulated/floor/tiled/steel_dirty/virgo3b, /area/mine/unexplored) "i" = ( -/mob/living/simple_mob/animal/space/goose/virgo3b, +/mob/living/simple_mob/animal/space/goose, /turf/simulated/floor/outdoors/grass/sif/virgo3b, /area/tether/outpost/exploration_plains) "j" = ( diff --git a/maps/triumph/levels/flagship.dmm b/maps/triumph/levels/flagship.dmm index a9bba780a6c5..9f62a245cd19 100644 --- a/maps/triumph/levels/flagship.dmm +++ b/maps/triumph/levels/flagship.dmm @@ -14,6 +14,15 @@ icon_state = "dark" }, /area/centcom/security) +"ag" = ( +/obj/structure/bed/chair/shuttle{ + dir = 1 + }, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = -26 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "ah" = ( /obj/effect/floor_decal/borderfloorblack, /obj/effect/floor_decal/corner/blue/border, @@ -583,12 +592,12 @@ /turf/unsimulated/floor/steel, /area/centcom/security) "bV" = ( -/obj/machinery/vending/cigarette, /obj/machinery/light{ dir = 8; use_power = 0; old_wall = 1 }, +/obj/structure/closet/emcloset, /turf/simulated/shuttle/floor/darkred, /area/shuttle/escape) "bX" = ( @@ -1154,8 +1163,8 @@ }, /area/centcom/medical) "dx" = ( -/obj/item/bedsheet/medical, -/obj/structure/bed/padded, +/obj/structure/medical_stand/anesthetic, +/obj/structure/bed/roller, /turf/simulated/shuttle/floor, /area/shuttle/escape) "dy" = ( @@ -1390,7 +1399,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/red, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -1842,17 +1851,17 @@ /area/centcom/specops) "fK" = ( /obj/structure/table/rack, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /obj/effect/floor_decal/industrial/outline/yellow, /obj/structure/window/reinforced{ dir = 1 @@ -1964,6 +1973,13 @@ "ga" = ( /turf/unsimulated/wall, /area/centcom/living) +"gb" = ( +/obj/structure/handrail, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "gc" = ( /obj/structure/table/reinforced, /obj/item/paper_bin{ @@ -2001,6 +2017,13 @@ }, /turf/unsimulated/floor/wood, /area/centcom/restaurant) +"gg" = ( +/obj/structure/handrail, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "gh" = ( /obj/effect/floor_decal/corner/grey/diagonal{ dir = 4 @@ -2274,6 +2297,15 @@ icon_state = "white" }, /area/centcom/medical) +"gS" = ( +/obj/structure/handrail{ + dir = 1 + }, +/obj/machinery/status_display{ + pixel_y = -32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "gW" = ( /obj/structure/table/glass, /obj/item/storage/firstaid/regular, @@ -2288,6 +2320,15 @@ icon_state = "white" }, /area/centcom/medical) +"gX" = ( +/obj/random/firstaid, +/obj/item/storage/firstaid/adv, +/obj/item/storage/firstaid/bonemed, +/obj/item/storage/firstaid/clotting, +/obj/item/storage/firstaid/clotting, +/obj/structure/table/reinforced, +/turf/simulated/shuttle/floor, +/area/shuttle/escape) "gY" = ( /obj/structure/sign/nanotrasen, /turf/unsimulated/wall, @@ -2645,11 +2686,19 @@ icon_state = "white" }, /area/centcom/control) +"im" = ( +/obj/machinery/computer/communications{ + dir = 4 + }, +/obj/machinery/keycard_auth{ + pixel_x = -24; + pixel_y = -24 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) "in" = ( -/obj/machinery/light{ - dir = 4; - use_power = 0; - old_wall = 1 +/obj/structure/handrail{ + dir = 8 }, /turf/simulated/shuttle/floor/white, /area/shuttle/escape) @@ -2700,6 +2749,19 @@ }, /turf/unsimulated/floor/steel, /area/centcom/security) +"iC" = ( +/obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_medical" + }, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/turf/unsimulated/floor{ + name = "plating" + }, +/area/shuttle/escape) "iE" = ( /turf/unsimulated/floor/steel{ icon_state = "white" @@ -3392,6 +3454,19 @@ /obj/spawner/window/low_wall/borosillicate/full/firelocks, /turf/simulated/floor/plating, /area/shuttle/specops/engine) +"kX" = ( +/obj/structure/handrail{ + dir = 1 + }, +/obj/machinery/door/window/brigdoor/southright{ + dir = 8; + name = "holding cell" + }, +/obj/machinery/status_display{ + pixel_y = -32 + }, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "kY" = ( /obj/effect/floor_decal/borderfloor, /obj/effect/floor_decal/borderfloor/corner2{ @@ -3455,6 +3530,13 @@ }, /turf/unsimulated/floor/steel, /area/centcom/evac) +"lo" = ( +/obj/structure/handrail, +/obj/machinery/vending/wallmed1/public{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "lp" = ( /obj/machinery/computer/card{ dir = 4 @@ -3756,6 +3838,12 @@ icon_state = "carpet" }, /area/centcom/command) +"mb" = ( +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_cockpit" + }, +/turf/simulated/floor, +/area/shuttle/escape) "md" = ( /obj/structure/window/reinforced{ dir = 1 @@ -3902,9 +3990,8 @@ }, /area/centcom/security) "mA" = ( -/obj/machinery/optable, -/obj/machinery/oxygen_pump/anesthetic{ - pixel_y = 29 +/obj/machinery/sleep_console{ + dir = 4 }, /turf/simulated/shuttle/floor, /area/shuttle/escape) @@ -3986,8 +4073,10 @@ }, /area/centcom/specops) "mE" = ( -/obj/spawner/window/low_wall/reinforced/full, /obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_medical" + }, /turf/unsimulated/floor{ name = "plating" }, @@ -4005,6 +4094,15 @@ /obj/map_helper/access_helper/airlock/station/command/vault, /turf/simulated/floor/reinforced, /area/centcom/command) +"mG" = ( +/obj/structure/bed/chair/shuttle{ + dir = 8 + }, +/obj/machinery/status_display{ + pixel_y = -32 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) "mH" = ( /obj/effect/floor_decal/corner/grey/diagonal{ dir = 4 @@ -4101,6 +4199,18 @@ icon_state = "dark" }, /area/centcom/specops) +"mU" = ( +/obj/item/defib_kit, +/obj/structure/table/reinforced, +/obj/item/storage/firstaid/surgery, +/obj/item/reagent_containers/spray/cleaner{ + desc = "Someone has crossed out the Space from Space Cleaner and written in Surgery. 'Do not remove under punishment of death!!!' is scrawled on the back."; + name = "Surgery Cleaner"; + pixel_x = 2; + pixel_y = 2 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/escape) "mV" = ( /obj/structure/bed/chair/office/dark{ dir = 4 @@ -4345,17 +4455,17 @@ /area/centcom/security) "nz" = ( /obj/structure/table/rack, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, -/obj/item/shield/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, +/obj/item/shield/transforming/energy, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -4475,6 +4585,13 @@ icon_state = "white" }, /area/centcom/control) +"nU" = ( +/obj/structure/bed/chair/shuttle, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "nV" = ( /obj/structure/table/rack, /obj/structure/window/reinforced, @@ -4630,6 +4747,15 @@ icon_state = "white" }, /area/centcom/bathroom) +"ov" = ( +/obj/structure/table/reinforced, +/obj/item/radio{ + pixel_x = 2; + pixel_y = 2 + }, +/obj/item/radio, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "ox" = ( /obj/structure/flora/ausbushes/ywflowers, /turf/simulated/floor/outdoors/grass/heavy/interior, @@ -4646,6 +4772,13 @@ icon_state = "white" }, /area/centcom/medical) +"oz" = ( +/obj/structure/bed/chair/shuttle, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "oA" = ( /obj/structure/table/standard, /obj/effect/floor_decal/borderfloor{ @@ -4703,7 +4836,11 @@ /area/centcom/living) "oL" = ( /obj/structure/fans/tiny, -/obj/spawner/window/low_wall/reinforced/full, +/obj/spawner/window/low_wall/reinforced/full/firelocks, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, /turf/simulated/floor, /area/shuttle/escape) "oN" = ( @@ -5147,6 +5284,24 @@ icon_state = "dark" }, /area/centcom/specops) +"qk" = ( +/obj/structure/closet/walllocker/autolok_wall{ + pixel_y = 32 + }, +/obj/machinery/light{ + dir = 4; + use_power = 0 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) +"qm" = ( +/obj/machinery/light{ + dir = 4; + use_power = 0 + }, +/obj/machinery/vending/fitness, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "qn" = ( /obj/machinery/door/firedoor, /turf/unsimulated/floor/steel, @@ -5263,6 +5418,13 @@ icon_state = "white" }, /area/centcom/medical) +"qH" = ( +/obj/structure/handrail, +/obj/machinery/status_display{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/escape) "qI" = ( /obj/structure/table/rack, /obj/item/hardsuit_module/chem_dispenser/combat, @@ -6655,6 +6817,13 @@ /obj/item/clothing/glasses/welding, /turf/unsimulated/floor/steel, /area/centcom/control) +"uV" = ( +/obj/structure/closet/hydrant{ + dir = 4; + pixel_x = -32 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) "uW" = ( /obj/item/storage/belt/security/tactical, /obj/item/storage/belt/security/tactical, @@ -6898,6 +7067,10 @@ /obj/machinery/light/flamp/noshade, /turf/unsimulated/floor/steel, /area/centcom/evac) +"vR" = ( +/obj/machinery/vending/mre, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "vS" = ( /obj/structure/sign/greencross, /turf/unsimulated/wall, @@ -7402,6 +7575,17 @@ }, /turf/unsimulated/floor/steel, /area/centcom/control) +"xj" = ( +/obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_security" + }, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/turf/simulated/floor, +/area/shuttle/escape) "xk" = ( /obj/machinery/computer/crew{ dir = 4 @@ -7450,7 +7634,7 @@ /obj/item/clothing/suit/armor/tdome/red, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -8089,7 +8273,7 @@ /obj/structure/table/rack, /obj/item/clothing/under/color/green, /obj/item/clothing/shoes/brown, -/obj/item/melee/energy/axe, +/obj/item/melee/transforming/energy/axe, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -8421,9 +8605,11 @@ /turf/unsimulated/floor/steel, /area/centcom/control) "AA" = ( -/obj/structure/table/steel, -/obj/item/defib_kit, -/turf/simulated/shuttle/floor, +/obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_medical" + }, +/turf/simulated/floor, /area/shuttle/escape) "AB" = ( /obj/structure/table/standard, @@ -9233,6 +9419,14 @@ }, /turf/simulated/shuttle/floor/black, /area/shuttle/escape) +"Dp" = ( +/obj/machinery/button/windowtint{ + id = "emergency_medical"; + pixel_x = 24; + pixel_y = -24 + }, +/turf/simulated/shuttle/floor, +/area/shuttle/escape) "Dq" = ( /obj/machinery/telecomms/processor/preset_cent, /turf/unsimulated/floor{ @@ -9430,6 +9624,12 @@ }, /turf/unsimulated/floor/steel, /area/centcom/evac) +"Ef" = ( +/obj/structure/shuttle/engine/propulsion{ + dir = 1 + }, +/turf/simulated/shuttle/plating/airless/carry, +/area/shuttle/escape) "Eg" = ( /obj/structure/sign/securearea{ name = "\improper ARMORY"; @@ -9749,6 +9949,17 @@ /obj/machinery/portable_atmospherics/powered/scrubber/huge/stationary/shuttle, /turf/unsimulated/floor/steel, /area/centcom/evac) +"Fl" = ( +/obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_cockpit" + }, +/obj/machinery/door/blast/regular/open{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Shuttle Blast Door" + }, +/turf/simulated/floor, +/area/shuttle/escape) "Fm" = ( /obj/structure/window/reinforced, /turf/unsimulated/floor/steel, @@ -9975,9 +10186,13 @@ /turf/unsimulated/floor/steel, /area/centcom/main_hall) "FX" = ( -/obj/structure/bed/chair/shuttle{ - dir = 1 +/obj/machinery/button/remote/blast_door{ + id = "emergency_shuttle_lockdown"; + name = "Emergency Blast Doors"; + pixel_x = -24; + pixel_y = -7 }, +/obj/machinery/pointdefense_control, /turf/simulated/shuttle/floor/white, /area/shuttle/escape) "FY" = ( @@ -10693,7 +10908,7 @@ /obj/item/clothing/suit/armor/tdome/green, /obj/item/clothing/head/helmet/thunderdome, /obj/item/melee/baton/loaded, -/obj/item/melee/energy/sword, +/obj/item/melee/transforming/energy/sword, /turf/unsimulated/floor{ icon_state = "dark" }, @@ -10838,6 +11053,12 @@ icon_state = "lino" }, /area/centcom/command) +"ID" = ( +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "IG" = ( /obj/structure/table/reinforced, /obj/item/tool/crowbar, @@ -11081,6 +11302,10 @@ icon_state = "white" }, /area/centcom/medical) +"Jr" = ( +/obj/structure/shuttle/engine/propulsion, +/turf/simulated/shuttle/plating/airless/carry, +/area/shuttle/escape) "Js" = ( /obj/machinery/r_n_d/protolathe, /turf/unsimulated/floor/steel, @@ -12392,6 +12617,12 @@ icon_state = "white" }, /area/centcom/medical) +"No" = ( +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = -26 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "Nq" = ( /obj/structure/table/rack, /obj/structure/window/reinforced{ @@ -12783,13 +13014,11 @@ /turf/simulated/shuttle/floor, /area/shuttle/escape) "Oz" = ( -/obj/machinery/vending/fitness, -/obj/machinery/light{ - dir = 8; - use_power = 0; - old_wall = 1 +/obj/structure/fans/tiny, +/obj/spawner/window/low_wall/reinforced/electrochromic/full/firelocks{ + id = "emergency_security" }, -/turf/simulated/shuttle/floor/darkred, +/turf/simulated/floor, /area/shuttle/escape) "OA" = ( /obj/machinery/tele_pad, @@ -12885,13 +13114,10 @@ }, /area/centcom/bathroom) "OZ" = ( -/obj/structure/table/steel, -/obj/random/firstaid, -/obj/item/storage/firstaid/adv, -/obj/item/storage/firstaid/bonemed, -/obj/item/storage/firstaid/clotting, -/obj/item/storage/firstaid/clotting, -/turf/simulated/shuttle/floor, +/obj/structure/handrail{ + dir = 1 + }, +/turf/simulated/shuttle/floor/darkred, /area/shuttle/escape) "Pa" = ( /obj/effect/floor_decal/industrial/warning/dust/corner, @@ -12918,6 +13144,17 @@ }, /turf/simulated/shuttle/wall/voidcraft, /area/shuttle/specops/engine) +"Ph" = ( +/obj/structure/handrail{ + dir = 1 + }, +/obj/machinery/button/windowtint{ + id = "emergency_security"; + pixel_x = 8; + pixel_y = -24 + }, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "Pj" = ( /obj/effect/floor_decal/borderfloorblack{ dir = 6 @@ -12946,11 +13183,10 @@ /turf/simulated/floor/plating, /area/shuttle/specops/engine) "Pn" = ( -/obj/machinery/light{ - dir = 4; - use_power = 0 +/obj/machinery/sleeper{ + dir = 4 }, -/turf/simulated/shuttle/floor/white, +/turf/simulated/shuttle/floor, /area/shuttle/escape) "Po" = ( /obj/effect/floor_decal/derelict/d13, @@ -13256,6 +13492,13 @@ icon_state = "white" }, /area/centcom/medical) +"PY" = ( +/obj/structure/table/reinforced, +/obj/machinery/holoplant{ + pixel_y = 6 + }, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "Qa" = ( /obj/structure/table/steel, /obj/item/storage/firstaid/regular, @@ -13303,6 +13546,10 @@ icon_state = "dark" }, /area/centcom/security) +"Qd" = ( +/obj/machinery/vending/blood, +/turf/simulated/shuttle/floor, +/area/shuttle/escape) "Qg" = ( /obj/effect/floor_decal/borderfloorblack{ dir = 8 @@ -13564,6 +13811,17 @@ }, /turf/unsimulated/floor/steel, /area/centcom/security) +"QW" = ( +/obj/structure/closet/walllocker/autolok_wall{ + pixel_y = -26 + }, +/obj/machinery/light{ + dir = 4; + use_power = 0; + old_wall = 1 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) "QX" = ( /obj/machinery/shieldwallgen, /turf/unsimulated/floor{ @@ -13766,6 +14024,9 @@ old_wall = 1; use_power = 0 }, +/obj/machinery/status_display{ + pixel_y = -32 + }, /turf/simulated/shuttle/floor/black, /area/shuttle/escape) "Ry" = ( @@ -13946,6 +14207,9 @@ /obj/structure/bed/chair/shuttle{ dir = 8 }, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = 32 + }, /turf/simulated/shuttle/floor, /area/shuttle/escape) "Sh" = ( @@ -14477,6 +14741,12 @@ icon_state = "white" }, /area/centcom/control) +"TV" = ( +/obj/machinery/computer/crew{ + dir = 4 + }, +/turf/simulated/shuttle/floor/white, +/area/shuttle/escape) "TW" = ( /obj/effect/floor_decal/steeldecal/steel_decals9, /obj/effect/floor_decal/steeldecal/steel_decals9{ @@ -14826,6 +15096,15 @@ icon_state = "dark" }, /area/centcom/specops) +"Vm" = ( +/obj/structure/handrail{ + dir = 1 + }, +/obj/structure/closet/walllocker/emergsuit_wall{ + pixel_y = -26 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "Vn" = ( /obj/structure/table/standard, /obj/effect/floor_decal/borderfloor{ @@ -15082,6 +15361,13 @@ /obj/structure/closet/secure_closet/hop, /turf/unsimulated/floor/steel, /area/centcom/command) +"Wj" = ( +/obj/structure/handrail, +/obj/machinery/status_display{ + pixel_y = 32 + }, +/turf/simulated/shuttle/floor/black, +/area/shuttle/escape) "Wk" = ( /obj/structure/sign/warning/caution, /turf/unsimulated/wall, @@ -15403,7 +15689,22 @@ }, /area/centcom/security) "Xq" = ( -/obj/structure/bed/chair/shuttle, +/obj/structure/table/reinforced, +/obj/machinery/button/windowtint{ + id = "emergency_cockpit"; + pixel_x = -24; + pixel_y = 6 + }, +/obj/machinery/keycard_auth{ + pixel_x = -24; + pixel_y = -8 + }, +/obj/item/folder/blue, +/obj/item/radio, +/obj/item/radio{ + pixel_x = 2; + pixel_y = 2 + }, /turf/simulated/shuttle/floor/white, /area/shuttle/escape) "Xr" = ( @@ -16234,6 +16535,14 @@ icon_state = "white" }, /area/centcom/medical) +"Zy" = ( +/obj/machinery/light{ + dir = 4; + use_power = 0 + }, +/obj/structure/closet/firecloset/full/double, +/turf/simulated/shuttle/floor/darkred, +/area/shuttle/escape) "ZA" = ( /obj/effect/floor_decal/borderfloorwhite, /obj/effect/floor_decal/corner/paleblue/border, @@ -16296,6 +16605,10 @@ icon_state = "steel" }, /area/centcom/bathroom) +"ZM" = ( +/obj/machinery/power/pointdefense, +/turf/simulated/floor/plating/eris/under, +/area/shuttle/escape) "ZN" = ( /obj/structure/sign/department/armory, /turf/unsimulated/wall, @@ -29061,11 +29374,11 @@ AF AF AF jQ -oL -oL -oL -oL -oL +jQ +Fl +Fl +Fl +jQ jQ AF AF @@ -29253,15 +29566,15 @@ AF AF AF AF -AF -jQ +ZM +Fl Xq -yz +TV PU -yz +im FX -jQ -AF +Fl +ZM AF AF AF @@ -29446,17 +29759,17 @@ AF AF AF AF -AF -AF +Ef jQ -Xq -yz +wG QG -yz -FX +QG +QG +QG +mG +wG jQ -AF -AF +Jr AF AF AF @@ -29643,11 +29956,11 @@ AF jQ jQ jQ -wG -Pn +qk +in yz in -wG +QW jQ jQ jQ @@ -29836,14 +30149,14 @@ AF AF jQ bV -al wG -jQ +mb +mb Kg -jQ +mb +mb wG -va -Oz +bV jQ AF AF @@ -30029,15 +30342,15 @@ AF AF AF jQ -Gg -yz +Wj +uV Ui pa yz pa Ui -yz -Jg +uV +gS jQ AF AF @@ -30223,7 +30536,7 @@ AF AF AF jQ -NW +oz yz Ew NW @@ -30231,7 +30544,7 @@ yz Ew NW yz -Dm +ag jQ AF AF @@ -30999,7 +31312,7 @@ AF AF AF jQ -NW +oz yz Ew NW @@ -31007,7 +31320,7 @@ yz Ew NW yz -Dm +ag jQ AF AF @@ -31195,12 +31508,12 @@ AF jQ Gg yz -Pn yz yz yz yz -Pn +yz +yz Jg jQ AF @@ -31387,15 +31700,15 @@ AF AF AF jQ -jQ -oL -oL -wG -CG -CG -wG -oL -oL +al +vR +Zy +ov +yz +yz +PY +qm +va jQ AF AF @@ -31581,15 +31894,15 @@ AF AF AF jQ -Ne -AA -OZ -jQ -Gg -Jg jQ -ze -lM +AA +AA +wG +gg +Vm +wG +Oz +Oz jQ AF AF @@ -31775,15 +32088,15 @@ AF AF AF jQ -Sp -Sp -Sp +Ne +mU +gX mE CG CG -oL -nb -lM +Oz +ze +OZ jQ AF AF @@ -31975,9 +32288,9 @@ dx mE CG CG -oL +Oz nb -lM +Ph jQ AF AF @@ -32162,17 +32475,17 @@ AF AF AF AF -jQ +iC mA Sp dx jQ -Gg +lo Jg jQ -nb -lM -jQ +nU +OZ +xj AF AF AF @@ -32356,8 +32669,8 @@ AF AF AF AF -jQ -Sp +iC +Pn Sp Sp Cw @@ -32365,8 +32678,8 @@ CG CG uq lM -lM -jQ +OZ +xj AF AF AF @@ -32551,15 +32864,15 @@ AF AF AF jQ -Sp -Sp -Sp +qH +Dp +Qd jQ Gg Jg jQ -lM -lM +gb +kX jQ AF AF @@ -32749,8 +33062,8 @@ Sg Xs wG wG -CG -CG +ID +No jQ FB ya diff --git a/sound/enigma/enigma_hit.ogg b/sound/enigma/enigma_hit.ogg new file mode 100644 index 000000000000..91f1a6a61d9e Binary files /dev/null and b/sound/enigma/enigma_hit.ogg differ diff --git a/sound/enigma/enigma_hit2.ogg b/sound/enigma/enigma_hit2.ogg new file mode 100644 index 000000000000..5f7b86175832 Binary files /dev/null and b/sound/enigma/enigma_hit2.ogg differ diff --git a/sound/enigma/enigma_move.ogg b/sound/enigma/enigma_move.ogg new file mode 100644 index 000000000000..fab264aea5cc Binary files /dev/null and b/sound/enigma/enigma_move.ogg differ diff --git a/sound/enigma/enigma_move2.ogg b/sound/enigma/enigma_move2.ogg new file mode 100644 index 000000000000..ce7f51ad23e3 Binary files /dev/null and b/sound/enigma/enigma_move2.ogg differ diff --git a/sound/soundbytes/effects/combat/block-metal-on-metal-1.ogg b/sound/soundbytes/effects/combat/block-metal-on-metal-1.ogg new file mode 100644 index 000000000000..10ed875b8d0f Binary files /dev/null and b/sound/soundbytes/effects/combat/block-metal-on-metal-1.ogg differ diff --git a/sound/soundbytes/effects/combat/block-metal-on-metal-2.ogg b/sound/soundbytes/effects/combat/block-metal-on-metal-2.ogg new file mode 100644 index 000000000000..b7ae0e247d01 Binary files /dev/null and b/sound/soundbytes/effects/combat/block-metal-on-metal-2.ogg differ diff --git a/sound/soundbytes/effects/combat/block-metal-on-wood-1.ogg b/sound/soundbytes/effects/combat/block-metal-on-wood-1.ogg new file mode 100644 index 000000000000..cf018635f375 Binary files /dev/null and b/sound/soundbytes/effects/combat/block-metal-on-wood-1.ogg differ diff --git a/sound/soundbytes/effects/combat/block-metal-on-wood-2.ogg b/sound/soundbytes/effects/combat/block-metal-on-wood-2.ogg new file mode 100644 index 000000000000..25694ec55785 Binary files /dev/null and b/sound/soundbytes/effects/combat/block-metal-on-wood-2.ogg differ diff --git a/sound/soundbytes/effects/combat/block-wood-on-wood-1.ogg b/sound/soundbytes/effects/combat/block-wood-on-wood-1.ogg new file mode 100644 index 000000000000..d2829db00c37 Binary files /dev/null and b/sound/soundbytes/effects/combat/block-wood-on-wood-1.ogg differ diff --git a/sound/soundbytes/effects/combat/block-wood-on-wood-2.ogg b/sound/soundbytes/effects/combat/block-wood-on-wood-2.ogg new file mode 100644 index 000000000000..816db1f7171c Binary files /dev/null and b/sound/soundbytes/effects/combat/block-wood-on-wood-2.ogg differ diff --git a/sound/soundbytes/effects/combat/parry-cycle.ogg b/sound/soundbytes/effects/combat/parry-cycle.ogg new file mode 100644 index 000000000000..3429031bd94d Binary files /dev/null and b/sound/soundbytes/effects/combat/parry-cycle.ogg differ diff --git a/sound/soundbytes/effects/combat/parry-metal1.ogg b/sound/soundbytes/effects/combat/parry-metal1.ogg new file mode 100644 index 000000000000..c0f98249cd3b Binary files /dev/null and b/sound/soundbytes/effects/combat/parry-metal1.ogg differ diff --git a/sound/soundbytes/effects/combat/parry-metal2.ogg b/sound/soundbytes/effects/combat/parry-metal2.ogg new file mode 100644 index 000000000000..58c1233ceae4 Binary files /dev/null and b/sound/soundbytes/effects/combat/parry-metal2.ogg differ diff --git a/sound/soundbytes/effects/combat/parry-wood1.ogg b/sound/soundbytes/effects/combat/parry-wood1.ogg new file mode 100644 index 000000000000..a01f3dbe0161 Binary files /dev/null and b/sound/soundbytes/effects/combat/parry-wood1.ogg differ diff --git a/sound/soundbytes/effects/combat/parry-wood2.ogg b/sound/soundbytes/effects/combat/parry-wood2.ogg new file mode 100644 index 000000000000..398a59e24863 Binary files /dev/null and b/sound/soundbytes/effects/combat/parry-wood2.ogg differ