diff --git a/code/datums/beam.dm b/code/datums/beam.dm new file mode 100644 index 00000000000..e87c60e54bd --- /dev/null +++ b/code/datums/beam.dm @@ -0,0 +1,136 @@ +//Beam Datum and effect +/datum/beam + var/atom/origin = null + var/atom/target = null + var/list/elements = list() + var/icon/base_icon = null + var/icon + var/icon_state = "" //icon state of the main segments of the beam + var/max_distance = 0 + var/endtime = 0 + var/sleep_time = 3 + var/finished = 0 + var/target_oldloc = null + var/origin_oldloc = null + var/static_beam = 0 + var/beam_type = /obj/effect/ebeam //must be subtype + + +/datum/beam/New(beam_origin,beam_target,beam_icon='icons/effects/beam.dmi',beam_icon_state="b_beam",time=50,maxdistance=10,btype = /obj/effect/ebeam) + endtime = world.time+time + origin = beam_origin + origin_oldloc = origin.loc + if(isarea(origin_oldloc)) + origin_oldloc = origin + target = beam_target + target_oldloc = target.loc + if(isarea(target_oldloc)) + target_oldloc = target + if(origin_oldloc == origin && target_oldloc == target) + static_beam = 1 + max_distance = maxdistance + base_icon = new(beam_icon,beam_icon_state) + icon = beam_icon + icon_state = beam_icon_state + beam_type = btype + + +/datum/beam/proc/Start() + Draw() + while(!finished && origin && target && world.time < endtime && get_dist(origin,target)length) + var/icon/II = new(icon, icon_state) + II.DrawBox(null,1,(length-N),32,32) + X.icon = II + else + X.icon = base_icon + X.transform = rot_matrix + + //Calculate pixel offsets (If necessary) + var/Pixel_x + var/Pixel_y + if(DX == 0) + Pixel_x = 0 + else + Pixel_x = round(sin(Angle)+32*sin(Angle)*(N+16)/32) + if(DY == 0) + Pixel_y = 0 + else + Pixel_y = round(cos(Angle)+32*cos(Angle)*(N+16)/32) + + //Position the effect so the beam is one continous line + var/a + if(abs(Pixel_x)>32) + a = Pixel_x > 0 ? round(Pixel_x/32) : Ceiling(Pixel_x/32) + X.x += a + Pixel_x %= 32 + if(abs(Pixel_y)>32) + a = Pixel_y > 0 ? round(Pixel_y/32) : Ceiling(Pixel_y/32) + X.y += a + Pixel_y %= 32 + + X.pixel_x = Pixel_x + X.pixel_y = Pixel_y + + +/obj/effect/ebeam + mouse_opacity = 0 + anchored = 1 + var/datum/beam/owner + + +/obj/effect/ebeam/Destroy() + owner = null + return ..() + + +/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10,beam_type=/obj/effect/ebeam) + var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type) + spawn(0) + newbeam.Start() + return newbeam \ No newline at end of file diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index 1c37cfe76a6..ea7f5e2abbd 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -827,6 +827,15 @@ var/list/uplink_items = list() gamemodes = list("nuclear emergency") surplus = 20 +/datum/uplink_item/device_tools/medgun + name = "Medbeam Gun" + desc = "Medical Beam Gun, useful in prolonged firefights." + item = /obj/item/weapon/gun/medbeam + reference = "MBG" + cost = 15 + gamemodes = list("nuclear emergency") + + // IMPLANTS /datum/uplink_item/implants diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 3fa9337059a..505c79b85fc 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -131,94 +131,6 @@ return found - - -/* -Beam code by Gunbuddy - -Beam() proc will only allow one beam to come from a source at a time. Attempting to call it more than -once at a time per source will cause graphical errors. -Also, the icon used for the beam will have to be vertical and 32x32. -The math involved assumes that the icon is vertical to begin with so unless you want to adjust the math, -its easier to just keep the beam vertical. -*/ -/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10) - //BeamTarget represents the target for the beam, basically just means the other end. - //Time is the duration to draw the beam - //Icon is obviously which icon to use for the beam, default is beam.dmi - //Icon_state is what icon state is used. Default is b_beam which is a blue beam. - //Maxdistance is the longest range the beam will persist before it gives up. - var/EndTime=world.time+time - var/broken = 0 - var/obj/item/projectile/beam/lightning/light = new - while(BeamTarget&&world.timelength) - var/icon/II=new(icon,icon_state) - II.DrawBox(null,1,(length-N),32,32) - II.Turn(Angle) - X.icon=II - else X.icon=I - var/Pixel_x=round(sin(Angle)+32*sin(Angle)*(N+16)/32) - var/Pixel_y=round(cos(Angle)+32*cos(Angle)*(N+16)/32) - if(DX==0) Pixel_x=0 - if(DY==0) Pixel_y=0 - if(Pixel_x>32) - for(var/a=0, a<=Pixel_x,a+=32) - X.x++ - Pixel_x-=32 - if(Pixel_x<-32) - for(var/a=0, a>=Pixel_x,a-=32) - X.x-- - Pixel_x+=32 - if(Pixel_y>32) - for(var/a=0, a<=Pixel_y,a+=32) - X.y++ - Pixel_y-=32 - if(Pixel_y<-32) - for(var/a=0, a>=Pixel_y,a-=32) - X.y-- - Pixel_y+=32 - X.pixel_x=Pixel_x - X.pixel_y=Pixel_y - var/turf/TT = get_turf(X.loc) - if(TT.density) - qdel(X) - break - for(var/obj/O in TT) - if(!O.CanPass(light)) - broken = 1 - break - else if(O.density) - broken = 1 - break - if(broken) - qdel(X) - break - sleep(3) //Changing this to a lower value will cause the beam to follow more smoothly with movement, but it will also be more laggy. - //I've found that 3 ticks provided a nice balance for my use. - for(var/obj/effect/overlay/beam/O in orange(10,src)) if(O.BeamSource==src) qdel(O) - - //All atoms /atom/proc/examine(mob/user, var/distance = -1, var/infix = "", var/suffix = "") //This reformat names to get a/an properly working on item descriptions when they are bloody diff --git a/code/game/response_team.dm b/code/game/response_team.dm index b25b2a87621..5d19a32f310 100644 --- a/code/game/response_team.dm +++ b/code/game/response_team.dm @@ -569,6 +569,8 @@ var/send_emergency_team M.equip_to_slot_or_del(new /obj/item/weapon/reagent_containers/hypospray/combat/nanites(src), slot_l_store) M.equip_to_slot_or_del(new /obj/item/weapon/melee/classic_baton/telescopic(M), slot_r_store) + M.equip_to_slot_or_del(new /obj/item/weapon/gun/medbeam(M), slot_r_hand) + if("Commander") M.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat/swat(M), slot_shoes) M.equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(M), slot_gloves) diff --git a/code/modules/projectiles/medbeam.dm b/code/modules/projectiles/medbeam.dm new file mode 100644 index 00000000000..e26a697f5a7 --- /dev/null +++ b/code/modules/projectiles/medbeam.dm @@ -0,0 +1,127 @@ +/obj/item/weapon/gun/medbeam + name = "Medical Beamgun" + desc = "Delivers medical nanites in a focused beam." + icon = 'icons/obj/chronos.dmi' + icon_state = "chronogun" + item_state = "chronogun" + w_class = 3.0 + + var/mob/living/current_target + var/last_check = 0 + var/check_delay = 10 //Check los as often as possible, max resolution is SSobj tick though + var/max_range = 8 + var/active = 0 + var/datum/beam/current_beam = null + + heavy_weapon = 1 + +/obj/item/weapon/gun/medbeam/New() + ..() + processing_objects.Add(src) + +/obj/item/weapon/gun/medbeam/Destroy() + processing_objects.Remove(src) + return ..() + +/obj/item/weapon/gun/medbeam/dropped(mob/user) + ..() + LoseTarget() + +/obj/item/weapon/gun/medbeam/proc/LoseTarget() + if(active) + qdel(current_beam) + active = 0 + on_beam_relase(current_target) + current_target = null + +/obj/item/weapon/gun/medbeam/Fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, params) + add_fingerprint(user) + + if(!isliving(target)) + return + if(current_target) + LoseTarget() + + current_target = target + active = 1 + current_beam = new(user,current_target,time=6000) + spawn(0) + current_beam.Start() + + feedback_add_details("gun_fired","[src.type]") + +/obj/item/weapon/gun/medbeam/afterattack(obj/target, mob/user, proximity) + if(target.loc == loc || target == user) + return + + if(!current_target && isliving(target)) + current_target = target + active = 1 + current_beam = new(user,current_target,time=6000) + spawn(0) + current_beam.Start() + +/obj/item/weapon/gun/medbeam/process() + var/mob/living/carbon/human/H = loc + if(!istype(H)) + LoseTarget() + return + + if(!current_target) + LoseTarget() + return + + if(world.time <= last_check+check_delay) + return + + last_check = world.time + + if(get_dist(H,current_target)>max_range || !los_check(H,current_target)) + LoseTarget() + H << "You lose control of the beam!" + return + + if(current_target) + on_beam_tick(current_target) + +/obj/item/weapon/gun/medbeam/proc/los_check(mob/user,mob/target) + var/turf/user_turf = user.loc + if(!istype(user_turf)) + return 0 + var/obj/dummy = new(user_turf) + dummy.pass_flags |= PASSTABLE & PASSGLASS & PASSGRILLE //Grille/Glass so it can be used through common windows + for(var/turf/turf in getline(user_turf,target)) + if(turf.density) + qdel(dummy) + return 0 + for(var/atom/movable/AM in turf) + if(!AM.CanPass(dummy,turf,1)) + qdel(dummy) + return 0 + for(var/obj/effect/ebeam/medical/B in turf)// Don't cross the str-beams! + if(B.owner != current_beam) + explosion(B.loc,0,3,5,8) + qdel(dummy) + return 0 + qdel(dummy) + return 1 + +/obj/item/weapon/gun/medbeam/proc/on_beam_hit(var/mob/living/target) + return + +/obj/item/weapon/gun/medbeam/proc/on_beam_tick(var/mob/living/target) + target.adjustBruteLoss(-4) + target.adjustFireLoss(-4) + if(ishuman(target)) + var/var/mob/living/carbon/human/H = target + for(var/obj/item/organ/external/E in H.organs) + if(prob(10)) + if(E.mend_fracture()) + E.perma_injury = 0 + return + +/obj/item/weapon/gun/medbeam/proc/on_beam_relase(var/mob/living/target) + return + +/obj/effect/ebeam/medical + name = "medical beam" \ No newline at end of file diff --git a/paradise.dme b/paradise.dme index 8589a2e22fc..41dd40be33c 100644 --- a/paradise.dme +++ b/paradise.dme @@ -176,6 +176,7 @@ #include "code\controllers\ProcessScheduler\core\processScheduler.dm" #include "code\datums\ai_law_sets.dm" #include "code\datums\ai_laws.dm" +#include "code\datums\beam.dm" #include "code\datums\browser.dm" #include "code\datums\cargoprofile.dm" #include "code\datums\computerfiles.dm" @@ -1627,6 +1628,7 @@ #include "code\modules\procedural_mapping\mapGenerators\syndicate.dm" #include "code\modules\projectiles\ammunition.dm" #include "code\modules\projectiles\gun.dm" +#include "code\modules\projectiles\medbeam.dm" #include "code\modules\projectiles\projectile.dm" #include "code\modules\projectiles\targeting.dm" #include "code\modules\projectiles\ammunition\ammo_casings.dm"