diff --git a/aurorastation.dme b/aurorastation.dme index b03e89a143c..85a359ef9be 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -475,6 +475,7 @@ #include "code\datums\wires\explosive.dm" #include "code\datums\wires\iff.dm" #include "code\datums\wires\ipc_tag_scanner.dm" +#include "code\datums\wires\landmine.dm" #include "code\datums\wires\nuclearbomb.dm" #include "code\datums\wires\particle_accelerator.dm" #include "code\datums\wires\radio.dm" @@ -1142,6 +1143,7 @@ #include "code\game\objects\items\weapons\grenades\flashbang.dm" #include "code\game\objects\items\weapons\grenades\fragmentation.dm" #include "code\game\objects\items\weapons\grenades\grenade.dm" +#include "code\game\objects\items\weapons\grenades\napalm_grenade.dm" #include "code\game\objects\items\weapons\grenades\smokebomb.dm" #include "code\game\objects\items\weapons\grenades\spawnergrenade.dm" #include "code\game\objects\items\weapons\grenades\stinger.dm" diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 5ec29e359f2..f671b9401a4 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -54,6 +54,51 @@ else if(dx < 0) . += 360 +/** + * Gets all turfs inside a cone, return a `/list` of `/turf` that are inside the cone + * + * * source - The source from which to calculate the cone from, an `/atom` + * * middle_angle - The angle that is considered the middle, if not specific (eg. from a click), you can use `dir2angle(dir)` to convert the direction of the atom to an angle + * * distance - How far to take turfs from + * * angle_spread - How much degrees does the cone spread, from the `middle_angle` + * + */ +/proc/get_turfs_in_cone(atom/source, middle_angle, distance, angle_spread) + SHOULD_NOT_SLEEP(TRUE) + SHOULD_BE_PURE(TRUE) + + if(!source) + crash_with("Source not specified") + + if(isnull(middle_angle) || middle_angle < 0) + crash_with("middle_angle not specified, or invalid") + + if(isnull(distance)) + crash_with("Distance not specified") + + if(angle_spread < 0) + crash_with("angle_spread cannot be negative") + + var/list/turf/turfs_in_cone = list() + RETURN_TYPE(turfs_in_cone) + + var/angle_left = (middle_angle - angle_spread + 360) % 360 + var/angle_right = (middle_angle + angle_spread) % 360 + + for(var/turf/turf in range(distance, source)) + var/angle_between_source_and_target = Get_Angle(source, turf) + + // Ensure correct handling of angles spanning the 0-degree mark + if(angle_left <= angle_right) + if((angle_between_source_and_target >= angle_left) && (angle_between_source_and_target <= angle_right)) + turfs_in_cone += turf + else + if((angle_between_source_and_target >= angle_left) || (angle_between_source_and_target <= angle_right)) + turfs_in_cone += turf + + return turfs_in_cone + + /proc/get_projectile_angle(atom/source, atom/target) var/sx = source.x * world.icon_size var/sy = source.y * world.icon_size diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index 2d5c887ae23..e2883576e04 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -235,7 +235,7 @@ var/obj/screen/robot_inventory else //Modules display is hidden //r.client.screen -= robot_inventory //"store" icon - for(var/atom/A in r.module.modules) + for(var/atom/A in r.module?.modules) if( (A != r.module_state_1) && (A != r.module_state_2) && (A != r.module_state_3) ) //Module is not currently active r.client.screen -= A diff --git a/code/datums/uplink/grenades.dm b/code/datums/uplink/grenades.dm index 4cec44c27d0..a4b14e071b2 100644 --- a/code/datums/uplink/grenades.dm +++ b/code/datums/uplink/grenades.dm @@ -39,3 +39,9 @@ telecrystal_cost = 2 path = /obj/item/storage/box/cardox desc = "A box of five grenades that deploy cardox smoke in the thrown area. This smoke is incredibly toxic, especially to vaurca. It can also clear K'ois outbreaks with ease." + +/datum/uplink_item/item/grenades/napalm + name = "5x Napalm Grenades" + telecrystal_cost = 8 + path = /obj/item/storage/box/grenades/napalm + desc = "A box of five grenades that deploy napalm in the thrown area, and ignite it." diff --git a/code/datums/uplink/highly visible and dangerous weapons.dm b/code/datums/uplink/highly visible and dangerous weapons.dm index da02566e956..9e657bf054b 100644 --- a/code/datums/uplink/highly visible and dangerous weapons.dm +++ b/code/datums/uplink/highly visible and dangerous weapons.dm @@ -269,3 +269,9 @@ name = "Psionic Jumpstarter" telecrystal_cost = 19 path = /obj/item/psionic_jumpstarter + +/datum/uplink_item/item/visible_weapons/flamethrower + name = "Flamethrower" + desc = "A flamethrower, with a full canister of Phoron installed to fuel it. Handle with caution." + telecrystal_cost = 10 + path = /obj/item/flamethrower/full diff --git a/code/datums/uplink/stealthy and inconspicuous weapons.dm b/code/datums/uplink/stealthy and inconspicuous weapons.dm index a9ab2bcd696..c9c4b5079bc 100644 --- a/code/datums/uplink/stealthy and inconspicuous weapons.dm +++ b/code/datums/uplink/stealthy and inconspicuous weapons.dm @@ -48,3 +48,22 @@ telecrystal_cost = 2 bluecrystal_cost = 2 path = /obj/item/trap/sharpened + +/datum/uplink_item/item/stealthy_weapons/punji_trap + name = "Punji Trap" + desc = "A box with 5 punji traps." + telecrystal_cost = 2 + bluecrystal_cost = 2 + path = /obj/item/storage/box/traps/punji + +/datum/uplink_item/item/stealthy_weapons/standstill_landmines + name = "Standstill Landmines" + desc = "A box with 5 standstill landmines." + telecrystal_cost = 5 + path = /obj/item/storage/box/landmines/standstill + +/datum/uplink_item/item/stealthy_weapons/claymore_mines + name = "Claymore Mines" + desc = "A box with 5 claymore miners, relative detonators (signalers), and a spare one for you to trigger them all." + telecrystal_cost = 8 + path = /obj/item/storage/box/landmines/claymore diff --git a/code/datums/wires/landmine.dm b/code/datums/wires/landmine.dm new file mode 100644 index 00000000000..8b7cddc7abd --- /dev/null +++ b/code/datums/wires/landmine.dm @@ -0,0 +1,29 @@ +/datum/wires/landmine + wire_count = 1 + +/datum/wires/landmine/proc/explode() + return + +/datum/wires/landmine/UpdatePulsed(var/index) + switch(index) + if(WIRE_EXPLODE) + explode() + +/datum/wires/landmine/UpdateCut(var/index, var/mended) + switch(index) + if(WIRE_EXPLODE) + if(!mended) + explode() + +/datum/wires/landmine/claymore + holder_type = /obj/item/landmine/claymore + +/datum/wires/landmine/claymore/CanUse(var/mob/living/L) + var/obj/item/landmine/claymore/claymore = holder + if(!claymore.deactivated && claymore.deployed) + return TRUE + return FALSE + +/datum/wires/landmine/claymore/explode() + var/obj/item/landmine/claymore/claymore = holder + claymore.trigger() diff --git a/code/game/objects/items/devices/uplink.dm b/code/game/objects/items/devices/uplink.dm index 304a09dab34..7c318c179fe 100644 --- a/code/game/objects/items/devices/uplink.dm +++ b/code/game/objects/items/devices/uplink.dm @@ -64,8 +64,13 @@ Then check if it's true, if true return. This will stop the normal menu appearin name = "hidden uplink" desc = "There is something wrong if you're examining this." var/active = 0 - var/datum/uplink_category/category = 0 // The current category we are in - var/exploit_id // Id of the current exploit record we are viewing + + /// The current category we are in, the open category + var/datum/uplink_category/category + + /// ID of the current exploit record we are viewing + var/exploit_id + var/pda_code = "" @@ -179,7 +184,7 @@ Then check if it's true, if true return. This will stop the normal menu appearin tgui_data["items"] = items else if(tgui_menu == 1) var/items[0] - for(var/datum/uplink_item/item in category.items) + for(var/datum/uplink_item/item in category?.items) if(item.can_view(src)) items[++items.len] = new_tgui_item_data(item) tgui_data["items"] = items diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index 0718c959061..d308cbda362 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -22,7 +22,6 @@ var/throw_amount = 100 var/lit = FALSE //on or off var/operating = FALSE //cooldown - var/turf/previous_turf = null var/obj/item/weldingtool/welding_tool = null var/obj/item/device/assembly/igniter/igniter = null var/obj/item/tank/gas_tank = null @@ -100,7 +99,7 @@ if(user && user.get_active_hand() == src) var/turf/target_turf = get_turf(target) if(target_turf) - var/turflist = getline(user, target_turf) + var/turflist = get_turfs_in_cone(user, Get_Angle(user, target_turf), get_dist(user, target_turf), 30) flame_turf(turflist) /obj/item/flamethrower/attackby(obj/item/W, mob/user) @@ -233,18 +232,22 @@ /obj/item/flamethrower/proc/flame_turf(turflist) if(!lit || operating) return + operating = TRUE - for(var/turf/T in turflist) + + var/turf/operator_turf = get_turf(src) + + for(var/turf/T in (turflist - operator_turf)) + if(T.density || istype(T, /turf/space)) - break - if(!previous_turf && length(turflist)>1) - previous_turf = get_turf(src) - continue //so we don't burn the tile we be standin on - if(previous_turf && LinkBlocked(previous_turf, T)) - break + continue + + if(LinkBlocked(operator_turf, T)) + continue + ignite_turf(T) sleep(1) - previous_turf = null + operating = FALSE diff --git a/code/game/objects/items/weapons/grenades/napalm_grenade.dm b/code/game/objects/items/weapons/grenades/napalm_grenade.dm new file mode 100644 index 00000000000..7d5dcb70482 --- /dev/null +++ b/code/game/objects/items/weapons/grenades/napalm_grenade.dm @@ -0,0 +1,12 @@ +/obj/item/grenade/napalm + name = "napalm grenade" + desc = "A grenade that delivers napalm, not as classy as an airstrike bomb, but still effective." + desc_extended = "A Necropolis Industries (now known as Zavodskoi Interstellar) engineered device, this grenade is one of the most effective and destructive portable emergency sterilization \ + device available, causing high-intensity sustained fires at over 2000K." + desc_antag = "This causes a 5-tiles large fire that burns for quite a while, don't be a dick with it." + +/obj/item/grenade/napalm/prime() + . = ..() + + for(var/turf/turf in view(5, src)) + new /obj/effect/decal/cleanable/liquid_fuel/napalm(turf, 100, TRUE) diff --git a/code/game/objects/items/weapons/landmines.dm b/code/game/objects/items/weapons/landmines.dm index 2c0f06eb1dc..6e475e7dbf3 100644 --- a/code/game/objects/items/weapons/landmines.dm +++ b/code/game/objects/items/weapons/landmines.dm @@ -1,7 +1,7 @@ /obj/item/landmine name = "land mine" desc = "An anti-personnel explosive device used for area denial." - icon = 'icons/obj/grenade.dmi' + icon = 'icons/obj/item/landmine/mine.dmi' icon_state = "landmine" throwforce = 0 var/deployed = FALSE @@ -10,9 +10,9 @@ /obj/item/landmine/update_icon() ..() if(!deployed) - icon_state = "[icon_state]" + icon_state = "[initial(icon_state)]" else - icon_state = "[icon_state]_on" + icon_state = "[initial(icon_state)]_on" /obj/item/landmine/verb/hide_under() set src in oview(1) @@ -37,21 +37,37 @@ "You begin deploying \the [src]!" ) - if (do_after(user, 6 SECONDS, DO_REPAIR_CONSTRUCT)) + if (do_after(user, 6 SECONDS, do_flags = DO_REPAIR_CONSTRUCT)) user.visible_message( "[user] has deployed \the [src].", "You have deployed \the [src]!" ) - deployed = TRUE - user.drop_from_inventory(src) - update_icon() - anchored = TRUE + deploy(user) -/obj/item/landmine/proc/trigger(mob/living/L) +/** + * Called when a landmine is deployed + * + * * deployer - The `/mob` that deployed the mine + */ +/obj/item/landmine/proc/deploy(mob/deployer) + SHOULD_CALL_PARENT(TRUE) + SHOULD_NOT_SLEEP(TRUE) + + src.deployed = TRUE + deployer.drop_from_inventory(src) + update_icon() + src.anchored = TRUE + +/** + * Called when a landmine was triggered, and is supposed to explode + * + * * triggerer - The `/mob/living` that triggered the mine + */ +/obj/item/landmine/proc/trigger(mob/living/triggerer) spark(src, 3, alldirs) - if(ishuman(L)) - L.Weaken(2) + if(ishuman(triggerer)) + triggerer.Weaken(2) explosion(loc, 0, 2, 2, 3) qdel(src) @@ -75,24 +91,56 @@ /obj/item/landmine/attack_hand(mob/user as mob) if(deployed && !use_check(user, USE_DISALLOW_SILICONS)) - user.visible_message( - "[user] triggers \the [src].", - "You trigger \the [src]!", - "You hear a mechanical click!" - ) - trigger(user) + attack_hand_trigger(user) else ..() +/** + * Called when the attack_hand is supposed to trigger the mine + * + * * user - The `/mob` that attacked the mine + */ +/obj/item/landmine/proc/attack_hand_trigger(mob/user) + user.visible_message( + SPAN_DANGER("[user] triggers \the [src]."), + SPAN_DANGER("You trigger \the [src]!"), + SPAN_DANGER("You hear a mechanical click!") + ) + trigger(user) + +/** + * Activates the landmine, arming it + * + * NOT the same as deploying it, which is putting it on the ground + * + * * user - The `/mob` that activated the mine + */ +/obj/item/landmine/proc/activate(mob/user) + SHOULD_NOT_SLEEP(TRUE) + + src.deactivated = FALSE + +/** + * Deactivates the landmine, disarming it + * + * NOT the same as removing it from the ground + * + * * user - The `/mob` that activated the mine + */ +/obj/item/landmine/proc/deactivate(mob/user) + SHOULD_NOT_SLEEP(TRUE) + + src.deactivated = TRUE + /obj/item/landmine/attackby(obj/item/I, mob/user) ..() if(deactivated && istype(I, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/C = I if(C.use(1)) to_chat(user, SPAN_NOTICE("You start carefully start rewiring \the [src].")) - if(do_after(user, 10 SECONDS, src, DO_REPAIR_CONSTRUCT)) + if(do_after(user, 10 SECONDS, do_flags = DO_REPAIR_CONSTRUCT)) to_chat(user, SPAN_NOTICE("You successfully rewire \the [src], priming it for use.")) - deactivated = FALSE + activate(user) return else to_chat(user, SPAN_WARNING("There's not enough cable to finish the task.")) @@ -104,7 +152,7 @@ if(I.use_tool(src, user, 150, volume = 50)) if(prob(W.bomb_defusal_chance)) to_chat(user, SPAN_NOTICE("You successfully defuse \the [src], though it's missing some essential wiring now.")) - deactivated = TRUE + deactivate(user) anchored = FALSE deployed = FALSE update_icon() @@ -122,8 +170,17 @@ if(deployed) trigger() -//landmines that do more than explode +/* +################## + Subtypes +################## +*/ +/** + * # Fragmentation Landmine + * + * A landmine that throws sharpnels around + */ /obj/item/landmine/frag var/num_fragments = 15 var/fragment_damage = 10 @@ -131,11 +188,16 @@ var/explosion_size = 3 var/spread_range = 7 -/obj/item/landmine/frag/trigger(mob/living/L) +/obj/item/landmine/frag/trigger(mob/living/triggerer) spark(src, 3, alldirs) fragem(src,num_fragments,num_fragments,explosion_size,explosion_size+1,fragment_damage,damage_step,TRUE) qdel(src) +/** + * # Radiation Landmine + * + * A landmine that irradiates the victim + */ /obj/item/landmine/radiation icon_state = "radlandmine" @@ -147,10 +209,15 @@ H.apply_radiation(50) qdel(src) +/** + * # Phoron Landmine + * + * A landmine that releases phoron + */ /obj/item/landmine/phoron icon_state = "phoronlandmine" -/obj/item/landmine/phoron/trigger(mob/living/L) +/obj/item/landmine/phoron/trigger(mob/living/triggerer) spark(src, 3, alldirs) for (var/turf/simulated/floor/target in range(1,src)) if(!target.blocks_air) @@ -160,6 +227,11 @@ qdel(src) +/** + * # Nitrous Oxide Landmine + * + * A landmine that releases nitrous oxide + */ /obj/item/landmine/n2o icon_state = "phoronlandmine" @@ -171,10 +243,164 @@ qdel(src) +/** + * # EMP Landmine + * + * A landmine that emits an EMP pulse + */ /obj/item/landmine/emp icon_state = "emplandmine" -/obj/item/landmine/emp/trigger(mob/living/L) +/obj/item/landmine/emp/trigger(mob/living/triggerer) spark(src, 3, alldirs) empulse(src.loc, 2, 4) qdel(src) + + +/** + * # Phoron Landmine + * + * A landmine that only explodes when pressure is released + */ +/obj/item/landmine/standstill + name = "Standstill Landmine" + desc = "A landmine that only triggers if you release the pressure from the trigger." + icon_state = "standstill" + var/engaged_by = null + +/obj/item/landmine/standstill/Destroy() + STOP_PROCESSING(SSfast_process, src) + engaged_by = null + . = ..() + +/obj/item/landmine/standstill/trigger(mob/living/triggerer) + if(!engaged_by && !deactivated) + to_chat(triggerer, SPAN_HIGHDANGER("Something clicks below your feet, a sense of dread permeates your skin, better not move...")) + engaged_by = ref(triggerer) + + //Because mobs can bump and swap with one another, and we use forcemove that doesn't call Crossed/Uncrossed/Entered/Exited, we have to + //keep looking if the victim is still on the mine, and otherwise explode the mine + START_PROCESSING(SSfast_process, src) + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tgui_alert), triggerer, "You feel your [pick("right", "left")] foot step down on a button with a click..., Uh..., Oh...", "Dread", list("Mom...")) + + playsound_allinrange(src, sound('sound/weapons/empty/empty6.ogg')) + + else + late_trigger(locate(engaged_by)) + +/obj/item/landmine/standstill/Uncrossed(O) + . = ..() + + //Oh no... + if(engaged_by && O == locate(engaged_by)) + var/mob/living/victim = O + to_chat(victim, SPAN_HIGHDANGER("The mine clicks below your feet.")) + addtimer(CALLBACK(src, PROC_REF(late_trigger), victim), 1 SECONDS) + +/obj/item/landmine/standstill/process() + if(!engaged_by || deactivated) + STOP_PROCESSING(SSfast_process, src) + return + + var/mob/living/victim = locate(engaged_by) + var/turf/our_turf = get_turf(src) + + if(!(victim in our_turf)) + late_trigger(locate(engaged_by)) + +/** + * Called when the actual explosion needs to be performed + * + * * victim - The `/mob` that triggers the explosion + */ +/obj/item/landmine/standstill/proc/late_trigger(mob/living/victim) + if(!deactivated) + for(var/mob/living/person_in_range in get_hearers_in_LOS(world.view, src)) + to_chat(person_in_range, SPAN_HIGHDANGER("[victim] does a sudden move, releasing the feet from the trigger...")) + + explosion(loc, 2, 5, 7, world.view) + qdel(src) + +/obj/item/landmine/standstill/deactivate(mob/user) + src.engaged_by = null + . = ..() + +/obj/item/landmine/standstill/attackby(obj/item/I, mob/user) + if(engaged_by && (user == locate(engaged_by))) + to_chat(user, SPAN_ALERT("You are unable to reach the mine without moving your foot, and you feel like doing so would not end well...")) + else + ..() + +/obj/item/landmine/standstill/attack_hand_trigger(mob/user) + if(!engaged_by) + return + + late_trigger(locate(engaged_by)) + +/** + * # Claymore mine + * + * A landmine that projects sharpnels in a cone of explosion, towards one direction + */ +/obj/item/landmine/claymore + name = "Claymore Landmine" + desc = "A landmine that projects sharpnels in a cone of explosion, towards one direction." + desc_extended = "A household name, this mine finds extensive use amongst military forces due to its ability to provide area penetration denial and aid ambushes. \ + It is narrated that Gadpathur, its largest manufacturer in modern times, have built more of these mines than the census of the core planets of the Solarian Alliance." + desc_info = "This device can be fitted with a signaler device for remotely actuated detonations, or can be activated with the press of a button directly above it." + icon = 'icons/obj/item/landmine/claymore.dmi' + icon_state = "m20" + var/datum/wires/landmine/claymore/trigger_wire + var/obj/item/device/assembly/signaler/signaler + +/obj/item/landmine/claymore/Initialize(mapload, ...) + . = ..() + trigger_wire = new(src) + +/obj/item/landmine/claymore/Destroy() + trigger_wire.holder = null + QDEL_NULL(trigger_wire) + QDEL_NULL(signaler) + . = ..() + +/obj/item/landmine/claymore/update_icon() + icon_state = (src.deployed) ? "[initial(icon_state)]_active" : initial(icon_state) + +/obj/item/landmine/claymore/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/device/assembly/signaler)) + if(!isnull(signaler)) + to_chat(user, SPAN_NOTICE("There is already a signaler inserted in \the [src].")) + return + + signaler = I + + user.drop_from_inventory(I, src) + + trigger_wire.Attach("red", signaler) + + else + . = ..() + +/obj/item/landmine/claymore/Crossed(AM, ignore_deployment) + return //Does nothing with mere crossing over + +/obj/item/landmine/claymore/deploy(mob/deployer) + . = ..() + src.dir = deployer.dir + +#define SHOTS_TO_LAUNCH 20 +/obj/item/landmine/claymore/trigger(mob/living/triggerer) + if(deactivated || !deployed) + return + + var/list/turf/candidate_turfs = get_turfs_in_cone(get_turf(src), dir2angle(src.dir), world.view, 60) + + for(var/i = 0; i < SHOTS_TO_LAUNCH; i++) + var/turf/to_hit = pick(candidate_turfs) + + var/obj/item/projectile/bullet/pellet/shotgun/pellet = new(get_turf(src)) + pellet.fire(Get_Angle(get_turf(src), to_hit)) + + qdel(src) + +#undef SHOTS_TO_LAUNCH diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index 1a81d66fde1..e72038263dc 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -489,6 +489,14 @@ illustration = "grenade" starts_with = list(/obj/item/grenade/frag = 5) +/obj/item/storage/box/grenades/napalm + name = "box of napalm grenades" + desc = "A box containing 3 napalm grenades." + icon_state = "secbox" + item_state = "secbox" + illustration = "grenade" + starts_with = list(/obj/item/grenade/napalm = 3) + /obj/item/storage/box/cardox name = "box of cardox grenades" desc = "A box containing 5 experimental cardox grenades." @@ -1238,3 +1246,20 @@ /obj/item/storage/box/led_collars/fill() ..() make_exact_fit() +/obj/item/storage/box/traps/punji + name = "box of punji traps" + desc = "A box containing 5 punji traps." + starts_with = list(/obj/item/trap/punji = 5) + +/obj/item/storage/box/landmines/standstill + name = "box of standstill landmines" + desc = "A box containing 5 standstill landmines." + starts_with = list(/obj/item/landmine/standstill = 5) + +/obj/item/storage/box/landmines/claymore + name = "box of claymore landmines" + desc = "A box containing 5 claymore landmines, relative detonators, and a spare one to trigger them all." + starts_with = list( + /obj/item/landmine/claymore = 5, + /obj/item/device/assembly/signaler = 6 + ) diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm index 386597692ff..f20e1dc2490 100644 --- a/code/game/objects/items/weapons/traps.dm +++ b/code/game/objects/items/weapons/traps.dm @@ -4,7 +4,7 @@ throw_speed = 2 throw_range = 1 gender = PLURAL - icon = 'icons/obj/item/traps.dmi' + icon = 'icons/obj/item/traps/traps.dmi' var/icon_base = "beartrap" icon_state = "beartrap0" randpixel = 0 @@ -136,11 +136,28 @@ /obj/item/trap/update_icon() icon_state = "[icon_base][deployed]" + +/* +################## + Subtypes +################## +*/ + +/** + * # Sharpened trap + * + * This device has an even higher chance of penetrating armor and locking foes in place + */ /obj/item/trap/sharpened name = "sharpened mechanical trap" desc_antag = "This device has an even higher chance of penetrating armor and locking foes in place." activated_armor_penetration = 100 +/** + * # Animal trap + * + * Used to catch small animals like rats, lizards, and chicks + */ /obj/item/trap/animal name = "small trap" desc = "A small mechanical trap that's used to catch small animals like rats, lizards, and chicks." @@ -506,6 +523,11 @@ /obj/item/trap/animal/CanPass(atom/movable/mover, turf/target, height=0, air_group=0) return TRUE +/** + * # Animal trap (Medium) + * + * Used to catch medium animals like cats, monkeys, nymphs, and wayward maintenance drones + */ /obj/item/trap/animal/medium name = "medium trap" desc = "A medium mechanical trap that is used to catch moderately-sized animals like cats, monkeys, nymphs, and wayward maintenance drones." @@ -527,6 +549,11 @@ /mob/living/simple_animal/chicken, /mob/living/simple_animal/yithian, /mob/living/carbon/alien/diona, /mob/living/silicon/robot/drone, /mob/living/silicon/pai, /mob/living/simple_animal/spiderbot, /mob/living/simple_animal/hostile/tree) +/** + * # Animal trap (Large) + * + * Used to catch larger animals, from spiders and dogs to bears and even larger mammals + */ /obj/item/trap/animal/large name = "large trap" desc = "A large mechanical trap that is used to catch larger animals, from spiders and dogs to bears and even larger mammals." @@ -613,7 +640,7 @@ /obj/item/large_trap_foundation name = "large trap foundation" desc = "A metal foundation for large trap, it is missing metals rods to hold the prey." - icon = 'icons/obj/item/traps.dmi' + icon = 'icons/obj/item/traps/traps.dmi' icon_state = "large_foundation" throwforce = 4 force = 5 @@ -641,7 +668,11 @@ else ..() - +/** + * # Tripwire trap + * + * A trap that makes you fall over + */ /obj/item/trap/tripwire name = "tripwire trap" desc = "A piece of cable coil strung between two metal rods. Low-tech, but reliable." @@ -689,3 +720,118 @@ if(!L.lying && (L.m_intent == M_RUN) || prob(5)) L.visible_message(SPAN_DANGER("\The [L] trips over \the [src]!"), FONT_LARGE(SPAN_DANGER("You trip over \the [src]!"))) L.Weaken(3) + +/** + * # Punji trap + * + * A trap that damages and gives an infection to the victim, can have a message attached + */ +/obj/item/trap/punji + name = "punji trap" + desc = "An horrendous trap." + icon = 'icons/obj/item/traps/punji.dmi' + icon_base = "punji" + icon_state = "punji0" + var/message = null + +/obj/item/trap/punji/Crossed(atom/movable/AM) + if(deployed && isliving(AM)) + var/mob/living/L = AM + attack_mob(L) + update_icon() + +/obj/item/trap/punji/attack_mob(mob/living/L) + + //Reveal the trap, if not already visible + hide(FALSE) + + //Select a target zone + var/target_zone + if(L.lying) + target_zone = pick(BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG, BP_L_HAND, BP_L_ARM, BP_R_HAND, BP_R_ARM) + else + target_zone = pick(BP_L_FOOT, BP_R_FOOT, BP_L_LEG, BP_R_LEG) + + //Try to apply the damage + var/success = L.apply_damage(60, DAMAGE_BRUTE, target_zone, used_weapon = src, armor_pen = activated_armor_penetration) + + //If successfully applied, give the message + if(success) + + //Show the leftover message, if any, after a little + addtimer(CALLBACK(src, PROC_REF(reveal_message), L), 3 SECONDS) + + //Give a simple message and return if it's not a human + if(!ishuman(L)) + L.visible_message(SPAN_DANGER("You step on \the [src]!")) + return + + var/mob/living/carbon/human/human = L + var/obj/item/organ/organ = human.get_organ(target_zone) + + human.visible_message(SPAN_DANGER("\The [human] steps on \the [src]!"), + SPAN_WARNING(FONT_LARGE(SPAN_DANGER("You step on \the [src], feel your body fall, and something sharp penetrate your [organ.name]!"))), + SPAN_WARNING("You feel your body fall, and something sharp penetrate your [organ.name]!")) + + //If it's a human and not an IPC, apply an infection + //We are returning early before this step in case something isn't a human, so this should be fine not to catch borgs/bot/exosuits/whatever + if(!isipc(L)) + + //If it's a Vaurca, there's a chance the spear wouldn't go in deep enough to apply an infection + //You're still damaged by falling on it though, which happens above, but at least you're spared the infection + //Glory to your carapace + if(isvaurca(L) && prob(50)) + return + + organ.germ_level += INFECTION_LEVEL_THREE + + //Add some fertilizer to poison the target whole, not only the external organ (leg/foot) + L.reagents.add_reagent(/singleton/reagent/toxin/fertilizer, 10) + +/obj/item/trap/punji/proc/reveal_message(mob/living/victim) + if(!message) + return + + //If the mob moved away and/or no longer sees the trap, do not show the message + if(!(src in oview(world.view, victim))) + return + + victim.visible_message(SPAN_ALERT("You notice something written on a plate inside the trap:
")+SPAN_BAD(message)) + +/obj/item/trap/punji/examine(mob/user, distance) + . = ..() + if(src.message && distance < 3) + to_chat(user, SPAN_ALERT("You notice something written on a plate inside the trap:
")+SPAN_BAD(message)) + +/obj/item/trap/punji/verb/hide_under() + set src in oview(1) + set name = "Hide" + set desc = "Hide the trap under the cover." + set category = "Object" + + if(use_check_and_message(usr, USE_DISALLOW_SILICONS)) + return + + to_chat(usr, SPAN_NOTICE("You begin hiding the trap...")) + if(!do_after(usr, 15 SECONDS)) + return + + hide(TRUE) + to_chat(usr, SPAN_ALERT("You hide \the [src], remember where you left it or suffer the very same warcrime you wanted to inflict!")) + +/obj/item/trap/punji/verb/set_message() + set src in oview(1) + set name = "Set Message" + set desc = "Set a message for the victim of the trap." + set category = "Object" + + if(src.message) + to_chat(usr, SPAN_NOTICE("There is already a carved message inside the trap, can't make more...")) + return + + var/added_message = tgui_input_text(usr, "Leave your message here...", "Punji trap message", multiline = TRUE, encode = FALSE) + + if(added_message) + to_chat(usr, SPAN_NOTICE("You begin carving the message inside the trap...")) + if(do_after(usr, 10 SECONDS)) + src.message = strip_html_readd_newlines(added_message) diff --git a/code/modules/item_worth/worths_list.dm b/code/modules/item_worth/worths_list.dm index c8bd40a7593..2eba28f86ed 100644 --- a/code/modules/item_worth/worths_list.dm +++ b/code/modules/item_worth/worths_list.dm @@ -343,6 +343,7 @@ var/list/worths = list( /obj/item/grenade/anti_photon = 200, /obj/item/grenade/empgrenade = 180, /obj/item/grenade/frag = 300, + /obj/item/grenade/napalm = 800, /obj/item/grenade/spawnergrenade/vaurca = 700, /obj/item/grenade/spawnergrenade/manhacks = 600, /obj/item/grenade/spawnergrenade/spesscarp = 350, diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index f5e6e51ae9f..3d33de2a6ed 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -62,22 +62,35 @@ return 0 -//For projectiles that actually represent clouds of projectiles +/** + * # Pellet projectiles + * + * For projectiles that actually represent clouds of projectiles + */ /obj/item/projectile/bullet/pellet name = "shrapnel" //'shrapnel' sounds more dangerous (i.e. cooler) than 'pellet' icon_state = "pellets" damage = 20 - 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 + + ///Number of pellets that will be ejected from this bullet + var/pellets = 4 + + ///The projectile will lose a pellet each time it travels this distance. Can be a non-integer. + var/range_step = 2 + + ///Lower means the pellets spread more across body parts. If zero then this is considered a shrapnel explosion instead of a shrapnel cone + var/base_spread = 90 + + ///Higher means the pellets spread more across body parts with distance + var/spread_step = 10 /obj/item/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/item/projectile/bullet/pellet/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier) - if (pellets < 0) return 1 + if (pellets < 0) + return TRUE var/total_pellets = get_pellets(distance) var/spread = max(base_spread - (spread_step*distance), 0) @@ -111,8 +124,8 @@ pellets -= hits //each hit reduces the number of pellets left if (hits >= total_pellets || pellets <= 0) - return 1 - return 0 + return TRUE + return FALSE /obj/item/projectile/bullet/pellet/get_structure_damage() var/distance = get_dist(loc, starting) diff --git a/html/changelogs/fluffyghost-auroravietnamedition.yml b/html/changelogs/fluffyghost-auroravietnamedition.yml new file mode 100644 index 00000000000..3d9678ce9e4 --- /dev/null +++ b/html/changelogs/fluffyghost-auroravietnamedition.yml @@ -0,0 +1,56 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +# balance +# admin +# backend +# security +# refactor +################################# + +# Your name. +author: FluffyGhost + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - backend: "Added get_turfs_in_cone(), to acquire turfs within a cone." + - rscadd: "Added napalm grenades, not as classy as an airstrike, but still pretty effective all the same." + - rscadd: "Added punji traps, gives a severe infection upon falling in one, can be hidden, can be engraved with a message." + - rscadd: "Added standstill mines, if walked over, they activate and wait for the victim to move out of them, can be disarmed with the bomb kit, pretty powerful explosion if you mess up." + - rscadd: "Added claymore mines, project pellets in a 60 degrees radius on each side from the direction they're aiming at, remotely activated via a signaler or by directly touching them." + - rscadd: "Reworked flamethrowers, now they light up on fire the turfs in a 30 degrees cone from each side of the clicked target, pressure effects excluded." + - rscadd: "Added the napalm grenades, punji traps, standstill mines, claymore mines and the flamethrower to the antag uplink." + - backend: "DMDoced some variables for the uplink." + - backend: "DMDoced some variables for the pellet bullets, and minor refactor of its attack_mob function." + - backend: "Refactored how mines work, added some documentation and sectioned them up to be more clear what is what." + - backend: "Moved landmines and traps sprites in a more congenial location." + - bugfix: "Fixed a runtime error when the uplink UI is opened without a category being selected." + - bugfix: "Fixed a runtime error with borgs being destroyed and their HUD refreshing." + - imageadd: "Added sprites for the punji trap, by Tomixcomics." + - imageadd: "Added sprites for the standstill mine, by Tomixcomics." + - imageadd: "Added sprites for the claymore mine, from CM." diff --git a/icons/obj/grenade.dmi b/icons/obj/grenade.dmi index 296b2c2ddfd..099a8d2ec83 100644 Binary files a/icons/obj/grenade.dmi and b/icons/obj/grenade.dmi differ diff --git a/icons/obj/item/landmine/claymore.dmi b/icons/obj/item/landmine/claymore.dmi new file mode 100644 index 00000000000..abadca804d6 Binary files /dev/null and b/icons/obj/item/landmine/claymore.dmi differ diff --git a/icons/obj/item/landmine/mine.dmi b/icons/obj/item/landmine/mine.dmi new file mode 100644 index 00000000000..0e3160d1b3b Binary files /dev/null and b/icons/obj/item/landmine/mine.dmi differ diff --git a/icons/obj/item/traps/punji.dmi b/icons/obj/item/traps/punji.dmi new file mode 100644 index 00000000000..a4fe3606ccd Binary files /dev/null and b/icons/obj/item/traps/punji.dmi differ diff --git a/icons/obj/item/traps.dmi b/icons/obj/item/traps/traps.dmi similarity index 100% rename from icons/obj/item/traps.dmi rename to icons/obj/item/traps/traps.dmi