diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm index 9ef5b57d1fa1..d18bdc7d8cbc 100644 --- a/code/datums/components/pellet_cloud.dm +++ b/code/datums/components/pellet_cloud.dm @@ -90,7 +90,10 @@ /datum/component/pellet_cloud/proc/create_casing_pellets(obj/item/ammo_casing/shell, atom/target, mob/living/user, fired_from, randomspread, spread, zone_override, params, distro) - shooter = user + if(user) + shooter = user + else + shooter = fired_from var/targloc = get_turf(target) if(!zone_override) zone_override = shooter.zone_selected @@ -106,8 +109,12 @@ RegisterSignal(shell.BB, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(pellet_hit)) RegisterSignal(shell.BB, list(COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PARENT_QDELETING), PROC_REF(pellet_range)) pellets += shell.BB - if(!shell.throw_proj(target, targloc, shooter, params, spread)) - return + if(user) + if(!shell.throw_proj(target, targloc, shooter, params, spread)) + return + else + if(!shell.throw_proj(target, targloc, null, params, spread, shooter)) + return if(i != num_pellets) shell.newshot() diff --git a/code/game/objects/items/devices/mines.dm b/code/game/objects/items/devices/mines.dm index 1772cbf497c3..6547fde77ada 100644 --- a/code/game/objects/items/devices/mines.dm +++ b/code/game/objects/items/devices/mines.dm @@ -11,6 +11,7 @@ icon_state = "mine" item_state = "assembly"//when we get custom sprites replace this. please base_icon_state = "mine" + light_color = "#FF0000" /// Is our mine live? var/armed = FALSE @@ -24,13 +25,11 @@ var/manufacturer = MANUFACTURER_NONE - /obj/item/mine/Initialize(mapload) . = ..() if(armed) now_armed() - /obj/item/mine/examine(mob/user) . = ..() if(!armed) @@ -45,33 +44,39 @@ . = ..() icon_state = "[base_icon_state][triggered ? "_exploding" : null][!armed && anchored ? "_arming" : null][armed && anchored && !triggered ? "_armed" : null]" -//mines have a small chance to be triggered by damage, but they take longer to explode +/// mines have a small chance to be triggered by damage, but they take longer to explode /obj/item/mine/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir) . = ..() if(prob(35) & obj_integrity > 0) blast_delay = blast_delay * 2 trigger_mine() -//insert your horrible fate here +/// insert your horrible fate here /obj/item/mine/proc/mine_effect(mob/victim) return -//handles controlled deactivation +/// handles controlled deactivation /obj/item/mine/proc/disarm() if(triggered) //no turning back now return + light_power = 0 + light_range = 0 anchored = FALSE armed = FALSE update_appearance(UPDATE_ICON_STATE) return -//using an unarmed mine inhand deploys it. +/// using an unarmed mine inhand deploys it. /obj/item/mine/attack_self(mob/user) if(!armed) + if(!loccheck(user)) + to_chat(user, span_warning("There's already a mine at this position!")) + return user.visible_message(span_danger("[user] deploys the [src]."), span_notice("You deploy the [src].")) user.dropItemToGround(src) anchored = TRUE + dir = user.dir playsound(src, 'sound/machines/click.ogg', 60, TRUE) if(arm_delay) @@ -82,27 +87,30 @@ armed = TRUE message_admins("[key_name(user)] has placed \a [src] at ([x],[y],[z]).") -//let them know the mine's done cooking +/obj/item/mine/proc/loccheck(mob/user) + for(var/obj/item/mine/alreadymined in user.loc) + if(alreadymined.anchored) + return FALSE + return TRUE + +/// let them know the mine's done cooking /obj/item/mine/proc/now_armed() armed = TRUE update_appearance(UPDATE_ICON_STATE) + light_power = 1 + light_range = 1 playsound(src, 'sound/machines/nuke/angry_beep.ogg', 55, FALSE, 1) visible_message("\The [src] beeps softly, indicating it is now active.", vision_distance = COMBAT_MESSAGE_RANGE) /// Can this mine trigger on the passed movable? /obj/item/mine/proc/can_trigger(atom/movable/on_who) - //var/badtype = typecacheof(list(/obj/effect, /obj/item/mine)) if(triggered || !isturf(loc) || !armed || iseffect(on_who) || istype(on_who, /obj/item/mine)) return FALSE - //if(on_who == badtype)//no recursive self triggering. Bad landmine - // return FALSE return TRUE /// When something sets off a mine /obj/item/mine/proc/trigger_mine(atom/movable/triggerer) - if(obj_integrity <= 0) - return - if(triggered) //too busy detonating to detonate again + if(obj_integrity <= 0 || triggered)//too busy detonating to detonate again return if(triggerer) triggerer.visible_message(span_danger("[icon2html(src, viewers(src))] [triggerer] sets off \the [src]. It's gonna blow!"), span_danger("[icon2html(src, viewers(src))] \The [src] activates.")) @@ -114,7 +122,6 @@ playsound(src, 'sound/items/mine_activate.ogg', 70, FALSE) else playsound(src, 'sound/items/mine_activate_short.ogg', 80, FALSE) - light_color = "#FF0000" light_power = 5 light_range = 3 if(!blast_delay)//addtimer gets mad if the delay is 0 @@ -122,7 +129,7 @@ else addtimer(CALLBACK(src, PROC_REF(blast_now), triggerer), blast_delay) -//NOW we actually blow up +///NOW we actually blow up /obj/item/mine/proc/blast_now(atom/movable/triggerer) var/datum/effect_system/spark_spread/sporks = new /datum/effect_system/spark_spread sporks.set_up(3, 1, src) @@ -142,7 +149,7 @@ user.visible_message(span_warning("[user] extends their hand towards \the [src]!"), span_userdanger("You extend your arms to pick up \the [src], knowing that it will likely blow up when you touch it!")) if(do_after(user, 5 SECONDS, target = src))//SO SO generous. You can still step back from the edge. if(prob(10)) - user.visible_message(span_notice("[user] picks up \the [src], which miraculously doesn't explode!"), span_notice("You pick up \the [src], which miraculously doesn't explode!")) + user.visible_message(span_notice("[user] picks up \the [src], which miraculously doesn't go off!"), span_notice("You pick up \the [src], which miraculously doesn't go off!")) disarm() else user.visible_message(span_danger("[user] attempts to pick up \the [src] only to hear a beep as it activates in their hand!"), span_danger("You attempt to pick up \the [src] only to hear a beep as it activates in your hands!")) @@ -166,8 +173,8 @@ trigger_mine(user) // -//PRESSURE BASED MINE: -//Mine that explodes when stepped on. +///PRESSURE BASED MINE: +///Mine that explodes when stepped on. /obj/item/mine/pressure name = "dummy landmine" /// When true, mines trigger instantly on being stepped upon @@ -254,11 +261,7 @@ clicked = FALSE . = ..() -/obj/item/mine/pressure/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - trigger_mine(AM) - ..() - -//handles disarming(and failing to disarm) +///handles disarming(and failing to disarm) /obj/item/mine/pressure/attackby(obj/item/I, mob/user) if(I.tool_behaviour == TOOL_SCREWDRIVER) if(sealed) @@ -275,18 +278,16 @@ else . = ..() -// -//PROXIMITY MINES -//Mines that explode when someone moves nearby. Simpler, because I don't have to worry about saving step info or disarming logic -// +//PROXIMITY MINES +///Mines that explode when someone moves nearby. Simpler, because I don't have to worry about saving step info or disarming logic /obj/item/mine/proximity name = "dummy proximity mine" blast_delay = 15 DECISECONDS arm_delay = 10 SECONDS//clear the area ///needed for the proximity checks. var/datum/proximity_monitor/proximity_monitor - var/proximity_range = 2 + var/proximity_range = 3 /obj/item/mine/proximity/Initialize(mapload) . = ..() @@ -302,12 +303,8 @@ /obj/item/mine/proximity/now_armed() . = ..() proximity_monitor = new(src, proximity_range) - light_color = "#FF0000" - light_power = 1 - light_range = 1 /obj/item/mine/proximity/disarm() - . = ..() QDEL_NULL(proximity_monitor) /obj/item/mine/proximity/Destroy() @@ -316,7 +313,11 @@ . = ..() /obj/item/mine/proximity/HasProximity(atom/movable/triggerer) - if(!iscarbon(triggerer))//let's keep these on player movements for now. + //let's keep these on player movements for now. + if(!iscarbon(triggerer)) + return + //Quick and dirty solution for preventing activations behind walls. + if(!(triggerer in view(proximity_range, src))) return if(!can_trigger(triggerer)) return @@ -326,9 +327,92 @@ QDEL_NULL(proximity_monitor) return +//DIRECTIONAL MINES +///Once deployed, keeps an eye on a line of turfs in the faced direction. If something moves in them, explode. +/obj/item/mine/directional + name = "directional mine" + desc = "An anti-personnel device that activates when an object moves in front of it. This one does nothing and is for testing purposes only." + + blast_delay = 1 DECISECONDS + arm_delay = 5 SECONDS + + ///range of tripwire + var/trigger_range = 4 + + ///projectile casing to fire in the selected direction when the mine is triggered. + //null prevents a projectile from being fired. + var/obj/item/ammo_casing/casingtype = null + + ///cache of turfs for detection area + var/list/tripwire_turfs + + ///for aiming the resulting projectiles + var/turf/target_turf + +///kills any existing tripwires +/obj/item/mine/directional/proc/remove_tripwires() + if(tripwire_turfs) + for(var/turf/affected_turf in tripwire_turfs) + UnregisterSignal(affected_turf, COMSIG_ATOM_ENTERED) + tripwire_turfs = null + if(target_turf) + target_turf = null + return + +///sets up tripwires(or recreates them, if already present) +/obj/item/mine/directional/proc/draw_tripwires() + if(tripwire_turfs) + remove_tripwires() + //we'll also use this to set up the pew + target_turf = get_ranged_target_turf(src, dir, trigger_range) + var/turf/starting_turf = get_turf(src) + tripwire_turfs = get_line(starting_turf, target_turf) + + for(var/turf/affected_turf in tripwire_turfs) + RegisterSignal(affected_turf, COMSIG_ATOM_ENTERED, PROC_REF(on_entered)) + +/obj/item/mine/directional/claymore/now_armed() + draw_tripwires() + . = ..() + +/obj/item/mine/directional/proc/on_entered(datum/source, atom/movable/arrived) + SIGNAL_HANDLER + if(!(arrived in view(trigger_range, src))) + return + if(!can_trigger(arrived)) + return + + if(ismob(arrived)) + var/mob/living/fool = arrived + fool.do_alert_animation(fool) + + visible_message(span_danger("[icon2html(src, viewers(src))] *click*")) + playsound(src, 'sound/machines/click.ogg', 100, TRUE) + INVOKE_ASYNC(src, PROC_REF(trigger_mine), arrived) + + +//pew pew +/obj/item/mine/directional/mine_effect(mob/victim) + if(casingtype && target_turf && victim ?(src.loc != victim.loc) : victim == null) + var/obj/item/ammo_casing/casing = new casingtype(src) + casing.fire_casing(target_turf, null, null, null, 30, ran_zone(), 60, src) + . = ..() + +/obj/item/mine/directional/disarm() + remove_tripwires() + visible_message(span_danger("With a soft clunk, the [src]'s securing bolts retract.")) + . = ..() + +///handles weird cases like ship movement or teleporting +/obj/item/mine/directional/Moved() + . = ..() + if(!loc) + return + if(armed & !triggered) + draw_tripwires() + // //LANDMINE TYPES -//Rylie please help me make these more immersive // /obj/item/mine/pressure/explosive @@ -380,7 +464,7 @@ shrapnel_magnitude = 4 /obj/item/mine/pressure/explosive/fire/mine_effect(mob/victim) - if(victim.is_holding(src))//in case it's been picked up + if(victim && victim.is_holding(src))//in case it's been picked up for(var/turf/T in view(4,victim)) T.IgniteTurf(15) new /obj/effect/hotspot(T) @@ -473,7 +557,7 @@ AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_magnitude) -//like all real 'less' than lethal crowd control options this is, in fact, not very good at being nonlethal +///like all real 'less' than lethal crowd control options this is, in fact, not very good at being nonlethal /obj/item/mine/proximity/explosive/sting name = "\improper'Stinger' Crowd Management Device" desc = "A \"less\" than lethal crowd control weapon, designed to demoralise and scatter anti-NT protestors. The bands of ballistic gel inside strike targets and incapacitate without causing serious maiming. In Theory." @@ -526,6 +610,76 @@ desc = "An anti-infantry device produced during the corporate wars. The explosive payload has been swapped out for 'viscerator'-type antipersonnel drones." spawn_type = /mob/living/simple_animal/hostile/viscerator + + +//Claymores +//shrapnel based dir explosive, extreme short range +//FRONT TOWARDS ENEMY +/obj/item/mine/directional/claymore + name = "C-10 Claymore" + desc = "A compact anti-personnel device with a directional trigger that responds to movement. A faded sticker on the back reads \"FRONT TOWARDS ENEMY\"." + icon = 'icons/obj/world/landmine.dmi' + icon_state = "mine_claymore" + base_icon_state = "mine_claymore" + + trigger_range = 2 + + //customize explosive power + var/range_devastation = -1 + var/range_heavy = 1 + var/range_light = 2 + var/range_flame = 0 + + //using this to indicate pb + var/range_flash = 1 + + //a second run of shrapnel, intended for maiming especially pb targets + var/obj/item/ammo_casing/shredtype = /obj/item/ammo_casing/caseless/shrapnel/shred + casingtype = /obj/item/ammo_casing/caseless/shrapnel + + manufacturer = MANUFACTURER_SCARBOROUGH + +//this will return to basic mines when we relegate them to specifically being on certain ruins & battlefields. For now, it's way too dangerous +/obj/item/mine/directional/claymore/Initialize() + . = ..() + AddElement(/datum/element/world_icon, null, icon, 'icons/obj/landmine.dmi') + +/obj/item/mine/directional/claymore/attackby(obj/item/I, mob/user) + if (I.tool_behaviour == TOOL_SCREWDRIVER && armed) + to_chat(user, "You begin unscrewing \the [src]'s arming pin...") + I.play_tool_sound(src, 50) + if(do_after(user, 10 SECONDS, target = src)) + to_chat(user, "You unscrew \the [src]'s arming pin, disarming it.") + disarm() + else + . = ..() + +/obj/item/mine/directional/claymore/mine_effect(mob/victim) + . = ..() + //if you somehow explode it while on the same tile, you win bonus shrapnel + //also spews stuff everywhere if it's triggered while not set up + if(!target_turf || victim ? (victim.loc == src.loc) : victim == null) + explosion(src, range_devastation, range_heavy, range_light, range_flash, 1, 0, range_flame, 0, 1) + var/casingammo = casingtype.projectile_type + var/shredammo = shredtype.projectile_type + if(casingtype) + AddComponent(/datum/component/pellet_cloud, projectile_type = casingammo, magnitude = 1) + if(shredtype) + AddComponent(/datum/component/pellet_cloud, projectile_type = shredammo, magnitude = 2) + else + var/blastloc = get_step_towards(src, target_turf) + explosion(blastloc, range_devastation, range_heavy, range_light, range_flash, 1, 0, range_flame, 0, 1) + if(shredtype) + var/obj/item/ammo_casing/shredcasing = new shredtype(src) + shredcasing.fire_casing(target_turf, null, null, null, 30, ran_zone(), 50, src) + +/obj/item/mine/directional/claymore/plasma + name = "\improper Etherbor EC-1" + desc = "A proximity explosive designed by the PGF for ambushing advancing infantry & defending corridors. Cooks armored targets to well-done." + shredtype = /obj/item/ammo_casing/caseless/shrapnel/shred/plasma + casingtype = /obj/item/ammo_casing/caseless/shrapnel/plasma + manufacturer = MANUFACTURER_PGF + // //GIMMICK MINES// //pretty much exclusively for adminbus & code dependencies @@ -671,6 +825,9 @@ LIVE_MINE_HELPER(proximity/explosive/sting) LIVE_MINE_HELPER(proximity/spawner/manhack) LIVE_MINE_HELPER(proximity/explosive/plasma) +LIVE_MINE_HELPER(directional/claymore) +LIVE_MINE_HELPER(directional/claymore/plasma) + LIVE_MINE_HELPER(pressure/gas) LIVE_MINE_HELPER(pressure/kickmine) LIVE_MINE_HELPER(pressure/sound) diff --git a/code/game/objects/items/shrapnel.dm b/code/game/objects/items/shrapnel.dm index 249ee7dc41ed..db676e60fc3c 100644 --- a/code/game/objects/items/shrapnel.dm +++ b/code/game/objects/items/shrapnel.dm @@ -35,7 +35,7 @@ name = "flying shrapnel shard" damage = 10 range = 10 - armour_penetration = -20 + armour_penetration = -5 dismemberment = 25 ricochets_max = 2 ricochet_chance = 40 @@ -49,7 +49,7 @@ /obj/projectile/bullet/shrapnel/rusty damage = 8 - armour_penetration = -35 + armour_penetration = -10 dismemberment = 15 ricochets_max = 3//duller = less likely to stick in a wall ricochet_chance = 60 @@ -115,3 +115,42 @@ /obj/projectile/bullet/pellet/stingball/on_ricochet(atom/A) hit_stunned_targets = TRUE // ducking will save you from the first wave, but not the rebounds + + +//claymore shrapnel stuff// +//2 small bursts- one that harasses people passing by a bit aways, one that brutalizes point-blank targets. +/obj/item/ammo_casing/caseless/shrapnel + name = "directional shrapnel burst :D" + desc = "I May Have Overreacted" + pellets = 4 + variance = 70 + projectile_type = /obj/projectile/bullet/shrapnel/claymore + randomspread = TRUE + +/obj/item/ammo_casing/caseless/shrapnel/shred + name = "point blank directional shrapnel burst" + desc = "Claymores are lethal to armored infantry at point blank range." + pellets = 3 + variance = 50 + projectile_type = /obj/projectile/bullet/shrapnel/claymore/pointbl + randomspread = TRUE + +/obj/projectile/bullet/shrapnel/claymore + name = "ceramic splinter" + range = 4 + armour_penetration = 0 + +/obj/projectile/bullet/shrapnel/claymore/pointbl + name = "large ceramic shard" + range = 2 + damage = 18 + dismemberment = 30 + armour_penetration = 10 + +/obj/item/ammo_casing/caseless/shrapnel/plasma + name = "directional plasma burst" + projectile_type = /obj/projectile/energy/plasmabolt + +/obj/item/ammo_casing/caseless/shrapnel/shred/plasma + name = "point blank directional plasma burst" + projectile_type = /obj/projectile/energy/plasmabolt/shred diff --git a/code/game/turfs/open/floor.dm b/code/game/turfs/open/floor.dm index 23fdfcc6d998..7b1603df119d 100644 --- a/code/game/turfs/open/floor.dm +++ b/code/game/turfs/open/floor.dm @@ -62,7 +62,7 @@ if(1) ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR) if(2) - if(prob(60)) + if(prob(50) && broken) ScrapeAway(flags = CHANGETURF_INHERIT_AIR) else break_tile() diff --git a/code/modules/cargo/packs/sec_supply.dm b/code/modules/cargo/packs/sec_supply.dm index e4208699338b..3ae043e4293d 100644 --- a/code/modules/cargo/packs/sec_supply.dm +++ b/code/modules/cargo/packs/sec_supply.dm @@ -155,3 +155,28 @@ contains = list(/obj/item/gun/energy/e_gun/advtaser, /obj/item/gun/energy/e_gun/advtaser) crate_name = "hybrid taser crate" + +/datum/supply_pack/sec_supply/claymore + name = "C-10 Claymore Crate" + desc = "Four motion-activated directional mines, perfect for ambushing enemy infantry. Still debatably legal to sell!" + cost = 3000 + contains = list(/obj/item/paper/fluff/claymore, + /obj/item/mine/directional/claymore, + /obj/item/mine/directional/claymore, + /obj/item/mine/directional/claymore, + /obj/item/mine/directional/claymore) + crate_name = "C-10 Claymore crate" + +/obj/item/paper/fluff/claymore + name = "PRODUCT USAGE GUIDE" + desc = "A dusty memo stamped with the Scarborough Arms logo." + default_raw_text = "ASSEMBLY:

