diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index 136d589b75d..6b23a7dae2f 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -10,8 +10,9 @@ var/caliber = "" //Which kind of guns it can be loaded into var/projectile_type = ""//The bullet type to create when New() is called var/obj/item/projectile/BB = null //The loaded bullet - var/buck = 0 + var/pellets = 1 var/deviation = 0 + var/spread = 0 /obj/item/ammo_casing/New() @@ -50,6 +51,12 @@ else user << "\blue There is no bullet in the casing to inscribe anything into." +/obj/item/ammo_casing/proc/newshot() //For energy weapons, shotgun shells and wands (!). + if (!BB) + BB = new projectile_type(src) + return + + //Boxes of ammo /obj/item/ammo_box diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm index b6313d6eae4..5ceb6aa386b 100644 --- a/code/modules/projectiles/ammunition/ammo_casings.dm +++ b/code/modules/projectiles/ammunition/ammo_casings.dm @@ -46,8 +46,8 @@ desc = "A 12 gauge buckshot shell." icon_state = "gshell" projectile_type = "/obj/item/projectile/bullet/pellet" - buck = 5 - deviation = 0.8 + pellets = 5 + deviation = 30 /obj/item/ammo_casing/shotgun/beanbag @@ -64,8 +64,8 @@ icon_state = "gshell" projectile_type = "/obj/item/projectile/bullet/pellet/weak" m_amt = 250 - buck = 5 - deviation = 0.8 + pellets = 5 + deviation = 30 /obj/item/ammo_casing/shotgun/improvised/overload name = "overloaded improvised shell" @@ -74,12 +74,12 @@ icon_state = "improvshell" projectile_type = /obj/item/projectile/bullet/pellet/random m_amt = 250 - buck = 5 - deviation = 1.0 + pellets = 5 + deviation = 30 /obj/item/ammo_casing/shotgun/improvised/overload/New() ..() - buck = rand(3, 8) + pellets = rand(3, 8) /obj/item/ammo_casing/shotgun/stunslug name = "taser slug" @@ -120,8 +120,8 @@ desc = "A shotgun shell which fires a spread of incendiary pellets." icon_state = "ishell2" projectile_type = "/obj/item/projectile/bullet/incendiary/shell/dragonsbreath" - buck = 4 - deviation = 0.9 + pellets = 4 + deviation = 30 /obj/item/ammo_casing/shotgun/ion name = "ion shell" @@ -129,8 +129,8 @@ The unique properties of the crystal splot the pulse into a spread of individually weaker bolts." icon_state = "ionshell" projectile_type = /obj/item/projectile/ion/weak - buck = 4 - deviation = 0.9 + pellets = 4 + deviation = 30 /obj/item/ammo_casing/shotgun/laserslug name = "laser slug" diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index a9ef5784dcc..4efee86bb44 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -35,13 +35,14 @@ var/automatic = 0 //Used to determine if you can target multiple people. var/tmp/mob/living/last_moved_mob //Used to fire faster at more than one person. var/tmp/told_cant_shoot = 0 //So that it doesn't spam them with the fact they cannot hit them. - var/firerate = 1 // 0 for one bullet after tarrget moves and aim is lowered, + var/firerate = 1 // 0 for one bullet after tarrget moves and aim is lowered, //1 for keep shooting until aim is lowered var/fire_delay = 0 var/last_fired = 0 var/obj/item/device/flashlight/F = null var/can_flashlight = 0 var/heavy_weapon = 0 + var/randomspread = 0 proc/ready_to_fire() if(world.time >= last_fired + fire_delay) @@ -73,8 +74,8 @@ return /obj/item/weapon/gun/afterattack(atom/A as mob|obj|turf|area, mob/living/user as mob|obj, flag, params) - if(flag) return //we're placing gun on a table or in backpack - if(istype(target, /obj/machinery/recharger) && istype(src, /obj/item/weapon/gun/energy)) return//Shouldnt flag take care of this? + if(flag) return //we're placing gun on a table or in backpack + if(istype(target, /obj/machinery/recharger) && istype(src, /obj/item/weapon/gun/energy)) return//Shouldnt flag take care of this? if(user && user.client && user.client.gun_mode && !(A in target)) PreFire(A,user,params) //They're using the new gun system, locate what they're aiming at. else @@ -113,11 +114,6 @@ add_fingerprint(user) - var/turf/curloc = get_turf(user) - var/turf/targloc = get_turf(target) - if (!istype(targloc) || !istype(curloc)) - return - if(!special_check(user)) return @@ -129,23 +125,40 @@ if(!process_chambered()) //CHECK return click_empty(user) - if(!in_chamber) - return - if(heavy_weapon) if(user.get_inactive_hand()) recoil = 4 //one-handed kick else recoil = initial(recoil) - in_chamber.firer = user - in_chamber.def_zone = user.zone_sel.selecting - if(targloc == curloc) - user.bullet_act(in_chamber) - qdel(in_chamber) - update_icon() + if (istype(in_chamber, /obj/item/projectile/bullet/blank)) // A hacky way of making blank shotgun shells work again. Honk. + in_chamber.delete() + in_chamber = null return + user.changeNext_move(CLICK_CD_RANGE) + + var/spread = 0 + var/turf/targloc = get_turf(target) + if(chambered) + for (var/i = max(1, chambered.pellets), i > 0, i--) //Previous way of doing it fucked up math for spreading. This way, even the first projectile is part of the spread code. + if(i != max(1, chambered.pellets)) //Have we fired the initial chambered bullet yet? + in_chamber = new chambered.projectile_type() + ready_projectile(target, user) + prepare_shot(in_chamber) + if(chambered.deviation) + if(randomspread) //Random spread + spread = (rand() - 0.5) * chambered.deviation + else //Smart spread + spread = (i / chambered.pellets - 0.5) * chambered.deviation + if(!process_projectile(targloc, user, params, spread)) + return 0 + else + ready_projectile(target, user) + prepare_shot(in_chamber) + if(!process_projectile(targloc, user, params, spread)) + return 0 + if(recoil) spawn() shake_camera(user, recoil + 1, recoil) @@ -164,76 +177,71 @@ user.visible_message("[src] flies out of [user]'s hands!", "[src] kicks out of your grip!") user.drop_item() - if (istype(in_chamber, /obj/item/projectile/bullet/blank)) // A hacky way of making blank shotgun shells work again. Honk. - in_chamber.delete() - in_chamber = null - return - + update_icon() + if(user.hand) + user.update_inv_l_hand() + else + user.update_inv_r_hand() +/obj/item/weapon/gun/proc/ready_projectile(atom/target as mob|obj|turf, mob/living/user) + in_chamber.firer = user + in_chamber.def_zone = user.zone_sel.selecting in_chamber.original = target + return + +/obj/item/weapon/gun/proc/process_projectile(var/turf/targloc, mob/living/user as mob|obj, params, spread) + var/turf/curloc = user.loc + if (!istype(targloc) || !istype(curloc) || !in_chamber) + return 0 + if(targloc == curloc) //Fire the projectile + user.bullet_act(in_chamber) + qdel(in_chamber) + update_icon() + return 1 + in_chamber.loc = get_turf(user) in_chamber.starting = get_turf(user) - in_chamber.shot_from = src - user.changeNext_move(CLICK_CD_RANGE) - in_chamber.silenced = silenced in_chamber.current = curloc in_chamber.yo = targloc.y - curloc.y in_chamber.xo = targloc.x - curloc.x - - if(istype(user, /mob/living/carbon)) - var/mob/living/carbon/mob = user - if(mob.shock_stage > 120) - in_chamber.yo += rand(-2,2) - in_chamber.xo += rand(-2,2) - else if(mob.shock_stage > 70) - in_chamber.yo += rand(-1,1) - in_chamber.xo += rand(-1,1) - - - if(chambered) // Beep boop buckshot - var/target_x = targloc.x - var/target_y = targloc.y - var/target_z = targloc.z - if(in_chamber) - in_chamber.process() - while(chambered.buck > 0) - var/dx = round(gaussian(0,chambered.deviation),1) - var/dy = round(gaussian(0,chambered.deviation),1) - targloc = locate(target_x+dx, target_y+dy, target_z) - if(!targloc || targloc == curloc) - break - in_chamber = new chambered.projectile_type() - prepare_shot(in_chamber) - in_chamber.original = target - in_chamber.loc = get_turf(user) - in_chamber.starting = get_turf(user) - in_chamber.current = curloc - in_chamber.yo = targloc.y - curloc.y - in_chamber.xo = targloc.x - curloc.x - if(in_chamber) - in_chamber.process() - chambered.buck-- - - if(params) var/list/mouse_control = params2list(params) if(mouse_control["icon-x"]) in_chamber.p_x = text2num(mouse_control["icon-x"]) if(mouse_control["icon-y"]) in_chamber.p_y = text2num(mouse_control["icon-y"]) + if(mouse_control["screen-loc"]) + //Split screen-loc up into X+Pixel_X and Y+Pixel_Y + var/list/screen_loc_params = text2list(mouse_control["screen-loc"], ",") - spawn() - if(in_chamber) - in_chamber.process() - sleep(1) + //Split X+Pixel_X up into list(X, Pixel_X) + var/list/screen_loc_X = text2list(screen_loc_params[1],":") + + //Split Y+Pixel_Y up into list(Y, Pixel_Y) + var/list/screen_loc_Y = text2list(screen_loc_params[2],":") + + var/x = text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32 + var/y = text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32 + var/ox = round(544/2) //"origin" x - Basically center of the screen. This is a bad way of doing it because if you are able to view MORE than 17 tiles at a time your aim will get fucked. + var/oy = round(544/2) //"origin" y - Basically center of the screen. + + var/angle = Atan2(y - oy, x - ox) + + in_chamber.Angle = angle + + if(istype(user, /mob/living/carbon)) //Increase spread based on shock + var/mob/living/carbon/mob = user + if(mob.shock_stage > 120) + spread += rand(-5,5) + else if(mob.shock_stage > 70) + spread += rand(-2,2) + + if(spread) + in_chamber.Angle += spread + if(in_chamber) + in_chamber.process() in_chamber = null - - update_icon() - - if(user.hand) - user.update_inv_l_hand() - else - user.update_inv_r_hand() + return 1 /obj/item/weapon/gun/proc/can_fire() return process_chambered() @@ -378,4 +386,5 @@ if(F) if(F.on) user.AddLuminosity(-F.brightness_on) - SetLuminosity(F.brightness_on) \ No newline at end of file + SetLuminosity(F.brightness_on) + diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 8424fb54b69..a4ce8cdeac2 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -19,6 +19,7 @@ pass_flags = PASSTABLE mouse_opacity = 0 hitsound = 'sound/weapons/pierce.ogg' + animate_movement = 0 var/bumped = 0 //Prevents it from hitting more than one guy at once var/def_zone = "" //Aiming at var/mob/firer = null//Who shot it @@ -60,6 +61,11 @@ var/chatlog_attacks = 1 + var/speed = 1 //Amount of deciseconds it takes for projectile to travel. Animation is adjusted accordingly. + var/Angle = 0 //For new projectiles + var/spread = 0 //Amount of degrees by which the projectiles will be spread DURING MOVEMENT. It exists for chaotic types of projectiles, like bees or something. + var/legacy = 0 //Use the legacy projectile system or new pixel movement? + proc/delete() // Garbage collect the projectiles loc = null @@ -174,43 +180,108 @@ return 1 - process() - spawn while(src) - if(kill_count < 1) - del(src) - return - kill_count-- - if((!( current ) || loc == current)) - current = locate(min(max(x + xo, 1), world.maxx), min(max(y + yo, 1), world.maxy), z) - if((x == 1 || x == world.maxx || y == 1 || y == world.maxy)) - del(src) - return - step_towards(src, current) - sleep(1) - if(!bumped && !isturf(original)) - if(loc == get_turf(original)) - if(!(original in permutated)) - Bump(original) - sleep(1) - Range() - return - proc/dumbfire(var/dir) // for spacepods, go snowflake go - if(!dir) - del(src) - if(kill_count < 1) - del(src) - kill_count-- - spawn while(src) - var/turf/T = get_step(src, dir) - step_towards(src, T) - sleep(1) - if(!bumped && !isturf(original)) - if(loc == get_turf(original)) - if(!(original in permutated)) - Bump(original) - sleep(1) - return + process(var/setAngle) + if(setAngle) Angle = setAngle + if(!legacy) + spawn() //New projectile system + while(loc) + if(kill_count < 1) + qdel(src) + return + kill_count-- + if((!( current ) || loc == current)) + current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) + if(!Angle) + Angle=round(Get_Angle(src,current)) + //world << "[Angle] angle" + //overlays.Cut() + //var/icon/I=new(initial(icon),icon_state) //using initial(icon) makes sure that the angle for that is reset as well + //I.Turn(Angle) + //I.DrawBox(rgb(255,0,0,50),1,1,32,32) + //icon = I + if(spread) //Chaotic spread + Angle += (rand() - 0.5) * spread + var/matrix/M = new//matrix(transform) + M.Turn(Angle) + transform = M + + var/Pixel_x=round(sin(Angle)+16*sin(Angle)*2) + var/Pixel_y=round(cos(Angle)+16*cos(Angle)*2) + var/pixel_x_offset = pixel_x + Pixel_x + var/pixel_y_offset = pixel_y + Pixel_y + var/new_x = x + var/new_y = y + //Not sure if using whiles for this is good + while(pixel_x_offset > 16) + //world << "Pre-adjust coords (x++): xy [pixel_x] xy offset [pixel_x_offset]" + pixel_x_offset -= 32 + pixel_x -= 32 + new_x++// x++ + while(pixel_x_offset < -16) + //world << "Pre-adjust coords (x--): xy [pixel_x] xy offset [pixel_x_offset]" + pixel_x_offset += 32 + pixel_x += 32 + new_x-- + + while(pixel_y_offset > 16) + //world << "Pre-adjust coords (y++): py [pixel_y] py offset [pixel_y_offset]" + pixel_y_offset -= 32 + pixel_y -= 32 + new_y++ + while(pixel_y_offset < -16) + //world << "Pre-adjust coords (y--): py [pixel_y] py offset [pixel_y_offset]" + pixel_y_offset += 32 + pixel_y += 32 + new_y-- + + speed = round(speed) //Just in case. + step_towards(src, locate(new_x, new_y, z)) //Original projectiles stepped towards 'current' + if(speed <= 1) //We should really only animate at speed 2 + pixel_x = pixel_x_offset + pixel_y = pixel_y_offset + else + animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (speed <= 3 ? speed - 1 : speed))) + +/* + var/turf/T = get_turf(src) + if(T) + T.color = "#6666FF" + spawn(10) + T.color = initial(T.color) +*/ + + if(!bumped && ((original && original.layer>=2.75) || ismob(original))) + if(loc == get_turf(original)) + if(!(original in permutated)) + Bump(original) + Range() + sleep(max(1, speed)) + else + spawn() //Old projectile system + while(loc) + if(kill_count < 1) + qdel(src) + return + kill_count-- + if((!( current ) || loc == current)) + current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) + if(!Angle) + Angle=round(Get_Angle(src,current)) + var/matrix/M = new//matrix(transform) + M.Turn(Angle) + transform = M //So there's no need to give icons directions again + step_towards(src, current) + if(!bumped && ((original && original.layer>=2.75) || ismob(original))) + if(loc == get_turf(original)) + if(!(original in permutated)) + Bump(original) + Range() + sleep(1) + + proc/dumbfire(var/dir) + current = get_ranged_target_turf(src, dir, world.maxx) //world.maxx is the range. Not sure how to handle this better. + process() /obj/item/projectile/test //Used to see if you can hit them. invisibility = 101 //Nope! Can't see me! diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index 57f75b0f54e..73177afd0bd 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