From 5fed7680539674e0392b724a89052e4417e2352b Mon Sep 17 00:00:00 2001 From: mwerezak Date: Fri, 24 Apr 2015 17:49:54 -0400 Subject: [PATCH 1/6] Fixes rounding in returned vector locations Fixes a rounding issue in returned vector location that would cause returned projectile trajectories to be biased towards the lower left corner of the map. --- code/__HELPERS/vector.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/__HELPERS/vector.dm b/code/__HELPERS/vector.dm index 970530e71df..f848b722b51 100644 --- a/code/__HELPERS/vector.dm +++ b/code/__HELPERS/vector.dm @@ -118,7 +118,7 @@ return_location() /datum/plot_vector/proc/return_location(var/datum/vector_loc/data) if(!data) data = new() - data.loc = locate(round(loc_x / world.icon_size), round(loc_y / world.icon_size), loc_z) + data.loc = locate(round(loc_x / world.icon_size, 1), round(loc_y / world.icon_size, 1), loc_z) if(!data.loc) return data.pixel_x = loc_x - (data.loc.x * world.icon_size) From f1c2cbe519452b7da9b96c53395942dd66d94097 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Fri, 24 Apr 2015 16:42:28 -0400 Subject: [PATCH 2/6] Adds gun firemodes and burst firing Adds support for different gun firemodes, and defines firemodes for various automatic weapons, as well as the double-barreled shotgun --- code/__HELPERS/vector.dm | 4 +- code/modules/projectiles/gun.dm | 193 +++++++++++++----- code/modules/projectiles/guns/projectile.dm | 2 +- .../projectiles/guns/projectile/automatic.dm | 25 ++- .../projectiles/guns/projectile/shotgun.dm | 6 + code/modules/projectiles/projectile.dm | 32 +-- 6 files changed, 197 insertions(+), 65 deletions(-) diff --git a/code/__HELPERS/vector.dm b/code/__HELPERS/vector.dm index f848b722b51..44d293734c5 100644 --- a/code/__HELPERS/vector.dm +++ b/code/__HELPERS/vector.dm @@ -52,7 +52,7 @@ return_location() var/offset_x = 0 // distance to increment each step var/offset_y = 0 -/datum/plot_vector/proc/setup(var/turf/S, var/turf/T, var/xo = 0, var/yo = 0) +/datum/plot_vector/proc/setup(var/turf/S, var/turf/T, var/xo = 0, var/yo = 0, var/angle_offset=0) source = S target = T @@ -78,7 +78,7 @@ return_location() return // calculate the angle - angle = Atan2(dx, dy) + angle = Atan2(dx, dy) + angle_offset // and some rounding to stop the increments jumping whole turfs - because byond favours certain angles if(angle > -135 && angle < 45) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 77693417dfd..b2cbb6b4be5 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -1,3 +1,32 @@ +/* + Defines a firing mode for a gun. + + burst number of shots to fire in a burst + burst_delay tick delay between shots in a burst + fire_delay tick delay after the last shot before the gun may be used again + move_delay tick delay after the last shot before the player may move + dispersion dispersion of each shot in the burst measured in tiles per 7 tiles angle ratio + accuracy accuracy modifier applied to each shot in tiles. Applied on top of the weapon accuracy. +*/ +/datum/firemode + var/name = "default" + var/burst = 1 + var/burst_delay = null + var/fire_delay = null + var/move_delay = 1 + var/list/accuracy = list(0) + var/list/dispersion = list(0) + +//using a list makes defining fire modes for new guns much nicer, +//however we convert the lists to datums in part so that firemodes can be VVed if necessary. +/datum/firemode/New(list/properties = null) + ..() + if(!properties) return + + for(var/propname in vars) + if(!isnull(properties[propname])) + src.vars[propname] = properties[propname] + //Parent gun type. Guns are weapons that can be aimed at mobs and act over a distance /obj/item/weapon/gun name = "gun" @@ -22,14 +51,18 @@ zoomdevicename = "scope" var/fire_delay = 6 + var/burst_delay = 2 //delay between shots, if firing in bursts var/fire_sound = 'sound/weapons/Gunshot.ogg' var/fire_sound_text = "gunshot" var/recoil = 0 //screen shake var/silenced = 0 - var/accuracy = 0 //accuracy is measured in tiles. +1 accuracy means that everything is effectively one tile closer for the purpose of miss chance, -1 means the opposite. launchers are not supported, at the moment. + var/accuracy = 0 //accuracy is measured in tiles. +1 accuracy means that everything is effectively one tile closer for the purpose of miss chance, -1 means the opposite. launchers are not supported, at the moment. var/scoped_accuracy = null - var/last_fired = 0 + var/next_fire_time = 0 + + var/sel_mode = 1 //index of the currently selected mode + var/list/firemodes = null //aiming system stuff var/keep_aim = 1 //1 for keep shooting until aim is lowered @@ -42,17 +75,18 @@ /obj/item/weapon/gun/New() ..() + if(!firemodes || !firemodes.len) + firemodes = list( new/datum/firemode ) + else + for(var/i in 1 to firemodes.len) + firemodes[i] = new/datum/firemode(firemodes[i]) + + if(firemodes.len <= 1) + verbs -= /obj/item/weapon/gun/verb/switch_firemodes + if(isnull(scoped_accuracy)) scoped_accuracy = accuracy -//Returns 1 if the gun is able to be fired -/obj/item/weapon/gun/proc/ready_to_fire() - if(world.time >= last_fired + fire_delay) - last_fired = world.time - return 1 - else - return 0 - //Checks whether a given mob can use the gun //Any checks that shouldn't result in handle_click_empty() being called if they fail should go here. //Otherwise, if you want handle_click_empty() to be called, check in consume_next_projectile() and return null there. @@ -114,7 +148,7 @@ else return ..() //Pistolwhippin' -/obj/item/weapon/gun/proc/Fire(atom/target, mob/living/user, params, pointblank=0, reflex=0) +/obj/item/weapon/gun/proc/Fire(atom/target, mob/living/user, clickparams, pointblank=0, reflex=0) if(!user || !target) return add_fingerprint(user) @@ -122,24 +156,49 @@ if(!special_check(user)) return - if (!ready_to_fire()) + if(world.time < next_fire_time) if (world.time % 3) //to prevent spam user << "[src] is not ready to fire again!" return + + //unpack firemode data + var/datum/firemode/firemode = firemodes[sel_mode] + var/_burst = firemode.burst + var/_burst_delay = isnull(firemode.burst_delay)? src.burst_delay : firemode.burst_delay + var/_fire_delay = isnull(firemode.fire_delay)? src.fire_delay : firemode.fire_delay + var/_move_delay = firemode.move_delay - var/obj/projectile = consume_next_projectile(user) - if(!projectile) - handle_click_empty(user) - return + var/shoot_time = (_burst - 1)*_burst_delay + user.next_move = world.time + shoot_time //no clicking on things while shooting + if(user.client) user.client.move_delay = world.time + shoot_time //no moving while shooting either + next_fire_time = world.time + shoot_time + //actually attempt to shoot + for(var/i in 1 to _burst) + var/obj/projectile = consume_next_projectile(user) + if(!projectile) + handle_click_empty(user) + break + + var/acc = firemode.accuracy[min(i, firemode.accuracy.len)] + var/disp = firemode.dispersion[min(i, firemode.dispersion.len)] + process_accuracy(projectile, user, target, acc, disp) + + if(pointblank) + process_point_blank(projectile, user, target) + + if(process_projectile(projectile, user, target, user.zone_sel.selecting, clickparams)) + handle_post_fire(user, target, pointblank, reflex) + update_icon() + + sleep(_burst_delay) + + update_held_icon() + + //update timing user.next_move = world.time + 4 - - if(process_projectile(projectile, user, target, user.zone_sel.selecting, params, pointblank, reflex)) - handle_post_fire(user, target, pointblank, reflex) - - update_icon() - update_held_icon() - + if(user.client) user.client.move_delay = world.time + _move_delay + next_fire_time = world.time + _fire_delay //obtains the next projectile to fire /obj/item/weapon/gun/proc/consume_next_projectile() @@ -186,12 +245,52 @@ shake_camera(user, recoil+1, recoil) update_icon() -//does the actual shooting -/obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, var/target_zone, var/params=null, var/pointblank=0, var/reflex=0) - if(!istype(projectile, /obj/item/projectile)) + +/obj/item/weapon/gun/proc/process_point_blank(obj/projectile, mob/user, atom/target) + var/obj/item/projectile/P = projectile + if(!istype(projectile)) + return //default behaviour only applies to true projectiles + + //default point blank multiplier + var/damage_mult = 1.3 + + //determine multiplier due to the target being grabbed + if(ismob(target)) + var/mob/M = target + if(M.grabbed_by.len) + var/grabstate = 0 + for(var/obj/item/weapon/grab/G in M.grabbed_by) + grabstate = max(grabstate, G.state) + if(grabstate >= GRAB_NECK) + damage_mult = 3.0 + else if(grabstate >= GRAB_AGGRESSIVE) + damage_mult = 1.5 + P.damage *= damage_mult + +/obj/item/weapon/gun/proc/process_accuracy(obj/projectile, mob/user, atom/target, acc_mod, dispersion) + var/obj/item/projectile/P = projectile + if(!istype(projectile)) + return //default behaviour only applies to true projectiles + + //Accuracy modifiers + P.accuracy = accuracy + acc_mod + P.dispersion = dispersion + + //accuracy bonus from aiming + if (aim_targets && (target in aim_targets)) + //If you aim at someone beforehead, it'll hit more often. + //Kinda balanced by fact you need like 2 seconds to aim + //As opposed to no-delay pew pew + P.accuracy += 2 + +//does the actual launching of the projectile +/obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, var/target_zone, var/params=null) + var/obj/item/projectile/P = projectile + if(!istype(projectile)) return 0 //default behaviour only applies to true projectiles - var/obj/item/projectile/P = projectile + if(params) + P.set_clickpoint(params) //shooting while in shock var/x_offset = 0 @@ -205,27 +304,6 @@ y_offset = rand(-1,1) x_offset = rand(-1,1) - //Point blank bonus - if(pointblank) - var/damage_mult = 1.3 //default point blank multiplier - - //determine multiplier due to the target being grabbed - if(ismob(target)) - var/mob/M = target - if(M.grabbed_by.len) - var/grabstate = 0 - for(var/obj/item/weapon/grab/G in M.grabbed_by) - grabstate = max(grabstate, G.state) - if(grabstate >= GRAB_NECK) - damage_mult = 3.0 - else if (grabstate >= GRAB_AGGRESSIVE) - damage_mult = 1.5 - - P.damage *= damage_mult - - if(params) - P.set_clickpoint(params) - return !P.launch(target, user, src, target_zone, x_offset, y_offset) //Suicide handling. @@ -287,3 +365,22 @@ if(!zoom) accuracy = initial(accuracy) recoil = initial(recoil) + +/obj/item/weapon/gun/examine(mob/user) + ..() + if(firemodes.len > 1) + var/datum/firemode/current_mode = firemodes[sel_mode] + user << "The fire selector is set to [current_mode.name]." + +/obj/item/weapon/gun/verb/switch_firemodes() + set name = "Switch Fire Mode" + set category = "Object" + set src in usr + + if(usr.stat || usr.restrained()) return + + sel_mode++ + if(sel_mode > firemodes.len) + sel_mode = 1 + var/datum/firemode/new_mode = firemodes[sel_mode] + usr << "You switch \the [src] to [new_mode.name]." diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 944a9fe99fd..8011dbe7f9c 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -189,9 +189,9 @@ /obj/item/weapon/gun/projectile/examine(mob/user) ..(user) - user << "Has [getAmmo()] round\s remaining." if(ammo_magazine) user << "It has \a [ammo_magazine] loaded." + user << "Has [getAmmo()] round\s remaining." return /obj/item/weapon/gun/projectile/proc/getAmmo() diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index ee7bfbfffff..090775f824e 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -10,7 +10,12 @@ slot_flags = SLOT_BELT ammo_type = /obj/item/ammo_casing/c9mm multi_aim = 1 - fire_delay = 0 + + firemodes = list( + list(name="semiauto", burst=1, fire_delay=0), + list(name="3-round bursts", burst=3, move_delay=4, accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.0, 0.6, 1.0)), + list(name="short bursts", burst=5, move_delay=4, accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.6, 1.0, 1.0, 1.0, 1.2)), + ) /obj/item/weapon/gun/projectile/automatic/mini_uzi name = "\improper Uzi" @@ -59,6 +64,12 @@ slot_flags = SLOT_BACK load_method = MAGAZINE magazine_type = /obj/item/ammo_magazine/c762 + + firemodes = list( + list(name="semiauto", burst=1, fire_delay=0), + list(name="3-round bursts", burst=3, move_delay=6, accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.0, 0.6, 0.6)), + list(name="short bursts", burst=5, move_delay=6, accuracy = list(0,-1,-1,-2,-2), dispersion = list(0.6, 1.0, 1.0, 1.0, 1.2)), + ) /obj/item/weapon/gun/projectile/automatic/sts35/update_icon() ..() @@ -104,6 +115,12 @@ auto_eject = 1 auto_eject_sound = 'sound/weapons/smg_empty_alarm.ogg' + burst_delay = 4 + firemodes = list( + list(name="semiauto", burst=1, fire_delay=0), + list(name="3-round bursts", burst=3, move_delay=6, accuracy = list(0,-1,-1), dispersion = list(0.0, 0.6, 0.6)), + ) + var/use_launcher = 0 var/obj/item/weapon/gun/launcher/grenade/underslung/launcher @@ -166,6 +183,12 @@ fire_sound = 'sound/weapons/Gunshot_smg.ogg' load_method = MAGAZINE magazine_type = /obj/item/ammo_magazine/a762 + + firemodes = list( + list(name="short bursts", burst=5, move_delay=6, accuracy = list(0,-1,-1,-2,-2,-2,-3,-3), dispersion = list(0.6, 1.0, 1.0, 1.0, 1.2)), + list(name="long bursts", burst=8, move_delay=8, accuracy = list(0,-1,-1,-2,-2,-2,-3,-3), dispersion = list(1.0, 1.0, 1.0, 1.0, 1.2)), + ) + var/cover_open = 0 /obj/item/weapon/gun/projectile/automatic/l6_saw/attack_self(mob/user as mob) diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index fa29d7e8ba3..73ecfdd8908 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -65,6 +65,12 @@ caliber = "shotgun" origin_tech = "combat=3;materials=1" ammo_type = /obj/item/ammo_casing/shotgun/beanbag + + burst_delay = 0 + firemodes = list( + list(name="one barrel at a time", burst=1), + list(name="both barrels at once", burst=2), + ) /obj/item/weapon/gun/projectile/shotgun/doublebarrel/flare name = "signal shotgun" diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 98293ebc7ff..6fdf040288a 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -33,6 +33,9 @@ var/p_x = 16 var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center + var/accuracy = 0 + var/dispersion = 0.0 + var/damage = 10 var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE are the only things that should be in here var/nodamage = 0 //Determines if the projectile will skip any damage inflictions @@ -100,6 +103,12 @@ p_x = text2num(mouse_control["icon-x"]) if(mouse_control["icon-y"]) p_y = text2num(mouse_control["icon-y"]) + + //randomize clickpoint a bit based on dispersion + if(dispersion) + var/radius = round((dispersion*0.443)*world.icon_size*0.8) //0.443 = sqrt(pi)/4 = 2a, where a is the side length of a square that shares the same area as a circle with diameter = dispersion + p_x = between(0, p_x + rand(-radius, radius), world.icon_size) + p_y = between(0, p_y + rand(-radius, radius), world.icon_size) //called to launch a projectile from a gun /obj/item/projectile/proc/launch(atom/target, mob/user, obj/item/weapon/gun/launcher, var/target_zone, var/x_offset=0, var/y_offset=0) @@ -147,24 +156,15 @@ yo = new_y - starting_loc.y xo = new_x - starting_loc.x + setup_trajectory() //Called when the projectile intercepts a mob. Returns 1 if the projectile hit the mob, 0 if it missed and should keep flying. /obj/item/projectile/proc/attack_mob(var/mob/living/target_mob, var/distance, var/miss_modifier=0) if(!istype(target_mob)) return - //accuracy bonus from aiming - if (istype(shot_from, /obj/item/weapon/gun)) - var/obj/item/weapon/gun/daddy = shot_from - miss_modifier -= round(15*daddy.accuracy) - - //If you aim at someone beforehead, it'll hit more often. - //Kinda balanced by fact you need like 2 seconds to aim - //As opposed to no-delay pew pew - if (daddy.aim_targets && original in daddy.aim_targets) - miss_modifier += -30 //roll to-hit - miss_modifier = max(miss_modifier + 15*(distance-2), 0) + miss_modifier = max(15*(distance-2) - round(15*accuracy) + miss_modifier, 0) var/hit_zone = get_zone_with_miss_chance(def_zone, target_mob, miss_modifier, ranged_attack=(distance > 1)) if(!hit_zone) visible_message("\The [src] misses [target_mob] narrowly!") @@ -314,10 +314,16 @@ /obj/item/projectile/proc/before_move() /obj/item/projectile/proc/setup_trajectory() + // trajectory dispersion + var/offset = 0 + if(dispersion) + var/radius = round(dispersion*9, 1) + offset = rand(-radius, radius) + // plot the initial trajectory trajectory = new() - trajectory.setup(starting, original, pixel_x, pixel_y) - + trajectory.setup(starting, original, pixel_x, pixel_y, angle_offset=offset) + // generate this now since all visual effects the projectile makes can use it effect_transform = new() effect_transform.Scale(trajectory.return_hypotenuse(), 1) From e93e9c1434016621e541530f4f2e6561e1160ee4 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Sun, 26 Apr 2015 23:11:23 -0400 Subject: [PATCH 3/6] Expands firemodes to include energy gun settings Expands on firemodes to include settings for the egun/adv egun, floraray, and pulse rifle. By default attack_self() now cycles firemodes for any gun. Updates several projectile guns in a desperate attempt to avoid the need for an unload ammo verb. --- code/modules/projectiles/gun.dm | 36 +++++++------- code/modules/projectiles/guns/energy.dm | 19 +++++++ .../projectiles/guns/energy/nuclear.dm | 41 ++++++---------- code/modules/projectiles/guns/energy/pulse.dm | 49 ++++++------------- .../projectiles/guns/energy/special.dm | 21 ++------ code/modules/projectiles/guns/launcher.dm | 3 -- code/modules/projectiles/guns/projectile.dm | 17 ++++++- .../projectiles/guns/projectile/automatic.dm | 47 ++++++++++++------ .../projectiles/guns/projectile/shotgun.dm | 10 +++- code/modules/projectiles/targeting.dm | 4 -- 10 files changed, 127 insertions(+), 120 deletions(-) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index b2cbb6b4be5..76728637591 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -1,12 +1,13 @@ /* Defines a firing mode for a gun. - burst number of shots to fire in a burst + burst number of shots fired when the gun is used burst_delay tick delay between shots in a burst fire_delay tick delay after the last shot before the gun may be used again move_delay tick delay after the last shot before the player may move dispersion dispersion of each shot in the burst measured in tiles per 7 tiles angle ratio - accuracy accuracy modifier applied to each shot in tiles. Applied on top of the weapon accuracy. + accuracy accuracy modifier applied to each shot in tiles. + applied on top of the base weapon accuracy. */ /datum/firemode var/name = "default" @@ -50,8 +51,8 @@ attack_verb = list("struck", "hit", "bashed") zoomdevicename = "scope" - var/fire_delay = 6 - var/burst_delay = 2 //delay between shots, if firing in bursts + var/fire_delay = 6 //delay after shooting before the gun can be used again + var/burst_delay = 2 //delay between shots, if firing in bursts var/fire_sound = 'sound/weapons/Gunshot.ogg' var/fire_sound_text = "gunshot" var/recoil = 0 //screen shake @@ -62,7 +63,8 @@ var/next_fire_time = 0 var/sel_mode = 1 //index of the currently selected mode - var/list/firemodes = null + var/list/firemodes = list() + var/firemode_type = /datum/firemode //for subtypes that need custom firemode data //aiming system stuff var/keep_aim = 1 //1 for keep shooting until aim is lowered @@ -75,14 +77,11 @@ /obj/item/weapon/gun/New() ..() - if(!firemodes || !firemodes.len) - firemodes = list( new/datum/firemode ) + if(!firemodes.len) + firemodes += new firemode_type else for(var/i in 1 to firemodes.len) - firemodes[i] = new/datum/firemode(firemodes[i]) - - if(firemodes.len <= 1) - verbs -= /obj/item/weapon/gun/verb/switch_firemodes + firemodes[i] = new firemode_type(firemodes[i]) if(isnull(scoped_accuracy)) scoped_accuracy = accuracy @@ -372,15 +371,14 @@ var/datum/firemode/current_mode = firemodes[sel_mode] user << "The fire selector is set to [current_mode.name]." -/obj/item/weapon/gun/verb/switch_firemodes() - set name = "Switch Fire Mode" - set category = "Object" - set src in usr - - if(usr.stat || usr.restrained()) return - +/obj/item/weapon/gun/proc/switch_firemodes(mob/user=null) sel_mode++ if(sel_mode > firemodes.len) sel_mode = 1 var/datum/firemode/new_mode = firemodes[sel_mode] - usr << "You switch \the [src] to [new_mode.name]." + user << "\The [src] is now set to [new_mode.name]." + +/obj/item/weapon/gun/attack_self(mob/user) + if(firemodes.len > 1) + switch_firemodes(user) + diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 994256b12a9..33ae2b03fd7 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -1,9 +1,16 @@ +/datum/firemode/energy + var/projectile_type = null + var/modifystate = null + var/charge_cost = null + var/fire_sound = null + /obj/item/weapon/gun/energy name = "energy gun" desc = "A basic energy-based gun." icon_state = "energy" fire_sound = 'sound/weapons/Taser.ogg' fire_sound_text = "laser blast" + firemode_type = /datum/firemode/energy var/obj/item/weapon/cell/power_supply //What type of power cell this uses var/charge_cost = 100 //How much energy is needed to fire. @@ -18,6 +25,18 @@ var/recharge_time = 4 var/charge_tick = 0 +/obj/item/weapon/gun/energy/switch_firemodes(mob/user=null) + ..() + var/datum/firemode/energy/current_mode = firemodes[sel_mode] + if(istype(current_mode)) + projectile_type = isnull(current_mode.projectile_type)? initial(projectile_type) : current_mode.projectile_type + modifystate = isnull(current_mode.modifystate)? initial(modifystate) : current_mode.modifystate + charge_cost = isnull(current_mode.charge_cost)? initial(charge_cost) : current_mode.charge_cost + fire_sound = isnull(current_mode.fire_sound)? initial(fire_sound) : current_mode.fire_sound + + update_icon() + update_held_icon() + /obj/item/weapon/gun/energy/emp_act(severity) ..() update_icon() diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm index 6b9f6f0d514..1e6d22a510a 100644 --- a/code/modules/projectiles/guns/energy/nuclear.dm +++ b/code/modules/projectiles/guns/energy/nuclear.dm @@ -10,26 +10,10 @@ origin_tech = "combat=3;magnets=2" modifystate = "energystun" - var/mode = 0 //0 = stun, 1 = kill - -/obj/item/weapon/gun/energy/gun/attack_self(mob/living/user as mob) - switch(mode) - if(0) - mode = 1 - charge_cost = 100 - fire_sound = 'sound/weapons/Laser.ogg' - user << "[src.name] is now set to kill." - projectile_type = /obj/item/projectile/beam - modifystate = "energykill" - if(1) - mode = 0 - charge_cost = 100 - fire_sound = 'sound/weapons/Taser.ogg' - user << "[src.name] is now set to stun." - projectile_type = /obj/item/projectile/beam/stun - modifystate = "energystun" - update_icon() - update_held_icon() + firemodes = list( + list(name="stun", projectile_type=/obj/item/projectile/beam/stun, modifystate="energystun", fire_sound='sound/weapons/Taser.ogg'), + list(name="lethal", projectile_type=/obj/item/projectile/beam, modifystate="energykill", fire_sound='sound/weapons/Laser.ogg'), + ) /obj/item/weapon/gun/energy/gun/mounted name = "mounted energy gun" @@ -43,7 +27,14 @@ origin_tech = "combat=3;materials=5;powerstorage=3" slot_flags = SLOT_BELT force = 8 //looks heavier than a pistol - self_recharge = 1 + self_recharge = 1 + modifystate = null + + firemodes = list( + list(name="stun", projectile_type=/obj/item/projectile/beam/stun, fire_sound='sound/weapons/Taser.ogg'), + list(name="lethal", projectile_type=/obj/item/projectile/beam, fire_sound='sound/weapons/Laser.ogg'), + ) + var/lightfail = 0 //override for failcheck behaviour @@ -101,10 +92,10 @@ overlays += "nucgun-clean" /obj/item/weapon/gun/energy/gun/nuclear/proc/update_mode() - if (mode == 0) - overlays += "nucgun-stun" - else if (mode == 1) - overlays += "nucgun-kill" + var/datum/firemode/current_mode = firemodes[sel_mode] + switch(current_mode.name) + if("stun") overlays += "nucgun-stun" + if("lethal") overlays += "nucgun-kill" /obj/item/weapon/gun/energy/gun/nuclear/emp_act(severity) ..() diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index 82ab47438b8..62c77cf4f3f 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -5,33 +5,16 @@ item_state = null //so the human update icon uses the icon_state instead. slot_flags = SLOT_BELT|SLOT_BACK force = 10 - fire_sound = 'sound/weapons/pulse.ogg' - charge_cost = 200 - projectile_type = /obj/item/projectile/beam/pulse - cell_type = /obj/item/weapon/cell/super - var/mode = 2 - fire_delay = 25 - -/obj/item/weapon/gun/energy/pulse_rifle/attack_self(mob/living/user as mob) - switch(mode) - if(2) - mode = 0 - charge_cost = 100 - fire_sound = 'sound/weapons/Taser.ogg' - user << "[src.name] is now set to stun." - projectile_type = /obj/item/projectile/beam/stun - if(0) - mode = 1 - charge_cost = 100 - fire_sound = 'sound/weapons/Laser.ogg' - user << "[src.name] is now set to kill." - projectile_type = /obj/item/projectile/beam - if(1) - mode = 2 - charge_cost = 200 - fire_sound = 'sound/weapons/pulse.ogg' - user << "[src.name] is now set to DESTROY." - projectile_type = /obj/item/projectile/beam/pulse + fire_sound='sound/weapons/Laser.ogg' + charge_cost = 50 + projectile_type = /obj/item/projectile/beam + sel_mode = 2 + + firemodes = list( + list(name="stun", projectile_type=/obj/item/projectile/beam/stun, fire_sound='sound/weapons/Taser.ogg'), + list(name="lethal", projectile_type=/obj/item/projectile/beam, fire_sound='sound/weapons/Laser.ogg'), + list(name="DESTROY", projectile_type=/obj/item/projectile/beam/pulse, fire_sound='sound/weapons/pulse.ogg', fire_delay=25, charge_cost=100), + ) /obj/item/weapon/gun/energy/pulse_rifle/mounted self_recharge = 1 @@ -40,12 +23,13 @@ /obj/item/weapon/gun/energy/pulse_rifle/destroyer name = "pulse destroyer" desc = "A heavy-duty, pulse-based energy weapon. Because of its complexity and cost, it is rarely seen in use except by specialists." - cell_type = /obj/item/weapon/cell/infinite - fire_delay = 10 + cell_type = /obj/item/weapon/cell/super + fire_delay = 25 + fire_sound='sound/weapons/pulse.ogg' + projectile_type=/obj/item/projectile/beam/pulse /obj/item/weapon/gun/energy/pulse_rifle/destroyer/attack_self(mob/living/user as mob) - user << "[src.name] has three settings, and they are all DESTROY." - + user << "[src.name] has three settings, and they are all DESTROY." //WHY? /obj/item/weapon/gun/energy/pulse_rifle/M1911 @@ -53,5 +37,4 @@ desc = "It's not the size of the gun, it's the size of the hole it puts through people." slot_flags = SLOT_BELT|SLOT_HOLSTER icon_state = "m1911-p" - cell_type = /obj/item/weapon/cell/infinite - fire_delay = 10 + cell_type = /obj/item/weapon/cell/crap diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index c7f74e1d958..5525c25bbf7 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -43,24 +43,11 @@ origin_tech = "materials=2;biotech=3;powerstorage=3" modifystate = "floramut" self_recharge = 1 - var/mode = 0 //0 = mutate, 1 = yield boost -/obj/item/weapon/gun/energy/floragun/attack_self(mob/living/user as mob) - switch(mode) - if(0) - mode = 1 - charge_cost = 100 - user << "The [src.name] is now set to increase yield." - projectile_type = /obj/item/projectile/energy/florayield - modifystate = "florayield" - if(1) - mode = 0 - charge_cost = 100 - user << "The [src.name] is now set to induce mutations." - projectile_type = /obj/item/projectile/energy/floramut - modifystate = "floramut" - update_icon() - update_held_icon() + firemodes = list( + list(name="induce mutations", projectile_type=/obj/item/projectile/energy/floramut, modifystate="floramut"), + list(name="increase yield", projectile_type=/obj/item/projectile/energy/florayield, modifystate="florayield"), + ) /obj/item/weapon/gun/energy/floragun/afterattack(obj/target, mob/user, adjacent_flag) //allow shooting into adjacent hydrotrays regardless of intent diff --git a/code/modules/projectiles/guns/launcher.dm b/code/modules/projectiles/guns/launcher.dm index 98b09e12c7a..29971e6b604 100644 --- a/code/modules/projectiles/guns/launcher.dm +++ b/code/modules/projectiles/guns/launcher.dm @@ -26,6 +26,3 @@ projectile.loc = get_turf(user) projectile.throw_at(target, throw_distance, release_force, user) return 1 - -/obj/item/weapon/gun/launcher/attack_self(mob/living/user as mob) - return diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 8011dbe7f9c..31ea6b03359 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -165,7 +165,10 @@ load_ammo(A, user) /obj/item/weapon/gun/projectile/attack_self(mob/user as mob) - unload_ammo(user) + if(firemodes.len > 1) + switch_firemodes(user) + else + unload_ammo(user) /obj/item/weapon/gun/projectile/attack_hand(mob/user as mob) if(user.get_inactive_hand() == src) @@ -203,3 +206,15 @@ if(chambered) bullets += 1 return bullets + +/* Unneeded -- so far. +//in case the weapon has firemodes and can't unload using attack_hand() +/obj/item/weapon/gun/projectile/verb/unload_gun() + set name = "Unload Ammo" + set category = "Object" + set src in usr + + if(usr.stat || usr.restrained()) return + + unload_ammo(usr) +*/ \ No newline at end of file diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index 090775f824e..2ad17537c26 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -98,6 +98,9 @@ icon_state = "wt550" return +/datum/firemode/z8 + var/use_launcher = 0 + /obj/item/weapon/gun/projectile/automatic/z8 name = "\improper Z8 Bulldog" desc = "An older model bullpup carbine, made by the now defunct Zendai Foundries. Uses armor piercing 5.56mm rounds. Makes you feel like a space marine when you hold it." @@ -116,22 +119,19 @@ auto_eject_sound = 'sound/weapons/smg_empty_alarm.ogg' burst_delay = 4 + firemode_type = /datum/firemode/z8 firemodes = list( list(name="semiauto", burst=1, fire_delay=0), list(name="3-round bursts", burst=3, move_delay=6, accuracy = list(0,-1,-1), dispersion = list(0.0, 0.6, 0.6)), + list(name="fire grenades", use_launcher=1) ) - var/use_launcher = 0 var/obj/item/weapon/gun/launcher/grenade/underslung/launcher /obj/item/weapon/gun/projectile/automatic/z8/New() ..() launcher = new(src) -/obj/item/weapon/gun/projectile/automatic/z8/attack_self(mob/user) - use_launcher = !use_launcher - user << "You switch to [use_launcher? "\the [launcher]" : "firing normally"]." - /obj/item/weapon/gun/projectile/automatic/z8/attackby(obj/item/I, mob/user) if((istype(I, /obj/item/weapon/grenade))) launcher.load(I, user) @@ -139,16 +139,18 @@ ..() /obj/item/weapon/gun/projectile/automatic/z8/attack_hand(mob/user) - if(user.get_inactive_hand() == src && use_launcher) + var/datum/firemode/z8/current_mode = firemodes[sel_mode] + if(user.get_inactive_hand() == src && current_mode.use_launcher) launcher.unload(user) else ..() /obj/item/weapon/gun/projectile/automatic/z8/Fire(atom/target, mob/living/user, params, pointblank=0, reflex=0) - if(use_launcher) + var/datum/firemode/z8/current_mode = firemodes[sel_mode] + if(current_mode.use_launcher) launcher.Fire(target, user, params, pointblank, reflex) if(!launcher.chambered) - use_launcher = 0 //switch back automatically + switch_firemodes() //switch back automatically else ..() @@ -191,20 +193,32 @@ var/cover_open = 0 -/obj/item/weapon/gun/projectile/automatic/l6_saw/attack_self(mob/user as mob) - cover_open = !cover_open - user << "You [cover_open ? "open" : "close"] [src]'s cover." - update_icon() - -/obj/item/weapon/gun/projectile/automatic/l6_saw/update_icon() - icon_state = "l6[cover_open ? "open" : "closed"][ammo_magazine ? round(ammo_magazine.stored_ammo.len, 25) : "-empty"]" - /obj/item/weapon/gun/projectile/automatic/l6_saw/special_check(mob/user) if(cover_open) user << "[src]'s cover is open! Close it before firing!" return 0 return ..() +/obj/item/weapon/gun/projectile/automatic/l6_saw/proc/toggle_cover(mob/user) + cover_open = !cover_open + user << "You [cover_open ? "open" : "close"] [src]'s cover." + update_icon() + +/obj/item/weapon/gun/projectile/automatic/l6_saw/attack_self(mob/user as mob) + if(cover_open) + toggle_cover(user) //close the cover + else + return ..() //once closed, behave like normal + +/obj/item/weapon/gun/projectile/automatic/l6_saw/attack_hand(mob/user as mob) + if(!cover_open && user.get_inactive_hand() == src) + toggle_cover(user) //open the cover + else + return ..() //once open, behave like normal + +/obj/item/weapon/gun/projectile/automatic/l6_saw/update_icon() + icon_state = "l6[cover_open ? "open" : "closed"][ammo_magazine ? round(ammo_magazine.stored_ammo.len, 25) : "-empty"]" + /obj/item/weapon/gun/projectile/automatic/l6_saw/load_ammo(var/obj/item/A, mob/user) if(!cover_open) user << "You need to open the cover to load [src]." @@ -213,5 +227,6 @@ /obj/item/weapon/gun/projectile/automatic/l6_saw/unload_ammo(mob/user, var/allow_dump=1) if(!cover_open) + user << "You need to open the cover to unload [src]." return ..() diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index 73ecfdd8908..1b3bb214519 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -68,15 +68,21 @@ burst_delay = 0 firemodes = list( - list(name="one barrel at a time", burst=1), - list(name="both barrels at once", burst=2), + list(name="fire one barrel at a time", burst=1), + list(name="fire both barrels at once", burst=2), ) +/obj/item/weapon/gun/projectile/shotgun/doublebarrel/pellet + ammo_type = /obj/item/ammo_casing/shotgun/pellet + /obj/item/weapon/gun/projectile/shotgun/doublebarrel/flare name = "signal shotgun" desc = "A double-barreled shotgun meant to fire signal flash shells." ammo_type = /obj/item/ammo_casing/shotgun/flash +/obj/item/weapon/gun/projectile/shotgun/doublebarrel/unload_ammo(user, allow_dump) + ..(user, allow_dump=1) + //this is largely hacky and bad :( -Pete /obj/item/weapon/gun/projectile/shotgun/doublebarrel/attackby(var/obj/item/A as obj, mob/user as mob) if(istype(A, /obj/item/weapon/circular_saw) || istype(A, /obj/item/weapon/melee/energy) || istype(A, /obj/item/weapon/pickaxe/plasmacutter)) diff --git a/code/modules/projectiles/targeting.dm b/code/modules/projectiles/targeting.dm index c69d8afda4d..d05a6e42d92 100644 --- a/code/modules/projectiles/targeting.dm +++ b/code/modules/projectiles/targeting.dm @@ -16,10 +16,6 @@ stop_aim() usr.visible_message(" \The [usr] lowers \the [src]...") -//Clicking gun will still lower aim for guns that don't overwrite this -/obj/item/weapon/gun/attack_self() - lower_aim() - //Removing the lock and the buttons. /obj/item/weapon/gun/dropped(mob/user as mob) stop_aim() From 472b284f24c60bac6b96b2a9b98e3aa84d4963f8 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Tue, 28 Apr 2015 02:08:12 -0400 Subject: [PATCH 4/6] Adds hitscan to the heavy sniper projectile --- code/modules/projectiles/projectile/bullets.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 977c9e26f49..3e5841a0f3e 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -148,6 +148,7 @@ stun = 3 weaken = 3 penetrating = 5 + hitscan = 1 //so the PTR isn't useless as a sniper weapon /obj/item/projectile/bullet/rifle/a556 damage = 40 From 9d9de73f039c4a5801565e8a7f84b7c25fd86936 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Sun, 26 Apr 2015 23:52:10 -0400 Subject: [PATCH 5/6] Firemode bugfixes --- code/modules/projectiles/gun.dm | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 76728637591..68f2e320ba3 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -173,6 +173,7 @@ next_fire_time = world.time + shoot_time //actually attempt to shoot + var/turf/targloc = get_turf(target) //cache this in case target gets deleted during shooting, e.g. if it was a securitron that got destroyed. for(var/i in 1 to _burst) var/obj/projectile = consume_next_projectile(user) if(!projectile) @@ -190,7 +191,12 @@ handle_post_fire(user, target, pointblank, reflex) update_icon() - sleep(_burst_delay) + if(i < _burst) + sleep(_burst_delay) + + if(!target) + target = targloc + pointblank = 0 update_held_icon() @@ -247,7 +253,7 @@ /obj/item/weapon/gun/proc/process_point_blank(obj/projectile, mob/user, atom/target) var/obj/item/projectile/P = projectile - if(!istype(projectile)) + if(!istype(P)) return //default behaviour only applies to true projectiles //default point blank multiplier @@ -268,7 +274,7 @@ /obj/item/weapon/gun/proc/process_accuracy(obj/projectile, mob/user, atom/target, acc_mod, dispersion) var/obj/item/projectile/P = projectile - if(!istype(projectile)) + if(!istype(P)) return //default behaviour only applies to true projectiles //Accuracy modifiers @@ -285,7 +291,7 @@ //does the actual launching of the projectile /obj/item/weapon/gun/proc/process_projectile(obj/projectile, mob/user, atom/target, var/target_zone, var/params=null) var/obj/item/projectile/P = projectile - if(!istype(projectile)) + if(!istype(P)) return 0 //default behaviour only applies to true projectiles if(params) From 39b185660e154f336221f0e78e525fa0c6365a54 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Tue, 28 Apr 2015 02:16:14 -0400 Subject: [PATCH 6/6] Updates changelog --- html/changelogs/HarpyEagle-PR-8971.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 html/changelogs/HarpyEagle-PR-8971.yml diff --git a/html/changelogs/HarpyEagle-PR-8971.yml b/html/changelogs/HarpyEagle-PR-8971.yml new file mode 100644 index 00000000000..0bfc556d727 --- /dev/null +++ b/html/changelogs/HarpyEagle-PR-8971.yml @@ -0,0 +1,13 @@ +# Your name. +author: HarpyEagle + +# 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, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Added new fire modes to various guns: c20r, STS-35, WT-550, Z8, L6 SAW, and double barreled shotgun. The firing modes work the same way as the egun; click on the weapon with it in your active hand to cycle between modes. Unloading these weapons now requires that you click on them with an empty hand."