From ad51379d86fbd7626aa823b5f00e8409e863cf06 Mon Sep 17 00:00:00 2001 From: Daranz Date: Sun, 19 Apr 2015 19:32:51 -0400 Subject: [PATCH 01/16] Make papers in a clipboard renameable without removing --- code/modules/paperwork/clipboard.dm | 18 +++++++++++++++--- code/modules/paperwork/paper.dm | 15 +++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index 9598800312..2253e049a5 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -68,14 +68,14 @@ //The topmost paper. I don't think there's any way to organise contents in byond, so this is what we're stuck with. -Pete if(toppaper) var/obj/item/weapon/paper/P = toppaper - dat += "Write Remove - [P.name]

" + dat += "Write Remove Rename - [P.name]

" for(var/obj/item/weapon/paper/P in src) if(P==toppaper) continue - dat += "Remove - [P.name]
" + dat += "Remove Rename - [P.name]
" for(var/obj/item/weapon/photo/Ph in src) - dat += "Remove - [Ph.name]
" + dat += "Remove Rename - [Ph.name]
" user << browse(dat, "window=clipboard") onclose(user, "clipboard") @@ -129,6 +129,18 @@ toppaper = newtop else toppaper = null + + else if(href_list["rename"]) + var/obj/item/weapon/O = locate(href_list["rename"]) + + if(O && (O.loc == src)) + if(istype(O, /obj/item/weapon/paper)) + var/obj/item/weapon/paper/to_rename = O + to_rename.rename() + + else if(istype(O, /obj/item/weapon/photo)) + var/obj/item/weapon/photo/to_rename = O + to_rename.rename() else if(href_list["read"]) var/obj/item/weapon/paper/P = locate(href_list["read"]) diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 9c9fe2e639..b578e33354 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -87,12 +87,15 @@ usr << "You cut yourself on the paper." return var/n_name = sanitizeSafe(input(usr, "What would you like to label the paper?", "Paper Labelling", null) as text, MAX_NAME_LEN) - if((loc == usr && usr.stat == 0)) - name = "[(n_name ? text("[n_name]") : initial(name))]" - if(name != "paper") - desc = "This is a paper titled '" + name + "'." - add_fingerprint(usr) - return + + // We check loc one level up, so we can rename in clipboards and such. See also: /obj/item/weapon/photo/rename() + if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == 0 && n_name) + name = n_name + if(n_name != "paper") + desc = "This is a paper titled '" + name + "'." + + add_fingerprint(usr) + return /obj/item/weapon/paper/attack_self(mob/living/user as mob) user.examinate(src) From 1bd25c54e317792f790f39af5e91cfe8a4d8e23d Mon Sep 17 00:00:00 2001 From: Daranz Date: Tue, 21 Apr 2015 12:51:48 -0400 Subject: [PATCH 02/16] Add random access inserting into paper bundles You can now insert papers into paper bundles by clicking on the next page or previous page link with a sheet of paper in hand. The bundle window also now correctly updates when the bundle is in a folder. --- code/modules/admin/topic.dm | 12 +- code/modules/paperwork/faxmachine.dm | 2 +- code/modules/paperwork/paper.dm | 4 +- code/modules/paperwork/paper_bundle.dm | 153 +++++++++--------- code/modules/paperwork/photocopier.dm | 8 +- .../changelogs/Daranz-paper_functionality.yml | 5 + 6 files changed, 94 insertions(+), 90 deletions(-) create mode 100644 html/changelogs/Daranz-paper_functionality.yml diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 8f943ccb6c..829e2c52af 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1369,8 +1369,8 @@ var/data = "" var/obj/item/weapon/paper_bundle/B = fax - for (var/page = 1, page <= B.amount, page++) - var/obj/pageobj = B.contents[page] + for (var/page = 1, page <= B.pages.len, page++) + var/obj/pageobj = B.pages[page] data += "Page [page] - [pageobj.name]
" usr << browse(data, "window=[B.name]") @@ -1383,11 +1383,11 @@ if (!bundle) return - if (istype(bundle.contents[page], /obj/item/weapon/paper)) - var/obj/item/weapon/paper/P = bundle.contents[page] + if (istype(bundle.pages[page], /obj/item/weapon/paper)) + var/obj/item/weapon/paper/P = bundle.pages[page] P.show_content(src.owner, 1) - else if (istype(bundle.contents[page], /obj/item/weapon/photo)) - var/obj/item/weapon/photo/H = bundle.contents[page] + else if (istype(bundle.pages[page], /obj/item/weapon/photo)) + var/obj/item/weapon/photo/H = bundle.pages[page] H.show(src.owner) return diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 33d0bb31c6..7cd104870f 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -189,7 +189,7 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins else if (istype(copyitem, /obj/item/weapon/photo)) rcvdcopy = photocopy(copyitem) else if (istype(copyitem, /obj/item/weapon/paper_bundle)) - rcvdcopy = bundlecopy(copyitem) + rcvdcopy = bundlecopy(copyitem, 0) else visible_message("[src] beeps, \"Error transmitting message.\"") return diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index b578e33354..d8f2be445a 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -440,7 +440,9 @@ user << "You clip the [P.name] to [(src.name == "paper") ? "the paper" : src.name]." src.loc = B P.loc = B - B.amount++ + + B.pages.Add(src) + B.pages.Add(P) B.update_icon() else if(istype(P, /obj/item/weapon/pen) || istype(P, /obj/item/toy/crayon)) diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm index dfa5103ec2..2100e64c56 100644 --- a/code/modules/paperwork/paper_bundle.dm +++ b/code/modules/paperwork/paper_bundle.dm @@ -11,50 +11,35 @@ layer = 4 pressure_resistance = 1 attack_verb = list("bapped") - var/amount = 0 //Amount of items clipped to the paper - var/page = 1 - var/screen = 0 + var/page = 1 // current page + var/list/pages = list() // Ordered list of pages as they are to be displayed. Can be different order than src.contents. /obj/item/weapon/paper_bundle/attackby(obj/item/weapon/W as obj, mob/user as mob) ..() - var/obj/item/weapon/paper/P - if(istype(W, /obj/item/weapon/paper)) - P = W - if (istype(P, /obj/item/weapon/paper/carbon)) - var/obj/item/weapon/paper/carbon/C = P - if (!C.iscopy && !C.copied) - user << "Take off the carbon copy first." - add_fingerprint(user) - return - amount++ - if(screen == 2) - screen = 1 - user << "You add [(P.name == "paper") ? "the paper" : P.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name]." - user.drop_from_inventory(P) - P.loc = src - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - else if(istype(W, /obj/item/weapon/photo)) - amount++ - if(screen == 2) - screen = 1 - user << "You add [(W.name == "photo") ? "the photo" : W.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name]." - user.drop_from_inventory(W) - W.loc = src + if (istype(W, /obj/item/weapon/paper/carbon)) + var/obj/item/weapon/paper/carbon/C = W + if (!C.iscopy && !C.copied) + user << "Take off the carbon copy first." + add_fingerprint(user) + return + // adding sheets + if(istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/weapon/photo)) + insert_sheet_at(user, pages.len+1, W) + + // burning else if(istype(W, /obj/item/weapon/flame)) burnpaper(W, user) + + // merging bundles else if(istype(W, /obj/item/weapon/paper_bundle)) user.drop_from_inventory(W) for(var/obj/O in W) O.loc = src O.add_fingerprint(usr) - src.amount++ - if(screen == 2) - screen = 1 + pages.Add(O) + user << "You add \the [W.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name]." del(W) else @@ -62,15 +47,27 @@ return 0 if(istype(W, /obj/item/weapon/pen) || istype(W, /obj/item/toy/crayon)) usr << browse("", "window=[name]") //Closes the dialog - P = src[page] + var/obj/P = pages[page] P.attackby(W, user) - update_icon() attack_self(usr) //Update the browsed page. add_fingerprint(usr) return +/obj/item/weapon/paper_bundle/proc/insert_sheet_at(mob/user, var/index, obj/item/weapon/sheet) + if(istype(sheet, /obj/item/weapon/paper)) + user << "You add [(sheet.name == "paper") ? "the paper" : sheet.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name]." + else if(istype(sheet, /obj/item/weapon/photo)) + user << "You add [(sheet.name == "photo") ? "the photo" : sheet.name] to [(src.name == "paper bundle") ? "the paper bundle" : src.name]." + + user.drop_from_inventory(sheet) + sheet.loc = src + + pages.Insert(index, sheet) + + if(index <= page) + page++ /obj/item/weapon/paper_bundle/proc/burnpaper(obj/item/weapon/flame/P, mob/user) var/class = "" @@ -105,28 +102,32 @@ /obj/item/weapon/paper_bundle/proc/show_content(mob/user as mob) var/dat - var/obj/item/weapon/W = src[page] - switch(screen) - if(0) - dat+= "
" - dat+= "
Remove [(istype(W, /obj/item/weapon/paper)) ? "paper" : "photo"]
" - dat+= "
Next Page


" - if(1) - dat+= "
Previous Page
" - dat+= "
Remove [(istype(W, /obj/item/weapon/paper)) ? "paper" : "photo"]
" - dat+= "
Next Page


" - if(2) - dat+= "
Previous Page
" - dat+= "
Remove [(istype(W, /obj/item/weapon/paper)) ? "paper" : "photo"]


" - dat+= "
" - if(istype(src[page], /obj/item/weapon/paper)) + var/obj/item/weapon/W = pages[page] + + // first + if(page == 1) + dat+= "
Front
" + dat+= "
Remove [(istype(W, /obj/item/weapon/paper)) ? "paper" : "photo"]
" + dat+= "
Next Page


" + // last + else if(page == pages.len) + dat+= "
Previous Page
" + dat+= "
Remove [(istype(W, /obj/item/weapon/paper)) ? "paper" : "photo"]
" + dat+= "
Back


" + // middle pages + else + dat+= "
Previous Page
" + dat+= "
Remove [(istype(W, /obj/item/weapon/paper)) ? "paper" : "photo"]
" + dat+= "
Next Page


