Merge remote-tracking branch 'remotes/upstream/master' into syndiborg_fix

This commit is contained in:
Xhuis
2015-09-28 07:15:49 -04:00
43 changed files with 408 additions and 143 deletions
+51 -49
View File
@@ -3,46 +3,11 @@
/world/IsBanned(key,address,computer_id)
if (!key || !address || !computer_id)
log_access("Failed Login (invalid data): [key] [address]-[computer_id]")
return list("reason"="invalid login data", "desc"="Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]' If you continue to get this error, please restart byond or contact byond support.")
if(ckey(key) in admin_datums)
//It has proven to be a bad idea to make admins completely immune to bans, making them have to wait for someone with daemon access
//to add a daemon ban to finally stop them. Admin tempbans and admin permabans are special, high-level ban types, which are there to help
//deal with rogue admins quicker. If admin tempbans or admin permabans are ever needed, it should be consider a big deal. The same applies if
//admin bans are ever abused. This ban type does NOT check for IP or Computer ID. The reason for this is so a player cannot find/steal an admin's
//computer id, set it on his computer, get himself banned, resulting in the admin getting banned aswell. - this happens to also be the reason why
//admins were immune to bans in the first place.
if(!config.ban_legacy_system)
var/ckeytext = ckey(key)
if(!establish_db_connection())
world.log << "Ban database connection failure. Admin [ckeytext] not checked"
diary << "Ban database connection failure. Admin [ckeytext] not checked"
return
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM [format_table_name("ban")] WHERE (ckey = '[ckeytext]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
query.Execute()
while(query.NextRow())
var/pckey = query.item[1]
//var/pip = query.item[2]
//var/pcid = query.item[3]
var/ackey = query.item[4]
var/reason = query.item[5]
var/expiration = query.item[6]
var/duration = query.item[7]
var/bantime = query.item[8]
var/bantype = query.item[9]
var/expires = ""
if(text2num(duration) > 0)
expires = " The ban is for [duration] minutes and expires on [expiration] (server time)."
var/desc = "\nReason: You, or another user of this computer or connection ([pckey]) is banned from playing here. The ban reason is:\n[reason]\nThis ban was applied by [ackey] on [bantime], [expires]"
return list("reason"="[bantype]", "desc"="[desc]")
return ..()
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]'. (If you continue to get this error, please restart byond or contact byond support.)")
var/admin = 0
var/ckey = ckey(key)
if((ckey in admin_datums) || (ckey in deadmins))
admin = 1
//Guest Checking
if(IsGuestKey(key))
@@ -54,7 +19,7 @@
return list("reason"="guest", "desc"="\nReason: Sorry but the server is currently not accepting connections from never before seen players or guests. If you have played on this server with a byond account before, please log in to the byond account you have played from.")
//Population Cap Checking
if(config.extreme_popcap && living_player_count() >= config.extreme_popcap && !(ckey(key) in admin_datums))
if(config.extreme_popcap && living_player_count() >= config.extreme_popcap && !admin)
log_access("Failed Login: [key] - Population cap reached")
return list("reason"="popcap", "desc"= "\nReason: [config.extreme_popcap_message]")
@@ -63,10 +28,13 @@
//Ban Checking
. = CheckBan( ckey(key), computer_id, address )
if(.)
log_access("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]")
return .
return ..() //default pager ban stuff
if (admin)
log_admin("The admin [key] has been allowed to bypass a matching ban on [.["key"]]")
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching ban on [.["key"]]</span>")
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching ban on [.["key"]]</span>")
else
log_access("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]")
return .
else
@@ -85,7 +53,7 @@
if(computer_id)
cidquery = " OR computerid = '[computer_id]' "
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM [format_table_name("ban")] WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM [format_table_name("ban")] WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND isnull(unbanned)")
query.Execute()
@@ -99,12 +67,46 @@
var/duration = query.item[7]
var/bantime = query.item[8]
var/bantype = query.item[9]
if (bantype == "ADMIN_PERMABAN" || bantype == "ADMIN_TEMPBAN")
//admin bans MUST match on ckey to prevent cid-spoofing attacks
// as well as dynamic ip abuse
if (pckey != ckey)
continue
if (admin)
if (bantype == "ADMIN_PERMABAN" || bantype == "ADMIN_TEMPBAN")
log_admin("The admin [key] is admin banned, and has been disallowed access")
message_admins("<span class='adminnotice'>The admin [key] is admin banned, and has been disallowed access</span>")
else
log_admin("The admin [key] has been allowed to bypass a matching ban on [pckey]")
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching ban on [pckey]</span>")
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching ban on [pckey]</span>")
continue
var/expires = ""
if(text2num(duration) > 0)
expires = " The ban is for [duration] minutes and expires on [expiration] (server time)."
else
expires = " The is a permanent ban."
var/desc = "\nReason: You, or another user of this computer or connection ([pckey]) is banned from playing here. The ban reason is:\n[reason]\nThis ban was applied by [ackey] on [bantime], [expires]"
return list("reason"="[bantype]", "desc"="[desc]")
return ..() //default pager ban stuff
. = list("reason"="[bantype]", "desc"="[desc]")
log_access("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]")
return .
. = ..() //default pager ban stuff
if (.)
//byond will not trigger isbanned() for "global" host bans,
//ie, ones where the "apply to this game only" checkbox is not checked (defaults to not checked)
//So it's safe to let admins walk thru host/sticky bans here
if (admin)
log_admin("The admin [key] has been allowed to bypass a matching host/sticky ban")
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching host/sticky ban</span>")
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching host/sticky ban</span>")
return null
else
log_access("Failed Login: [key] [computer_id] [address] - Banned [.["message"]]")
return .
+2 -1
View File
@@ -66,7 +66,8 @@ var/list/admin_verbs_ban = list(
/client/proc/unban_panel,
/client/proc/jobbans,
/client/proc/unjobban_panel,
/client/proc/DB_ban_panel
/client/proc/DB_ban_panel,
/client/proc/stickybanpanel
)
var/list/admin_verbs_sounds = list(
/client/proc/play_local_sound,
+204
View File
@@ -0,0 +1,204 @@
/datum/admins/proc/stickyban(action,data)
if(!check_rights(R_BAN))
return
switch (action)
if ("show")
stickyban_show()
if ("add")
var/list/ban = list()
var/ckey
ban["admin"] = usr.key
ban["type"] = list("sticky")
ban["reason"] = "(InGameBan)([usr.key])" //this will be displayed in dd only
if (data["ckey"])
ckey = ckey(data["ckey"])
else
ckey = input(usr,"Ckey","Ckey","") as text|null
if (!ckey)
return
ckey = ckey(ckey)
if (get_stickyban_from_ckey(ckey))
usr << "<span class='adminnotice'>Error: Can not add a stickyban: User already has a current sticky ban</span>"
if (data["reason"])
ban["message"] = data["reason"]
else
var/reason = input(usr,"Reason","Reason","Ban Evasion") as text|null
if (!reason)
return
ban["message"] = "[reason]"
world.SetConfig("ban",ckey,list2stickyban(ban))
log_admin("[key_name(usr)] has stickybanned [ckey].\nReason: [ban["message"]]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has stickybanned [ckey].\nReason: [ban["message"]]</span>")
if ("remove")
if (!data["ckey"])
return
var/ckey = data["ckey"]
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
usr << "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>"
return
if (alert("Are you sure you want to remove the sticky ban on [ckey]?","Are you sure","Yes","No") == "No")
return
if (!get_stickyban_from_ckey(ckey))
usr << "<span class='adminnotice'>Error: The ban disappeared.</span>"
return
world.SetConfig("ban",ckey, null)
log_admin("[key_name(usr)] removed [ckey]'s stickyban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed [ckey]'s stickyban</span>")
if ("remove_alt")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (!data["alt"])
return
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
usr << "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>"
return
var/found = 0
//we have to do it this way because byond keeps the case in its sticky ban matches WHY!!!
for (var/key in ban["keys"])
if (ckey(key) == alt)
found = 1
break
if (!found)
usr << "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>"
return
if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
usr << "<span class='adminnotice'>Error: The ban disappeared.</span>"
return
found = 0
for (var/key in ban["keys"])
if (ckey(key) == alt)
ban["keys"] -= key
found = 1
break
if (!found)
usr << "<span class='adminnotice'>Error: [alt] link to [ckey]'s sticky ban disappeared.</span>"
return
world.SetConfig("ban",ckey,list2stickyban(ban))
log_admin("[key_name(usr)] has disassociated [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has disassociated [alt] from [ckey]'s sticky ban</span>")
if ("edit")
if (!data["ckey"])
return
var/ckey = data["ckey"]
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
usr << "<span class='adminnotice'>Error: No sticky ban for [ckey] found!"
return
var/oldreason = ban["message"]
var/reason = input(usr,"Reason","Reason","[ban["message"]]") as text|null
if (!reason || reason == oldreason)
return
//we have to do this again incase something changed while we waited for input
ban = get_stickyban_from_ckey(ckey)
if (!ban)
usr << "<span class='adminnotice'>Error: The ban disappeared.</span>"
return
ban["message"] = "[reason]"
world.SetConfig("ban",ckey,list2stickyban(ban))
log_admin("[key_name(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]</span>")
/datum/admins/proc/stickyban_gethtml(ckey, ban)
. = "<a href='?_src_=holder;stickyban=remove&ckey=[ckey]'>\[-\]</a><b>[ckey]</b><br />"
. += "[ban["message"]] <b><a href='?_src_=holder;stickyban=edit&ckey=[ckey]'>\[Edit\]</a></b><br />"
if (ban["admin"])
. += "[ban["admin"]]<br />"
else
. += "LEGACY<br />"
. += "Caught keys<br />\n<ol>"
for (var/key in ban["keys"])
if (ckey(key) == ckey)
continue
. += "<li><a href='?_src_=holder;stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]</li>"
. += "</ol>\n"
/datum/admins/proc/stickyban_show()
if(!check_rights(R_BAN))
return
var/list/bans = world.GetConfig("ban")
var/banhtml = ""
for(var/key in bans)
var/ckey = ckey(key)
var/ban = stickyban2list(world.GetConfig("ban",key))
banhtml += "<br /><hr />\n"
banhtml += stickyban_gethtml(ckey,ban)
var/html = {"
<head>
<title>Sticky Bans</title>
</head>
<body>
<h2>All Sticky Bans:</h2> <a href='?_src_=holder;stickyban=add'>\[+\]</a><br>
[banhtml]
</body>
"}
usr << browse(html,"window=stickybans;size=700x400")
/proc/get_stickyban_from_ckey(var/ckey)
if (!ckey)
return null
ckey = ckey(ckey)
. = null
for (var/key in world.GetConfig("ban"))
if (ckey(key) == ckey)
. = stickyban2list(world.GetConfig("ban",key))
break
/proc/stickyban2list(var/ban)
if (!ban)
return null
. = params2list(ban)
.["keys"] = text2list(.["keys"], ",")
.["type"] = text2list(.["type"], ",")
.["IP"] = text2list(.["IP"], ",")
.["computer_id"] = text2list(.["computer_id"], ",")
/proc/list2stickyban(var/list/ban)
if (!ban || !islist(ban))
return null
. = ban.Copy()
if (.["keys"])
.["keys"] = list2text(.["keys"], ",")
if (.["type"])
.["type"] = list2text(.["type"], ",")
if (.["IP"])
.["IP"] = list2text(.["IP"], ",")
if (.["computer_id"])
.["computer_id"] = list2text(.["computer_id"], ",")
. = list2params(.)
/client/proc/stickybanpanel()
set name = "Sticky Ban Panel"
set category = "Admin"
if (!holder)
return
holder.stickyban_show()
+3 -1
View File
@@ -23,6 +23,8 @@
message_admins("[key_name_admin(usr)] Rejected [C.key]'s admin help. [C.key]'s Adminhelp verb has been returned to them")
log_admin("[key_name(usr)] Rejected [C.key]'s admin help")
else if(href_list["stickyban"])
stickyban(href_list["stickyban"],href_list)
else if(href_list["makeAntag"])
if (!ticker.mode)
@@ -2135,4 +2137,4 @@
O.ears = text2path(href_list["outfit_ears"])
custom_outfits.Add(O)
message_admins("[key_name(usr)] created \"[O.name]\" outfit!")
message_admins("[key_name(usr)] created \"[O.name]\" outfit!")
+1
View File
@@ -1,3 +1,4 @@
/client
////////////////
//ADMIN THINGS//
+9 -3
View File
@@ -175,13 +175,19 @@ var/next_external_rsc = 0
src.changes()
else
winset(src, "rpane.changelogb", "background-color=#eaeaea;font-style=bold")
if (ckey in clientmessages)
for (var/message in clientmessages[ckey])
src << message
clientmessages.Remove(ckey)
if (config && config.autoconvert_notes)
convert_notes_sql(ckey)
//////////////
//DISCONNECT//
//////////////
//////////////
//DISCONNECT//
//////////////
/client/Del()
if(holder)
adminGreet(1)
+9
View File
@@ -0,0 +1,9 @@
var/list/clientmessages = list()
proc/addclientmessage(var/ckey, var/message)
ckey = ckey(ckey)
if (!ckey || !message)
return
if (!(ckey in clientmessages))
clientmessages[ckey] = list()
clientmessages[ckey] += message
+3 -3
View File
@@ -37,9 +37,9 @@
if(istype(C, /mob/living/carbon/human))
var/mob/living/carbon/human/H = C
if(prob(5))
H.irradiate(rand(100, 160))
H.rad_act(rand(100, 160))
else
H.irradiate(rand(15, 75))
H.rad_act(rand(15, 75))
if(prob(25))
if(prob(75))
randmutb(H)
@@ -49,7 +49,7 @@
else if(istype(C, /mob/living/carbon/monkey))
var/mob/living/carbon/monkey/M = C
M.irradiate(rand(15, 75))
M.rad_act(rand(15, 75))
/datum/round_event/radiation_storm/end()
@@ -684,7 +684,7 @@
if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && unEquip(hand))
step_towards(hand, src)
src << "<span class='warning'>\The [S] pulls \the [hand] from your grip!</span>"
irradiate(current_size * 3)
rad_act(current_size * 3)
if(mob_negates_gravity())
return
..()
@@ -122,7 +122,7 @@ datum/species/human/spec_death(gibbed, mob/living/carbon/human/H)
switch(proj_type)
if(/obj/item/projectile/energy/floramut)
if(prob(15))
H.irradiate(rand(30,80))
H.rad_act(rand(30,80))
H.Weaken(5)
H.visible_message("<span class='warning'>[H] writhes in pain as \his vacuoles boil.</span>", "<span class='userdanger'>You writhe in pain as your vacuoles boil!</span>", "<span class='italics'>You hear the crunching of leaves.</span>")
if(prob(80))
+1 -4
View File
@@ -293,7 +293,4 @@
if(stat || paralysis || stunned || weakened || restrained())
return 1
/mob/living/proc/irradiate(amount)
if(amount)
var/blocked = run_armor_check(null, "rad", "Your clothes feel warm", "Your clothes feel warm")
apply_effect(amount, IRRADIATE, blocked)
//Looking for irradiate()? It's been moved to radiation.dm under the rad_act() for mobs.
+2 -2
View File
@@ -74,6 +74,7 @@ var/list/ai_list = list()
var/obj/machinery/camera/portable/builtInCamera
/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/device/mmi/B, var/safety = 0)
..()
rename_self("ai", 1)
name = real_name
anchored = 1
@@ -144,8 +145,7 @@ var/list/ai_list = list()
builtInCamera = new /obj/machinery/camera/portable(src)
builtInCamera.network = list("SS13")
..()
return
/mob/living/silicon/ai/Destroy()
ai_list -= src
@@ -94,14 +94,14 @@
return
/mob/living/simple_animal/hostile/proc/PickTarget(list/Targets)//Step 3, pick amongst the possible, attackable targets
if(!Targets.len)//We didnt find nothin!
return
if(target != null)//If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets
for(var/atom/A in Targets)
var/target_dist = get_dist(src, target)
var/possible_target_distance = get_dist(src, A)
if(target_dist < possible_target_distance)
Targets -= A
if(!Targets.len)//We didnt find nothin!
return
var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random
return chosen_target
+1 -2
View File
@@ -350,8 +350,7 @@ var/const/GRAV_NEEDS_WRENCH = 3
/obj/machinery/gravity_generator/main/proc/pulse_radiation()
for(var/mob/living/L in view(7, src))
L.irradiate(20)
radiation_pulse(get_turf(src), 3, 7, 20, 1)
// Shake everyone on the z level to let them know that gravity was enagaged/disenagaged.
/obj/machinery/gravity_generator/main/proc/shake_everyone()
@@ -56,7 +56,7 @@
/obj/effect/accelerated_particle/proc/toxmob(mob/living/M)
M.irradiate(energy*6)
M.rad_act(energy*6)
M.updatehealth()
return
@@ -364,13 +364,13 @@
radiation += round((energy-150)/10,1)
radiationmin = round((radiation/5),1)
for(var/mob/living/M in view(toxrange, src.loc))
M.irradiate(rand(radiationmin,radiation))
M.rad_act(rand(radiationmin,radiation))
/obj/singularity/proc/combust_mobs()
for(var/mob/living/carbon/C in orange(20, src))
C.visible_message("<span class='warning'>[C]'s skin bursts into flame!</span>", \
"<span class='boldannounce'>You feel an inner fire as your skin is suddenly covered in fire!</span>")
"<span class='userdanger'>You feel an inner fire as your skin bursts into flames!</span>")
C.adjust_fire_stacks(5)
C.IgniteMob()
return
@@ -119,7 +119,7 @@
var/mob/living/carbon/human/H = mob
H.hallucination += max(50, min(300, DETONATION_HALLUCINATION * sqrt(1 / (get_dist(mob, src) + 1)) ) )
var/rads = DETONATION_RADS * sqrt( 1 / (get_dist(mob, src) + 1) )
mob.irradiate(rads)
mob.rad_act(rads)
explode()
@@ -181,7 +181,7 @@
for(var/mob/living/l in range(src, round((power / 100) ** 0.25)))
var/rads = (power / 10) * sqrt( 1 / max(get_dist(l, src),1) )
l.irradiate(rads)
l.rad_act(rads)
power -= (power/500)**3
@@ -259,7 +259,7 @@
playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, 1)
user.irradiate(150)
radiation_pulse(get_turf(src), 1, 1, 150, 1)
/obj/machinery/power/supermatter_shard/Bumped(atom/AM as mob|obj)
@@ -292,9 +292,8 @@
power += 200
//Some poor sod got eaten, go ahead and irradiate people nearby.
radiation_pulse(get_turf(src), 4, 10, 500, 1)
for(var/mob/living/L in range(10))
var/rads = 500 * sqrt( 1 / (get_dist(L, src) + 1) )
L.irradiate(rads)
investigate_log("has irradiated [L] after consuming [AM].", "supermatter")
if(L in view())
L.show_message("<span class='danger'>As \the [src] slowly stops resonating, you find your skin covered in new radiation burns.</span>", 1,\
+5 -4
View File
@@ -138,7 +138,8 @@
var/mob/living/M = user
if (M.disabilities & CLUMSY && prob(40))
user << "<span class='userdanger'>You shoot yourself in the foot with \the [src]!</span>"
process_fire(user,user,0,params)
var/shot_leg = pick("l_leg", "r_leg")
process_fire(user,user,0,params, zone_override = shot_leg)
M.drop_item()
return
@@ -182,7 +183,7 @@
return 0
/obj/item/weapon/gun/proc/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params)
/obj/item/weapon/gun/proc/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override)
add_fingerprint(user)
if(semicd)
@@ -200,7 +201,7 @@
if( i>1 && !(src in get_both_hands(user))) //for burst firing
break
if(chambered)
if(!chambered.fire(target, user, params, , suppressed))
if(!chambered.fire(target, user, params, , suppressed, zone_override))
shoot_with_empty_chamber(user)
break
else
@@ -216,7 +217,7 @@
sleep(fire_delay)
else
if(chambered)
if(!chambered.fire(target, user, params, , suppressed))
if(!chambered.fire(target, user, params, , suppressed, zone_override))
shoot_with_empty_chamber(user)
return
else
@@ -90,11 +90,11 @@
switch(fail_tick)
if(0 to 200)
fail_tick += (2*(100-reliability))
M.irradiate(40)
M.rad_act(40)
M << "<span class='userdanger'>Your [name] feels warmer.</span>"
if(201 to INFINITY)
SSobj.processing.Remove(src)
M.irradiate(80)
M.rad_act(80)
crit_fail = 1
M << "<span class='userdanger'>Your [name]'s reactor overloads!</span>"
@@ -25,6 +25,7 @@
icon = 'icons/mecha/mecha_equipment.dmi'
icon_state = "mecha_grenadelnchr"
mag_type = /obj/item/ammo_box/magazine/internal/cylinder/grenademulti
pin = /obj/item/device/firing_pin
/obj/item/weapon/gun/projectile/revolver/grenadelauncher/cyborg/attack_self()
return
+1 -3
View File
@@ -286,9 +286,7 @@
ejectItem()
if(prob(EFFECT_PROB_VERYLOW-badThingCoeff))
visible_message("<span class='danger'>[src] malfunctions, melting [exp_on] and leaking radiation!</span>")
for(var/mob/living/m in oview(1, src))
m.irradiate(25)
investigate_log("Experimentor has irradiated [m]", "experimentor") //One entry per person so we know what was irradiated.
radiation_pulse(get_turf(src), 1, 1, 25, 1)
ejectItem(TRUE)
if(prob(EFFECT_PROB_LOW-badThingCoeff))
visible_message("<span class='warning'>[src] malfunctions, spewing toxic waste!</span>")