Files
Bubberstation/code/game/objects/objs.dm
Ryll Ryll 93cb4cddc4 Execution/gunpoint shots work properly with pellet clouds, buffs piercing wounds a bit (#52995)
Made a few changes to gunpoints and some related mechanics while tidying up and adding docs. Here's a quick list

-Execution (point blank help intent shot aimed at the mouth) and gunpoint shots now apply their bonuses to every pellet fired rather than only the first, generally making them gushier. They also buff wound power as well as damage
-You can no longer punch yourself while holding someone up to trigger the charged shot
-You can no longer purposely fail executions and gunpoints with pax or whatever to endlessly spike the damage in a loaded round
-Attacks with extremely high wounding power can now outright dismember limbs regardless of mangled status. The threshold is high enough that it mostly applies for admin edited weapons or execution shots with shotguns (or people with the frail quirk!)
-Piercing wounds make further wounds a bit easier to apply to give them a bit more power
-Hellguns now cost 2250 credits instead of 2000 to make them a bit harder for random crew to get
-Adds special bouncy L6 rounds for admins to use to bounce off anything and everything en-masse

Also as a minor note

Projectiles with no trajectory (meaning they were likely spawned in manually) now qdel themselves on bumping something, rather than runtiming helplessly
2020-08-26 17:22:59 -07:00

371 lines
12 KiB
Plaintext

/obj
animate_movement = SLIDE_STEPS
speech_span = SPAN_ROBOT
var/obj_flags = CAN_BE_HIT
var/set_obj_flags // ONLY FOR MAPPING: Sets flags from a string list, handled in Initialize. Usage: set_obj_flags = "EMAGGED;!CAN_BE_HIT" to set EMAGGED and clear CAN_BE_HIT.
var/damtype = BRUTE
var/force = 0
/// How good a given object is at causing wounds on carbons. Higher values equal better shots at creating serious wounds.
var/wound_bonus = 0
/// If this attacks a human with no wound armor on the affected body part, add this to the wound mod. Some attacks may be significantly worse at wounding if there's even a slight layer of armor to absorb some of it vs bare flesh
var/bare_wound_bonus = 0
var/datum/armor/armor
var/obj_integrity //defaults to max_integrity
var/max_integrity = 500
var/integrity_failure = 0 //0 if we have no special broken behavior, otherwise is a percentage of at what point the obj breaks. 0.5 being 50%
///Damage under this value will be completely ignored
var/damage_deflection = 0
var/resistance_flags = NONE // INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ON_FIRE | UNACIDABLE | ACID_PROOF
var/acid_level = 0 //how much acid is on that obj
var/persistence_replacement //have something WAY too amazing to live to the next round? Set a new path here. Overuse of this var will make me upset.
var/current_skin //Has the item been reskinned?
var/list/unique_reskin //List of options to reskin.
// Access levels, used in modules\jobs\access.dm
var/list/req_access
var/req_access_txt = "0"
var/list/req_one_access
var/req_one_access_txt = "0"
/// Custom fire overlay icon
var/custom_fire_overlay
var/renamedByPlayer = FALSE //set when a player uses a pen on a renamable object
var/drag_slowdown // Amont of multiplicative slowdown applied if pulled. >1 makes you slower, <1 makes you faster.
vis_flags = VIS_INHERIT_PLANE //when this be added to vis_contents of something it inherit something.plane, important for visualisation of obj in openspace.
/obj/vv_edit_var(vname, vval)
if(vname == NAMEOF(src, obj_flags))
if ((obj_flags & DANGEROUS_POSSESSION) && !(vval & DANGEROUS_POSSESSION))
return FALSE
return ..()
/obj/Initialize()
if (islist(armor))
armor = getArmor(arglist(armor))
else if (!armor)
armor = getArmor()
else if (!istype(armor, /datum/armor))
stack_trace("Invalid type [armor.type] found in .armor during /obj Initialize()")
if(obj_integrity == null)
obj_integrity = max_integrity
. = ..() //Do this after, else mat datums is mad.
if (set_obj_flags)
var/flagslist = splittext(set_obj_flags,";")
var/list/string_to_objflag = GLOB.bitfields["obj_flags"]
for (var/flag in flagslist)
if(flag[1] == "!")
flag = copytext(flag, length(flag[1]) + 1) // Get all but the initial !
obj_flags &= ~string_to_objflag[flag]
else
obj_flags |= string_to_objflag[flag]
if((obj_flags & ON_BLUEPRINTS) && isturf(loc))
var/turf/T = loc
T.add_blueprints_preround(src)
/obj/Destroy(force=FALSE)
if(!ismachinery(src))
STOP_PROCESSING(SSobj, src) // TODO: Have a processing bitflag to reduce on unnecessary loops through the processing lists
SStgui.close_uis(src)
. = ..()
/obj/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
..()
if(obj_flags & FROZEN)
visible_message("<span class='danger'>[src] shatters into a million pieces!</span>")
qdel(src)
/obj/assume_air(datum/gas_mixture/giver)
if(loc)
return loc.assume_air(giver)
else
return null
/obj/remove_air(amount)
if(loc)
return loc.remove_air(amount)
else
return null
/obj/return_air()
if(loc)
return loc.return_air()
else
return null
/obj/proc/handle_internal_lifeform(mob/lifeform_inside_me, breath_request)
//Return: (NONSTANDARD)
// null if object handles breathing logic for lifeform
// datum/air_group to tell lifeform to process using that breath return
//DEFAULT: Take air from turf to give to have mob process
if(breath_request>0)
var/datum/gas_mixture/environment = return_air()
var/breath_percentage = BREATH_VOLUME / environment.return_volume()
return remove_air(environment.total_moles() * breath_percentage)
else
return null
/obj/proc/updateUsrDialog()
if((obj_flags & IN_USE) && !(obj_flags & USES_TGUI))
var/is_in_use = FALSE
var/list/nearby = viewers(1, src)
for(var/mob/M in nearby)
if ((M.client && M.machine == src))
is_in_use = TRUE
ui_interact(M)
if(issilicon(usr) || isAdminGhostAI(usr))
if (!(usr in nearby))
if (usr.client && usr.machine==src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh.
is_in_use = TRUE
ui_interact(usr)
// check for TK users
if(ishuman(usr))
var/mob/living/carbon/human/H = usr
if(!(usr in nearby))
if(usr.client && usr.machine==src)
if(H.dna.check_mutation(TK))
is_in_use = TRUE
ui_interact(usr)
if (is_in_use)
obj_flags |= IN_USE
else
obj_flags &= ~IN_USE
/obj/proc/updateDialog(update_viewers = TRUE,update_ais = TRUE)
// Check that people are actually using the machine. If not, don't update anymore.
if(obj_flags & IN_USE)
var/is_in_use = FALSE
if(update_viewers)
for(var/mob/M in viewers(1, src))
if ((M.client && M.machine == src))
is_in_use = TRUE
src.interact(M)
var/ai_in_use = FALSE
if(update_ais)
ai_in_use = AutoUpdateAI(src)
if(update_viewers && update_ais) //State change is sure only if we check both
if(!ai_in_use && !is_in_use)
obj_flags &= ~IN_USE
/obj/attack_ghost(mob/user)
. = ..()
if(.)
return
ui_interact(user)
/mob/proc/unset_machine()
if(machine)
machine.on_unset_machine(src)
machine = null
//called when the user unsets the machine.
/atom/movable/proc/on_unset_machine(mob/user)
return
/mob/proc/set_machine(obj/O)
if(src.machine)
unset_machine()
src.machine = O
if(istype(O))
O.obj_flags |= IN_USE
/obj/item/proc/updateSelfDialog()
var/mob/M = src.loc
if(istype(M) && M.client && M.machine == src)
src.attack_self(M)
/obj/singularity_pull(S, current_size)
..()
if(move_resist == INFINITY)
return
if(!anchored || current_size >= STAGE_FIVE)
step_towards(src,S)
/obj/get_dumping_location(datum/component/storage/source,mob/user)
return get_turf(src)
/obj/proc/CanAStarPass()
. = !density
/obj/proc/check_uplink_validity()
return 1
/obj/vv_get_dropdown()
. = ..()
VV_DROPDOWN_OPTION("", "---")
VV_DROPDOWN_OPTION(VV_HK_MASS_DEL_TYPE, "Delete all of type")
VV_DROPDOWN_OPTION(VV_HK_OSAY, "Object Say")
VV_DROPDOWN_OPTION(VV_HK_ARMOR_MOD, "Modify armor values")
/obj/vv_do_topic(list/href_list)
if(!(. = ..()))
return
if(href_list[VV_HK_OSAY])
if(check_rights(R_FUN, FALSE))
usr.client.object_say(src)
if(href_list[VV_HK_ARMOR_MOD])
var/list/pickerlist = list()
var/list/armorlist = armor.getList()
for (var/i in armorlist)
pickerlist += list(list("value" = armorlist[i], "name" = i))
var/list/result = presentpicker(usr, "Modify armor", "Modify armor: [src]", Button1="Save", Button2 = "Cancel", Timeout=FALSE, inputtype = "text", values = pickerlist)
if (islist(result))
if (result["button"] != 2) // If the user pressed the cancel button
// text2num conveniently returns a null on invalid values
armor = armor.setRating(melee = text2num(result["values"][MELEE]),\
bullet = text2num(result["values"][BULLET]),\
laser = text2num(result["values"][LASER]),\
energy = text2num(result["values"][ENERGY]),\
bomb = text2num(result["values"][BOMB]),\
bio = text2num(result["values"][BIO]),\
rad = text2num(result["values"][RAD]),\
fire = text2num(result["values"][FIRE]),\
acid = text2num(result["values"][ACID]))
log_admin("[key_name(usr)] modified the armor on [src] ([type]) to melee: [armor.melee], bullet: [armor.bullet], laser: [armor.laser], energy: [armor.energy], bomb: [armor.bomb], bio: [armor.bio], rad: [armor.rad], fire: [armor.fire], acid: [armor.acid]")
message_admins("<span class='notice'>[key_name_admin(usr)] modified the armor on [src] ([type]) to melee: [armor.melee], bullet: [armor.bullet], laser: [armor.laser], energy: [armor.energy], bomb: [armor.bomb], bio: [armor.bio], rad: [armor.rad], fire: [armor.fire], acid: [armor.acid]</span>")
if(href_list[VV_HK_MASS_DEL_TYPE])
if(check_rights(R_DEBUG|R_SERVER))
var/action_type = alert("Strict type ([type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel")
if(action_type == "Cancel" || !action_type)
return
if(alert("Are you really sure you want to delete all objects of type [type]?",,"Yes","No") != "Yes")
return
if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes")
return
var/O_type = type
switch(action_type)
if("Strict type")
var/i = 0
for(var/obj/Obj in world)
if(Obj.type == O_type)
i++
qdel(Obj)
CHECK_TICK
if(!i)
to_chat(usr, "No objects of this type exist")
return
log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) ")
message_admins("<span class='notice'>[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted) </span>")
if("Type and subtypes")
var/i = 0
for(var/obj/Obj in world)
if(istype(Obj,O_type))
i++
qdel(Obj)
CHECK_TICK
if(!i)
to_chat(usr, "No objects of this type exist")
return
log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) ")
message_admins("<span class='notice'>[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted) </span>")
/obj/examine(mob/user)
. = ..()
if(obj_flags & UNIQUE_RENAME)
. += "<span class='notice'>Use a pen on it to rename it or change its description.</span>"
if(unique_reskin && !current_skin)
. += "<span class='notice'>Alt-click it to reskin it.</span>"
/obj/AltClick(mob/user)
. = ..()
if(unique_reskin && !current_skin && user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY))
reskin_obj(user)
/**
* Reskins object based on a user's choice
*
* Arguments:
* * M The mob choosing a reskin option
*/
/obj/proc/reskin_obj(mob/M)
if(!LAZYLEN(unique_reskin))
return
var/list/items = list()
for(var/reskin_option in unique_reskin)
var/image/item_image = image(icon = src.icon, icon_state = unique_reskin[reskin_option])
items += list("[reskin_option]" = item_image)
sortList(items)
var/pick = show_radial_menu(M, src, items, custom_check = CALLBACK(src, .proc/check_reskin_menu, M), radius = 38, require_near = TRUE)
if(!pick)
return
if(!unique_reskin[pick])
return
current_skin = pick
icon_state = unique_reskin[pick]
to_chat(M, "[src] is now skinned as '[pick].'")
/**
* Checks if we are allowed to interact with a radial menu for reskins
*
* Arguments:
* * user The mob interacting with the menu
*/
/obj/proc/check_reskin_menu(mob/user)
if(QDELETED(src))
return FALSE
if(current_skin)
return FALSE
if(!istype(user))
return FALSE
if(user.incapacitated())
return FALSE
return TRUE
/obj/analyzer_act(mob/living/user, obj/item/I)
if(atmosanalyzer_scan(user, src))
return TRUE
return ..()
/obj/proc/plunger_act(obj/item/plunger/P, mob/living/user, reinforced)
return
// Should move all contained objects to it's location.
/obj/proc/dump_contents()
CRASH("Unimplemented.")
/obj/handle_ricochet(obj/projectile/P)
. = ..()
if(. && receive_ricochet_damage_coeff)
take_damage(P.damage * receive_ricochet_damage_coeff, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration) // pass along receive_ricochet_damage_coeff damage to the structure for the ricochet
/obj/update_overlays()
. = ..()
if(acid_level)
. += GLOB.acid_overlay
if(resistance_flags & ON_FIRE)
. += custom_fire_overlay ? custom_fire_overlay : GLOB.fire_overlay
/// Handles exposing an object to reagents.
/obj/expose_reagents(list/reagents, datum/reagents/source, methods=TOUCH, volume_modifier=1, show_message=TRUE)
if((. = ..()) & COMPONENT_NO_EXPOSE_REAGENTS)
return
for(var/reagent in reagents)
var/datum/reagent/R = reagent
. |= R.expose_obj(src, reagents[R])