" + + if(istype(pages[page], /obj/item/weapon/paper)) var/obj/item/weapon/paper/P = W if(!(istype(usr, /mob/living/carbon/human) || istype(usr, /mob/dead/observer) || istype(usr, /mob/living/silicon))) dat+= "[P.name][stars(P.info)][P.stamps]" else dat+= "[P.name][P.info][P.stamps]" user << browse(dat, "window=[name]") - else if(istype(src[page], /obj/item/weapon/photo)) + else if(istype(pages[page], /obj/item/weapon/photo)) var/obj/item/weapon/photo/P = W user << browse_rsc(P.img, "tmp_photo.png") user << browse(dat + "[P.name]" \ @@ -145,48 +146,44 @@ ..() if((src in usr.contents) || (istype(src.loc, /obj/item/weapon/folder) && (src.loc in usr.contents))) usr.set_machine(src) + var/obj/item/weapon/in_hand = usr.get_active_hand() if(href_list["next_page"]) - if(page == amount) - screen = 2 - else if(page == 1) - screen = 1 - else if(page == amount+1) - return - page++ - playsound(src.loc, "pageturn", 50, 1) + if(in_hand && (istype(in_hand, /obj/item/weapon/paper) || istype(in_hand, /obj/item/weapon/photo))) + insert_sheet_at(usr, page+1, in_hand) + else if(page != pages.len) + page++ + playsound(src.loc, "pageturn", 50, 1) if(href_list["prev_page"]) - if(page == 1) - return - else if(page == 2) - screen = 0 - else if(page == amount+1) - screen = 1 - page-- - playsound(src.loc, "pageturn", 50, 1) + if(in_hand && (istype(in_hand, /obj/item/weapon/paper) || istype(in_hand, /obj/item/weapon/photo))) + insert_sheet_at(usr, page, in_hand) + else if(page > 1) + page-- + playsound(src.loc, "pageturn", 50, 1) if(href_list["remove"]) - var/obj/item/weapon/W = src[page] + var/obj/item/weapon/W = pages[page] usr.put_in_hands(W) + pages.Remove(pages[page]) + usr << "You remove the [W.name] from the bundle." - if(amount == 1) + + if(pages.len <= 1) var/obj/item/weapon/paper/P = src[1] usr.drop_from_inventory(src) usr.put_in_hands(P) del(src) - else if(page == amount) - screen = 2 - else if(page == amount+1) - page-- - - amount-- + + return + + if(page > pages.len) + page = pages.len + update_icon() else usr << "You need to hold it in hands!" if (istype(src.loc, /mob) ||istype(src.loc.loc, /mob)) - src.attack_self(src.loc) + src.attack_self(usr) updateUsrDialog() - - /obj/item/weapon/paper_bundle/verb/rename() set name = "Rename bundle" set category = "Object" @@ -215,7 +212,7 @@ /obj/item/weapon/paper_bundle/update_icon() - var/obj/item/weapon/paper/P = src[1] + var/obj/item/weapon/paper/P = pages[1] icon_state = P.icon_state overlays = P.overlays underlays = 0 diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 7f0ed21c78..a42cb2615f 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -56,7 +56,7 @@ sleep(15) else if (istype(copyitem, /obj/item/weapon/paper_bundle)) var/obj/item/weapon/paper_bundle/B = bundlecopy(copyitem) - sleep(15*B.amount) + sleep(15*B.pages.len) else usr << "\The [copyitem] can't be copied by \the [src]." break @@ -216,7 +216,7 @@ //If need_toner is 0, the copies will still be lightened when low on toner, however it will not be prevented from printing. TODO: Implement print queues for fax machines and get rid of need_toner /obj/machinery/photocopier/proc/bundlecopy(var/obj/item/weapon/paper_bundle/bundle, var/need_toner=1) var/obj/item/weapon/paper_bundle/p = new /obj/item/weapon/paper_bundle (src) - for(var/obj/item/weapon/W in bundle) + for(var/obj/item/weapon/W in bundle.pages) if(toner <= 0 && need_toner) toner = 0 visible_message("A red light on \the [src] flashes, indicating that it is out of toner.") @@ -227,8 +227,8 @@ else if(istype(W, /obj/item/weapon/photo)) W = photocopy(W) W.loc = p - p.amount++ - //p.amount-- + p.pages += W + p.loc = src.loc p.update_icon() p.icon_state = "paper_words" diff --git a/html/changelogs/Daranz-paper_functionality.yml b/html/changelogs/Daranz-paper_functionality.yml new file mode 100644 index 0000000000..5bdb81041e --- /dev/null +++ b/html/changelogs/Daranz-paper_functionality.yml @@ -0,0 +1,5 @@ +author: Daranz +delete-after: True + +changes: + - rscadd: "Paper bundles can now have papers inserted at arbitrary points. This can be done by clicking the previous/next page links with a sheet of paper in hand." \ No newline at end of file From 12a6797723b816a0a6decc39af2954f1df38ad76 Mon Sep 17 00:00:00 2001 From: Daranz Date: Tue, 21 Apr 2015 19:13:00 -0400 Subject: [PATCH 03/16] Add renaming of things (papers, etc.) in folders --- code/modules/paperwork/folders.dm | 23 +++++++++++++++++++---- code/modules/paperwork/paper_bundle.dm | 2 +- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm index ddc211cc7c..c10b940c1e 100644 --- a/code/modules/paperwork/folders.dm +++ b/code/modules/paperwork/folders.dm @@ -44,11 +44,11 @@ var/dat = "[name]" for(var/obj/item/weapon/paper/P in src) - dat += "Remove - [P.name]
" + dat += "Remove Rename - [P.name]
" for(var/obj/item/weapon/photo/Ph in src) - dat += "Remove - [Ph.name]
" + dat += "Remove Rename - [Ph.name]
" for(var/obj/item/weapon/paper_bundle/Pb in src) - dat += "Remove - [Pb.name]
" + dat += "Remove Rename - [Pb.name]
" user << browse(dat, "window=folder") onclose(user, "folder") add_fingerprint(usr) @@ -85,7 +85,22 @@ if(P && (P.loc == src) && istype(P)) P.attack_self(usr) onclose(usr, "[P.name]") - + else if(href_list["rename"]) + var/obj/item/weapon/O = locate(href_list["rename"]) + + if(O && (O.loc == src)) + if(istype(O, /obj/item/weapon/paper)) + var/obj/item/weapon/paper/to_rename = O + to_rename.rename() + + else if(istype(O, /obj/item/weapon/photo)) + var/obj/item/weapon/photo/to_rename = O + to_rename.rename() + + else if(istype(O, /obj/item/weapon/paper_bundle)) + var/obj/item/weapon/paper_bundle/to_rename = O + to_rename.rename() + //Update everything attack_self(usr) update_icon() diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm index 2100e64c56..b604daded5 100644 --- a/code/modules/paperwork/paper_bundle.dm +++ b/code/modules/paperwork/paper_bundle.dm @@ -190,7 +190,7 @@ set src in usr var/n_name = sanitizeSafe(input(usr, "What would you like to label the bundle?", "Bundle Labelling", null) as text, MAX_NAME_LEN) - if((loc == usr && usr.stat == 0)) + if((loc == usr || loc.loc && loc.loc == usr) && usr.stat == 0) name = "[(n_name ? text("[n_name]") : "paper")]" add_fingerprint(usr) return From 5fed7680539674e0392b724a89052e4417e2352b Mon Sep 17 00:00:00 2001 From: mwerezak Date: Fri, 24 Apr 2015 17:49:54 -0400 Subject: [PATCH 04/16] 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 970530e71d..f848b722b5 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 05/16] 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 f848b722b5..44d293734c 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 77693417df..b2cbb6b4be 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 944a9fe99f..8011dbe7f9 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 ee7bfbffff..090775f824 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 fa29d7e8ba..73ecfdd890 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 98293ebc7f..6fdf040288 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 e2ce3d1e010bad511f53ddb9394b86d04cf11d97 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Sun, 26 Apr 2015 17:34:56 +0200 Subject: [PATCH 06/16] Scrubber NanoUI interface. Ports https://github.com/d3athrow/vgstation13/pull/4142 with added Bay-flavor. To operate once must be adjacent to the scrubber. Silicons can view the status panel from any distance they are able to access the scrubber from. This preserves the current behavior. --- baystation12.dme | 1 + code/game/machinery/atmoalter/scrubber.dm | 99 +++++++++++------------ code/modules/nano/interaction/default.dm | 8 +- code/modules/nano/interaction/physical.dm | 18 +++++ nano/templates/portscrubber.tmpl | 73 +++++++++++++++++ 5 files changed, 145 insertions(+), 54 deletions(-) create mode 100644 code/modules/nano/interaction/physical.dm create mode 100644 nano/templates/portscrubber.tmpl diff --git a/baystation12.dme b/baystation12.dme index d70e35eb20..1929265deb 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -1297,6 +1297,7 @@ #include "code\modules\nano\interaction\contained.dm" #include "code\modules\nano\interaction\default.dm" #include "code\modules\nano\interaction\inventory.dm" +#include "code\modules\nano\interaction\physical.dm" #include "code\modules\nano\interaction\zlevel.dm" #include "code\modules\nano\modules\alarm_monitor.dm" #include "code\modules\nano\modules\crew_monitor.dm" diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index d8c23cc5f0..0092ab92d1 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -13,12 +13,19 @@ power_rating = 7500 //7500 W ~ 10 HP power_losses = 150 + var/minrate = 0 + var/maxrate = 10 * ONE_ATMOSPHERE + var/list/scrubbing_gas = list("phoron", "carbon_dioxide", "sleeping_agent", "oxygen_agent_b") /obj/machinery/portable_atmospherics/powered/scrubber/New() ..() cell = new/obj/item/weapon/cell(src) +/obj/machinery/portable_atmospherics/powered/scrubber/Destroy() + qdel(holding) + ..() + /obj/machinery/portable_atmospherics/powered/scrubber/emp_act(severity) if(stat & (BROKEN|NOPOWER)) ..(severity) @@ -82,63 +89,55 @@ /obj/machinery/portable_atmospherics/powered/scrubber/return_air() return air_contents -/obj/machinery/portable_atmospherics/powered/scrubber/attack_ai(var/mob/user as mob) +/obj/machinery/portable_atmospherics/powered/scrubber/attack_ai(var/mob/user) + src.add_hiddenprint(user) return src.attack_hand(user) -/obj/machinery/portable_atmospherics/powered/scrubber/attack_hand(var/mob/user as mob) +/obj/machinery/portable_atmospherics/powered/scrubber/attack_ghost(var/mob/user) + return src.attack_hand(user) - user.set_machine(src) - var/holding_text - - if(holding) - holding_text = {"
Tank Pressure: [round(holding.air_contents.return_pressure(), 0.01)] kPa
-Remove Tank -"} - var/output_text = {"[name]
-Pressure: [round(air_contents.return_pressure(), 0.01)] kPa
-Flow Rate: [round(last_flow_rate, 0.1)] L/s
-Port Status: [(connected_port)?("Connected"):("Disconnected")] -[holding_text]
-
-Cell Charge: [cell? "[round(cell.percent())]%" : "N/A"] | Load: [round(last_power_draw)] W
-Power Switch: [on?("On"):("Off")]
-Flow Rate Regulator: - - - - [volume_rate] L/s + + + +
- -
-Close
-"} - - user << browse(output_text, "window=scrubber;size=600x300") - onclose(user, "scrubber") +/obj/machinery/portable_atmospherics/powered/scrubber/attack_hand(var/mob/user) + ui_interact(user) return +/obj/machinery/portable_atmospherics/powered/scrubber/ui_interact(mob/user, ui_key = "rcon", datum/nanoui/ui=null, force_open=1) + var/list/data[0] + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["rate"] = round(volume_rate) + data["minrate"] = round(minrate) + data["maxrate"] = round(maxrate) + data["on"] = on ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if (holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "portscrubber.tmpl", "Portable Scrubber", 480, 400, state = physical_state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + + /obj/machinery/portable_atmospherics/powered/scrubber/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained()) - return + if(..()) + return 1 - if (((get_dist(src, usr) <= 1) && istype(src.loc, /turf))) - usr.set_machine(src) - - if(href_list["power"]) - on = !on - - if (href_list["remove_tank"]) - if(holding) - holding.loc = loc - holding = null - - if (href_list["volume_adj"]) - var/diff = text2num(href_list["volume_adj"]) - volume_rate = min(initial(volume_rate), max(0, volume_rate+diff)) - - src.updateUsrDialog() - src.add_fingerprint(usr) - update_icon() - else - usr << browse(null, "window=scrubber") - return - return + if(href_list["power"]) + on = !on + . = 1 + if (href_list["remove_tank"]) + if(holding) + holding.loc = loc + holding = null + . = 1 + if (href_list["volume_adj"]) + var/diff = text2num(href_list["volume_adj"]) + volume_rate = Clamp(volume_rate+diff, minrate, maxrate) + . = 1 + update_icon() //Huge scrubber diff --git a/code/modules/nano/interaction/default.dm b/code/modules/nano/interaction/default.dm index 6fd931160d..742f672eb0 100644 --- a/code/modules/nano/interaction/default.dm +++ b/code/modules/nano/interaction/default.dm @@ -89,15 +89,15 @@ /mob/living/default_can_use_topic(var/src_object) . = shared_nano_interaction(src_object) - if(. == STATUS_INTERACTIVE) + if(. != STATUS_CLOSE) if(loc) - . = loc.contents_nano_distance(src_object, src) + . = min(., loc.contents_nano_distance(src_object, src)) if(STATUS_INTERACTIVE) return STATUS_UPDATE /mob/living/carbon/human/default_can_use_topic(var/src_object) . = shared_nano_interaction(src_object) - if(. == STATUS_INTERACTIVE) - . = shared_living_nano_distance(src_object) + if(. != STATUS_CLOSE) + . = min(., shared_living_nano_distance(src_object)) if(. == STATUS_UPDATE && (TK in mutations)) // If we have telekinesis and remain close enough, allow interaction. return STATUS_INTERACTIVE diff --git a/code/modules/nano/interaction/physical.dm b/code/modules/nano/interaction/physical.dm new file mode 100644 index 0000000000..36f9a20971 --- /dev/null +++ b/code/modules/nano/interaction/physical.dm @@ -0,0 +1,18 @@ +/var/global/datum/topic_state/physical/physical_state = new() + +/datum/topic_state/physical/can_use_topic(var/src_object, var/mob/user) + . = user.shared_nano_interaction(src_object) + if(. > STATUS_CLOSE) + return min(., user.check_physical_distance(src_object)) + +/mob/proc/check_physical_distance(var/src_object) + return STATUS_CLOSE + +/mob/dead/observer/check_physical_distance(var/src_object) + return default_can_use_topic(src_object) + +/mob/living/check_physical_distance(var/src_object) + return shared_living_nano_distance(src_object) + +/mob/living/silicon/check_physical_distance(var/src_object) + return max(STATUS_UPDATE, shared_living_nano_distance(src_object)) diff --git a/nano/templates/portscrubber.tmpl b/nano/templates/portscrubber.tmpl new file mode 100644 index 0000000000..c012898e6f --- /dev/null +++ b/nano/templates/portscrubber.tmpl @@ -0,0 +1,73 @@ +

