mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 21:18:37 +01:00
Siege Warfare: Adds two new craftable emplaced guns, a bigger laser musket, and a new spear for crew-sided mining. (#94102)
<img width="517" height="305" alt="image" src="https://github.com/user-attachments/assets/e8cd8bcd-dd3c-4733-99fe-0cdfb9799182" /> So, been a while, hasn't it? This PR adds three things. First Up: The Ratvarian Repeater.  The Ratvarian repeater is a mounted gun made from bronze, stock parts, and some drinking glasses. it is the emplaced counterpart to the laser musket, as the pipe organ gun is to the crossbow. Being a laser musket derivative, this means it has the benefit of _infinite ammo._ However, this also means to reload it, you turn the crank on the back. My personal headcanon was it works via a spring mechanism not unlike a watch, so naturally, it turned into a clock-cult flavored weapon. Each individual volley per winding shoots 12 laser musket shots in bursts of two. https://github.com/user-attachments/assets/152c3ce8-3560-4c8a-b618-7f2d81fb75a0 However, this isnt the only thing it's capable of. If you prefer your weapons more iconoclastic, you can take a welder to the stand, and carry it around, for the ability to shoot two laser musket shots instead of one per trigger pull... with the price being a decreased movespeed, and the second shot having random spread. Also you cant redeploy the stand, because iconoclasm. Still, the power is there, if you desire it. Overall, the repeater is the "Expensive but reliable" option of the three craftable gun emplacements. It has the most work to get, but once you have it, theres no need for ammo. On the complete opposite side of the coin, we have the Improvised Ballista. https://github.com/user-attachments/assets/16262965-a860-4205-bc3e-eeb8ec593647 This thing is as cheap as it gets - taking iron and wire to make (plus tools), and fires the classic spears greytiders love for 80 damage. This sounds like a lot (and it is), but once the ballista has fired - that's it, and reloading a second spear takes a full six seconds. Of the four emplaced guns, it has the least damage per firing sequence, but has all that damage in one big projectile. The ballista overall is designed to be dirt cheap to make and generally useful, but requiring a lot of work to keep running. And lastly, we have the Giant-killer spear. <img width="417" height="287" alt="image" src="https://github.com/user-attachments/assets/613379aa-379c-40b3-b4fc-ccdaa5ea4678" /> Crafted from titanium, plasteel and seven knives, and then forged by throwing it into lava this monstrously oversized spear is designed to fight monstrously oversized... monsters. While only doing 21 damage when wielded, this damage is multiplied by 3.5 against lavaland fauna, dealing damage equal to a crusher mark detonation. However, despite the ease of use, it lacks the ability to mount trophies like the crusher, so is much more suited to killing basic enemies like legions or goliaths, rather than megafauna. (It still does decently versus them, but youre prone to just dying.) Additionally, due to lacking the requirements of mining access or lavaland monsters, it can be made by the crew at large, providing them a meaningful defense on lavaland/icemoon. It can also be loaded into the ballista, so you can, if you desire, show those ashwalkers how the dark ages were REALLY fought. <img width="202" height="236" alt="image" src="https://github.com/user-attachments/assets/d21a7275-3d9e-427b-ac14-fee080541fb5" /> This PR has been six months in the making, so it feels good to finally get it out. Why its good for the game: Similar logic to my previous improvised weapon prs; I feel the dichotomy of "High tech laser rifle" and "Gun made from stuff lying around" is an interesting one. Of the damage values, the only one that has the potential for a balance issue is the spear, but due to being basically a plastitanium spear that does better versus mining mobs, I don't forsee this becoming a weapon that you can arm an entire revolution with. ## Changelog 🆑 Webcomicartist add: Added the ratvarian repeater, a craftable emplaced crank laser weapon that can be carried by hand, themed after clock cult. add: Added the improvised ballista, the rebar crossbow's bigger brother, that fires spears. Huge damage, terrible fire rate. add: Added the giant-slayer spear, a spear made at a necropolis tendril that does extra damage to lavaland creatures. (Ballista compatible) image: added sprites for the above. qol: The pipe organ gun should be easier to light now. fix: Fixed Pipe Organ Gun firing sprites not always going back to normal after finishing firing. refactor: Mounted guns are now EVEN MORE flexible codewise, now supporting secondary ammo types. /🆑 --------- Co-authored-by: Jacquerel <hnevard@gmail.com>
This commit is contained in:
@@ -9,20 +9,35 @@
|
||||
icon = 'icons/obj/weapons/cannons.dmi'
|
||||
icon_state = "falconet_patina"
|
||||
var/icon_state_base = "falconet_patina"
|
||||
var/icon_state_loaded = "falconet_patina"
|
||||
var/icon_state_fire = "falconet_patina_fire"
|
||||
max_integrity = 300
|
||||
///whether the cannon can be unwrenched from the ground. Anchorable_cannon equivalent.
|
||||
var/anchorable_gun = TRUE
|
||||
/// does this thing need ammo at all or does it just make ammo?
|
||||
var/uses_ammo = TRUE
|
||||
///Max shots per firing of the gun.
|
||||
var/max_shots_per_fire = 1
|
||||
///Delay it takes to load the gun. Set to 0 if none.
|
||||
var/load_delay = 0
|
||||
///Message displayed when loading gun
|
||||
var/loading_message = "gun loaded"
|
||||
///Shots currently loaded. Should never be more than max_shots_per_fire.
|
||||
var/shots_in_gun = 1
|
||||
///shots added to gun, per piece of ammo loaded.
|
||||
var/shots_per_load = 1
|
||||
///Does it have an alternative ammo type it can uses
|
||||
var/has_alt_ammo = FALSE
|
||||
//Is it loaded with said alternative ammo?
|
||||
var/use_alt_ammo = FALSE
|
||||
///Accepted "ammo" type
|
||||
var/obj/item/ammo_type = /obj/item/ammo_casing/strilka310
|
||||
///Alternative ammo types, for extra effects!
|
||||
var/obj/item/alt_ammo_type = /obj/item/ammo_casing/strilka310
|
||||
///Projectile from said gun. Doesnt automatically inherit said ammo's projectile in case you wanted to make a gun that shoots floor tiles or something.
|
||||
var/obj/projectile/projectile_type = /obj/projectile/bullet/strilka310
|
||||
///Projectile from said gun. Doesnt automatically inherit said ammo's projectile in case you wanted to make a gun that shoots floor tiles or something.
|
||||
var/obj/projectile/alt_projectile_type = /obj/projectile/bullet/strilka310
|
||||
///If the gun has anything in it.
|
||||
var/loaded_gun = TRUE
|
||||
///If the gun is currently loaded with its maximum capacity.
|
||||
@@ -37,6 +52,8 @@
|
||||
var/fire_sound = 'sound/items/weapons/gun/general/mountedgun.ogg'
|
||||
///sound of firing for last shot
|
||||
var/last_fire_sound = 'sound/items/weapons/gun/general/mountedgunend.ogg'
|
||||
///So you can't reload it mid-firing
|
||||
var/is_firing = FALSE
|
||||
|
||||
/obj/structure/mounted_gun/wrench_act(mob/living/user, obj/item/tool)
|
||||
. = ..()
|
||||
@@ -46,37 +63,45 @@
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
|
||||
///Covers Reloading and lighting of the gun
|
||||
/obj/structure/mounted_gun/attackby(obj/item/ammo_casing/used_item, mob/user, list/modifiers, list/attack_modifiers)
|
||||
var/ignition_message = used_item.ignition_effect(src, user) // Checks if item used can ignite stuff.
|
||||
if(istype(used_item, ammo_type))
|
||||
/obj/structure/mounted_gun/attackby(obj/item/ammo_casing/used_item, mob/user, params)
|
||||
if(is_firing)
|
||||
balloon_alert(user, "gun is firing!")
|
||||
return
|
||||
if(istype(used_item, ammo_type) && (uses_ammo == TRUE)) //see if the gun needs to be loaded in some way.
|
||||
if(fully_loaded_gun)
|
||||
balloon_alert(user, "already fully loaded!")
|
||||
return
|
||||
else
|
||||
shots_in_gun = shots_in_gun + shots_per_load //Add one to the shots in the gun
|
||||
|
||||
loaded_gun = TRUE // Make sure it registers theres ammo in there, so it can fire.
|
||||
QDEL_NULL(used_item)
|
||||
if(shots_in_gun >= max_shots_per_fire)
|
||||
shots_in_gun = max_shots_per_fire // in case of somehow firing only some of a guns shots, and reloading, you still cant get above the maximum ammo size.
|
||||
fully_loaded_gun = TRUE //So you cant load extra.
|
||||
if(load_delay > 0 && !do_after(user, load_delay, target = src))
|
||||
return
|
||||
|
||||
else if(ignition_message) // if item the player used ignites, light the gun!
|
||||
visible_message(ignition_message)
|
||||
user.log_message("fired a cannon", LOG_ATTACK)
|
||||
log_game("[key_name(user)] fired a cannon in [AREACOORD(src)]")
|
||||
addtimer(CALLBACK(src, PROC_REF(fire)), fire_delay) //uses fire proc as shown below to shoot the gun
|
||||
shots_in_gun = shots_in_gun + shots_per_load //Add one to the shots in the gun
|
||||
balloon_alert(user, loading_message)
|
||||
loaded_gun = TRUE // Make sure it registers theres ammo in there, so it can fire.
|
||||
QDEL_NULL(used_item)
|
||||
if(shots_in_gun >= max_shots_per_fire)
|
||||
shots_in_gun = max_shots_per_fire // in case of somehow firing only some of a guns shots, and reloading, you still cant get above the maximum ammo size.
|
||||
fully_loaded_gun = TRUE //So you cant load extra.
|
||||
icon_state = icon_state_loaded
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/structure/mounted_gun/attack_hand(mob/living/user, list/modifiers)
|
||||
if(is_firing)
|
||||
balloon_alert(user, "gun is firing!")
|
||||
return
|
||||
user.log_message("fired a mounted gun", LOG_ATTACK)
|
||||
log_game("[key_name(user)] fired a mounted gun in [AREACOORD(src)]")
|
||||
addtimer(CALLBACK(src, PROC_REF(fire)), fire_delay) //uses fire proc as shown below to shoot the gun
|
||||
return
|
||||
|
||||
/obj/structure/mounted_gun/proc/fire()
|
||||
if (!loaded_gun)
|
||||
balloon_alert_to_viewers("gun is not loaded!","",2)
|
||||
return
|
||||
is_firing = TRUE
|
||||
for(var/times_fired = 1, times_fired <= shots_in_gun, times_fired++) //The normal DM for loop structure since the times it has fired is changing in the loop itself.
|
||||
for(var/mob/shaken_mob in urange(10, src))
|
||||
if(shaken_mob.stat == CONSCIOUS && firing_shakes_camera == TRUE)
|
||||
if(shaken_mob.stat == CONSCIOUS && firing_shakes_camera == TRUE) //is the mob awake to feel the shaking?
|
||||
shake_camera(shaken_mob, 3, 1)
|
||||
icon_state = icon_state_fire
|
||||
if(loaded_gun)
|
||||
@@ -84,23 +109,29 @@
|
||||
if (times_fired < shots_in_gun)
|
||||
playsound(src, fire_sound, 50, FALSE, 5)
|
||||
else
|
||||
playsound(src, last_fire_sound, 50, TRUE, 5)
|
||||
var/obj/projectile/fired_projectile = new projectile_type(get_turf(src))
|
||||
playsound(src, last_fire_sound, 50, TRUE, 5) //for the empty fire sound
|
||||
|
||||
var/obj/projectile/fired_projectile = (use_alt_ammo) ? new alt_projectile_type(get_turf(src)) : new projectile_type(get_turf(src))
|
||||
fired_projectile.firer = src
|
||||
fired_projectile.fired_from = src
|
||||
fired_projectile.fire(dir2angle(dir))
|
||||
sleep(shot_delay)
|
||||
loaded_gun = FALSE
|
||||
shots_in_gun = 0
|
||||
fully_loaded_gun = FALSE
|
||||
|
||||
if(uses_ammo == TRUE)
|
||||
loaded_gun = FALSE
|
||||
use_alt_ammo = FALSE
|
||||
shots_in_gun = 0
|
||||
fully_loaded_gun = FALSE
|
||||
is_firing = FALSE
|
||||
icon_state = icon_state_base
|
||||
|
||||
/obj/structure/mounted_gun/pipe
|
||||
/obj/structure/mounted_gun/organ_gun
|
||||
|
||||
name = "Pipe Organ Gun"
|
||||
desc = "To become master over one who has killed, one must become a better killer. This engine of destruction is one of many things made to that end."
|
||||
icon_state = "pipeorgangun"
|
||||
icon_state_base = "pipeorgangun"
|
||||
icon_state_loaded = "pipeorgangun"
|
||||
icon_state_fire = "pipeorgangun_fire"
|
||||
anchored = FALSE
|
||||
anchorable_gun = TRUE
|
||||
@@ -116,7 +147,7 @@
|
||||
firing_shakes_camera = FALSE
|
||||
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 24.5, /datum/material/wood = SHEET_MATERIAL_AMOUNT * 15, /datum/material/glass = SMALL_MATERIAL_AMOUNT)
|
||||
|
||||
/obj/structure/mounted_gun/pipe/examine_more(mob/user)
|
||||
/obj/structure/mounted_gun/organ_gun/examine_more(mob/user)
|
||||
. = ..()
|
||||
. += span_notice("<b><i>Looking down at \the [src], you recall a tale told to you in some distant memory...</i></b>")
|
||||
|
||||
@@ -126,7 +157,7 @@
|
||||
. += span_info("As such, the man who ended another's life with a stone, was in turn smote himself by another wielding a spear. After spears, bows. Swords. Guns. Tanks. Missiles. And on and on Vengeance fed. Growing stronger. Growing Worse.")
|
||||
. += span_info("Vengeance persists to this day. It sometimes may slumber, seemingly content with having gorged itself, but in the end, its ceaseless hunger can be neither numbed nor sated.")
|
||||
|
||||
/obj/structure/mounted_gun/pipe/fire()
|
||||
/obj/structure/mounted_gun/organ_gun/fire()
|
||||
if (!loaded_gun)
|
||||
balloon_alert_to_viewers("Gun is not loaded!","",2)
|
||||
return
|
||||
@@ -165,6 +196,7 @@
|
||||
desc = "''Quantity has a quality of its own.''"
|
||||
icon_state = "canister_gatling"
|
||||
icon_state_base = "canister_gatling"
|
||||
icon_state_loaded = "canister_gatling"
|
||||
icon_state_fire = "canister_gatling_fire"
|
||||
anchored = FALSE
|
||||
anchorable_gun = TRUE
|
||||
@@ -187,3 +219,136 @@
|
||||
throwforce = 0
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
projectile_type = /obj/projectile/bullet/shrapnel
|
||||
|
||||
/obj/structure/mounted_gun/ratvarian_repeater
|
||||
name = "Ratvarian Repeater"
|
||||
desc = "''Brains? Bronze? Why not both?''"
|
||||
icon_state = "ratvarian_repeater"
|
||||
icon_state_base = "ratvarian_repeater"
|
||||
icon_state_fire = "ratvarian_repeater_fire"
|
||||
loading_message = "gun charged"
|
||||
anchored = FALSE
|
||||
anchorable_gun = TRUE
|
||||
uses_ammo = FALSE
|
||||
load_delay = 30
|
||||
max_shots_per_fire = 12
|
||||
shots_per_load = 12
|
||||
shots_in_gun = 12
|
||||
fire_sound = 'sound/items/weapons/thermalpistol.ogg'
|
||||
last_fire_sound = 'sound/items/weapons/thermalpistol.ogg'
|
||||
projectile_type = /obj/projectile/beam/laser/musket/repeater
|
||||
loaded_gun = TRUE
|
||||
fully_loaded_gun = TRUE
|
||||
fire_delay = 1
|
||||
shot_delay = 2
|
||||
firing_shakes_camera = FALSE
|
||||
custom_materials = list(
|
||||
/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5.25,
|
||||
/datum/material/bronze = SHEET_MATERIAL_AMOUNT * 5,
|
||||
/datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.29
|
||||
)
|
||||
|
||||
/obj/structure/mounted_gun/ratvarian_repeater/dump_contents()
|
||||
return // Stub because we don't want to do anything here
|
||||
|
||||
/obj/structure/mounted_gun/ratvarian_repeater/attack_hand(mob/user, params) //the repeater is weird so has to have its own code since it takes no ammo.
|
||||
|
||||
if(is_firing)
|
||||
balloon_alert(user, "gun is firing!")
|
||||
return
|
||||
|
||||
if(!fully_loaded_gun)
|
||||
if(!do_after(user, load_delay, target = src))
|
||||
return
|
||||
shots_in_gun = shots_per_load //Add one to the shots in the gun
|
||||
balloon_alert(user, "mechanism wound.")
|
||||
playsound(src, 'sound/effects/magic/clockwork/fellowship_armory.ogg', 50, FALSE, 5)
|
||||
loaded_gun = TRUE // Make sure it registers theres ammo in there, so it can fire.
|
||||
if(shots_in_gun >= max_shots_per_fire)
|
||||
shots_in_gun = max_shots_per_fire // in case of somehow firing only some of a guns shots, and reloading, you still cant get above the maximum ammo size.
|
||||
fully_loaded_gun = TRUE //So you cant load extra.
|
||||
return
|
||||
|
||||
user.log_message("fired a ratvatian repeater", LOG_ATTACK)
|
||||
log_game("[key_name(user)] fired a ratvatian repeater in [AREACOORD(src)]")
|
||||
addtimer(CALLBACK(src, PROC_REF(fire)), fire_delay) //uses fire proc as shown below to shoot the gun
|
||||
|
||||
/obj/structure/mounted_gun/ratvarian_repeater/fire()
|
||||
if (!loaded_gun)
|
||||
balloon_alert_to_viewers("needs winding!", vision_distance = 2)
|
||||
return
|
||||
is_firing = TRUE
|
||||
for(var/times_fired = 1, times_fired <= shots_in_gun, times_fired++) //The normal DM for loop structure since the times it has fired is changing in the loop itself.
|
||||
if(loaded_gun)
|
||||
icon_state = icon_state_fire
|
||||
if (times_fired < shots_in_gun)
|
||||
playsound(src, fire_sound, 50, FALSE, 5)
|
||||
else
|
||||
playsound(src, last_fire_sound, 50, TRUE, 5) //for the empty fire sound
|
||||
var/obj/projectile/fired_projectile = new projectile_type(get_turf(src))
|
||||
fired_projectile.firer = src
|
||||
fired_projectile.fired_from = src
|
||||
fired_projectile.fire(dir2angle(dir))
|
||||
if(times_fired % 2 != 1)//Burst Fire.
|
||||
sleep(shot_delay)
|
||||
sleep(shot_delay)
|
||||
loaded_gun = FALSE
|
||||
shots_in_gun = 0
|
||||
fully_loaded_gun = FALSE
|
||||
is_firing = FALSE
|
||||
icon_state = icon_state_base
|
||||
|
||||
/obj/structure/mounted_gun/ballista
|
||||
name = "Improvised Ballista"
|
||||
desc = "''Engineers like to solve problems. If there are no problems handily available, they will create their own problems.''"
|
||||
icon_state = "Improvised_Ballista"
|
||||
icon_state_base = "Improvised_Ballista"
|
||||
icon_state_loaded = "Improvised_Ballista_Loaded"
|
||||
icon_state_fire = "Improvised_Ballista"
|
||||
anchored = FALSE
|
||||
anchorable_gun = TRUE
|
||||
uses_ammo = TRUE
|
||||
load_delay = 60
|
||||
max_shots_per_fire = 1
|
||||
shots_per_load = 1
|
||||
shots_in_gun = 1
|
||||
fire_sound = 'sound/items/xbow_lock.ogg'
|
||||
last_fire_sound = 'sound/items/xbow_lock.ogg'
|
||||
has_alt_ammo = TRUE
|
||||
ammo_type = /obj/item/spear
|
||||
projectile_type = /obj/projectile/bullet/ballista_spear
|
||||
alt_ammo_type = /obj/item/spear/dragonator
|
||||
alt_projectile_type = /obj/projectile/bullet/ballista_spear_dragon
|
||||
loaded_gun = FALSE
|
||||
fully_loaded_gun = FALSE
|
||||
fire_delay = 1
|
||||
shot_delay = 1
|
||||
firing_shakes_camera = FALSE
|
||||
custom_materials = list(
|
||||
/datum/material/iron = SHEET_MATERIAL_AMOUNT * 15.15,
|
||||
/datum/material/glass = SMALL_MATERIAL_AMOUNT * 1.5
|
||||
)
|
||||
|
||||
/obj/structure/mounted_gun/ballista/attackby(obj/item/ammo_casing/used_item, mob/user, params) //again its single shot so its kinda weird.
|
||||
if(is_firing)
|
||||
balloon_alert(user, "gun is firing")
|
||||
return
|
||||
|
||||
if(!istype(used_item, ammo_type))
|
||||
return
|
||||
|
||||
if(fully_loaded_gun)
|
||||
balloon_alert(user, "already fully loaded!")
|
||||
return
|
||||
|
||||
if(istype(used_item, alt_ammo_type))
|
||||
use_alt_ammo = TRUE
|
||||
|
||||
playsound(src, 'sound/items/weapons/draw_bow.ogg', 50, FALSE, 5)
|
||||
do_after(user, load_delay, target = src)
|
||||
shots_in_gun = 1 //MAX OF ONE SHOT.
|
||||
balloon_alert(user, loading_message)
|
||||
loaded_gun = TRUE
|
||||
QDEL_NULL(used_item)
|
||||
fully_loaded_gun = TRUE
|
||||
icon_state = icon_state_loaded
|
||||
|
||||
Reference in New Issue
Block a user