Skip to content

Commit

Permalink
M707 "Vulture" Anti-Materiel Rifle (#4253)
Browse files Browse the repository at this point in the history
# About the pull request

## The M707 is not made player-accessible in this PR.

Adds the M707 "Vulture" anti-materiel rifle to the game. [Design doc
here.](https://github.com/cmss13-devs/cmss13/files/12433899/M707_Final.pdf)

The M707 is meant to take the place of a heavy support weapon, not
unlike the mortar. It is a 20mm bolt-action rifle, capable of loading
from 4-round magazines. Each round does 400 damage with full AP (50),
but it is not a simple task to fire the weapon. The gun, being as
high-caliber as it is, will immediately break your arm & hand if you do
not fire it without use of the built-in bipod. In addition, its accuracy
is massively reduced below its ideal range (10 tiles), which means the
scope is necessary to be used.

The scope does not function like a regular scope. (see screenshot
section for details) Instead, it shows a 5x5 area (the rest is blacked
out) 12 tiles ahead, with an aiming reticle in the center. The aiming
reticle will drift over time within the 5x5, requiring you to re-adjust
or use the **Hold Breath** ability to temporarily stop the sway. If you
open up the scope's UI, you will be able to modify the scope and the
reticle's location, one tile at a time, very slowly.

To assist with this, the Vulture comes with a spotting scope & tripod. A
secondary user is able to assemble and use the spotting scope. The scope
is a complement to the Vulture's, allowing a communicative team to
become far more effective. The spotter's view, on use, will be locked to
the location of the Vulture scope. However, the spotter's view is not
locked to a 5x5 area, instead getting a view of the full area, on top of
an extra 2 tiles (in each direction) of view range. Finally, both the
spotter and sniper's scopes have night vision equivalent to an SG's
goggles.

The bullet itself is a powerful beast. Powerful enough to pierce walls,
people, and barricades, but with 2 caveats. The first is that every
wall/cade penetration removes 75 damage from the round, and any
cades/tables that the round passes over will be immediately destroyed by
the round. In addition, anyone in a large range will hear the report of
the rifle sound and the direction it came from.

Update as of 8/31:
Vulture and its spotter scope now require a pamphlet to use (a pamphlet
gives the trait needed to use both), guncase spawns with 2.

# Explain why it's good for the game

It's a unique weapon that encourages communication inside a team, while
simultaneously not contributing to the IFF ungaball. The weapon promotes
thoughtful gameplay and repositioning to be able to hit a target without
friendlies getting in the way or getting overrun.

# Screenshots
<details>
<summary>Screenshots & Videos</summary>

Scope UI

![image](https://github.com/cmss13-devs/cmss13/assets/41448081/c5ff4c44-65ac-41be-a30a-1dbca019c8ab)

The vulture's scope.

![image](https://github.com/cmss13-devs/cmss13/assets/41448081/ea004c6f-10a6-4f02-a439-303710956286)

Sniper's nest

![image](https://github.com/cmss13-devs/cmss13/assets/41448081/6c9a5165-b831-43a8-ad48-a044c434fcfa)

Closeup

![image](https://github.com/cmss13-devs/cmss13/assets/41448081/6244a435-2c1f-43b8-b15d-003e247bf156)

Spotter's vision

![image](https://github.com/cmss13-devs/cmss13/assets/41448081/82259e26-5355-4362-a300-1ebe409bcb6d)


</details>

# Changelog
:cl: Zonepace, Thwomper
add: Added the M707 "Vulture" anti-materiel rifle. Not currently
player-obtainable. Credit to Tophat and Kaga for the lore description.
/:cl:

---------

Co-authored-by: harryob <[email protected]>
  • Loading branch information
Zonespace27 and harryob committed Sep 8, 2023
1 parent 3534372 commit 7cb618c
Show file tree
Hide file tree
Showing 76 changed files with 1,526 additions and 13 deletions.
2 changes: 1 addition & 1 deletion code/__DEFINES/conflict.dm
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#define AMMO_IGNORE_RESIST (1<<10)
#define AMMO_BALLISTIC (1<<11)
#define AMMO_IGNORE_COVER (1<<12)
// (1<<13) unused, previously was AMMO_SCANS_NEARBY
#define AMMO_ANTIVEHICLE (1<<13)
#define AMMO_STOPPED_BY_COVER (1<<14)
#define AMMO_SPECIAL_EMBED (1<<15)
/// If the projectile hits a dense turf it'll do on_hit_turf on the turf just in front of the turf instead of on the turf itself
Expand Down
6 changes: 6 additions & 0 deletions code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,9 @@
#define COMSIG_MOB_MOUSEDRAG "mob_mousedrag" //from /client/MouseDrag(): (atom/src_object, atom/over_object, turf/src_location, turf/over_location, src_control, over_control, params)
#define COMSIG_MOB_CLICK_CANCELED (1<<0)
#define COMSIG_MOB_CLICK_HANDLED (1<<1)

#define COMSIG_MOB_DEPLOYED_BIPOD "mob_deployed_bipod"
#define COMSIG_MOB_UNDEPLOYED_BIPOD "mob_undeployed_bipod"

/// From /obj/item/proc/pickup() : (obj/item/picked_up)
#define COMSIG_MOB_PICKUP_ITEM "mob_pickup_item"
5 changes: 5 additions & 0 deletions code/__DEFINES/dcs/signals/atom/signals_item.dm
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@
#define COMSIG_GUN_BURST_SHOTS_TO_FIRE_MODIFIED "gun_burst_shots_to_fire_modified"
#define COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED "gun_burst_shot_delay_modified"

#define COMSIG_GUN_VULTURE_FIRED_ONEHAND "gun_vulture_fired_onehand"
#define COMSIG_VULTURE_SCOPE_MOVED "vulture_scope_moved"
#define COMSIG_VULTURE_SCOPE_SCOPED "vulture_scope_scoped"
#define COMSIG_VULTURE_SCOPE_UNSCOPED "vulture_scope_unscoped"

/// from /obj/item/weapon/gun/proc/recalculate_attachment_bonuses() : ()
#define COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES "gun_recalculate_attachment_bonuses"

Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/equipment.dm
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@
#define SLOT_LEGS (1<<13)
#define SLOT_ACCESSORY (1<<14)
#define SLOT_SUIT_STORE (1<<15) //this allows items to be stored in the suit slot regardless of suit
/// Anything with this flag cannot be worn in suit storage, period.
#define SLOT_BLOCK_SUIT_STORE (1<<16)
//=================================================

//slots
Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/layers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@
#define FULLSCREEN_BLIND_LAYER 17.15
/// pain flashes
#define FULLSCREEN_PAIN_LAYER 17.2
/// Vulture sniper/spotter scope
#define FULLSCREEN_VULTURE_SCOPE_LAYER 17.21
/// in critical
#define FULLSCREEN_CRIT_LAYER 17.25

Expand Down
10 changes: 9 additions & 1 deletion code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@
#define TRAIT_USING_WHEELCHAIR "t_using_wheelchair"
/// If the mob will instantly go permadead upon death
#define TRAIT_HARDCORE "t_hardcore"
/// If the mob is able to use the vulture rifle or spotting scope
#define TRAIT_VULTURE_USER "t_vulture_user"

// -- ability traits --
/// Xenos with this trait cannot have plasma transfered to them
Expand All @@ -203,6 +205,9 @@

// GUN TRAITS
#define TRAIT_GUN_SILENCED "t_gun_silenced"

#define TRAIT_GUN_BIPODDED "t_gun_bipodded"

#define TRAIT_GUN_LIGHT_DEACTIVATED "t_gun_light_deactivated"

// Miscellaneous item traits.
Expand Down Expand Up @@ -239,7 +244,8 @@ GLOBAL_LIST_INIT(mob_traits, list(
TRAIT_LEADERSHIP,
TRAIT_DEXTROUS,
TRAIT_REAGENT_SCANNER,
TRAIT_ABILITY_BURROWED
TRAIT_ABILITY_BURROWED,
TRAIT_VULTURE_USER,
))

/*
Expand Down Expand Up @@ -271,6 +277,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_EMOTE_CD_EXEMPT" = TRAIT_EMOTE_CD_EXEMPT,
"TRAIT_LISPING" = TRAIT_LISPING,
"TRAIT_CANNOT_EAT" = TRAIT_CANNOT_EAT,
"TRAIT_VULTURE_USER" = TRAIT_VULTURE_USER,
),
/mob/living/carbon/xenomorph = list(
"TRAIT_ABILITY_NO_PLASMA_TRANSFER" = TRAIT_ABILITY_NO_PLASMA_TRANSFER,
Expand Down Expand Up @@ -299,6 +306,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/obj/item/weapon/gun = list(
"TRAIT_GUN_SILENCED" = TRAIT_GUN_SILENCED,
"TRAIT_GUN_BIPODDED" = TRAIT_GUN_BIPODDED,
),
/obj/structure/surface/table = list(
"TRAIT_STRUCTURE_FLIPPING" = TRAIT_TABLE_FLIPPING,
Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/weapon_stats.dm
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ As such, don't expect any values assigned to common firearms to even consider ho
//How many ticks you have to wait between firing. Burst delay uses the same variable!
*/

#define FIRE_DELAY_TIER_VULTURE 20
#define FIRE_DELAY_TIER_1 12
#define FIRE_DELAY_TIER_2 10
#define FIRE_DELAY_TIER_3 9
Expand Down
27 changes: 27 additions & 0 deletions code/__HELPERS/unsorted.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2066,3 +2066,30 @@ GLOBAL_LIST_INIT(duplicate_forbidden_vars,list(
if(stop_type && istype(turf_to_check, stop_type))
break
return turf_to_check

/// Given a direction, return the direction and the +-45 degree directions next to it
/proc/get_related_directions(direction = NORTH)
switch(direction)
if(NORTH)
return list(NORTH, NORTHEAST, NORTHWEST)

if(EAST)
return list(EAST, NORTHEAST, SOUTHEAST)

if(SOUTH)
return list(SOUTH, SOUTHEAST, SOUTHWEST)

if(WEST)
return list(WEST, NORTHWEST, SOUTHWEST)

if(NORTHEAST)
return list(NORTHEAST, NORTH, EAST)

if(SOUTHEAST)
return list(SOUTHEAST, EAST, SOUTH)

if(SOUTHWEST)
return list(SOUTHWEST, SOUTH, WEST)

if(NORTHWEST)
return list(NORTHWEST, NORTH, WEST)
1 change: 1 addition & 0 deletions code/_globalvars/bitfields.dm
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ DEFINE_BITFIELD(valid_equip_slots, list(
"SLOT_LEGS" = SLOT_LEGS,
"SLOT_ACCESSORY" = SLOT_ACCESSORY,
"SLOT_SUIT_STORE" = SLOT_SUIT_STORE,
"SLOT_BLOCK_SUIT_STORE" = SLOT_BLOCK_SUIT_STORE,
))

DEFINE_BITFIELD(flags_alarm_state, list(
Expand Down
12 changes: 11 additions & 1 deletion code/_onclick/hud/fullscreen.dm
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@
var/severity = 0
var/fs_view = 7
var/show_when_dead = FALSE
/// If this should try and resize if the user's view is bigger than the default
var/should_resize = TRUE

/atom/movable/screen/fullscreen/proc/update_for_view(client_view)
if (screen_loc == "CENTER-7,CENTER-7" && fs_view != client_view)
if (screen_loc == "CENTER-7,CENTER-7" && fs_view != client_view && should_resize)
var/list/actualview = getviewsize(client_view)
fs_view = client_view
transform = matrix(actualview[1]/FULLSCREEN_OVERLAY_RESOLUTION_X, 0, 0, 0, actualview[2]/FULLSCREEN_OVERLAY_RESOLUTION_Y, 0)
Expand Down Expand Up @@ -169,6 +171,14 @@
/atom/movable/screen/fullscreen/laser_blind
icon_state = "impairedoverlay1"

/atom/movable/screen/fullscreen/vulture
icon_state = "vulture_scope_overlay_sniper"
layer = FULLSCREEN_VULTURE_SCOPE_LAYER

/atom/movable/screen/fullscreen/vulture/spotter
icon_state = "vulture_scope_overlay_spotter"
should_resize = FALSE

//Weather overlays//

/atom/movable/screen/fullscreen/weather
Expand Down
4 changes: 4 additions & 0 deletions code/_onclick/hud/screen_objects.dm
Original file line number Diff line number Diff line change
Expand Up @@ -639,3 +639,7 @@
/atom/movable/screen/rotate/alt
dir = WEST
rotate_amount = -90

/atom/movable/screen/vulture_scope // The part of the vulture's scope that drifts over time
icon_state = "vulture_unsteady"
screen_loc = "CENTER,CENTER"
47 changes: 47 additions & 0 deletions code/datums/elements/bullet_trait/penetrating/heavy.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/datum/element/bullet_trait_penetrating/heavy
// Generic bullet trait vars
element_flags = ELEMENT_DETACH|ELEMENT_BESPOKE
id_arg_index = 3

/// For every turf this pierces, how much damage should this lose?
var/damage_lost_per_pen = 100
/// Typecache of things to annihilate if the bullet is on a tile with it
var/static/list/bullet_destroy_structures = typecacheof(list(
/obj/structure/surface,
/obj/structure/barricade,
))

/datum/element/bullet_trait_penetrating/heavy/Attach(datum/target, distance_loss_per_hit = 3, damage_lost_per_pen = 75)
. = ..()
if(. == ELEMENT_INCOMPATIBLE)
return

src.damage_lost_per_pen = damage_lost_per_pen

/datum/element/bullet_trait_penetrating/heavy/handle_passthrough_movables(obj/item/projectile/bullet, atom/movable/hit_movable, did_hit)
if(did_hit)
var/slow_mult = 1
if(ismob(hit_movable))
var/mob/mob = hit_movable
if(mob.mob_size >= MOB_SIZE_BIG)
slow_mult = 2

bullet.distance_travelled += (distance_loss_per_hit * slow_mult)

if(is_type_in_typecache(hit_movable, bullet_destroy_structures))
var/obj/structure/cade = hit_movable
cade.deconstruct() // This bullet just tears through whatever cades you put it up against from either side
bullet.damage -= damage_lost_per_pen

return COMPONENT_BULLET_PASS_THROUGH

/datum/element/bullet_trait_penetrating/heavy/handle_passthrough_turf(obj/item/projectile/bullet, turf/closed/wall/hit_wall)
bullet.distance_travelled += distance_loss_per_hit
bullet.damage -= damage_lost_per_pen

if(!istype(hit_wall))
return COMPONENT_BULLET_PASS_THROUGH

if(!hit_wall.hull)
return COMPONENT_BULLET_PASS_THROUGH

12 changes: 11 additions & 1 deletion code/datums/supply_packs/weapons.dm
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
group = "Weapons"

/datum/supply_packs/grenade_launchers
name = "M79 Grenade Launcher Crate (x2 Guncasess)"
name = "M79 Grenade Launcher Crate (x2 Guncases)"
contains = list(
/obj/item/storage/box/guncase/m79,
/obj/item/storage/box/guncase/m79,
Expand Down Expand Up @@ -120,6 +120,16 @@
containername = "\improper XM88 Heavy Rifle crate"
group = "Weapons"

/* Uncomment me if it's decided to let the m707 be purchasable through req
/datum/supply_packs/gun/m707
name = "M707 Anti-Materiel Rifle crate (M707 x1)"
contains = list()
cost = 120
containertype = /obj/structure/closet/crate/secure/vulture
containername = "M707 crate"
group = "Weapons"
*/

/datum/supply_packs/gun/merc
contains = list()
name = "black market firearms (x1)"
Expand Down
3 changes: 3 additions & 0 deletions code/game/objects/items.dm
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ cases. Override_icon_state should be a list.*/
/obj/item/proc/pickup(mob/user, silent)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user)
SEND_SIGNAL(user, COMSIG_MOB_PICKUP_ITEM, src)
setDir(SOUTH)//Always rotate it south. This resets it to default position, so you wouldn't be putting things on backwards
if(pickup_sound && !silent && src.loc?.z)
playsound(src, pickup_sound, pickupvol, pickup_vary)
Expand Down Expand Up @@ -635,6 +636,8 @@ cases. Override_icon_state should be a list.*/
return FALSE
if(flags_equip_slot & SLOT_SUIT_STORE)
return TRUE
if(flags_equip_slot & SLOT_BLOCK_SUIT_STORE)
return FALSE
if(!H.wear_suit && (WEAR_JACKET in mob_equip))
if(!disable_warning)
to_chat(H, SPAN_WARNING("You need a suit before you can attach this [name]."))
Expand Down
2 changes: 1 addition & 1 deletion code/game/objects/items/devices/device.dm
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
icon = 'icons/obj/items/devices.dmi'
var/serial_number

/obj/item/device/Initialize()
/obj/item/device/Initialize(mapload, ...)
. = ..()
serial_number = "[rand(0,9)][pick(alphabet_uppercase)][rand(0,9)][rand(0,9)][rand(0,9)][rand(0,9)][pick(alphabet_uppercase)]"

Expand Down
41 changes: 41 additions & 0 deletions code/game/objects/items/devices/vulture_spotter.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/obj/item/device/vulture_spotter_scope
name = "\improper M707 spotter scope"
desc = "A scope that, when mounted on a tripod, allows a user to assist the M707's firer in target acquisition."
icon_state = "vulture_scope"
item_state = "electronic"
flags_atom = FPRINT|CONDUCT
unacidable = TRUE
indestructible = TRUE
/// A weakref to the corresponding rifle
var/datum/weakref/bound_rifle

/obj/item/device/vulture_spotter_scope/Initialize(mapload, datum/weakref/rifle)
. = ..()
if(rifle)
bound_rifle = rifle

/obj/item/device/vulture_spotter_scope/attack_self(mob/user)
. = ..()
to_chat(user, SPAN_WARNING("[src] needs to be mounted on a tripod to use!"))

/obj/item/device/vulture_spotter_tripod
name = "\improper M707 spotter tripod"
desc = "A tripod, meant for stabilizing a spotting scope for the M707 anti-materiel rifle."
icon_state = "vulture_tripod"
item_state = "electronic"
flags_atom = FPRINT|CONDUCT
unacidable = TRUE
indestructible = TRUE

/obj/item/device/vulture_spotter_tripod/get_examine_text(mob/user)
. = ..()
. += SPAN_NOTICE("[src] can be set down by <b>using in-hand</b>.")

/obj/item/device/vulture_spotter_tripod/attack_self(mob/user)
. = ..()
user.balloon_alert(user, "setting up tripod...")
if(!do_after(user, 1.5 SECONDS, target = user))
return

new /obj/structure/vulture_spotter_tripod(get_turf(user))
qdel(src)
35 changes: 35 additions & 0 deletions code/game/objects/items/pamphlets.dm
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,38 @@
desc = "A piece of paper covered in crude depictions of bananas and various types of primates. Probably drawn by a three-year-old child - or an unusually intelligent marine."
trait = /datum/character_trait/language/primitive


/obj/item/pamphlet/trait
bypass_pamphlet_limit = TRUE
/// What trait to give the user
var/trait_to_give

/obj/item/pamphlet/trait/can_use(mob/living/carbon/human/user)
if(!istype(user))
return FALSE

if(HAS_TRAIT(user, trait_to_give))
to_chat(user, SPAN_WARNING("You know this already!"))
return FALSE

if(user.job != JOB_SQUAD_MARINE)
to_chat(user, SPAN_WARNING("Only squad riflemen can use this."))
return FALSE

if(user.has_used_pamphlet && !bypass_pamphlet_limit)
to_chat(user, SPAN_WARNING("You've already used a pamphlet!"))
return FALSE

return TRUE

/obj/item/pamphlet/trait/on_use(mob/living/carbon/human/user)
to_chat(user, SPAN_NOTICE(flavour_text))
ADD_TRAIT(user, trait_to_give, "pamphlet")
if(!bypass_pamphlet_limit)
user.has_used_pamphlet = TRUE

/obj/item/pamphlet/trait/vulture
name = "\improper M707 instructional pamphlet"
desc = "A pamphlet used to quickly impart vital knowledge of how to shoot big guns and spot for them."
icon_state = "pamphlet_vulture"
trait_to_give = TRAIT_VULTURE_USER
2 changes: 1 addition & 1 deletion code/game/objects/structures.dm
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
projectile_coverage = PROJECTILE_COVERAGE_MEDIUM
can_block_movement = TRUE

/obj/structure/Initialize()
/obj/structure/Initialize(mapload, ...)
. = ..()
if(climbable)
verbs += /obj/structure/proc/climb_on
Expand Down
12 changes: 12 additions & 0 deletions code/game/objects/structures/crates_lockers/secure_crates.dm
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,18 @@
icon_locked = "secure_locked_weyland"
icon_unlocked = "secure_unlocked_weyland"

/obj/structure/closet/crate/secure/vulture
name = "secure M707 crate"
desc = "A secure crate, containing an M707 anti-materiel rifle."
icon_state = "secure_locked_vulture"
icon_opened = "secure_open_vulture"
icon_locked = "secure_locked_vulture"
icon_unlocked = "secure_unlocked_vulture"

/obj/structure/closet/crate/secure/vulture/Initialize()
. = ..()
new /obj/item/storage/box/guncase/vulture(src)

//special version, able to store OB fuel and warheads only
/obj/structure/closet/crate/secure/ob
name = "secure orbital bombardment ammunition crate"
Expand Down
Loading

0 comments on commit 7cb618c

Please sign in to comment.