Scrubber Status

+
+
+ Tank Pressure: +
+
+ {{:data.tankPressure}} kPa +
+
+ +
+
+ Port Status: +
+
+ {{:data.portConnected ? 'Connected' : 'Disconnected'}} +
+
+ +

Holding Tank Status

+{{if data.hasHoldingTank}} +
+
+ Tank Label: +
+
+
{{:data.holdingTank.name}}
{{:helper.link('Eject', 'eject', {'remove_tank' : 1})}} +
+
q + +
+
+ Tank Pressure: +
+
+ {{:data.holdingTank.tankPressure}} kPa +
+
+{{else}} +
No holding tank inserted.
+
 
+{{/if}} + + +

Power Regulator Status

+
+
+ Volume Rate: +
+
+ {{:helper.displayBar(data.rate, data.minrate, data.maxrate)}} +
+ {{:helper.link('-', null, {'volume_adj' : -1000}, (data.rate > data.minrate) ? null : 'disabled')}} + {{:helper.link('-', null, {'volume_adj' : -100}, (data.rate > data.minrate) ? null : 'disabled')}} + {{:helper.link('-', null, {'volume_adj' : -10}, (data.rate > data.minrate) ? null : 'disabled')}} + {{:helper.link('-', null, {'volume_adj' : -1}, (data.rate > data.minrate) ? null : 'disabled')}} +
 {{:data.rate}} kPa 