\ + -Deploy mounting legs and emplace device. Front should be placed in direction of enemy egress, no more then three meters from intended target area.

\ + -INFORM ALLIES OF PLACEMENT LOCATION.

\ + -Wait for arming sequence to complete.

\ + -Enjoy hands-free area denial, courtesy of Scarborough Arms.


\ + DISASSEMBLY & STORAGE:

\ + -Insert screwdriver into arming pin access and turn 180 degrees. There will be considerable resistance. DO NOT Step onto or in front of device.

\ + -When pressure releases, reach below device and lift via underside in one clean motion. Mounting legs will automatically retract.

\ + -The device is now safe to handle.

\ + -Safely stow device in secure, moisture-free location, away from fire and blunt force. " diff --git a/code/modules/projectiles/ammunition/_firing.dm b/code/modules/projectiles/ammunition/_firing.dm index bdc5254f13c9..cfa4e91e645c 100644 --- a/code/modules/projectiles/ammunition/_firing.dm +++ b/code/modules/projectiles/ammunition/_firing.dm @@ -8,7 +8,7 @@ spread = round((rand() - 0.5) * distro) else //Smart spread spread = round(1 - 0.5) * distro - if(!throw_proj(target, targloc, user, params, spread)) + if(!throw_proj(target, targloc, user, params, spread, fired_from)) return FALSE else if(isnull(BB)) @@ -45,7 +45,7 @@ reagents.trans_to(BB, reagents.total_volume, transfered_by = user) //For chemical darts/bullets qdel(reagents) -/obj/item/ammo_casing/proc/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread) +/obj/item/ammo_casing/proc/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread, atom/fired_from) var/turf/curloc if(user) curloc = get_turf(user) diff --git a/code/modules/projectiles/projectile/energy/misc.dm b/code/modules/projectiles/projectile/energy/misc.dm index 11f948ddb415..c9e113455387 100644 --- a/code/modules/projectiles/projectile/energy/misc.dm +++ b/code/modules/projectiles/projectile/energy/misc.dm @@ -27,18 +27,33 @@ /obj/projectile/energy/plasmabolt name = "ionized plasma" damage = 25 - armour_penetration = -10 + armour_penetration = -15 range = 8 damage_type = BURN icon_state = "blastwave" color = "#00ff00" hitsound = 'sound/weapons/sear.ogg' + var/heatpwr = 350 /obj/projectile/energy/plasmabolt/on_hit(atom/target, blocked = FALSE) . = ..() if(iscarbon(target)) - var/mob/living/carbon/M = target - M.adjust_bodytemperature(350) + var/mob/living/carbon/cooked = target + cooked.adjust_bodytemperature(heatpwr) if(prob(35)) - M.adjust_fire_stacks(15) - M.IgniteMob() + cooked.adjust_fire_stacks(15) + cooked.IgniteMob() + else + if(cooked.on_fire) + cooked.adjust_fire_stacks(10) + +/obj/projectile/energy/plasmabolt/shred + name = "high-energy ionized plasma" + damage = 35 + armour_penetration = -5 + range = 2 + damage_type = BURN + icon_state = "blastwave" + color = "#00ff00" + hitsound = 'sound/weapons/sear.ogg' + heatpwr = 700 diff --git a/icons/obj/landmine.dmi b/icons/obj/landmine.dmi index dd19fd9d3991..40b3e3e4ba12 100644 Binary files a/icons/obj/landmine.dmi and b/icons/obj/landmine.dmi differ diff --git a/icons/obj/world/landmine.dmi b/icons/obj/world/landmine.dmi new file mode 100644 index 000000000000..be8db65a3f5f Binary files /dev/null and b/icons/obj/world/landmine.dmi differ