+ {{:helper.link('+', null, {'volume_adj' : 1}, (data.rate < data.maxrate) ? null : 'disabled')}} + {{:helper.link('+', null, {'volume_adj' : 10}, (data.rate < data.maxrate) ? null : 'disabled')}} + {{:helper.link('+', null, {'volume_adj' : 100}, (data.rate < data.maxrate) ? null : 'disabled')}} + {{:helper.link('+', null, {'volume_adj' : 1000}, (data.rate < data.maxrate) ? null : 'disabled')}} +
+
+
+ +
+
+ Power Switch: +
+
+ {{:helper.link('On', 'unlocked', {'power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'locked', {'power' : 1}, data.on ? null : 'selected')}} +
+
From c07ffeab53d8125674cb9955313317d4a2952564 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Sun, 26 Apr 2015 17:44:28 +0200 Subject: [PATCH 07/16] Portable Pump NanoUI interface. Ports https://github.com/d3athrow/vgstation13/pull/4142 with added Bay-flavor. To operate once must be adjacent to the PAP. Silicons can view the status panel from any distance they are able to access the PAP from. This preserves the current behavior. --- .../atmoalter/portable_atmospherics.dm | 3 +- code/game/machinery/atmoalter/pump.dm | 95 +++++++++---------- code/game/machinery/atmoalter/scrubber.dm | 4 - nano/templates/portpump.tmpl | 82 ++++++++++++++++ 4 files changed, 129 insertions(+), 55 deletions(-) create mode 100644 nano/templates/portpump.tmpl diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index bb9d399cb8..5e8817359f 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -21,7 +21,8 @@ return 1 /obj/machinery/portable_atmospherics/Destroy() - del(air_contents) + qdel(air_contents) + qdel(holding) ..() /obj/machinery/portable_atmospherics/initialize() diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 9f2c4023b2..34de1f9fe5 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -9,6 +9,9 @@ var/direction_out = 0 //0 = siphoning, 1 = releasing var/target_pressure = 100 + var/pressuremin = 0 + var/pressuremax = 10 * ONE_ATMOSPHERE + volume = 1000 power_rating = 7500 //7500 W ~ 10 HP @@ -106,64 +109,56 @@ /obj/machinery/portable_atmospherics/powered/pump/return_air() return air_contents -/obj/machinery/portable_atmospherics/powered/pump/attack_ai(var/mob/user as mob) +/obj/machinery/portable_atmospherics/powered/pump/attack_ai(var/mob/user) + src.add_hiddenprint(user) return src.attack_hand(user) -/obj/machinery/portable_atmospherics/powered/pump/attack_hand(var/mob/user as mob) +/obj/machinery/portable_atmospherics/powered/pump/attack_ghost(var/mob/user) + return src.attack_hand(user) - user.set_machine(src) - var/holding_text +/obj/machinery/portable_atmospherics/powered/pump/attack_hand(var/mob/user) + ui_interact(user) - if(holding) - holding_text = {"
Tank Pressure: [round(holding.air_contents.return_pressure(), 0.01)] kPa
-Remove Tank -"} - var/output_text = {"[capitalize(name)]
-Pressure: [round(air_contents.return_pressure(), 0.01)] kPa
-Flow Rate: [round(last_flow_rate, 0.1)] L/s
-Port Status: [(connected_port)?("Connected"):("Disconnected")] -[holding_text]
-
-Cell Charge: [cell? "[round(cell.percent())]%" : "N/A"] | Load: [round(last_power_draw)] W
-Power Switch: [on?("On"):("Off")]
-Pump Direction: [direction_out?("Out"):("In")]
-Target Pressure: - - - - [target_pressure] kPa+ + + +
-
-Close
-"} +/obj/machinery/portable_atmospherics/powered/pump/ui_interact(mob/user, ui_key = "rcon", datum/nanoui/ui=null, force_open=1) + var/list/data[0] + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["targetpressure"] = round(target_pressure) + data["pump_dir"] = direction_out + data["minpressure"] = round(pressuremin) + data["maxpressure"] = round(pressuremax) + data["on"] = on ? 1 : 0 - user << browse(output_text, "window=pump;size=600x300") - onclose(user, "pump") + data["hasHoldingTank"] = holding ? 1 : 0 + if (holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) - return + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 400, state = physical_state) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) /obj/machinery/portable_atmospherics/powered/pump/Topic(href, href_list) - ..() - if (usr.stat || usr.restrained()) - return + if(..()) + return 1 - if (((get_dist(src, usr) <= 1) && istype(src.loc, /turf))) - usr.set_machine(src) + if(href_list["power"]) + on = !on + . = 1 + if(href_list["direction"]) + direction_out = !direction_out + . = 1 + if (href_list["remove_tank"]) + if(holding) + holding.loc = loc + holding = null + . = 1 + if (href_list["pressure_adj"]) + var/diff = text2num(href_list["pressure_adj"]) + target_pressure = min(10*ONE_ATMOSPHERE, max(0, target_pressure+diff)) + . = 1 - if(href_list["power"]) - on = !on - - if(href_list["direction"]) - direction_out = !direction_out - - if (href_list["remove_tank"]) - if(holding) - holding.loc = loc - holding = null - - if (href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - target_pressure = min(10*ONE_ATMOSPHERE, max(0, target_pressure+diff)) - - src.updateUsrDialog() - src.add_fingerprint(usr) + if(.) update_icon() - else - usr << browse(null, "window=pump") - return - return \ No newline at end of file diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 0092ab92d1..c2cd45c15d 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -22,10 +22,6 @@ ..() cell = new/obj/item/weapon/cell(src) -/obj/machinery/portable_atmospherics/powered/scrubber/Destroy() - qdel(holding) - ..() - /obj/machinery/portable_atmospherics/powered/scrubber/emp_act(severity) if(stat & (BROKEN|NOPOWER)) ..(severity) diff --git a/nano/templates/portpump.tmpl b/nano/templates/portpump.tmpl new file mode 100644 index 0000000000..c65af4f020 --- /dev/null +++ b/nano/templates/portpump.tmpl @@ -0,0 +1,82 @@ +

Scrubber Status

+
+
+ Tank Pressure: +
+
+ {{:data.tankPressure}} kPa +
+
+ +
+
+ Port Status: +
+
+ {{:data.portConnected ? 'Connected' : 'Disconnected'}} +
+
+ +

Holding Tank Status

+{{if data.hasHoldingTank}} +
+
+ Tank Label: +
+
+
{{:data.holdingTank.name}}
{{:helper.link('Eject', 'eject', {'remove_tank' : 1})}} +
+
+ +
+
+ Tank Pressure: +
+
+ {{:data.holdingTank.tankPressure}} kPa +
+
+{{else}} +
No holding tank inserted.
+
 
+{{/if}} + + +

Power Regulator Status

+
+
+ Volume Rate: +
+
+ {{:helper.displayBar(data.targetpressure, data.minpressure, data.maxpressure)}} +
+ {{:helper.link('-', null, {'pressure_adj' : -1000}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} + {{:helper.link('-', null, {'pressure_adj' : -100}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} + {{:helper.link('-', null, {'pressure_adj' : -10}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} + {{:helper.link('-', null, {'pressure_adj' : -1}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} +
 {{:data.targetpressure}} kPa 
+ {{:helper.link('+', null, {'pressure_adj' : 1}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} + {{:helper.link('+', null, {'pressure_adj' : 10}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} + {{:helper.link('+', null, {'pressure_adj' : 100}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} + {{:helper.link('+', null, {'pressure_adj' : 1000}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} +
+
+
+ +
+
+ Power Switch: +
+
+ {{:helper.link('On', 'unlocked', {'power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'locked', {'power' : 1}, data.on ? null : 'selected')}} +
+
+ +
+
+ Pump Direction: +
+
+ {{:helper.link('Out', 'arrowreturn-1-e', {'direction' : 1}, data.pump_dir ? 'selected' : null)}}{{:helper.link('In', 'arrowreturn-1-w', {'direction' : 1}, data.pump_dir ? null : 'selected')}} +
+
From 85473bf3560e58c8617e6b4072c38937b0584094 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Sun, 26 Apr 2015 17:52:23 +0200 Subject: [PATCH 08/16] Changelog. --- html/changelogs/PsiOmegaDelta-PortableNanoUI.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/PsiOmegaDelta-PortableNanoUI.yml diff --git a/html/changelogs/PsiOmegaDelta-PortableNanoUI.yml b/html/changelogs/PsiOmegaDelta-PortableNanoUI.yml new file mode 100644 index 0000000000..1c8757b19f --- /dev/null +++ b/html/changelogs/PsiOmegaDelta-PortableNanoUI.yml @@ -0,0 +1,4 @@ +author: PsiOmegaDelta +changes: + - rscadd: "Portable atmospheric pumps and scrubbers now use NanoUI." +delete-after: true From 13fd1764b2dd3a1e1fd4ce23cd73429f27777fa4 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Sun, 26 Apr 2015 18:14:06 +0200 Subject: [PATCH 09/16] Adds cell charge status for the pumps and scrubbers. --- code/game/machinery/atmoalter/pump.dm | 2 ++ code/game/machinery/atmoalter/scrubber.dm | 2 ++ nano/templates/portpump.tmpl | 17 +++++++++++++---- nano/templates/portscrubber.tmpl | 13 +++++++++++-- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 34de1f9fe5..b8f9762e61 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -127,6 +127,8 @@ data["pump_dir"] = direction_out data["minpressure"] = round(pressuremin) data["maxpressure"] = round(pressuremax) + data["cellCharge"] = cell ? cell.charge : 0 + data["cellMaxCharge"] = cell ? cell.maxcharge : 1 data["on"] = on ? 1 : 0 data["hasHoldingTank"] = holding ? 1 : 0 diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index c2cd45c15d..026ca21dc4 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -103,6 +103,8 @@ data["rate"] = round(volume_rate) data["minrate"] = round(minrate) data["maxrate"] = round(maxrate) + data["cellCharge"] = cell ? cell.charge : 0 + data["cellMaxCharge"] = cell ? cell.maxcharge : 1 data["on"] = on ? 1 : 0 data["hasHoldingTank"] = holding ? 1 : 0 diff --git a/nano/templates/portpump.tmpl b/nano/templates/portpump.tmpl index c65af4f020..1152c4643d 100644 --- a/nano/templates/portpump.tmpl +++ b/nano/templates/portpump.tmpl @@ -1,4 +1,4 @@ -

Scrubber Status

+

Pump Status

Tank Pressure: @@ -17,6 +17,15 @@
+
+
+ Cell Charge: +
+
+ {{:helper.displayBar(data.cellCharge, 0, data.cellMaxCharge)}} +
+
+

Holding Tank Status

{{if data.hasHoldingTank}}
@@ -54,7 +63,7 @@ {{:helper.link('-', null, {'pressure_adj' : -100}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} {{:helper.link('-', null, {'pressure_adj' : -10}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} {{:helper.link('-', null, {'pressure_adj' : -1}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} -
 {{:data.targetpressure}} kPa 
+
 {{:data.targetpressure}} L/s 
{{:helper.link('+', null, {'pressure_adj' : 1}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} {{:helper.link('+', null, {'pressure_adj' : 10}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} {{:helper.link('+', null, {'pressure_adj' : 100}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} @@ -68,7 +77,7 @@ Power Switch:
- {{:helper.link('On', 'unlocked', {'power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'locked', {'power' : 1}, data.on ? null : 'selected')}} + {{:helper.link('On', 'unlocked', {'power' : 1}, data.on ? 'selected' : null)}} {{:helper.link('Off', 'locked', {'power' : 1}, data.on ? null : 'selected')}}
@@ -77,6 +86,6 @@ Pump Direction:
- {{:helper.link('Out', 'arrowreturn-1-e', {'direction' : 1}, data.pump_dir ? 'selected' : null)}}{{:helper.link('In', 'arrowreturn-1-w', {'direction' : 1}, data.pump_dir ? null : 'selected')}} + {{:helper.link('Out', 'arrowreturn-1-e', {'direction' : 1}, data.pump_dir ? 'selected' : null)}} {{:helper.link('In', 'arrowreturn-1-w', {'direction' : 1}, data.pump_dir ? null : 'selected')}}
diff --git a/nano/templates/portscrubber.tmpl b/nano/templates/portscrubber.tmpl index c012898e6f..a53d7a2419 100644 --- a/nano/templates/portscrubber.tmpl +++ b/nano/templates/portscrubber.tmpl @@ -17,6 +17,15 @@ +
+
+ Cell Charge: +
+
+ {{:helper.displayBar(data.cellCharge, 0, data.cellMaxCharge)}} +
+
+

Holding Tank Status

{{if data.hasHoldingTank}}
@@ -54,7 +63,7 @@ {{:helper.link('-', null, {'volume_adj' : -100}, (data.rate > data.minrate) ? null : 'disabled')}} {{:helper.link('-', null, {'volume_adj' : -10}, (data.rate > data.minrate) ? null : 'disabled')}} {{:helper.link('-', null, {'volume_adj' : -1}, (data.rate > data.minrate) ? null : 'disabled')}} -
 {{:data.rate}} kPa 
+
 {{:data.rate}} L/s 
{{:helper.link('+', null, {'volume_adj' : 1}, (data.rate < data.maxrate) ? null : 'disabled')}} {{:helper.link('+', null, {'volume_adj' : 10}, (data.rate < data.maxrate) ? null : 'disabled')}} {{:helper.link('+', null, {'volume_adj' : 100}, (data.rate < data.maxrate) ? null : 'disabled')}} @@ -68,6 +77,6 @@ Power Switch:
- {{:helper.link('On', 'unlocked', {'power' : 1}, data.on ? 'selected' : null)}}{{:helper.link('Off', 'locked', {'power' : 1}, data.on ? null : 'selected')}} + {{:helper.link('On', 'unlocked', {'power' : 1}, data.on ? 'selected' : null)}} {{:helper.link('Off', 'locked', {'power' : 1}, data.on ? null : 'selected')}}
From 7f1ed80b7443bc2d300a2d6829622b1a88b34949 Mon Sep 17 00:00:00 2001 From: PsiOmega Date: Sun, 26 Apr 2015 21:32:42 +0200 Subject: [PATCH 10/16] Adds power draw. --- code/game/machinery/atmoalter/pump.dm | 5 +++-- code/game/machinery/atmoalter/scrubber.dm | 1 + nano/templates/portpump.tmpl | 13 +++++++++++-- nano/templates/portscrubber.tmpl | 9 +++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index b8f9762e61..659e17d4b2 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -7,7 +7,7 @@ var/on = 0 var/direction_out = 0 //0 = siphoning, 1 = releasing - var/target_pressure = 100 + var/target_pressure = ONE_ATMOSPHERE var/pressuremin = 0 var/pressuremax = 10 * ONE_ATMOSPHERE @@ -127,6 +127,7 @@ data["pump_dir"] = direction_out data["minpressure"] = round(pressuremin) data["maxpressure"] = round(pressuremax) + data["powerDraw"] = round(last_power_draw) data["cellCharge"] = cell ? cell.charge : 0 data["cellMaxCharge"] = cell ? cell.maxcharge : 1 data["on"] = on ? 1 : 0 @@ -137,7 +138,7 @@ ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) - ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 400, state = physical_state) + ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 410, state = physical_state) ui.set_initial_data(data) ui.open() ui.set_auto_update(1) diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 026ca21dc4..1977e55646 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -103,6 +103,7 @@ data["rate"] = round(volume_rate) data["minrate"] = round(minrate) data["maxrate"] = round(maxrate) + data["powerDraw"] = round(last_power_draw) data["cellCharge"] = cell ? cell.charge : 0 data["cellMaxCharge"] = cell ? cell.maxcharge : 1 data["on"] = on ? 1 : 0 diff --git a/nano/templates/portpump.tmpl b/nano/templates/portpump.tmpl index 1152c4643d..3e14aac8f9 100644 --- a/nano/templates/portpump.tmpl +++ b/nano/templates/portpump.tmpl @@ -17,6 +17,15 @@ +
+
+ Load: +
+
+ {{:data.powerDraw}} W +
+
+
Cell Charge: @@ -54,7 +63,7 @@

Power Regulator Status

- Volume Rate: + Target Pressure:
{{:helper.displayBar(data.targetpressure, data.minpressure, data.maxpressure)}} @@ -63,7 +72,7 @@ {{:helper.link('-', null, {'pressure_adj' : -100}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} {{:helper.link('-', null, {'pressure_adj' : -10}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} {{:helper.link('-', null, {'pressure_adj' : -1}, (data.targetpressure > data.minpressure) ? null : 'disabled')}} -
 {{:data.targetpressure}} L/s 
+
 {{:data.targetpressure}} kPa 
{{:helper.link('+', null, {'pressure_adj' : 1}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} {{:helper.link('+', null, {'pressure_adj' : 10}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} {{:helper.link('+', null, {'pressure_adj' : 100}, (data.targetpressure < data.maxpressure) ? null : 'disabled')}} diff --git a/nano/templates/portscrubber.tmpl b/nano/templates/portscrubber.tmpl index a53d7a2419..c99f53344c 100644 --- a/nano/templates/portscrubber.tmpl +++ b/nano/templates/portscrubber.tmpl @@ -17,6 +17,15 @@
+
+
+ Load: +
+
+ {{:data.powerDraw}} W +
+
+
Cell Charge: From e93e9c1434016621e541530f4f2e6561e1160ee4 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Sun, 26 Apr 2015 23:11:23 -0400 Subject: [PATCH 11/16] 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 b2cbb6b4be..7672863759 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 994256b12a..33ae2b03fd 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 6b9f6f0d51..1e6d22a510 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 82ab47438b..62c77cf4f3 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 c7f74e1d95..5525c25bbf 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 98b09e12c7..29971e6b60 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 8011dbe7f9..31ea6b0335 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 090775f824..2ad17537c2 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 73ecfdd890..1b3bb21451 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 c69d8afda4..d05a6e42d9 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 12/16] 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 977c9e26f4..3e5841a0f3 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 13/16] 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 7672863759..68f2e320ba 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 14/16] 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 0000000000..0bfc556d72 --- /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." From 19aff88b906edef43cdf767b4ca3cf8ce53924c6 Mon Sep 17 00:00:00 2001 From: mwerezak Date: Tue, 28 Apr 2015 12:22:21 -0400 Subject: [PATCH 15/16] Fixes #9017 --- code/modules/mob/living/carbon/human/update_icons.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index a8ab21f359..d7776fa18d 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -765,8 +765,8 @@ var/global/list/damage_icon_parts = list() overlay_icon = rig.mob_icon else if(back.sprite_sheets && back.sprite_sheets[species.name]) overlay_icon = back.sprite_sheets[species.name] - else if(back.item_icons && (slot_l_hand_str in back.item_icons)) - overlay_icon = back.item_icons[slot_l_hand_str] + else if(back.item_icons && (slot_back_str in back.item_icons)) + overlay_icon = back.item_icons[slot_back_str] else overlay_icon = INV_BACK_DEF_ICON From bd6a8e4d5489aeaed0db8670ea09a3a718d3d12a Mon Sep 17 00:00:00 2001 From: Kelenius Date: Wed, 29 Apr 2015 13:53:04 +0300 Subject: [PATCH 16/16] Chemistry-Holder.dm: absolute paths --- code/modules/reagents/Chemistry-Holder.dm | 1213 ++++++++++----------- 1 file changed, 605 insertions(+), 608 deletions(-) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index eca291deed..a9562ca861 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -5,613 +5,49 @@ var/const/INGEST = 2 /////////////////////////////////////////////////////////////////////////////////// -datum - reagents - var/list/datum/reagent/reagent_list = new/list() - var/total_volume = 0 - var/maximum_volume = 100 - var/atom/my_atom = null - - New(maximum=100) - maximum_volume = maximum - - //I dislike having these here but map-objects are initialised before world/New() is called. >_> - if(!chemical_reagents_list) - //Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id - var/paths = typesof(/datum/reagent) - /datum/reagent - chemical_reagents_list = list() - for(var/path in paths) - var/datum/reagent/D = new path() - chemical_reagents_list[D.id] = D - if(!chemical_reactions_list) - //Chemical Reactions - Initialises all /datum/chemical_reaction into a list - // It is filtered into multiple lists within a list. - // For example: - // chemical_reaction_list["phoron"] is a list of all reactions relating to phoron - - var/paths = typesof(/datum/chemical_reaction) - /datum/chemical_reaction - chemical_reactions_list = list() - - for(var/path in paths) - - var/datum/chemical_reaction/D = new path() - var/list/reaction_ids = list() - - if(D.required_reagents && D.required_reagents.len) - for(var/reaction in D.required_reagents) - reaction_ids += reaction - - // Create filters based on each reagent id in the required reagents list - for(var/id in reaction_ids) - if(!chemical_reactions_list[id]) - chemical_reactions_list[id] = list() - chemical_reactions_list[id] += D - break // Don't bother adding ourselves to other reagent ids, it is redundant. - - proc - - remove_any(var/amount=1) - var/total_transfered = 0 - var/current_list_element = 1 - - current_list_element = rand(1,reagent_list.len) - - while(total_transfered != amount) - if(total_transfered >= amount) break - if(total_volume <= 0 || !reagent_list.len) break - - if(current_list_element > reagent_list.len) current_list_element = 1 - var/datum/reagent/current_reagent = reagent_list[current_list_element] - - src.remove_reagent(current_reagent.id, 1) - - current_list_element++ - total_transfered++ - src.update_total() - - handle_reactions() - return total_transfered - - get_master_reagent() - var/the_reagent = null - var/the_volume = 0 - for(var/datum/reagent/A in reagent_list) - if(A.volume > the_volume) - the_volume = A.volume - the_reagent = A - - return the_reagent - - get_master_reagent_name() - var/the_name = null - var/the_volume = 0 - for(var/datum/reagent/A in reagent_list) - if(A.volume > the_volume) - the_volume = A.volume - the_name = A.name - - return the_name - - get_master_reagent_id() - var/the_id = null - var/the_volume = 0 - for(var/datum/reagent/A in reagent_list) - if(A.volume > the_volume) - the_volume = A.volume - the_id = A.id - - return the_id - - trans_to(var/obj/target, var/amount=1, var/multiplier=1, var/preserve_data=1)//if preserve_data=0, the reagents data will be lost. Usefull if you use data for some strange stuff and don't want it to be transferred. - if (!target ) - return - if (!target.reagents || src.total_volume<=0) - return - var/datum/reagents/R = target.reagents - amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume) - var/part = amount / src.total_volume - var/trans_data = null - for (var/datum/reagent/current_reagent in src.reagent_list) - if (!current_reagent) - continue - if (current_reagent.id == "blood" && ishuman(target)) - var/mob/living/carbon/human/H = target - H.inject_blood(my_atom, amount) - continue - var/current_reagent_transfer = current_reagent.volume * part - if(preserve_data) - trans_data = copy_data(current_reagent) - - R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, safety = 1) //safety checks on these so all chemicals are transferred - src.remove_reagent(current_reagent.id, current_reagent_transfer, safety = 1) // to the target container before handling reactions - - src.update_total() - R.update_total() - R.handle_reactions() - src.handle_reactions() - return amount - - trans_to_ingest(var/obj/target, var/amount=1, var/multiplier=1, var/preserve_data=1)//For items ingested. A delay is added between ingestion and addition of the reagents - if (!target ) - return - if (!target.reagents || src.total_volume<=0) - return - - /*var/datum/reagents/R = target.reagents - - var/obj/item/weapon/reagent_containers/glass/beaker/noreact/B = new /obj/item/weapon/reagent_containers/glass/beaker/noreact //temporary holder - - amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume) - var/part = amount / src.total_volume - var/trans_data = null - for (var/datum/reagent/current_reagent in src.reagent_list) - if (!current_reagent) - continue - //if (current_reagent.id == "blood" && ishuman(target)) - // var/mob/living/carbon/human/H = target - // H.inject_blood(my_atom, amount) - // continue - var/current_reagent_transfer = current_reagent.volume * part - if(preserve_data) - trans_data = current_reagent.data - - B.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, safety = 1) //safety checks on these so all chemicals are transferred - src.remove_reagent(current_reagent.id, current_reagent_transfer, safety = 1) // to the target container before handling reactions - - src.update_total() - B.update_total() - B.handle_reactions() - src.handle_reactions()*/ - - var/obj/item/weapon/reagent_containers/glass/beaker/noreact/B = new /obj/item/weapon/reagent_containers/glass/beaker/noreact //temporary holder - B.volume = 1000 - - var/datum/reagents/BR = B.reagents - var/datum/reagents/R = target.reagents - - amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume) - - src.trans_to(B, amount) - - spawn(95) - BR.reaction(target, INGEST) - spawn(5) - BR.trans_to(target, BR.total_volume) - qdel(B) - - return amount - - copy_to(var/obj/target, var/amount=1, var/multiplier=1, var/preserve_data=1, var/safety = 0) - if(!target) - return - if(!target.reagents || src.total_volume<=0) - return - var/datum/reagents/R = target.reagents - amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume) - var/part = amount / src.total_volume - var/trans_data = null - for (var/datum/reagent/current_reagent in src.reagent_list) - var/current_reagent_transfer = current_reagent.volume * part - if(preserve_data) - trans_data = copy_data(current_reagent) - R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, safety = 1) //safety check so all chemicals are transferred before reacting - - src.update_total() - R.update_total() - if(!safety) - R.handle_reactions() - src.handle_reactions() - return amount - - trans_id_to(var/obj/target, var/reagent, var/amount=1, var/preserve_data=1)//Not sure why this proc didn't exist before. It does now! /N - if (!target) - return - if (!target.reagents || src.total_volume<=0 || !src.get_reagent_amount(reagent)) - return - - var/datum/reagents/R = target.reagents - if(src.get_reagent_amount(reagent) R.maximum_volume) return 0 - - current_list_element = rand(1,reagent_list.len) //Eh, bandaid fix. - - while(total_transfered != amount) - if(total_transfered >= amount) break //Better safe than sorry. - if(total_volume <= 0 || !reagent_list.len) break - if(R.total_volume >= R.maximum_volume) break - - if(current_list_element > reagent_list.len) current_list_element = 1 - var/datum/reagent/current_reagent = reagent_list[current_list_element] - if(preserve_data) - trans_data = current_reagent.data - R.add_reagent(current_reagent.id, (1 * multiplier), trans_data) - src.remove_reagent(current_reagent.id, 1) - - current_list_element++ - total_transfered++ - src.update_total() - R.update_total() - R.handle_reactions() - handle_reactions() - - return total_transfered -*/ - - metabolize(var/mob/M,var/alien) - - for(var/A in reagent_list) - var/datum/reagent/R = A - if(M && R) - R.on_mob_life(M,alien) - update_total() - - conditional_update_move(var/atom/A, var/Running = 0) - for(var/datum/reagent/R in reagent_list) - R.on_move (A, Running) - update_total() - - conditional_update(var/atom/A, ) - for(var/datum/reagent/R in reagent_list) - R.on_update (A) - update_total() - - handle_reactions() - if(my_atom.flags & NOREACT) return //Yup, no reactions here. No siree. - - var/reaction_occured = 0 - do - reaction_occured = 0 - for(var/datum/reagent/R in reagent_list) // Usually a small list - for(var/reaction in chemical_reactions_list[R.id]) // Was a big list but now it should be smaller since we filtered it with our reagent id - - if(!reaction) - continue - - var/datum/chemical_reaction/C = reaction - - //check if this recipe needs to be heated to mix - if(C.requires_heating) - if(istype(my_atom.loc, /obj/machinery/bunsen_burner)) - if(!my_atom.loc:heated) - continue - else - continue - - var/total_required_reagents = C.required_reagents.len - var/total_matching_reagents = 0 - var/total_required_catalysts = C.required_catalysts.len - var/total_matching_catalysts= 0 - var/matching_container = 0 - var/matching_other = 0 - var/list/multipliers = new/list() - - for(var/B in C.required_reagents) - if(!has_reagent(B, C.required_reagents[B])) break - total_matching_reagents++ - multipliers += round(get_reagent_amount(B) / C.required_reagents[B]) - for(var/B in C.required_catalysts) - if(!has_reagent(B, C.required_catalysts[B])) break - total_matching_catalysts++ - - if(!C.required_container) - matching_container = 1 - - else - if(my_atom.type == C.required_container) - matching_container = 1 - - if(!C.required_other) - matching_other = 1 - - else - /*if(istype(my_atom, /obj/item/slime_core)) - var/obj/item/slime_core/M = my_atom - - if(M.POWERFLAG == C.required_other && M.Uses > 0) // added a limit to slime cores -- Muskets requested this - matching_other = 1*/ - if(istype(my_atom, /obj/item/slime_extract)) - var/obj/item/slime_extract/M = my_atom - - if(M.Uses > 0) // added a limit to slime cores -- Muskets requested this - matching_other = 1 - - - - - if(total_matching_reagents == total_required_reagents && total_matching_catalysts == total_required_catalysts && matching_container && matching_other) - var/multiplier = min(multipliers) - var/preserved_data = null - for(var/B in C.required_reagents) - if(!preserved_data) - preserved_data = get_data(B) - remove_reagent(B, (multiplier * C.required_reagents[B]), safety = 1) - - var/created_volume = C.result_amount*multiplier - if(C.result) - feedback_add_details("chemical_reaction","[C.result]|[C.result_amount*multiplier]") - multiplier = max(multiplier, 1) //this shouldnt happen ... - if(!isnull(C.resultcolor)) //paints - add_reagent(C.result, C.result_amount*multiplier, C.resultcolor) - else - add_reagent(C.result, C.result_amount*multiplier) - set_data(C.result, preserved_data) - - //add secondary products - for(var/S in C.secondary_results) - add_reagent(S, C.result_amount * C.secondary_results[S] * multiplier) - - var/list/seen = viewers(4, get_turf(my_atom)) - for(var/mob/M in seen) - M << "\blue \icon[my_atom] The solution begins to bubble." - - /* if(istype(my_atom, /obj/item/slime_core)) - var/obj/item/slime_core/ME = my_atom - ME.Uses-- - if(ME.Uses <= 0) // give the notification that the slime core is dead - for(var/mob/M in viewers(4, get_turf(my_atom)) ) - M << "\blue \icon[my_atom] The innards begin to boil!" - */ - if(istype(my_atom, /obj/item/slime_extract)) - var/obj/item/slime_extract/ME2 = my_atom - ME2.Uses-- - if(ME2.Uses <= 0) // give the notification that the slime core is dead - for(var/mob/M in seen) - M << "\blue \icon[my_atom] The [my_atom]'s power is consumed in the reaction." - ME2.name = "used slime extract" - ME2.desc = "This extract has been used up." - - playsound(get_turf(my_atom), 'sound/effects/bubbles.ogg', 80, 1) - - C.on_reaction(src, created_volume) - reaction_occured = 1 - break - - while(reaction_occured) - update_total() - return 0 - - isolate_reagent(var/reagent) - for(var/A in reagent_list) - var/datum/reagent/R = A - if (R.id != reagent) - del_reagent(R.id) - update_total() - - del_reagent(var/reagent) - for(var/A in reagent_list) - var/datum/reagent/R = A - if (R.id == reagent) - reagent_list -= A - qdel(A) - update_total() - my_atom.on_reagent_change() - return 0 - - - return 1 - - update_total() - total_volume = 0 - for(var/datum/reagent/R in reagent_list) - if(R.volume < 0.1) - del_reagent(R.id) - else - total_volume += R.volume - - return 0 - - clear_reagents() - for(var/datum/reagent/R in reagent_list) - del_reagent(R.id) - return 0 - - reaction(var/atom/A, var/method=TOUCH, var/volume_modifier=0) - - switch(method) - if(TOUCH) - for(var/datum/reagent/R in reagent_list) - if(ismob(A)) - spawn(0) - if(!R) return - else R.reaction_mob(A, TOUCH, R.volume+volume_modifier) - if(isturf(A)) - spawn(0) - if(!R) return - else R.reaction_turf(A, R.volume+volume_modifier) - if(isobj(A)) - spawn(0) - if(!R) return - else R.reaction_obj(A, R.volume+volume_modifier) - if(INGEST) - for(var/datum/reagent/R in reagent_list) - if(ismob(A) && R) - spawn(0) - if(!R) return - else R.reaction_mob(A, INGEST, R.volume+volume_modifier) - if(isturf(A) && R) - spawn(0) - if(!R) return - else R.reaction_turf(A, R.volume+volume_modifier) - if(isobj(A) && R) - spawn(0) - if(!R) return - else R.reaction_obj(A, R.volume+volume_modifier) - return - - add_reagent(var/reagent, var/amount, var/data=null, var/safety = 0) - if(!isnum(amount)) return 1 - update_total() - if(total_volume + amount > maximum_volume) amount = (maximum_volume - total_volume) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen. - - for(var/A in reagent_list) - - var/datum/reagent/R = A - if (R.id == reagent) - R.on_merge(data, amount) - R.volume += amount - update_total() - if(!safety) - handle_reactions() - my_atom.on_reagent_change() - return 0 - - var/datum/reagent/D = chemical_reagents_list[reagent] - if(D) - - var/datum/reagent/R = new D.type() - reagent_list += R - R.holder = src - R.volume = amount - if(reagent == "paint") - R.color = data - else - SetViruses(R, data) // Includes setting data for blood - - //debug - //world << "Adding data" - //for(var/D in R.data) - // world << "Container data: [D] = [R.data[D]]" - //debug - update_total() - my_atom.on_reagent_change() - if(!safety) - handle_reactions() - return 0 - else - warning("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])") - - if(!safety) - handle_reactions() - - return 1 - - remove_reagent(var/reagent, var/amount, var/safety = 0)//Added a safety check for the trans_id_to - if(!isnum(amount)) return 1 - - for(var/A in reagent_list) - var/datum/reagent/R = A - if (R.id == reagent) - R.volume -= amount - update_total() - if(!safety)//So it does not handle reactions when it need not to - handle_reactions() - my_atom.on_reagent_change() - return 0 - - return 1 - - has_reagent(var/reagent, var/amount = -1) - - for(var/A in reagent_list) - var/datum/reagent/R = A - if (R.id == reagent) - if(!amount) return R - else - if(R.volume >= amount) return R - else return 0 - - return 0 - - get_reagent_amount(var/reagent) - for(var/A in reagent_list) - var/datum/reagent/R = A - if (R.id == reagent) - return R.volume - - return 0 - - get_reagents() - var/res = "" - for(var/datum/reagent/A in reagent_list) - if (res != "") res += "," - res += A.name - - return res - - remove_all_type(var/reagent_type, var/amount, var/strict = 0, var/safety = 1) // Removes all reagent of X type. @strict set to 1 determines whether the childs of the type are included. - if(!isnum(amount)) return 1 - - var/has_removed_reagent = 0 - - for(var/datum/reagent/R in reagent_list) - var/matches = 0 - // Switch between how we check the reagent type - if(strict) - if(R.type == reagent_type) - matches = 1 - else - if(istype(R, reagent_type)) - matches = 1 - // We found a match, proceed to remove the reagent. Keep looping, we might find other reagents of the same type. - if(matches) - // Have our other proc handle removement - has_removed_reagent = remove_reagent(R.id, amount, safety) - - return has_removed_reagent - - //two helper functions to preserve data across reactions (needed for xenoarch) - get_data(var/reagent_id) - for(var/datum/reagent/D in reagent_list) - if(D.id == reagent_id) - //world << "proffering a data-carrying reagent ([reagent_id])" - return D.data - - set_data(var/reagent_id, var/new_data) - for(var/datum/reagent/D in reagent_list) - if(D.id == reagent_id) - //world << "reagent data set ([reagent_id])" - D.data = new_data - - delete() - for(var/datum/reagent/R in reagent_list) - R.holder = null - if(my_atom) - my_atom.reagents = null - - copy_data(var/datum/reagent/current_reagent) - if (current_reagent.id == "paint") return current_reagent.color - if (!current_reagent || !current_reagent.data) return null - if (!istype(current_reagent.data, /list)) return current_reagent.data - - var/list/trans_data = current_reagent.data.Copy() - - // We do this so that introducing a virus to a blood sample - // doesn't automagically infect all other blood samples from - // the same donor. - // - // Technically we should probably copy all data lists, but - // that could possibly eat up a lot of memory needlessly - // if most data lists are read-only. - if (trans_data["virus2"]) - var/list/v = trans_data["virus2"] - trans_data["virus2"] = v.Copy() - - return trans_data - -datum/reagents/Destroy() +/datum/reagents + var/list/datum/reagent/reagent_list = new/list() + var/total_volume = 0 + var/maximum_volume = 100 + var/atom/my_atom = null + +/datum/reagents/New(maximum=100) + maximum_volume = maximum + + //I dislike having these here but map-objects are initialised before world/New() is called. >_> + if(!chemical_reagents_list) + //Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id + var/paths = typesof(/datum/reagent) - /datum/reagent + chemical_reagents_list = list() + for(var/path in paths) + var/datum/reagent/D = new path() + chemical_reagents_list[D.id] = D + if(!chemical_reactions_list) + //Chemical Reactions - Initialises all /datum/chemical_reaction into a list + // It is filtered into multiple lists within a list. + // For example: + // chemical_reaction_list["phoron"] is a list of all reactions relating to phoron + + var/paths = typesof(/datum/chemical_reaction) - /datum/chemical_reaction + chemical_reactions_list = list() + + for(var/path in paths) + + var/datum/chemical_reaction/D = new path() + var/list/reaction_ids = list() + + if(D.required_reagents && D.required_reagents.len) + for(var/reaction in D.required_reagents) + reaction_ids += reaction + + // Create filters based on each reagent id in the required reagents list + for(var/id in reaction_ids) + if(!chemical_reactions_list[id]) + chemical_reactions_list[id] = list() + chemical_reactions_list[id] += D + break // Don't bother adding ourselves to other reagent ids, it is redundant. + +/datum/reagents/Destroy() ..() for(var/datum/reagent/R in reagent_list) qdel(R) @@ -620,11 +56,572 @@ datum/reagents/Destroy() if(my_atom && my_atom.reagents == src) my_atom.reagents = null +/datum/reagents/proc/remove_any(var/amount=1) + var/total_transfered = 0 + var/current_list_element = 1 + + current_list_element = rand(1,reagent_list.len) + + while(total_transfered != amount) + if(total_transfered >= amount) break + if(total_volume <= 0 || !reagent_list.len) break + + if(current_list_element > reagent_list.len) current_list_element = 1 + var/datum/reagent/current_reagent = reagent_list[current_list_element] + + src.remove_reagent(current_reagent.id, 1) + + current_list_element++ + total_transfered++ + src.update_total() + + handle_reactions() + return total_transfered + +/datum/reagents/proc/get_master_reagent() + var/the_reagent = null + var/the_volume = 0 + for(var/datum/reagent/A in reagent_list) + if(A.volume > the_volume) + the_volume = A.volume + the_reagent = A + + return the_reagent + +/datum/reagents/proc/get_master_reagent_name() + var/the_name = null + var/the_volume = 0 + for(var/datum/reagent/A in reagent_list) + if(A.volume > the_volume) + the_volume = A.volume + the_name = A.name + + return the_name + +/datum/reagents/proc/get_master_reagent_id() + var/the_id = null + var/the_volume = 0 + for(var/datum/reagent/A in reagent_list) + if(A.volume > the_volume) + the_volume = A.volume + the_id = A.id + + return the_id + +/datum/reagents/proc/trans_to(var/obj/target, var/amount=1, var/multiplier=1, var/preserve_data=1)//if preserve_data=0, the reagents data will be lost. Usefull if you use data for some strange stuff and don't want it to be transferred. + if (!target ) + return + if (!target.reagents || src.total_volume<=0) + return + var/datum/reagents/R = target.reagents + amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume) + var/part = amount / src.total_volume + var/trans_data = null + for (var/datum/reagent/current_reagent in src.reagent_list) + if (!current_reagent) + continue + if (current_reagent.id == "blood" && ishuman(target)) + var/mob/living/carbon/human/H = target + H.inject_blood(my_atom, amount) + continue + var/current_reagent_transfer = current_reagent.volume * part + if(preserve_data) + trans_data = copy_data(current_reagent) + + R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, safety = 1) //safety checks on these so all chemicals are transferred + src.remove_reagent(current_reagent.id, current_reagent_transfer, safety = 1) // to the target container before handling reactions + + src.update_total() + R.update_total() + R.handle_reactions() + src.handle_reactions() + return amount + +/datum/reagents/proc/trans_to_ingest(var/obj/target, var/amount=1, var/multiplier=1, var/preserve_data=1)//For items ingested. A delay is added between ingestion and addition of the reagents + if (!target ) + return + if (!target.reagents || src.total_volume<=0) + return + + /*var/datum/reagents/R = target.reagents + + var/obj/item/weapon/reagent_containers/glass/beaker/noreact/B = new /obj/item/weapon/reagent_containers/glass/beaker/noreact //temporary holder + + amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume) + var/part = amount / src.total_volume + var/trans_data = null + for (var/datum/reagent/current_reagent in src.reagent_list) + if (!current_reagent) + continue + //if (current_reagent.id == "blood" && ishuman(target)) + // var/mob/living/carbon/human/H = target + // H.inject_blood(my_atom, amount) + // continue + var/current_reagent_transfer = current_reagent.volume * part + if(preserve_data) + trans_data = current_reagent.data + + B.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, safety = 1) //safety checks on these so all chemicals are transferred + src.remove_reagent(current_reagent.id, current_reagent_transfer, safety = 1) // to the target container before handling reactions + + src.update_total() + B.update_total() + B.handle_reactions() + src.handle_reactions()*/ + + var/obj/item/weapon/reagent_containers/glass/beaker/noreact/B = new /obj/item/weapon/reagent_containers/glass/beaker/noreact //temporary holder + B.volume = 1000 + + var/datum/reagents/BR = B.reagents + var/datum/reagents/R = target.reagents + + amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume) + + src.trans_to(B, amount) + + spawn(95) + BR.reaction(target, INGEST) + spawn(5) + BR.trans_to(target, BR.total_volume) + qdel(B) + + return amount + +/datum/reagents/proc/copy_to(var/obj/target, var/amount=1, var/multiplier=1, var/preserve_data=1, var/safety = 0) + if(!target) + return + if(!target.reagents || src.total_volume<=0) + return + var/datum/reagents/R = target.reagents + amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume) + var/part = amount / src.total_volume + var/trans_data = null + for (var/datum/reagent/current_reagent in src.reagent_list) + var/current_reagent_transfer = current_reagent.volume * part + if(preserve_data) + trans_data = copy_data(current_reagent) + R.add_reagent(current_reagent.id, (current_reagent_transfer * multiplier), trans_data, safety = 1) //safety check so all chemicals are transferred before reacting + + src.update_total() + R.update_total() + if(!safety) + R.handle_reactions() + src.handle_reactions() + return amount + +/datum/reagents/proc/trans_id_to(var/obj/target, var/reagent, var/amount=1, var/preserve_data=1)//Not sure why this proc didn't exist before. It does now! /N + if (!target) + return + if (!target.reagents || src.total_volume<=0 || !src.get_reagent_amount(reagent)) + return + + var/datum/reagents/R = target.reagents + if(src.get_reagent_amount(reagent) R.maximum_volume) return 0 + + current_list_element = rand(1,reagent_list.len) //Eh, bandaid fix. + + while(total_transfered != amount) + if(total_transfered >= amount) break //Better safe than sorry. + if(total_volume <= 0 || !reagent_list.len) break + if(R.total_volume >= R.maximum_volume) break + + if(current_list_element > reagent_list.len) current_list_element = 1 + var/datum/reagent/current_reagent = reagent_list[current_list_element] + if(preserve_data) + trans_data = current_reagent.data + R.add_reagent(current_reagent.id, (1 * multiplier), trans_data) + src.remove_reagent(current_reagent.id, 1) + + current_list_element++ + total_transfered++ + src.update_total() + R.update_total() + R.handle_reactions() + handle_reactions() + + return total_transfered +*/ + +/datum/reagents/proc/metabolize(var/mob/M,var/alien) + + for(var/A in reagent_list) + var/datum/reagent/R = A + if(M && R) + R.on_mob_life(M,alien) + update_total() + +/datum/reagents/proc/conditional_update_move(var/atom/A, var/Running = 0) + for(var/datum/reagent/R in reagent_list) + R.on_move (A, Running) + update_total() + +/datum/reagents/proc/conditional_update(var/atom/A, ) + for(var/datum/reagent/R in reagent_list) + R.on_update (A) + update_total() + +/datum/reagents/proc/handle_reactions() + if(my_atom.flags & NOREACT) return //Yup, no reactions here. No siree. + + var/reaction_occured = 0 + do + reaction_occured = 0 + for(var/datum/reagent/R in reagent_list) // Usually a small list + for(var/reaction in chemical_reactions_list[R.id]) // Was a big list but now it should be smaller since we filtered it with our reagent id + + if(!reaction) + continue + + var/datum/chemical_reaction/C = reaction + + //check if this recipe needs to be heated to mix + if(C.requires_heating) + if(istype(my_atom.loc, /obj/machinery/bunsen_burner)) + if(!my_atom.loc:heated) + continue + else + continue + + var/total_required_reagents = C.required_reagents.len + var/total_matching_reagents = 0 + var/total_required_catalysts = C.required_catalysts.len + var/total_matching_catalysts= 0 + var/matching_container = 0 + var/matching_other = 0 + var/list/multipliers = new/list() + + for(var/B in C.required_reagents) + if(!has_reagent(B, C.required_reagents[B])) break + total_matching_reagents++ + multipliers += round(get_reagent_amount(B) / C.required_reagents[B]) + for(var/B in C.required_catalysts) + if(!has_reagent(B, C.required_catalysts[B])) break + total_matching_catalysts++ + + if(!C.required_container) + matching_container = 1 + + else + if(my_atom.type == C.required_container) + matching_container = 1 + + if(!C.required_other) + matching_other = 1 + + else + /*if(istype(my_atom, /obj/item/slime_core)) + var/obj/item/slime_core/M = my_atom + + if(M.POWERFLAG == C.required_other && M.Uses > 0) // added a limit to slime cores -- Muskets requested this + matching_other = 1*/ + if(istype(my_atom, /obj/item/slime_extract)) + var/obj/item/slime_extract/M = my_atom + + if(M.Uses > 0) // added a limit to slime cores -- Muskets requested this + matching_other = 1 + + + + + if(total_matching_reagents == total_required_reagents && total_matching_catalysts == total_required_catalysts && matching_container && matching_other) + var/multiplier = min(multipliers) + var/preserved_data = null + for(var/B in C.required_reagents) + if(!preserved_data) + preserved_data = get_data(B) + remove_reagent(B, (multiplier * C.required_reagents[B]), safety = 1) + + var/created_volume = C.result_amount*multiplier + if(C.result) + feedback_add_details("chemical_reaction","[C.result]|[C.result_amount*multiplier]") + multiplier = max(multiplier, 1) //this shouldnt happen ... + if(!isnull(C.resultcolor)) //paints + add_reagent(C.result, C.result_amount*multiplier, C.resultcolor) + else + add_reagent(C.result, C.result_amount*multiplier) + set_data(C.result, preserved_data) + + //add secondary products + for(var/S in C.secondary_results) + add_reagent(S, C.result_amount * C.secondary_results[S] * multiplier) + + var/list/seen = viewers(4, get_turf(my_atom)) + for(var/mob/M in seen) + M << "\blue \icon[my_atom] The solution begins to bubble." + + /* if(istype(my_atom, /obj/item/slime_core)) + var/obj/item/slime_core/ME = my_atom + ME.Uses-- + if(ME.Uses <= 0) // give the notification that the slime core is dead + for(var/mob/M in viewers(4, get_turf(my_atom)) ) + M << "\blue \icon[my_atom] The innards begin to boil!" + */ + if(istype(my_atom, /obj/item/slime_extract)) + var/obj/item/slime_extract/ME2 = my_atom + ME2.Uses-- + if(ME2.Uses <= 0) // give the notification that the slime core is dead + for(var/mob/M in seen) + M << "\blue \icon[my_atom] The [my_atom]'s power is consumed in the reaction." + ME2.name = "used slime extract" + ME2.desc = "This extract has been used up." + + playsound(get_turf(my_atom), 'sound/effects/bubbles.ogg', 80, 1) + + C.on_reaction(src, created_volume) + reaction_occured = 1 + break + + while(reaction_occured) + update_total() + return 0 + +/datum/reagents/proc/isolate_reagent(var/reagent) + for(var/A in reagent_list) + var/datum/reagent/R = A + if (R.id != reagent) + del_reagent(R.id) + update_total() + +/datum/reagents/proc/del_reagent(var/reagent) + for(var/A in reagent_list) + var/datum/reagent/R = A + if (R.id == reagent) + reagent_list -= A + qdel(A) + update_total() + my_atom.on_reagent_change() + return 0 + + + return 1 + +/datum/reagents/proc/update_total() + total_volume = 0 + for(var/datum/reagent/R in reagent_list) + if(R.volume < 0.1) + del_reagent(R.id) + else + total_volume += R.volume + + return 0 + +/datum/reagents/proc/clear_reagents() + for(var/datum/reagent/R in reagent_list) + del_reagent(R.id) + return 0 + +/datum/reagents/proc/reaction(var/atom/A, var/method=TOUCH, var/volume_modifier=0) + + switch(method) + if(TOUCH) + for(var/datum/reagent/R in reagent_list) + if(ismob(A)) + spawn(0) + if(!R) return + else R.reaction_mob(A, TOUCH, R.volume+volume_modifier) + if(isturf(A)) + spawn(0) + if(!R) return + else R.reaction_turf(A, R.volume+volume_modifier) + if(isobj(A)) + spawn(0) + if(!R) return + else R.reaction_obj(A, R.volume+volume_modifier) + if(INGEST) + for(var/datum/reagent/R in reagent_list) + if(ismob(A) && R) + spawn(0) + if(!R) return + else R.reaction_mob(A, INGEST, R.volume+volume_modifier) + if(isturf(A) && R) + spawn(0) + if(!R) return + else R.reaction_turf(A, R.volume+volume_modifier) + if(isobj(A) && R) + spawn(0) + if(!R) return + else R.reaction_obj(A, R.volume+volume_modifier) + return + +/datum/reagents/proc/add_reagent(var/reagent, var/amount, var/data=null, var/safety = 0) + if(!isnum(amount)) return 1 + update_total() + if(total_volume + amount > maximum_volume) amount = (maximum_volume - total_volume) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen. + + for(var/A in reagent_list) + + var/datum/reagent/R = A + if (R.id == reagent) + R.on_merge(data, amount) + R.volume += amount + update_total() + if(!safety) + handle_reactions() + my_atom.on_reagent_change() + return 0 + + var/datum/reagent/D = chemical_reagents_list[reagent] + if(D) + + var/datum/reagent/R = new D.type() + reagent_list += R + R.holder = src + R.volume = amount + if(reagent == "paint") + R.color = data + else + SetViruses(R, data) // Includes setting data for blood + + //debug + //world << "Adding data" + //for(var/D in R.data) + // world << "Container data: [D] = [R.data[D]]" + //debug + update_total() + my_atom.on_reagent_change() + if(!safety) + handle_reactions() + return 0 + else + warning("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])") + + if(!safety) + handle_reactions() + + return 1 + +/datum/reagents/proc/remove_reagent(var/reagent, var/amount, var/safety = 0)//Added a safety check for the trans_id_to + if(!isnum(amount)) return 1 + + for(var/A in reagent_list) + var/datum/reagent/R = A + if (R.id == reagent) + R.volume -= amount + update_total() + if(!safety)//So it does not handle reactions when it need not to + handle_reactions() + my_atom.on_reagent_change() + return 0 + + return 1 + +/datum/reagents/proc/has_reagent(var/reagent, var/amount = -1) + + for(var/A in reagent_list) + var/datum/reagent/R = A + if (R.id == reagent) + if(!amount) return R + else + if(R.volume >= amount) return R + else return 0 + + return 0 + +/datum/reagents/proc/get_reagent_amount(var/reagent) + for(var/A in reagent_list) + var/datum/reagent/R = A + if (R.id == reagent) + return R.volume + + return 0 + +/datum/reagents/proc/get_reagents() + var/res = "" + for(var/datum/reagent/A in reagent_list) + if (res != "") res += "," + res += A.name + + return res + +/datum/reagents/proc/remove_all_type(var/reagent_type, var/amount, var/strict = 0, var/safety = 1) // Removes all reagent of X type. @strict set to 1 determines whether the childs of the type are included. + if(!isnum(amount)) return 1 + + var/has_removed_reagent = 0 + + for(var/datum/reagent/R in reagent_list) + var/matches = 0 + // Switch between how we check the reagent type + if(strict) + if(R.type == reagent_type) + matches = 1 + else + if(istype(R, reagent_type)) + matches = 1 + // We found a match, proceed to remove the reagent. Keep looping, we might find other reagents of the same type. + if(matches) + // Have our other proc handle removement + has_removed_reagent = remove_reagent(R.id, amount, safety) + + return has_removed_reagent + +//two helper functions to preserve data across reactions (needed for xenoarch) +/datum/reagents/proc/get_data(var/reagent_id) + for(var/datum/reagent/D in reagent_list) + if(D.id == reagent_id) + //world << "proffering a data-carrying reagent ([reagent_id])" + return D.data + +/datum/reagents/proc/set_data(var/reagent_id, var/new_data) + for(var/datum/reagent/D in reagent_list) + if(D.id == reagent_id) + //world << "reagent data set ([reagent_id])" + D.data = new_data + +/datum/reagents/proc/delete() + for(var/datum/reagent/R in reagent_list) + R.holder = null + if(my_atom) + my_atom.reagents = null + +/datum/reagents/proc/copy_data(var/datum/reagent/current_reagent) + if (current_reagent.id == "paint") return current_reagent.color + if (!current_reagent || !current_reagent.data) return null + if (!istype(current_reagent.data, /list)) return current_reagent.data + + var/list/trans_data = current_reagent.data.Copy() + + // We do this so that introducing a virus to a blood sample + // doesn't automagically infect all other blood samples from + // the same donor. + // + // Technically we should probably copy all data lists, but + // that could possibly eat up a lot of memory needlessly + // if most data lists are read-only. + if (trans_data["virus2"]) + var/list/v = trans_data["virus2"] + trans_data["virus2"] = v.Copy() + + return trans_data + /////////////////////////////////////////////////////////////////////////////////// // Convenience proc to create a reagents holder for an atom // Max vol is maximum volume of holder -atom/proc/create_reagents(var/max_vol) +/atom/proc/create_reagents(var/max_vol) reagents = new/datum/reagents(max_vol) reagents.my_atom = src