Merge branch 'dev' of https://github.com/Baystation12/Baystation12 into 11/6/2015_neerti_breaks_everything_bay_merge

Conflicts:
	.travis.yml
	code/_helpers/lists.dm
	code/game/objects/structures/crates_lockers/closets/secure/engineering.dm
	code/global.dm
	code/modules/client/preferences.dm
	code/modules/client/preferences_savefile.dm
	code/modules/reagents/dispenser/dispenser2.dm
	polaris.dme
This commit is contained in:
Neerti
2015-11-06 17:41:18 -05:00
235 changed files with 9004 additions and 8450 deletions

View File

@@ -1,13 +1,9 @@
/*
Making Bombs with ZAS:
Make burny fire with lots of burning
Draw off 5000K gas from burny fire
Separate gas into oxygen and phoron components
Obtain phoron and oxygen tanks filled up about 50-75% with normal-temp gas
Fill rest with super hot gas from separated canisters, they should be about 125C now.
Attach to transfer valve and open. BOOM.
Get gas to react in an air tank so that it gains pressure. If it gains enough pressure, it goes boom.
The more pressure, the more boom.
If it gains pressure too slowly, it may leak or just rupture instead of exploding.
*/
//#define FIREDBG
@@ -268,16 +264,16 @@ turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
//determine how far the reaction can progress
var/reaction_limit = min(total_oxidizers*(FIRE_REACTION_FUEL_AMOUNT/FIRE_REACTION_OXIDIZER_AMOUNT), total_fuel) //stoichiometric limit
//calculate the firelevel.
var/firelevel = calculate_firelevel(total_fuel, total_oxidizers, reaction_limit)
var/firelevel_ratio = firelevel / vsc.fire_firelevel_multiplier
//vapour fuels are extremely volatile! The reaction progress is a percentage of the total fuel (similar to old zburn).)
var/gas_firelevel = calculate_firelevel(gas_fuel, total_oxidizers, reaction_limit, volume*group_multiplier) / vsc.fire_firelevel_multiplier
var/min_burn = 0.30*volume*group_multiplier/CELL_VOLUME //in moles - so that fires with very small gas concentrations burn out fast
var/gas_reaction_progress = min(max(min_burn, firelevel_ratio*gas_fuel)*FIRE_GAS_BURNRATE_MULT, gas_fuel)
var/gas_reaction_progress = min(max(min_burn, gas_firelevel*gas_fuel)*FIRE_GAS_BURNRATE_MULT, gas_fuel)
//liquid fuels are not as volatile, and the reaction progress depends on the size of the area that is burning. Limit the burn rate to a certain amount per area.
var/liquid_reaction_progress = min((firelevel_ratio*0.2 + 0.05)*fuel_area*FIRE_LIQUID_BURNRATE_MULT, liquid_fuel)
var/liquid_firelevel = calculate_firelevel(liquid_fuel, total_oxidizers, reaction_limit, 0) / vsc.fire_firelevel_multiplier
var/liquid_reaction_progress = min((liquid_firelevel*0.2 + 0.05)*fuel_area*FIRE_LIQUID_BURNRATE_MULT, liquid_fuel)
var/firelevel = (gas_fuel*gas_firelevel + liquid_fuel*liquid_firelevel)/total_fuel
var/total_reaction_progress = gas_reaction_progress + liquid_reaction_progress
var/used_fuel = min(total_reaction_progress, reaction_limit)
@@ -286,7 +282,7 @@ turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
#ifdef FIREDBG
log_debug("gas_fuel = [gas_fuel], liquid_fuel = [liquid_fuel], total_oxidizers = [total_oxidizers]")
log_debug("fuel_area = [fuel_area], total_fuel = [total_fuel], reaction_limit = [reaction_limit]")
log_debug("firelevel -> [firelevel] / [vsc.fire_firelevel_multiplier]")
log_debug("firelevel -> [firelevel] (gas: [gas_firelevel], liquid: [liquid_firelevel])")
log_debug("liquid_reaction_progress = [liquid_reaction_progress]")
log_debug("gas_reaction_progress = [gas_reaction_progress]")
log_debug("total_reaction_progress = [total_reaction_progress]")
@@ -315,13 +311,13 @@ turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
//calculate the energy produced by the reaction and then set the new temperature of the mix
temperature = (starting_energy + vsc.fire_fuel_energy_release * (used_gas_fuel + used_liquid_fuel)) / heat_capacity()
update_values()
#ifdef FIREDBG
log_debug("used_gas_fuel = [used_gas_fuel]; used_liquid_fuel = [used_liquid_fuel]; total = [used_fuel]")
log_debug("new temperature = [temperature]")
log_debug("new temperature = [temperature]; new pressure = [return_pressure()]")
#endif
update_values()
return firelevel
datum/gas_mixture/proc/check_recombustability(list/fuel_objs)
@@ -363,27 +359,31 @@ datum/gas_mixture/proc/check_recombustability(list/fuel_objs)
break
//returns a value between 0 and vsc.fire_firelevel_multiplier
/datum/gas_mixture/proc/calculate_firelevel(total_fuel, total_oxidizers, reaction_limit)
/datum/gas_mixture/proc/calculate_firelevel(total_fuel, total_oxidizers, reaction_limit, gas_volume)
//Calculates the firelevel based on one equation instead of having to do this multiple times in different areas.
var/firelevel = 0
var/total_combustables = (total_fuel + total_oxidizers)
var/active_combustables = (FIRE_REACTION_OXIDIZER_AMOUNT/FIRE_REACTION_FUEL_AMOUNT + 1)*reaction_limit
if(total_combustables > 0)
//slows down the burning when the concentration of the reactants is low
var/dampening_multiplier = min(1, reaction_limit / (total_moles/group_multiplier))
var/damping_multiplier = min(1, active_combustables / (total_moles/group_multiplier))
//weight the damping mult so that it only really brings down the firelevel when the ratio is closer to 0
damping_multiplier = 2*damping_multiplier - (damping_multiplier*damping_multiplier)
//calculates how close the mixture of the reactants is to the optimum
//fires burn better when there is more oxidizer -- too much fuel will choke them out a bit, reducing firelevel.
//fires burn better when there is more oxidizer -- too much fuel will choke the fire out a bit, reducing firelevel.
var/mix_multiplier = 1 / (1 + (5 * ((total_fuel / total_combustables) ** 2)))
#ifdef FIREDBG
ASSERT(dampening_multiplier <= 1)
ASSERT(damping_multiplier <= 1)
ASSERT(mix_multiplier <= 1)
#endif
//toss everything together -- should produce a value between 0 and fire_firelevel_multiplier
firelevel = vsc.fire_firelevel_multiplier * mix_multiplier * dampening_multiplier
firelevel = vsc.fire_firelevel_multiplier * mix_multiplier * damping_multiplier
return max( 0, firelevel)

View File

@@ -9,7 +9,8 @@ var/global/vs_control/vsc = new
var/fire_firelevel_multiplier_NAME = "Fire - Firelevel Constant"
var/fire_firelevel_multiplier_DESC = "Multiplied by the equation for firelevel, affects mainly the extingiushing of fires."
var/fire_fuel_energy_release = 397000
//Note that this parameter and the phoron heat capacity have a significant impact on TTV yield.
var/fire_fuel_energy_release = 866000 //J/mol. Adjusted to compensate for fire energy release being fixed, was 397000
var/fire_fuel_energy_release_NAME = "Fire - Fuel energy release"
var/fire_fuel_energy_release_DESC = "The energy in joule released when burning one mol of a burnable substance"

View File

@@ -59,15 +59,15 @@
//These control the speed at which fire burns
#define FIRE_GAS_BURNRATE_MULT 1
#define FIRE_LIQUID_BURNRATE_MULT 1
#define FIRE_LIQUID_BURNRATE_MULT 0.225
//If the fire is burning slower than this rate then the reaction is going too slow to be self sustaining and the fire burns itself out.
//This ensures that fires don't grind to a near-halt while still remaining active forever.
#define FIRE_GAS_MIN_BURNRATE 0.01
#define FIRE_LIQUD_MIN_BURNRATE 0.01
#define FIRE_LIQUD_MIN_BURNRATE 0.0025
//How many moles of fuel are contained within one solid/liquid fuel volume unit
#define LIQUIDFUEL_AMOUNT_TO_MOL 1 //mol/volume unit
#define LIQUIDFUEL_AMOUNT_TO_MOL 0.45 //mol/volume unit
// XGM gas flags.
#define XGM_GAS_FUEL 1

View File

@@ -82,6 +82,7 @@
// Click cooldown
#define DEFAULT_ATTACK_COOLDOWN 8 //Default timeout for aggressive actions
#define DEFAULT_QUICK_COOLDOWN 4
#define MIN_SUPPLIED_LAW_NUMBER 15
@@ -114,3 +115,13 @@
#define MOB_SMALL 10
#define MOB_TINY 5
#define MOB_MINISCULE 1
#define TINT_NONE 0
#define TINT_MODERATE 1
#define TINT_HEAVY 2
#define TINT_BLIND 3
#define FLASH_PROTECTION_REDUCED -1
#define FLASH_PROTECTION_NONE 0
#define FLASH_PROTECTION_MODERATE 1
#define FLASH_PROTECTION_MAJOR 2

21
code/_helpers/areas.dm Normal file
View File

@@ -0,0 +1,21 @@
//Takes: Area type as text string or as typepath OR an instance of the area.
//Returns: A list of all turfs in areas of that type in the world.
/proc/get_area_turfs(var/areatype, var/list/predicates)
if(!areatype) return null
if(istext(areatype)) areatype = text2path(areatype)
if(isarea(areatype))
var/area/areatemp = areatype
areatype = areatemp.type
var/list/turfs = new/list()
for(var/areapath in typesof(areatype))
var/area/A = locate(areapath)
for(var/turf/T in A.contents)
if(!predicates || all_predicates_true(T, predicates))
turfs += T
return turfs
/proc/pick_area_turf(var/areatype, var/list/predicates)
var/list/turfs = get_area_turfs(areatype, predicates)
if(turfs && turfs.len)
return pick(turfs)

View File

@@ -0,0 +1,14 @@
/proc/all_predicates_true(var/input, var/list/predicates)
for(var/i = 1 to predicates.len)
if(!call(predicates[i])(input))
return FALSE
return TRUE
/proc/any_predicate_true(var/input, var/list/predicates)
if(!predicates.len)
return TRUE
for(var/i = 1 to predicates.len)
if(call(predicates[i])(input))
return TRUE
return FALSE

View File

@@ -71,6 +71,12 @@ proc/isemptylist(list/list)
return 1
return 0
/proc/instances_of_type_in_list(var/atom/A, var/list/L)
var/instances = 0
for(var/type in L)
if(istype(A, type))
instances++
return instances
//Checks for specific paths in a list
/proc/is_path_in_list(var/atom/A, var/list/L)
for(var/path in L)
@@ -78,11 +84,11 @@ proc/isemptylist(list/list)
return 1
return 0
//Empties the list by setting the length to 0. Hopefully the elements get garbage collected
proc/clearlist(list/list)
if(istype(list))
list.len = 0
return
//Empties the list by .Cut(). Setting lenght = 0 has been confirmed to leak references.
proc/clearlist(var/list/L)
if(islist(L))
L.Cut()
//Removes any null entries from the list
proc/listclearnulls(list/list)

View File

@@ -32,3 +32,12 @@
if(!available_turfs.len)
available_turfs = start_turfs
return pick(available_turfs)
/proc/turf_contains_dense_objects(var/turf/T)
return T.contains_dense_objects()
/proc/not_turf_contains_dense_objects(var/turf/T)
return !turf_contains_dense_objects(T)
/proc/is_station_turf(var/turf/T)
return T && isStationLevel(T.z)

View File

@@ -558,7 +558,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
var/y = min(world.maxy, max(1, A.y + dy))
return locate(x,y,A.z)
//Makes sure MIDDLE is between LOW and HIGH. If not, it adjusts it. Returns the adjusted value.
//Makes sure MIDDLE is between LOW and HIGH. If not, it adjusts it. Returns the adjusted value. Lower bound takes priority.
/proc/between(var/low, var/middle, var/high)
return max(min(middle, high), low)
@@ -718,21 +718,6 @@ proc/GaussRandRound(var/sigma,var/roundto)
if(istype(N, areatype)) areas += N
return areas
//Takes: Area type as text string or as typepath OR an instance of the area.
//Returns: A list of all turfs in areas of that type of that type in the world.
/proc/get_area_turfs(var/areatype)
if(!areatype) return null
if(istext(areatype)) areatype = text2path(areatype)
if(isarea(areatype))
var/area/areatemp = areatype
areatype = areatemp.type
var/list/turfs = new/list()
for(var/area/N in world)
if(istype(N, areatype))
for(var/turf/T in N) turfs += T
return turfs
//Takes: Area type as text string or as typepath OR an instance of the area.
//Returns: A list of all atoms (objs, turfs, mobs) in areas of that type of that type in the world.
/proc/get_area_all_atoms(var/areatype)

View File

@@ -1,8 +1,33 @@
/*
=== Item Click Call Sequences ===
These are the default click code call sequences used when clicking on stuff with an item.
Atoms:
mob/ClickOn() calls the item's resolve_attackby() proc.
item/resolve_attackby() calls the target atom's attackby() proc.
Mobs:
mob/living/attackby() after checking for surgery, calls the item's attack() proc.
item/attack() generates attack logs, sets click cooldown and calls the mob's attacked_with_item() proc. If you override this, consider whether you need to set a click cooldown, play attack animations, and generate logs yourself.
mob/attacked_with_item() should then do mob-type specific stuff (like determining hit/miss, handling shields, etc) and then possibly call the item's apply_hit_effect() proc to actually apply the effects of being hit.
Item Hit Effects:
item/apply_hit_effect() can be overriden to do whatever you want. However "standard" physical damage based weapons should make use of the target mob's hit_with_weapon() proc to
avoid code duplication. This includes items that may sometimes act as a standard weapon in addition to having other effects (e.g. stunbatons on harm intent).
*/
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user)
return
//I would prefer to rename this to attack(), but that would involve touching hundreds of files.
/obj/item/proc/resolve_attackby(atom/A, mob/user)
add_fingerprint(user)
return A.attackby(src, user)
// No comment
/atom/proc/attackby(obj/item/W, mob/user)
return
@@ -12,29 +37,23 @@
visible_message("<span class='danger'>[src] has been hit by [user] with [W].</span>")
/mob/living/attackby(obj/item/I, mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(istype(I) && ismob(user))
I.attack(src, user)
if(!ismob(user))
return 0
if(can_operate(src) && do_surgery(src,user,I)) //Surgery
return 1
return I.attack(src, user, user.zone_sel.selecting)
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
// Click parameters is the params string from byond Click() code, see that documentation.
/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
return
//TODO: refactor mob attack code.
/*
Busy writing something else that I don't want to get mixed up in a general attack code, and I don't want to forget this so leaving a note here.
leave attackby() as handling the general case of "using an item on a mob"
attackby() will decide to call attacked_by() or not.
attacked_by() will be made a living level proc and handle the specific case of "attacking with an item to cause harm"
attacked_by() will then call attack() so that stunbatons and other weapons that have special attack effects can do their thing.
attacked_by() will handle hitting/missing/logging as it does now, and will call attack() to apply the attack effects (damage) instead of the other way around (as it is now).
*/
/obj/item/proc/attack(mob/living/M as mob, mob/living/user as mob, def_zone)
if(!istype(M) || (can_operate(M) && do_surgery(M,user,src))) return 0
//I would prefer to rename this attack_as_weapon(), but that would involve touching hundreds of files.
/obj/item/proc/attack(mob/living/M, mob/living/user, var/target_zone)
if(!force || (flags & NOBLUDGEON))
return 0
if(M == user && user.a_intent != I_HURT)
return 0
/////////////////////////
user.lastattacked = M
@@ -46,49 +65,22 @@ attacked_by() will handle hitting/missing/logging as it does now, and will call
msg_admin_attack("[key_name(user)] attacked [key_name(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" )
/////////////////////////
// Attacking someone with a weapon while they are neck-grabbed
if(user.a_intent == I_HURT)
for(var/obj/item/weapon/grab/G in M.grabbed_by)
if(G.assailant == user && G.state >= GRAB_NECK)
M.attack_throat(src, G, user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(M)
var/hit_zone = M.resolve_item_attack(src, user, target_zone)
if(hit_zone)
apply_hit_effect(M, user, hit_zone)
return 1
//Called when a weapon is used to make a successful melee attack on a mob. Returns the blocked result
/obj/item/proc/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone)
if(hitsound)
playsound(loc, hitsound, 50, 1, -1)
var/power = force
if(HULK in user.mutations)
power *= 2
return target.hit_with_weapon(src, user, power, hit_zone)
// TODO: needs to be refactored into a mob/living level attacked_by() proc. ~Z
user.do_attack_animation(M)
if(istype(M, /mob/living/carbon/human))
var/mob/living/carbon/human/H = M
// Handle striking to cripple.
var/dislocation_str
if(user.a_intent == I_DISARM)
dislocation_str = H.attack_joint(src, user, def_zone)
if(H.attacked_by(src, user, def_zone) && hitsound)
playsound(loc, hitsound, 50, 1, -1)
spawn(1) //ugh I hate this but I don't want to root through human attack procs to print it after this call resolves.
if(dislocation_str) user.visible_message("<span class='danger'>[dislocation_str]</span>")
return 1
return 0
else
if(attack_verb.len)
user.visible_message("<span class='danger'>[M] has been [pick(attack_verb)] with [src] by [user]!</span>")
else
user.visible_message("<span class='danger'>[M] has been attacked with [src] by [user]!</span>")
if (hitsound)
playsound(loc, hitsound, 50, 1, -1)
switch(damtype)
if("brute")
M.take_organ_damage(power)
if(prob(33)) // Added blood for whacking non-humans too
var/turf/simulated/location = get_turf(M)
if(istype(location)) location.add_blood_floor(M)
if("fire")
if (!(COLD_RESISTANCE in M.mutations))
M.take_organ_damage(0, power)
M << "Aargh it burns!"
M.updatehealth()
add_fingerprint(user)
return 1

75
code/datums/category.dm Normal file
View File

@@ -0,0 +1,75 @@
/**********************
* Category Collection *
**********************/
/datum/category_collection
var/category_group_type // The type of categories to initialize
var/list/datum/category_group/categories // The list of initialized categories
/datum/category_collection/New()
..()
categories = new()
for(var/category_type in typesof(category_group_type))
var/datum/category_group/category = category_type
if(initial(category.name))
category = new category(src)
categories += category
categories = dd_sortedObjectList(categories)
/datum/category_collection/Destroy()
for(var/category in categories)
qdel(category)
categories.Cut()
return ..()
/******************
* Category Groups *
******************/
/datum/category_group
var/name = ""
var/category_item_type // The type of items to initialize
var/list/datum/category_item/items // The list of initialized items
var/datum/category_collection/collection // The collection this group belongs to
/datum/category_group/New(var/datum/category_collection/cc)
..()
collection = cc
items = new()
for(var/item_type in typesof(category_item_type))
var/datum/category_item/item = item_type
if(initial(item.name))
item = new item(src)
items += item
// For whatever reason dd_insertObjectList(items, item) doesn't insert in the correct order
// If you change this, confirm that character setup doesn't become completely unordered.
items = dd_sortedObjectList(items)
/datum/category_group/Destroy()
for(var/item in items)
qdel(item)
items.Cut()
collection = null
return ..()
datum/category_group/dd_SortValue()
return name
/*****************
* Category Items *
*****************/
/datum/category_item
var/name = ""
var/list/datum/category_group/category // The group this item belongs to
/datum/category_item/New(var/datum/category_group/cg)
..()
category = cg
/datum/category_item/Destroy()
category = null
return ..()
datum/category_item/dd_SortValue()
return name

View File

@@ -53,9 +53,12 @@
affected_mob.updatehealth()
if(prob(40))
if(gibbed != 0) return 0
var/turf/T = find_loc(affected_mob)
gibs(T)
src.cure(0)
gibbed = 1
affected_mob:Alienize()
var/mob/living/carbon/human/H = affected_mob
if(istype(H))
var/turf/origin = find_loc(affected_mob)
gibs(origin)
H.set_species("Xenomorph [pick(list("Hunter","Sentinel","Drone"))]")
return
affected_mob.gib()

View File

@@ -41,7 +41,7 @@ var/const/AIRLOCK_WIRE_LIGHT = 2048
(A.locked ? "The door bolts have fallen!" : "The door bolts look up."),
((A.lights && haspower) ? "The door bolt lights are on." : "The door bolt lights are off!"),
((haspower) ? "The test light is on." : "The test light is off!"),
((A.backupPowerCablesCut()) ? "The backup power light is off!" : "The backup power light is on."),
((A.backup_power_lost_until) ? "The backup power light is off!" : "The backup power light is on."),
((A.aiControlDisabled==0 && !A.emagged && haspower)? "The 'AI control allowed' light is on." : "The 'AI control allowed' light is off."),
((A.safe==0 && haspower)? "The 'Check Wiring' light is on." : "The 'Check Wiring' light is off."),
((A.normalspeed==0 && haspower)? "The 'Check Timing Mechanism' light is on." : "The 'Check Timing Mechanism' light is off."),

View File

@@ -21,6 +21,9 @@
/decl/xgm_gas/phoron
id = "phoron"
name = "Phoron"
//Note that this has a significant impact on TTV yield.
//Because it is so high, any leftover phoron soaks up a lot of heat and drops the yield pressure.
specific_heat = 200 // J/(mol*K)
//Hypothetical group 14 (same as carbon), period 8 element.

View File

@@ -61,7 +61,7 @@ proc/can_process_hud(var/mob/M)
return 1
//Deletes the current HUD images so they can be refreshed with new ones.
mob/proc/handle_regular_hud_updates() //Used in the life.dm of mobs that can use HUDs.
mob/proc/handle_hud_glasses() //Used in the life.dm of mobs that can use HUDs.
if(client)
for(var/image/hud in client.images)
if(copytext(hud.icon_state,1,4) == "hud")

View File

@@ -26,6 +26,7 @@
#define ANTAG_RANDSPAWN 256 // Potentially randomly spawns due to events.
#define ANTAG_VOTABLE 512 // Can be voted as an additional antagonist before roundstart.
#define ANTAG_SET_APPEARANCE 1024 // Causes antagonists to use an appearance modifier on spawn.
#define ANTAG_RANDOM_EXCEPTED 2048 // If a game mode randomly selects antag types, antag types with this flag should be excluded.
// Globals.
var/global/list/all_antag_types = list()

View File

@@ -11,6 +11,11 @@ var/datum/antagonist/xenos/xenomorphs
welcome_text = "Hiss! You are a larval alien. Hide and bide your time until you are ready to evolve."
antaghud_indicator = "hudalien"
faction_role_text = "Xenomorph Thrall"
faction_descriptor = "Hive"
faction_welcome = "Your will is ripped away as your humanity merges with the xenomorph overmind. You are now \
a thrall to the queen and her brood. Obey their instructions without question. Serve the hive."
hard_cap = 5
hard_cap_round = 8
initial_spawn_req = 4

View File

@@ -86,7 +86,8 @@
/datum/antagonist/proc/tick()
return 1
/datum/antagonist/proc/get_candidates(var/ghosts_only)
// Get the raw list of potential players.
/datum/antagonist/proc/build_candidate_list(var/ghosts_only)
candidates = list() // Clear.
// Prune restricted status. Broke it up for readability.
@@ -108,46 +109,57 @@
return candidates
/datum/antagonist/proc/attempt_random_spawn()
update_current_antag_max()
build_candidate_list(flags & (ANTAG_OVERRIDE_MOB|ANTAG_OVERRIDE_JOB))
attempt_spawn()
finalize_spawn()
/datum/antagonist/proc/attempt_late_spawn(var/datum/mind/player)
/datum/antagonist/proc/attempt_auto_spawn()
if(!can_late_spawn())
return 0
if(!istype(player))
var/list/players = get_candidates(is_latejoin_template())
if(players && players.len)
player = pick(players)
if(!istype(player))
message_admins("AUTO[uppertext(ticker.mode.name)]: Failed to find a candidate for [role_text].")
return 0
player.current << "<span class='danger'><i>You have been selected this round as an antagonist!</i></span>"
message_admins("AUTO[uppertext(ticker.mode.name)]: Selected [player] as a [role_text].")
if(istype(player.current, /mob/dead))
create_default(player.current)
else
add_antagonist(player,0,0,0,1,1)
return 1
/datum/antagonist/proc/build_candidate_list(var/ghosts_only)
// Get the raw list of potential players.
update_current_antag_max()
candidates = get_candidates(ghosts_only)
var/active_antags = get_active_antag_count()
log_debug("[uppertext(id)]: Found [active_antags]/[cur_max] active [role_text_plural].")
if(active_antags >= cur_max)
log_debug("Could not auto-spawn a [role_text], active antag limit reached.")
return 0
build_candidate_list(flags & (ANTAG_OVERRIDE_MOB|ANTAG_OVERRIDE_JOB))
if(!candidates.len)
log_debug("Could not auto-spawn a [role_text], no candidates found.")
return 0
attempt_spawn(1) //auto-spawn antags one at a time
if(!pending_antagonists.len)
log_debug("Could not auto-spawn a [role_text], none of the available candidates could be selected.")
return 0
var/datum/mind/player = pending_antagonists[1]
if(!add_antagonist(player,0,0,0,1,1))
log_debug("Could not auto-spawn a [role_text], failed to add antagonist.")
return 0
reset_antag_selection()
return 1
//Selects players that will be spawned in the antagonist role from the potential candidates
//Selected players are added to the pending_antagonists lists.
//Attempting to spawn an antag role with ANTAG_OVERRIDE_JOB should be done before jobs are assigned,
//so that they do not occupy regular job slots. All other antag roles should be spawned after jobs are
//assigned, so that job restrictions can be respected.
/datum/antagonist/proc/attempt_spawn(var/rebuild_candidates = 1)
/datum/antagonist/proc/attempt_spawn(var/spawn_target = null)
if(spawn_target == null)
spawn_target = initial_spawn_target
// Update our boundaries.
if(!candidates.len)
return 0
//Grab candidates randomly until we have enough.
while(candidates.len && pending_antagonists.len < initial_spawn_target)
while(candidates.len && pending_antagonists.len < spawn_target)
var/datum/mind/player = pick(candidates)
candidates -= player
draft_antagonist(player)
@@ -187,10 +199,15 @@
pending_antagonists -= player
add_antagonist(player,0,0,1)
//Resets all pending_antagonists, clearing their special_role (and assigned_role if ANTAG_OVERRIDE_JOB is set)
/datum/antagonist/proc/reset()
reset_antag_selection()
//Resets the antag selection, clearing all pending_antagonists and their special_role
//(and assigned_role if ANTAG_OVERRIDE_JOB is set) as well as clearing the candidate list.
//Existing antagonists are left untouched.
/datum/antagonist/proc/reset_antag_selection()
for(var/datum/mind/player in pending_antagonists)
if(flags & ANTAG_OVERRIDE_JOB)
player.assigned_role = null
player.special_role = null
pending_antagonists.Cut()
candidates.Cut()

View File

@@ -16,6 +16,7 @@
create_objectives(target)
update_icons_added(target)
greet(target)
if(!gag_announcement)
announce_antagonist_spawn()
/datum/antagonist/proc/create_default(var/mob/source)

View File

@@ -20,6 +20,17 @@
/datum/antagonist/proc/get_antag_count()
return current_antagonists ? current_antagonists.len : 0
/datum/antagonist/proc/get_active_antag_count()
var/active_antags = 0
for(var/datum/mind/player in current_antagonists)
var/mob/living/L = player.current
if(!L || L.stat == DEAD)
continue //no mob or dead
if(!L.client && !L.teleop)
continue //SSD
active_antags++
return active_antags
/datum/antagonist/proc/is_antagonist(var/datum/mind/player)
if(player in current_antagonists)
return 1
@@ -33,10 +44,15 @@
return (flags & ANTAG_VOTABLE)
/datum/antagonist/proc/can_late_spawn()
update_current_antag_max()
if(get_antag_count() >= cur_max)
return 0
return 1
/datum/antagonist/proc/is_latejoin_template()
return (flags & (ANTAG_OVERRIDE_MOB|ANTAG_OVERRIDE_JOB))
/proc/all_random_antag_types()
// No caching as the ANTAG_RANDOM_EXCEPTED flag can be added/removed mid-round.
var/list/antag_candidates = all_antag_types.Copy()
for(var/datum/antagonist/antag in antag_candidates)
if(antag.flags & ANTAG_RANDOM_EXCEPTED)
antag_candidates -= antag
return antag_candidates

View File

@@ -23,5 +23,5 @@
/datum/antagonist/proc/place_mob(var/mob/living/mob)
if(!starting_locations || !starting_locations.len)
return
var/turf/T = pick_mobless_turf_if_exists(mob)
var/turf/T = pick_mobless_turf_if_exists(starting_locations)
mob.forceMove(T)

View File

@@ -7,7 +7,7 @@ var/datum/antagonist/deathsquad/deathsquad
role_text_plural = "Death Commandos"
welcome_text = "You work in the service of corporate Asset Protection, answering directly to the Board of Directors."
landmark_id = "Commando"
flags = ANTAG_OVERRIDE_JOB | ANTAG_OVERRIDE_MOB | ANTAG_HAS_NUKE | ANTAG_HAS_LEADER
flags = ANTAG_OVERRIDE_JOB | ANTAG_OVERRIDE_MOB | ANTAG_HAS_NUKE | ANTAG_HAS_LEADER | ANTAG_RANDOM_EXCEPTED
default_access = list(access_cent_general, access_cent_specops, access_cent_living, access_cent_storage)
antaghud_indicator = "huddeathsquad"

View File

@@ -11,7 +11,7 @@ var/datum/antagonist/ert/ert
landmark_id = "Response Team"
id_type = /obj/item/weapon/card/id/centcom/ERT
flags = ANTAG_OVERRIDE_JOB | ANTAG_SET_APPEARANCE | ANTAG_HAS_LEADER | ANTAG_CHOOSE_NAME
flags = ANTAG_OVERRIDE_JOB | ANTAG_SET_APPEARANCE | ANTAG_HAS_LEADER | ANTAG_CHOOSE_NAME | ANTAG_RANDOM_EXCEPTED
antaghud_indicator = "hudloyalist"
hard_cap = 5

View File

@@ -22,13 +22,11 @@ var/datum/antagonist/rogue_ai/malf
malf = src
/datum/antagonist/rogue_ai/get_candidates()
/datum/antagonist/rogue_ai/build_candidate_list()
..()
for(var/datum/mind/player in candidates)
if(player.assigned_role && player.assigned_role != "AI")
candidates -= player
if(!candidates.len)
return list()
return candidates

View File

@@ -61,8 +61,8 @@ var/list/teleportlocs = list()
for(var/area/AR in world)
if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue
if(teleportlocs.Find(AR.name)) continue
var/turf/picked = pick(get_area_turfs(AR.type))
if (picked.z in config.station_levels)
var/turf/picked = pick_area_turf(AR.type, list(/proc/is_station_turf))
if (picked)
teleportlocs += AR.name
teleportlocs[AR.name] = AR
@@ -78,8 +78,8 @@ var/list/ghostteleportlocs = list()
if(istype(AR, /area/turret_protected/aisat) || istype(AR, /area/derelict) || istype(AR, /area/tdome) || istype(AR, /area/shuttle/specops/centcom))
ghostteleportlocs += AR.name
ghostteleportlocs[AR.name] = AR
var/turf/picked = pick(get_area_turfs(AR.type))
if (picked.z in config.player_levels)
var/turf/picked = pick_area_turf(AR.type, list(/proc/is_station_turf))
if (picked)
ghostteleportlocs += AR.name
ghostteleportlocs[AR.name] = AR

View File

@@ -1,6 +1,5 @@
var/global/list/space_surprises = list( /obj/item/clothing/mask/facehugger =4,
/obj/item/weapon/pickaxe/silver =4,
var/global/list/space_surprises = list( /obj/item/weapon/pickaxe/silver =4,
/obj/item/weapon/pickaxe/drill =4,
/obj/item/weapon/pickaxe/jackhammer =4,
//mob/living/simple_animal/hostile/carp =3,
@@ -22,7 +21,7 @@ proc/spawn_room(var/atom/start_loc,var/x_size,var/y_size,var/wall,var/floor , va
//world << "Room spawned at [start_loc.x],[start_loc.y],[start_loc.z]"
if(!wall)
wall = pick(/turf/simulated/wall/r_wall,/turf/simulated/wall,/obj/effect/alien/resin)
wall = pick(/turf/simulated/wall/r_wall,/turf/simulated/wall,/obj/structure/alien/resin)
if(!floor)
floor = pick(/turf/simulated/floor,/turf/simulated/floor/tiled,/turf/simulated/floor/reinforced)
@@ -43,9 +42,9 @@ proc/spawn_room(var/atom/start_loc,var/x_size,var/y_size,var/wall,var/floor , va
if(x == 0 || x==x_size-1 || y==0 || y==y_size-1)
if(wall == /obj/effect/alien/resin)
if(wall == /obj/structure/alien/resin)
T = new floor(cur_loc)
new /obj/effect/alien/resin(T)
new /obj/structure/alien/resin(T)
else
T = new wall(cur_loc)
room_turfs["walls"] += T
@@ -73,7 +72,7 @@ proc/admin_spawn_room_at_pos()
if("Regular wall")
wall=/turf/simulated/wall
if("Resin wall")
wall=/obj/effect/alien/resin
wall=/obj/structure/alien/resin
switch(alert("Floor type",null,"Regular floor","Reinforced floor"))
if("Regular floor")
floor=/turf/simulated/floor/tiled

View File

@@ -11,15 +11,14 @@
event_delay_mod_major = 0.75
/datum/game_mode/calamity/create_antagonists()
var/list/antag_candidates = all_random_antag_types()
shuffle(all_antag_types) // This is probably the only instance in the game where the order will be important.
var/i = 1
var/grab_antags = round(num_players()/ANTAG_TYPE_RATIO)+1
for(var/antag_id in all_antag_types)
if(i > grab_antags)
break
while(antag_candidates.len && antag_tags.len < grab_antags)
var/antag_id = pick(antag_candidates)
antag_candidates -= antag_id
antag_tags |= antag_id
i++
..()
/datum/game_mode/calamity/check_victory()

View File

@@ -12,7 +12,6 @@
certain though... there is never just one of them. Good luck."
config_tag = "changeling"
required_players = 2
required_players_secret = 10
required_enemies = 1
end_on_antag_death = 0
antag_scaling_coeff = 10

View File

@@ -4,7 +4,6 @@
extended_round_description = "The station has been infiltrated by a fanatical group of death-cultists! They will use powers from beyond your comprehension to subvert you to their cause and ultimately please their gods through sacrificial summons and physical immolation! Try to survive!"
config_tag = "cult"
required_players = 5
required_players_secret = 15
required_enemies = 3
end_on_antag_death = 0
antag_tags = list(MODE_CULTIST)

View File

@@ -6,23 +6,35 @@
w_class = 4
force = 30
throwforce = 10
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
/obj/item/weapon/melee/cultblade/cultify()
return
/obj/item/weapon/melee/cultblade/attack(mob/living/target as mob, mob/living/carbon/human/user as mob)
/obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone)
if(iscultist(user))
playsound(loc, 'sound/weapons/bladeslice.ogg', 50, 1, -1)
return ..()
var/zone = (user.hand ? "l_arm":"r_arm")
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/external/affecting = H.get_organ(zone)
user << "<span class='danger'>An unexplicable force rips through your [affecting.name], tearing the sword from your grasp!</span>"
else
user.Paralyse(5)
user << "<span class='warning'>An unexplicable force powerfully repels the sword from [target]!</span>"
var/organ = ((user.hand ? "l_":"r_") + "arm")
var/obj/item/organ/external/affecting = user.get_organ(organ)
if(affecting.take_damage(rand(force/2, force))) //random amount of damage between half of the blade's force and the full force of the blade.
user.UpdateDamageIcon()
return
user << "<span class='danger'>An unexplicable force rips through you, tearing the sword from your grasp!</span>"
//random amount of damage between half of the blade's force and the full force of the blade.
user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1)
user.Weaken(5)
user.drop_from_inventory(src)
throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), throw_speed)
var/spooky = pick('sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg', 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/wail.ogg')
playsound(loc, spooky, 50, 1)
return 1
/obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob)
if(!iscultist(user))

View File

@@ -527,7 +527,11 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa","
user << "<span class='notice'>You do not have enough space to write a proper rune.</span>"
var/list/runes = list("teleport", "itemport", "tome", "armor", "convert", "tear in reality", "emp", "drain", "seer", "raise", "obscure", "reveal", "astral journey", "manifest", "imbue talisman", "sacrifice", "wall", "freedom", "cultsummon", "deafen", "blind", "bloodboil", "communicate", "stun")
r = input("Choose a rune to scribe", "Rune Scribing") in runes //not cancellable.
var/obj/effect/rune/R = new /obj/effect/rune
if(locate(/obj/effect/rune) in user.loc)
user << "<span class='warning'>There is already a rune in this location.</span>"
return
var/obj/effect/rune/R = new /obj/effect/rune(user.loc)
if(istype(user, /mob/living/carbon/human))
var/mob/living/carbon/human/H = user
R.blood_DNA = list()

View File

@@ -2,7 +2,6 @@
name = "epidemic"
config_tag = "epidemic"
required_players = 1
required_players_secret = 15
round_description = "A deadly epidemic is spreading on the station. Find a cure as fast as possible, and keep your distance to anyone who speaks in a hoarse voice!"
var/cruiser_arrival

View File

@@ -10,7 +10,6 @@ var/global/list/additional_antag_types = list()
var/probability = 0
var/required_players = 0 // Minimum players for round to start if voted in.
var/required_players_secret = 0 // Minimum number of players for that game mode to be chose in Secret
var/required_enemies = 0 // Minimum antagonists for round to start.
var/newscaster_announcements = null
var/end_on_antag_death = 0 // Round will end when all antagonists are dead.
@@ -22,7 +21,6 @@ var/global/list/additional_antag_types = list()
var/list/antag_tags = list() // Core antag templates to spawn.
var/list/antag_templates // Extra antagonist types to include.
var/list/latejoin_templates = list()
var/round_autoantag = 0 // Will this round attempt to periodically spawn more antagonists?
var/antag_scaling_coeff = 5 // Coefficient for scaling max antagonists to player count.
var/require_all_templates = 0 // Will only start if all templates are checked and can spawn.
@@ -147,10 +145,6 @@ var/global/list/additional_antag_types = list()
if((player.client)&&(player.ready))
playerC++
if(master_mode=="secret")
if(playerC < required_players_secret)
return 0
else
if(playerC < required_players)
return 0
@@ -188,6 +182,7 @@ var/global/list/additional_antag_types = list()
/datum/game_mode/proc/pre_setup()
for(var/datum/antagonist/antag in antag_templates)
antag.update_current_antag_max()
antag.build_candidate_list() //compile a list of all eligible candidates
//antag roles that replace jobs need to be assigned before the job controller hands out jobs.
@@ -212,8 +207,6 @@ var/global/list/additional_antag_types = list()
if(!(antag.flags & ANTAG_OVERRIDE_JOB))
antag.attempt_spawn() //select antags to be spawned
antag.finalize_spawn() //actually spawn antags
if(antag.is_latejoin_template())
latejoin_templates |= antag
if(emergency_shuttle && auto_recall_shuttle)
emergency_shuttle.auto_recall = 1
@@ -226,7 +219,7 @@ var/global/list/additional_antag_types = list()
/datum/game_mode/proc/fail_setup()
for(var/datum/antagonist/antag in antag_templates)
antag.reset()
antag.reset_antag_selection()
/datum/game_mode/proc/announce_ert_disabled()
if(!ert_disabled)
@@ -498,6 +491,7 @@ var/global/list/additional_antag_types = list()
if(antag)
antag_templates |= antag
shuffle(antag_templates) //In the case of multiple antag types
newscaster_announcements = pick(newscaster_standard_feeds)
/datum/game_mode/proc/check_victory()

View File

@@ -3,55 +3,39 @@
/datum/game_mode/var/max_autotraitor_delay = 12000 // Approx 20 minutes.
/datum/game_mode/var/process_count = 0
/datum/game_mode/proc/get_usable_templates(var/list/supplied_templates)
var/list/usable_templates = list()
for(var/datum/antagonist/A in supplied_templates)
if(A.can_late_spawn())
message_admins("AUTO[uppertext(name)]: [A.id] selected for spawn attempt.")
usable_templates |= A
return usable_templates
///process()
///Called by the gameticker
/datum/game_mode/proc/process()
// Slow this down a bit so latejoiners have a chance of being antags.
process_count++
if(process_count >= 10)
process_count = 0
try_latespawn()
if(round_autoantag && world.time < next_spawn && !emergency_shuttle.departed)
process_autoantag()
/datum/game_mode/proc/latespawn(var/mob/living/carbon/human/character)
if(!character.mind)
return
try_latespawn(character.mind)
//This can be overriden in case a game mode needs to do stuff when a player latejoins
/datum/game_mode/proc/handle_latejoin(var/mob/living/carbon/human/character)
return 0
/datum/game_mode/proc/try_latespawn(var/datum/mind/player, var/latejoin_only)
/datum/game_mode/proc/process_autoantag()
message_admins("[uppertext(name)]: Attempting autospawn.")
if(emergency_shuttle.departed || !round_autoantag)
return
var/list/usable_templates = list()
for(var/datum/antagonist/A in antag_templates)
if(A.can_late_spawn())
message_admins("[uppertext(name)]: [A.id] selected for spawn attempt.")
usable_templates |= A
if(world.time < next_spawn)
return
message_admins("AUTO[uppertext(name)]: Attempting spawn.")
var/list/usable_templates
if(latejoin_only && latejoin_templates.len)
usable_templates = get_usable_templates(latejoin_templates)
else if (antag_templates && antag_templates.len)
usable_templates = get_usable_templates(antag_templates)
else
message_admins("AUTO[uppertext(name)]: Failed to find configured mode spawn templates, please disable auto-antagonists until one is added.")
if(!usable_templates.len)
message_admins("[uppertext(name)]: Failed to find configured mode spawn templates, please re-enable auto-antagonists after one is added.")
round_autoantag = 0
return
while(usable_templates.len)
var/datum/antagonist/spawn_antag = pick(usable_templates)
usable_templates -= spawn_antag
if(spawn_antag.attempt_late_spawn(player))
message_admins("AUTO[uppertext(name)]: Attempting to latespawn [spawn_antag.id]. ([spawn_antag.get_antag_count()]/[spawn_antag.cur_max])")
if(spawn_antag.attempt_auto_spawn())
message_admins("[uppertext(name)]: Auto-added a new [spawn_antag.role_text].")
message_admins("There are now [spawn_antag.get_active_antag_count()]/[spawn_antag.cur_max] active [spawn_antag.role_text_plural].")
next_spawn = world.time + rand(min_autotraitor_delay, max_autotraitor_delay)
return
message_admins("AUTO[uppertext(name)]: Failed to proc a viable spawn template.")
next_spawn = world.time + rand(min_autotraitor_delay, max_autotraitor_delay)
message_admins("[uppertext(name)]: Failed to proc a viable spawn template.")
next_spawn = world.time + min_autotraitor_delay //recheck again in the miniumum time

View File

@@ -8,7 +8,6 @@ var/global/list/obj/cortical_stacks = list() //Stacks for 'leave nobody behind'
name = "heist"
config_tag = "heist"
required_players = 15
required_players_secret = 25
required_enemies = 4
round_description = "An unidentified bluespace signature has slipped past the Icarus and is approaching the station!"
end_on_antag_death = 0

View File

@@ -4,7 +4,6 @@
extended_round_description = "The AI will attempt to hack the APCs around the station in order to gain as much control as possible."
config_tag = "malfunction"
required_players = 2
required_players_secret = 7
required_enemies = 1
end_on_antag_death = 0
auto_recall_shuttle = 0

View File

@@ -85,7 +85,7 @@
/datum/game_mode/malfunction/verb/advanced_encryption_hack()
set category = "Software"
set name = "Advanced Encrypthion Hack"
set name = "Advanced Encryption Hack"
set desc = "75 CPU - Attempts to bypass encryption on the Command Quantum Relay, giving you ability to fake legitimate messages. Has chance of failing."
var/price = 75
var/mob/living/silicon/ai/user = usr
@@ -106,15 +106,12 @@
announce_hack_failure(user, "quantum message relay")
return
var/datum/announcement/priority/command/AN = new/datum/announcement/priority/command()
AN.title = title
AN.Announce(text)
command_announcement.Announce(text, title)
/datum/game_mode/malfunction/verb/elite_encryption_hack()
set category = "Software"
set name = "Elite Encryption Hack"
set desc = "200 CPU - Allows you to hack station's ALERTCON system, changing alert level. Has high chance of failijng."
set desc = "200 CPU - Allows you to hack station's ALERTCON system, changing alert level. Has high chance of failing."
var/price = 200
var/mob/living/silicon/ai/user = usr
if(!ability_prechecks(user, price))

View File

@@ -6,7 +6,6 @@
name = "Memetic Anomaly"
config_tag = "meme"
required_players = 3
required_players_secret = 10
restricted_jobs = list("AI", "Cyborg")
recommended_enemies = 2 // need at least a meme and a host
votable = 0 // temporarily disable this mode for voting

View File

@@ -3,7 +3,6 @@
round_description = "A mercenary strike force is approaching the station to eradicate a xenomorph infestation!"
config_tag = "bughunt"
required_players = 15
required_players_secret = 25
required_enemies = 1
end_on_antag_death = 1
antag_tags = list(MODE_XENOMORPH, MODE_DEATHSQUAD)

View File

@@ -4,7 +4,6 @@
extended_round_description = "Cultists and wizards spawn during this round."
config_tag = "conflux"
required_players = 15
required_players_secret = 15
required_enemies = 5
end_on_antag_death = 1
antag_tags = list(MODE_WIZARD, MODE_CULTIST)

View File

@@ -4,7 +4,6 @@
extended_round_description = "Rampant AIs, renegades and changelings spawn in this mode."
config_tag = "paranoia"
required_players = 2
required_players_secret = 7
required_enemies = 1
end_on_antag_death = 1
require_all_templates = 1

View File

@@ -4,7 +4,6 @@
extended_round_description = "Traitors and changelings both spawn during this mode."
config_tag = "traitorling"
required_players = 10
required_players_secret = 15
required_enemies = 5
end_on_antag_death = 1
require_all_templates = 1

View File

@@ -4,7 +4,6 @@
round_description = "Some crewmembers are attempting to start a revolution while a cult plots in the shadows!"
extended_round_description = "Cultists and revolutionaries spawn in this round."
required_players = 15
required_players_secret = 15
required_enemies = 3
end_on_antag_death = 1
antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST, MODE_CULTIST)

View File

@@ -10,7 +10,6 @@
only hope this unknown assassin isn't here for you."
config_tag = "ninja"
required_players = 1
required_players_secret = 10
required_enemies = 1
end_on_antag_death = 0
antag_tags = list(MODE_NINJA)

View File

@@ -9,7 +9,6 @@ var/list/nuke_disks = list()
round_description = "A mercenary strike force is approaching the station!"
config_tag = "mercenary"
required_players = 15
required_players_secret = 25 // 25 players - 5 players to be the nuke ops = 20 players remaining
required_enemies = 1
end_on_antag_death = 0
var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station

View File

@@ -4,7 +4,6 @@
round_description = "Some crewmembers are attempting to start a revolution!"
extended_round_description = "Revolutionaries - Remove the heads of staff from power. Convert other crewmembers to your cause using the 'Convert Bourgeoise' verb. Protect your leaders."
required_players = 4
required_players_secret = 15
required_enemies = 3
auto_recall_shuttle = 0 //NO THANKS
end_on_antag_death = 0

View File

@@ -4,7 +4,6 @@
extended_round_description = "A powerful entity capable of manipulating the elements around him, most commonly referred to as a 'wizard', has infiltrated the station. They have a wide variety of powers and spells available to them that makes your own simple moral self tremble with fear and excitement. Ultimately, their purpose is unknown. However, it is up to you and your crew to decide if their powers can be used for good or if their arrival foreshadows the destruction of the entire station."
config_tag = "wizard"
required_players = 1
required_players_secret = 10
required_enemies = 1
end_on_antag_death = 0
antag_tags = list(MODE_WIZARD)

View File

@@ -127,11 +127,4 @@
M.deconstruct(src)
qdel(src)
else
src.attack_hand(user)
return
..()

View File

@@ -1081,8 +1081,8 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/emp_act(var/severity)
if(prob(40/severity))
var/duration = world.time + SecondsToTicks(30 / severity)
if(duration > electrified_until)
var/duration = SecondsToTicks(30 / severity)
if(electrified_until > -1 && (duration + world.time) > electrified_until)
electrify(duration)
..()

View File

@@ -191,7 +191,10 @@ Class Procs:
return (stat & (NOPOWER|BROKEN|additional_flags))
/obj/machinery/CanUseTopic(var/mob/user)
if(!interact_offline && (stat & (NOPOWER|BROKEN)))
if(stat & BROKEN)
return STATUS_CLOSE
if(!interact_offline && (stat & NOPOWER))
return STATUS_CLOSE
return ..()

View File

@@ -418,10 +418,14 @@ obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob)
nuke_disks |= src
/obj/item/weapon/disk/nuclear/Destroy()
if(!nuke_disks.len && blobstart.len > 0)
var/obj/D = new /obj/item/weapon/disk/nuclear(pick(blobstart))
message_admins("[src], the last authentication disk, has been destroyed. Spawning [D] at ([D.x], [D.y], [D.z]).")
log_game("[src], the last authentication disk, has been destroyed. Spawning [D] at ([D.x], [D.y], [D.z]).")
nuke_disks -= src
if(!nuke_disks.len)
var/turf/T = pick_area_turf(/area/maintenance, list(/proc/is_station_turf, /proc/not_turf_contains_dense_objects))
if(T)
var/obj/D = new /obj/item/weapon/disk/nuclear(T)
log_and_message_admins_with_location("[src], the last authentication disk, has been destroyed. Spawning [D] at ([D.x], [D.y], [D.z]).", T.x, T.y, T.z)
else
log_and_message_admins("[src], the last authentication disk, has been destroyed. Failed to respawn disc!")
..()
/obj/item/weapon/disk/nuclear/touch_map_edge()

View File

@@ -1,271 +1,11 @@
/* Alien Effects!
* Contains:
* effect/alien
* Resin
* Weeds
* Acid
* Egg
*/
/*
* effect/alien
*/
/obj/effect/alien
name = "alien thing"
desc = "theres something alien about this"
icon = 'icons/mob/alien.dmi'
/*
* Resin
*/
/obj/effect/alien/resin
name = "resin"
desc = "Looks like some kind of slimy growth."
icon_state = "resin"
density = 1
opacity = 1
anchored = 1
var/health = 200
//var/mob/living/affecting = null
/obj/effect/alien/resin/wall
name = "resin wall"
desc = "Purple slime solidified into a wall."
icon_state = "resinwall" //same as resin, but consistency ho!
/obj/effect/alien/resin/membrane
name = "resin membrane"
desc = "Purple slime just thin enough to let light pass through."
icon_state = "resinmembrane"
opacity = 0
health = 120
/obj/effect/alien/resin/New()
..()
var/turf/T = get_turf(src)
T.thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT
/obj/effect/alien/resin/Destroy()
var/turf/T = get_turf(src)
T.thermal_conductivity = initial(T.thermal_conductivity)
..()
/obj/effect/alien/resin/proc/healthcheck()
if(health <=0)
density = 0
qdel(src)
return
/obj/effect/alien/resin/bullet_act(var/obj/item/projectile/Proj)
health -= Proj.damage
..()
healthcheck()
return
/obj/effect/alien/resin/ex_act(severity)
switch(severity)
if(1.0)
health-=50
if(2.0)
health-=50
if(3.0)
if (prob(50))
health-=50
else
health-=25
healthcheck()
return
/obj/effect/alien/resin/hitby(AM as mob|obj)
..()
for(var/mob/O in viewers(src, null))
O.show_message("<span class='danger'>[src] was hit by [AM].</span>", 1)
var/tforce = 0
if(ismob(AM))
tforce = 10
else
tforce = AM:throwforce
playsound(loc, 'sound/effects/attackblob.ogg', 100, 1)
health = max(0, health - tforce)
healthcheck()
..()
return
/obj/effect/alien/resin/attack_hand()
usr.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if (HULK in usr.mutations)
usr << "<span class='notice'>You easily destroy the [name].</span>"
for(var/mob/O in oviewers(src))
O.show_message("<span class='warning'>[usr] destroys the [name]!</span>", 1)
health = 0
else
// Aliens can get straight through these.
if(istype(usr,/mob/living/carbon))
var/mob/living/carbon/M = usr
if(locate(/obj/item/organ/xenos/hivenode) in M.internal_organs)
for(var/mob/O in oviewers(src))
O.show_message("<span class='warning'>[usr] strokes the [name] and it melts away!</span>", 1)
health = 0
healthcheck()
return
usr << "<span class='notice'>You claw at the [name].</span>"
for(var/mob/O in oviewers(src))
O.show_message("<span class='warning'>[usr] claws at the [name]!</span>", 1)
health -= rand(5,10)
healthcheck()
return
/obj/effect/alien/resin/attackby(obj/item/weapon/W as obj, mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
var/aforce = W.force
health = max(0, health - aforce)
playsound(loc, 'sound/effects/attackblob.ogg', 100, 1)
healthcheck()
..()
return
/obj/effect/alien/resin/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group) return 0
if(istype(mover) && mover.checkpass(PASSGLASS))
return !opacity
return !density
/*
* Weeds
*/
#define NODERANGE 3
/obj/effect/alien/weeds
name = "weeds"
desc = "Weird purple weeds."
icon_state = "weeds"
anchored = 1
density = 0
layer = 2
var/health = 15
var/obj/effect/alien/weeds/node/linked_node = null
/obj/effect/alien/weeds/node
icon_state = "weednode"
name = "purple sac"
desc = "Weird purple octopus-like thing."
layer = 3
light_range = NODERANGE
var/node_range = NODERANGE
/obj/effect/alien/weeds/node/New()
..(src.loc, src)
/obj/effect/alien/weeds/New(pos, node)
..()
if(istype(loc, /turf/space))
qdel(src)
return
linked_node = node
if(icon_state == "weeds")icon_state = pick("weeds", "weeds1", "weeds2")
spawn(rand(150, 200))
if(src)
Life()
return
/obj/effect/alien/weeds/proc/Life()
set background = 1
var/turf/U = get_turf(src)
/*
if (locate(/obj/movable, U))
U = locate(/obj/movable, U)
if(U.density == 1)
qdel(src)
return
Alien plants should do something if theres a lot of poison
if(U.poison> 200000)
health -= round(U.poison/200000)
update()
return
*/
if (istype(U, /turf/space))
qdel(src)
return
if(!linked_node || (get_dist(linked_node, src) > linked_node.node_range) )
return
direction_loop:
for(var/dirn in cardinal)
var/turf/T = get_step(src, dirn)
if (!istype(T) || T.density || locate(/obj/effect/alien/weeds) in T || istype(T.loc, /area/arrival) || istype(T, /turf/space))
continue
// if (locate(/obj/movable, T)) // don't propogate into movables
// continue
for(var/obj/O in T)
if(O.density)
continue direction_loop
PoolOrNew(/obj/effect/alien/weeds, T, linked_node)
/obj/effect/alien/weeds/ex_act(severity)
switch(severity)
if(1.0)
qdel(src)
if(2.0)
if (prob(50))
qdel(src)
if(3.0)
if (prob(5))
qdel(src)
return
/obj/effect/alien/weeds/attackby(var/obj/item/weapon/W, var/mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(W.attack_verb.len)
visible_message("<span class='danger'>\The [src] have been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]</span>")
else
visible_message("<span class='danger'>\The [src] have been attacked with \the [W][(user ? " by [user]." : ".")]</span>")
var/damage = W.force / 4.0
if(istype(W, /obj/item/weapon/weldingtool))
var/obj/item/weapon/weldingtool/WT = W
if(WT.remove_fuel(0, user))
damage = 15
playsound(loc, 'sound/items/Welder.ogg', 100, 1)
health -= damage
healthcheck()
/obj/effect/alien/weeds/proc/healthcheck()
if(health <= 0)
qdel(src)
/obj/effect/alien/weeds/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(exposed_temperature > 300 + T0C)
health -= 5
healthcheck()
#undef NODERANGE
/*
* Acid
*/
/obj/effect/alien/acid
/obj/effect/acid
name = "acid"
desc = "Burbling corrossive stuff. I wouldn't want to touch it."
desc = "Burbling corrosive stuff. Probably a bad idea to roll around in it."
icon_state = "acid"
icon = 'icons/mob/alien.dmi'
density = 0
opacity = 0
@@ -275,9 +15,9 @@ Alien plants should do something if theres a lot of poison
var/ticks = 0
var/target_strength = 0
/obj/effect/alien/acid/New(loc, target)
/obj/effect/acid/New(loc, supplied_target)
..(loc)
src.target = target
target = supplied_target
if(isturf(target)) // Turf take twice as long to take down.
target_strength = 8
@@ -285,17 +25,13 @@ Alien plants should do something if theres a lot of poison
target_strength = 4
tick()
/obj/effect/alien/acid/proc/tick()
/obj/effect/acid/proc/tick()
if(!target)
qdel(src)
ticks += 1
ticks++
if(ticks >= target_strength)
for(var/mob/O in hearers(src, null))
O.show_message("<span class='alium'>[src.target] collapses under its own weight into a puddle of goop and undigested debris!</span>", 1)
target.visible_message("<span class='alium'>\The [target] collapses under its own weight into a puddle of goop and undigested debris!</span>")
if(istype(target, /turf/simulated/wall)) // I hate turf code.
var/turf/simulated/wall/W = target
W.dismantle_wall(1)
@@ -306,135 +42,11 @@ Alien plants should do something if theres a lot of poison
switch(target_strength - ticks)
if(6)
visible_message("<span class='alium'>[src.target] is holding up against the acid!</span>")
visible_message("<span class='alium'>\The [src.target] is holding up against the acid!</span>")
if(4)
visible_message("<span class='alium'>[src.target]\s structure is being melted by the acid!</span>")
visible_message("<span class='alium'>\The [src.target]\s structure is being melted by the acid!</span>")
if(2)
visible_message("<span class='alium'>[src.target] is struggling to withstand the acid!</span>")
visible_message("<span class='alium'>\The [src.target] is struggling to withstand the acid!</span>")
if(0 to 1)
visible_message("<span class='alium'>[src.target] begins to crumble under the acid!</span>")
visible_message("<span class='alium'>\The [src.target] begins to crumble under the acid!</span>")
spawn(rand(150, 200)) tick()
/*
* Egg
*/
/var/const //for the status var
BURST = 0
BURSTING = 1
GROWING = 2
GROWN = 3
MIN_GROWTH_TIME = 1800 //time it takes to grow a hugger
MAX_GROWTH_TIME = 3000
/obj/effect/alien/egg
desc = "It looks like a weird egg"
name = "egg"
icon_state = "egg_growing"
density = 0
anchored = 1
var/health = 100
var/status = GROWING //can be GROWING, GROWN or BURST; all mutually exclusive
flags = PROXMOVE
/obj/effect/alien/egg/New()
if(config.aliens_allowed)
..()
spawn(rand(MIN_GROWTH_TIME,MAX_GROWTH_TIME))
Grow()
else
qdel(src)
/obj/effect/alien/egg/attack_hand(user as mob)
var/mob/living/carbon/M = user
if(!istype(M) || !(locate(/obj/item/organ/xenos/hivenode) in M.internal_organs))
return attack_hand(user)
switch(status)
if(BURST)
user << "<span class='warning'>You clear the hatched egg.</span>"
qdel(src)
return
if(GROWING)
user << "<span class='warning'>The child is not developed yet.</span>"
return
if(GROWN)
user << "<span class='warning'>You retrieve the child.</span>"
Burst(0)
return
/obj/effect/alien/egg/proc/GetFacehugger()
return locate(/obj/item/clothing/mask/facehugger) in contents
/obj/effect/alien/egg/proc/Grow()
icon_state = "egg"
status = GROWN
new /obj/item/clothing/mask/facehugger(src)
return
/obj/effect/alien/egg/proc/Burst(var/kill = 1) //drops and kills the hugger if any is remaining
if(status == GROWN || status == GROWING)
var/obj/item/clothing/mask/facehugger/child = GetFacehugger()
icon_state = "egg_hatched"
flick("egg_opening", src)
status = BURSTING
spawn(15)
status = BURST
child.loc = get_turf(src)
if(kill && istype(child))
child.Die()
else
for(var/mob/M in range(1,src))
if(CanHug(M))
child.Attach(M)
break
/obj/effect/alien/egg/bullet_act(var/obj/item/projectile/Proj)
health -= Proj.damage
..()
healthcheck()
return
/obj/effect/alien/egg/attackby(var/obj/item/weapon/W, var/mob/user)
if(health <= 0)
return
if(W.attack_verb.len)
src.visible_message("<span class='danger'>\The [src] has been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]</span>")
else
src.visible_message("<span class='danger'>\The [src] has been attacked with \the [W][(user ? " by [user]." : ".")]</span>")
var/damage = W.force / 4.0
if(istype(W, /obj/item/weapon/weldingtool))
var/obj/item/weapon/weldingtool/WT = W
if(WT.remove_fuel(0, user))
damage = 15
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
src.health -= damage
src.healthcheck()
/obj/effect/alien/egg/proc/healthcheck()
if(health <= 0)
Burst()
/obj/effect/alien/egg/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(exposed_temperature > 500 + T0C)
health -= 5
healthcheck()
/obj/effect/alien/egg/HasProximity(atom/movable/AM as mob|obj)
if(status == GROWN)
if(!CanHug(AM))
return
var/mob/living/carbon/C = AM
if(C.stat == CONSCIOUS && C.status_flags & XENO_HOST)
return
Burst(0)

View File

@@ -4,14 +4,76 @@
/obj/effect/effect/smoke/chem
icon = 'icons/effects/chemsmoke.dmi'
opacity = 0
layer = 6
time_to_live = 300
pass_flags = PASSTABLE | PASSGRILLE | PASSGLASS //PASSGLASS is fine here, it's just so the visual effect can "flow" around glass
var/splash_amount = 10 //atoms moving through a smoke cloud get splashed with up to 10 units of reagent
var/turf/destination
/obj/effect/effect/smoke/chem/New(var/newloc, smoke_duration, turf/dest_turf = null, icon/cached_icon = null)
time_to_live = smoke_duration
/obj/effect/effect/smoke/chem/New()
..()
create_reagents(500)
return
create_reagents(500)
if(cached_icon)
icon = cached_icon
set_dir(pick(cardinal))
pixel_x = -32 + rand(-8, 8)
pixel_y = -32 + rand(-8, 8)
//switching opacity on after the smoke has spawned, and then turning it off before it is deleted results in cleaner
//lighting and view range updates (Is this still true with the new lighting system?)
opacity = 1
//float over to our destination, if we have one
destination = dest_turf
if(destination)
walk_to(src, destination)
/obj/effect/effect/smoke/chem/Destroy()
opacity = 0
fadeOut()
..()
/obj/effect/effect/smoke/chem/Move()
var/list/oldlocs = view(1, src)
. = ..()
if(.)
for(var/turf/T in view(1, src) - oldlocs)
for(var/atom/movable/AM in T)
if(!istype(AM, /obj/effect/effect/smoke/chem))
reagents.splash(AM, splash_amount, copy = 1)
if(loc == destination)
bound_width = 96
bound_height = 96
/obj/effect/effect/smoke/chem/Crossed(atom/movable/AM)
..()
if(!istype(AM, /obj/effect/effect/smoke/chem))
reagents.splash(AM, splash_amount, copy = 1)
/obj/effect/effect/smoke/chem/proc/initial_splash()
for(var/turf/T in view(1, src))
for(var/atom/movable/AM in T)
if(!istype(AM, /obj/effect/effect/smoke/chem))
reagents.splash(AM, splash_amount, copy = 1)
// Fades out the smoke smoothly using it's alpha variable.
/obj/effect/effect/smoke/chem/proc/fadeOut(var/frames = 16)
if(!alpha) return //already transparent
frames = max(frames, 1) //We will just assume that by 0 frames, the coder meant "during one frame".
var/alpha_step = round(alpha / frames)
while(alpha > 0)
alpha = max(0, alpha - alpha_step)
sleep(world.tick_lag)
/////////////////////////////////////////////
// Chem Smoke Effect System
/////////////////////////////////////////////
/datum/effect/effect/system/smoke_spread/chem
smoke_type = /obj/effect/effect/smoke/chem
var/obj/chemholder
@@ -115,13 +177,21 @@
else
I = icon('icons/effects/96x96.dmi', "smoke")
//Calculate smoke duration
var/smoke_duration = 150
var/pressure = 0
var/datum/gas_mixture/environment = location.return_air()
if(environment) pressure = environment.return_pressure()
smoke_duration = between(5, smoke_duration*pressure/(ONE_ATMOSPHERE/3), smoke_duration)
var/const/arcLength = 2.3559 //distance between each smoke cloud
for(var/i = 0, i < range, i++) //calculate positions for smoke coverage - then spawn smoke
var/radius = i * 1.5
if(!radius)
spawn(0)
spawnSmoke(location, I, 1)
spawnSmoke(location, I, 1, 1)
continue
var/offset = 0
@@ -146,43 +216,26 @@
// Randomizes and spawns the smoke effect.
// Also handles deleting the smoke once the effect is finished.
//------------------------------------------
/datum/effect/effect/system/smoke_spread/chem/proc/spawnSmoke(var/turf/T, var/icon/I, var/dist = 1, var/obj/effect/effect/smoke/chem/passed_smoke)
/datum/effect/effect/system/smoke_spread/chem/proc/spawnSmoke(var/turf/T, var/icon/I, var/smoke_duration, var/dist = 1, var/splash_initial=0, var/obj/effect/effect/smoke/chem/passed_smoke)
var/obj/effect/effect/smoke/chem/smoke
if(passed_smoke)
smoke = passed_smoke
else
smoke = PoolOrNew(/obj/effect/effect/smoke/chem, location)
smoke = PoolOrNew(/obj/effect/effect/smoke/chem, list(location, smoke_duration + rand(0, 20), T, I))
if(chemholder.reagents.reagent_list.len)
chemholder.reagents.trans_to_obj(smoke, chemholder.reagents.total_volume / dist, copy = 1) //copy reagents to the smoke so mob/breathe() can handle inhaling the reagents
smoke.icon = I
smoke.layer = 6
smoke.set_dir(pick(cardinal))
smoke.pixel_x = -32 + rand(-8, 8)
smoke.pixel_y = -32 + rand(-8, 8)
walk_to(smoke, T)
smoke.opacity = 1 //switching opacity on after the smoke has spawned, and then
sleep(150+rand(0,20)) // turning it off before it is deleted results in cleaner
smoke.opacity = 0 // lighting and view range updates
fadeOut(smoke)
qdel(src)
/datum/effect/effect/system/smoke_spread/chem/spores/spawnSmoke(var/turf/T, var/icon/I, var/dist = 1)
//Kinda ugly, but needed unless the system is reworked
if(splash_initial)
smoke.initial_splash()
/datum/effect/effect/system/smoke_spread/chem/spores/spawnSmoke(var/turf/T, var/smoke_duration, var/icon/I, var/dist = 1)
var/obj/effect/effect/smoke/chem/spores = PoolOrNew(/obj/effect/effect/smoke/chem, location)
spores.name = "cloud of [seed.seed_name] [seed.seed_noun]"
..(T, I, dist, spores)
/datum/effect/effect/system/smoke_spread/chem/proc/fadeOut(var/atom/A, var/frames = 16) // Fades out the smoke smoothly using it's alpha variable.
if(A.alpha == 0) //Handle already transparent case
return
if(frames == 0)
frames = 1 //We will just assume that by 0 frames, the coder meant "during one frame".
var/step = A.alpha / frames
for(var/i = 0, i < frames, i++)
A.alpha -= step
sleep(world.tick_lag)
return
..(T, I, smoke_duration, dist, spores)
/datum/effect/effect/system/smoke_spread/chem/proc/smokeFlow() // Smoke pathfinder. Uses a flood fill method based on zones to quickly check what turfs the smoke (airflow) can actually reach.

View File

@@ -52,10 +52,6 @@
prisonsecuritywarp += loc
qdel(src)
return
if("blobstart")
blobstart += loc
qdel(src)
return
if("xeno_spawn")
xeno_spawn += loc
qdel(src)

View File

@@ -105,27 +105,61 @@
qdel(src)
*/
/client/proc/spawn_tanktransferbomb()
set category = "Debug"
set desc = "Spawn a tank transfer valve bomb"
set name = "Instant TTV"
if(!check_rights(R_SPAWN)) return
var/obj/effect/spawner/newbomb/proto = /obj/effect/spawner/newbomb/radio/custom
var/p = input("Enter phoron amount (mol):","Phoron", initial(proto.phoron_amt)) as num|null
if(p == null) return
var/o = input("Enter oxygen amount (mol):","Oxygen", initial(proto.oxygen_amt)) as num|null
if(o == null) return
var/c = input("Enter carbon dioxide amount (mol):","Carbon Dioxide", initial(proto.carbon_amt)) as num|null
if(c == null) return
new /obj/effect/spawner/newbomb/radio/custom(get_turf(mob), p, o, c)
/obj/effect/spawner/newbomb
name = "bomb"
name = "TTV bomb"
icon = 'icons/mob/screen1.dmi'
icon_state = "x"
var/btype = 0 // 0=radio, 1=prox, 2=time
timer
btype = 2
var/assembly_type = /obj/item/device/assembly/signaler
syndicate
//Note that the maximum amount of gas you can put in a 70L air tank at 1013.25 kPa and 519K is 16.44 mol.
var/phoron_amt = 10.96
var/oxygen_amt = 16.44
var/carbon_amt = 0.0
proximity
btype = 1
/obj/effect/spawner/newbomb/timer
name = "TTV bomb - timer"
assembly_type = /obj/item/device/assembly/timer
radio
btype = 0
/obj/effect/spawner/newbomb/timer/syndicate
name = "TTV bomb - merc"
//High yield bombs. Yes, it is possible to make these with toxins
phoron_amt = 15.66
oxygen_amt = 24.66
/obj/effect/spawner/newbomb/proximity
name = "TTV bomb - proximity"
assembly_type = /obj/item/device/assembly/prox_sensor
/obj/effect/spawner/newbomb/New()
/obj/effect/spawner/newbomb/radio/custom/New(var/newloc, ph, ox, co)
if(ph != null) phoron_amt = ph
if(ox != null) oxygen_amt = ox
if(co != null) carbon_amt = co
..()
/obj/effect/spawner/newbomb/New(newloc)
..(newloc)
var/obj/item/device/transfer_valve/V = new(src.loc)
var/obj/item/weapon/tank/phoron/PT = new(V)
var/obj/item/weapon/tank/oxygen/OT = new(V)
@@ -137,28 +171,15 @@
OT.master = V
PT.air_contents.temperature = PHORON_FLASHPOINT
PT.air_contents.adjust_multi("phoron", 12, "carbon_dioxide", 8)
PT.air_contents.gas["phoron"] = phoron_amt
PT.air_contents.gas["carbon_dioxide"] = carbon_amt
PT.air_contents.update_values()
OT.air_contents.temperature = PHORON_FLASHPOINT
OT.air_contents.adjust_gas("oxygen", 20)
OT.air_contents.gas["oxygen"] = oxygen_amt
OT.air_contents.update_values()
var/obj/item/device/assembly/S
switch (src.btype)
// radio
if (0)
S = new/obj/item/device/assembly/signaler(V)
// proximity
if (1)
S = new/obj/item/device/assembly/prox_sensor(V)
// timer
if (2)
S = new/obj/item/device/assembly/timer(V)
var/obj/item/device/assembly/S = new assembly_type(V)
V.attached_device = S

View File

@@ -438,6 +438,9 @@ var/list/global/slot_flags_enumeration = list(
M.attack_log += "\[[time_stamp()]\]<font color='orange'> Attacked by [user.name] ([user.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])</font>"
msg_admin_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)") //BS12 EDIT ALG
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(M)
src.add_fingerprint(user)
//if((CLUMSY in user.mutations) && prob(50))
// M = user
@@ -611,7 +614,3 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
/obj/item/proc/pwr_drain()
return 0 // Process Kill
/obj/item/proc/resolve_attackby(atom/A, mob/source)
return A.attackby(src,source)

View File

@@ -722,7 +722,8 @@ var/global/list/obj/item/device/pda/PDAs = list()
if("Message")
var/obj/item/device/pda/P = locate(href_list["target"])
src.create_message(U, P, !href_list["notap"])
var/tap = istype(U, /mob/living/carbon)
src.create_message(U, P, tap)
if(mode == 2)
if(href_list["target"] in conversations) // Need to make sure the message went through, if not welp.
active_conversation = href_list["target"]
@@ -1041,7 +1042,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
new_message(sending_device, sending_device.owner, sending_device.ownjob, message)
/obj/item/device/pda/proc/new_message(var/sending_unit, var/sender, var/sender_job, var/message)
var/reception_message = "\icon[src] <b>Message from [sender] ([sender_job]), </b>\"[message]\" (<a href='byond://?src=\ref[src];choice=Message;notap=[istype(loc, /mob/living/silicon)];skiprefresh=1;target=\ref[sending_unit]'>Reply</a>)"
var/reception_message = "\icon[src] <b>Message from [sender] ([sender_job]), </b>\"[message]\" (<a href='byond://?src=\ref[src];choice=Message;skiprefresh=1;target=\ref[sending_unit]'>Reply</a>)"
new_info(message_silent, ttone, reception_message)
log_pda("[usr] (PDA: [sending_unit]) sent \"[message]\" to [name]")
@@ -1053,7 +1054,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(ismob(sending_unit.loc) && isAI(loc))
track = "(<a href='byond://?src=\ref[loc];track=\ref[sending_unit.loc];trackname=[html_encode(sender)]'>Follow</a>)"
var/reception_message = "\icon[src] <b>Message from [sender] ([sender_job]), </b>\"[message]\" (<a href='byond://?src=\ref[src];choice=Message;notap=1;skiprefresh=1;target=\ref[sending_unit]'>Reply</a>) [track]"
var/reception_message = "\icon[src] <b>Message from [sender] ([sender_job]), </b>\"[message]\" (<a href='byond://?src=\ref[src];choice=Message;skiprefresh=1;target=\ref[sending_unit]'>Reply</a>) [track]"
new_info(message_silent, newstone, reception_message)
log_pda("[usr] (PDA: [sending_unit]) sent \"[message]\" to [name]")

View File

@@ -31,14 +31,17 @@
last_used = world.time
times_used = max(0,round(times_used)) //sanity
/obj/item/device/flash/attack(mob/living/M as mob, mob/user as mob)
//attack_as_weapon
/obj/item/device/flash/attack(mob/living/M, mob/living/user, var/target_zone)
if(!user || !M) return //sanity
M.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been flashed (attempt) with [src.name] by [user.name] ([user.ckey])</font>")
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Used the [src.name] to flash [M.name] ([M.ckey])</font>")
msg_admin_attack("[user.name] ([user.ckey]) Used the [src.name] to flash [M.name] ([M.ckey]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(M)
if(!clown_check(user)) return
if(broken)
user << "<span class='warning'>\The [src] is broken.</span>"
@@ -65,8 +68,9 @@
if(iscarbon(M))
if(M.stat!=DEAD)
var/safety = M:eyecheck()
if(safety <= 0)
var/mob/living/carbon/C = M
var/safety = C.eyecheck()
if(safety < FLASH_PROTECTION_MODERATE)
var/flash_strength = 10
if(ishuman(M))
var/mob/living/carbon/human/H = M
@@ -112,6 +116,9 @@
/obj/item/device/flash/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0)
if(!user || !clown_check(user)) return
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(broken)
user.show_message("<span class='warning'>The [src.name] is broken</span>", 2)
return
@@ -150,8 +157,8 @@
for(var/obj/item/weapon/cloaking_device/S in M)
S.active = 0
S.icon_state = "shield0"
var/safety = M:eyecheck()
if(!safety)
var/safety = M.eyecheck()
if(safety < FLASH_PROTECTION_MODERATE)
if(!M.blinded)
flick("flash", M.flash)
@@ -170,7 +177,7 @@
if(istype(loc, /mob/living/carbon))
var/mob/living/carbon/M = loc
var/safety = M.eyecheck()
if(safety <= 0)
if(safety < FLASH_PROTECTION_MODERATE)
M.Weaken(10)
flick("e_flash", M.flash)
for(var/mob/O in viewers(M, null))
@@ -183,7 +190,8 @@
icon_state = "sflash"
origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1)
/obj/item/device/flash/synthetic/attack(mob/living/M as mob, mob/user as mob)
//attack_as_weapon
/obj/item/device/flash/synthetic/attack(mob/living/M, mob/living/user, var/target_zone)
..()
if(!broken)
broken = 1

View File

@@ -79,6 +79,8 @@
user << "<span class='notice'>\The [M]'s pupils narrow slightly, but are still very dilated.</span>"
else
user << "<span class='notice'>\The [M]'s pupils narrow.</span>"
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) //can be used offensively
flick("flash", M.flash)
else
return ..()

View File

@@ -83,58 +83,66 @@ var/global/list/default_medbay_channels = list(
secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT)
/obj/item/device/radio/attack_ghost(mob/user)
interact(user)
return ui_interact(user)
/obj/item/device/radio/attack_self(mob/user as mob)
user.set_machine(src)
interact(user)
/obj/item/device/radio/interact(mob/user as mob)
if(!on)
return
/obj/item/device/radio/interact(mob/user)
if(!user)
return 0
if(active_uplink_check(user))
return
if(b_stat)
wires.Interact(user)
var/dat = "<html><head><title>[src]</title></head><body><TT>"
return ui_interact(user)
if(!istype(src, /obj/item/device/radio/headset)) //Headsets dont get a mic button
dat += "Microphone: [broadcasting ? "<A href='byond://?src=\ref[src];talk=0'>Engaged</A>" : "<A href='byond://?src=\ref[src];talk=1'>Disengaged</A>"]<BR>"
/obj/item/device/radio/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
dat += {"
Speaker: [listening ? "<A href='byond://?src=\ref[src];listen=0'>Engaged</A>" : "<A href='byond://?src=\ref[src];listen=1'>Disengaged</A>"]<BR>
Frequency:
<A href='byond://?src=\ref[src];freq=-10'>-</A>
<A href='byond://?src=\ref[src];freq=-2'>-</A>
[format_frequency(frequency)]
<A href='byond://?src=\ref[src];freq=2'>+</A>
<A href='byond://?src=\ref[src];freq=10'>+</A><BR>
"}
data["mic_status"] = broadcasting
data["speaker"] = listening
data["freq"] = format_frequency(frequency)
data["rawfreq"] = num2text(frequency)
dat+=list_channels(user)
data["mic_cut"] = (wires.IsIndexCut(WIRE_TRANSMIT) || wires.IsIndexCut(WIRE_SIGNAL))
data["spk_cut"] = (wires.IsIndexCut(WIRE_RECEIVE) || wires.IsIndexCut(WIRE_SIGNAL))
dat+={"[text_wires()]</TT></body></html>"}
user << browse(dat, "window=radio")
onclose(user, "radio")
return
var/list/chanlist = list_channels(user)
if(islist(chanlist) && chanlist.len)
data["chan_list"] = chanlist
data["chan_list_len"] = chanlist.len
if(syndie)
data["useSyndMode"] = 1
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
ui = new(user, src, ui_key, "radio_basic.tmpl", "[name]", 400, 430)
ui.set_initial_data(data)
ui.open()
/obj/item/device/radio/proc/list_channels(var/mob/user)
return list_internal_channels(user)
/obj/item/device/radio/proc/list_secure_channels(var/mob/user)
var/dat = ""
var/dat[0]
for(var/ch_name in channels)
dat+=text_sec_channel(ch_name, channels[ch_name])
var/chan_stat = channels[ch_name]
var/listening = !!(chan_stat & FREQ_LISTENING) != 0
dat.Add(list(list("chan" = ch_name, "display_name" = ch_name, "secure_channel" = 1, "sec_channel_listen" = !listening, "chan_span" = frequency_span_class(radiochannels[ch_name]))))
return dat
/obj/item/device/radio/proc/list_internal_channels(var/mob/user)
var/dat = ""
var/dat[0]
for(var/internal_chan in internal_channels)
if(has_channel_access(user, internal_chan))
dat+="<A href='byond://?src=\ref[src];spec_freq=[internal_chan]'>[get_frequency_name(text2num(internal_chan))]</A><br>"
dat.Add(list(list("chan" = internal_chan, "display_name" = get_frequency_name(text2num(internal_chan)), "chan_span" = frequency_span_class(text2num(internal_chan)))))
if(dat)
dat = "<br><b>Internal Channels</b><br>" + dat
return dat
/obj/item/device/radio/proc/has_channel_access(var/mob/user, var/freq)
@@ -172,9 +180,13 @@ var/global/list/default_medbay_channels = list(
/obj/item/device/radio/proc/ToggleReception()
listening = !listening && !(wires.IsIndexCut(WIRE_RECEIVE) || wires.IsIndexCut(WIRE_SIGNAL))
/obj/item/device/radio/CanUseTopic()
if(!on)
return STATUS_CLOSE
return ..()
/obj/item/device/radio/Topic(href, href_list)
if(..() || !on)
usr << browse(null, "window=radio")
if(..())
return 1
usr.set_machine(src)
@@ -216,7 +228,7 @@ var/global/list/default_medbay_channels = list(
return 1
if(.)
interact(usr)
nanomanager.update_uis(src)
/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel) //BS12 EDIT
var/datum/radio_frequency/connection = null
@@ -683,31 +695,40 @@ var/global/list/default_medbay_channels = list(
. = 1
if(.)
interact(usr)
nanomanager.update_uis(src)
/obj/item/device/radio/borg/interact(mob/user as mob)
if(!on)
return
var/dat = "<html><head><title>[src]</title></head><body><TT>"
dat += {"
Speaker: [listening ? "<A href='byond://?src=\ref[src];listen=0'>Engaged</A>" : "<A href='byond://?src=\ref[src];listen=1'>Disengaged</A>"]<BR>
Frequency:
<A href='byond://?src=\ref[src];freq=-10'>-</A>
<A href='byond://?src=\ref[src];freq=-2'>-</A>
[format_frequency(frequency)]
<A href='byond://?src=\ref[src];freq=2'>+</A>
<A href='byond://?src=\ref[src];freq=10'>+</A><BR>
Broadcasting: [subspace_transmission ? "<A href='byond://?src=\ref[src];mode=0'>Disable</A>" : "<A href='byond://?src=\ref[src];mode=1'>Enable</A>"]<BR>
Loudspeaker: [shut_up ? "<A href='byond://?src=\ref[src];shutup=0'>Enable</A>" : "<A href='byond://?src=\ref[src];shutup=1'>Disable</A>"]<BR>
"}
. = ..()
if(subspace_transmission)//Don't even bother if subspace isn't turned on
dat+=list_channels(user)
dat+={"[text_wires()]</TT></body></html>"}
user << browse(dat, "window=radio")
onclose(user, "radio")
return
/obj/item/device/radio/borg/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
var/data[0]
data["mic_status"] = broadcasting
data["speaker"] = listening
data["freq"] = format_frequency(frequency)
data["rawfreq"] = num2text(frequency)
var/list/chanlist = list_channels(user)
if(islist(chanlist) && chanlist.len)
data["chan_list"] = chanlist
data["chan_list_len"] = chanlist.len
if(syndie)
data["useSyndMode"] = 1
data["has_loudspeaker"] = 1
data["loudspeaker"] = !shut_up
data["has_subspace"] = 1
data["subspace"] = subspace_transmission
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
ui = new(user, src, ui_key, "radio_basic.tmpl", "[name]", 400, 430)
ui.set_initial_data(data)
ui.open()
/obj/item/device/radio/proc/config(op)
if(radio_controller)

View File

@@ -100,17 +100,9 @@
if (src.loc != usr)
return 0
if(tank_one && href_list["tankone"])
split_gases()
valve_open = 0
tank_one.loc = get_turf(src)
tank_one = null
update_icon()
remove_tank(tank_one)
else if(tank_two && href_list["tanktwo"])
split_gases()
valve_open = 0
tank_two.loc = get_turf(src)
tank_two = null
update_icon()
remove_tank(tank_two)
else if(href_list["open"])
toggle_valve()
else if(attached_device)
@@ -149,29 +141,51 @@
if(attached_device)
overlays += "device"
/obj/item/device/transfer_valve/proc/remove_tank(obj/item/weapon/tank/T)
if(tank_one == T)
split_gases()
tank_one = null
else if(tank_two == T)
split_gases()
tank_two = null
else
return
T.loc = get_turf(src)
update_icon()
/obj/item/device/transfer_valve/proc/merge_gases()
if(valve_open)
return
tank_two.air_contents.volume += tank_one.air_contents.volume
var/datum/gas_mixture/temp
temp = tank_one.air_contents.remove_ratio(1)
tank_two.air_contents.merge(temp)
valve_open = 1
/obj/item/device/transfer_valve/proc/split_gases()
if (!valve_open || !tank_one || !tank_two)
if(!valve_open)
return
valve_open = 0
if(deleted(tank_one) || deleted(tank_two))
return
var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume
var/datum/gas_mixture/temp
temp = tank_two.air_contents.remove_ratio(ratio1)
tank_one.air_contents.merge(temp)
tank_two.air_contents.volume -= tank_one.air_contents.volume
/*
Exadv1: I know this isn't how it's going to work, but this was just to check
it explodes properly when it gets a signal (and it does).
*/
/obj/item/device/transfer_valve/proc/toggle_valve()
if(valve_open==0 && (tank_one && tank_two))
valve_open = 1
if(!valve_open && (tank_one && tank_two))
var/turf/bombturf = get_turf(src)
var/area/A = get_area(bombturf)
@@ -197,15 +211,10 @@
message_admins(log_str, 0, 1)
log_game(log_str)
merge_gases()
spawn(20) // In case one tank bursts
for (var/i=0,i<5,i++)
src.update_icon()
sleep(10)
src.update_icon()
else if(valve_open==1 && (tank_one && tank_two))
split_gases()
valve_open = 0
src.update_icon()
// this doesn't do anything but the timer etc. expects it to be here

View File

@@ -10,31 +10,20 @@
icon = 'icons/obj/decals.dmi'
icon_state = "shock"
/obj/item/borg/stun/attack(var/mob/living/M, var/mob/living/silicon/robot/user)
if(!istype(M))
return
// How the Hell.
/obj/item/borg/stun/apply_hit_effect(mob/living/M, mob/living/silicon/robot/user, var/hit_zone)
if(!istype(user))
var/mob/living/temp = user
if(istype(temp))
temp.drop_from_inventory(src)
qdel(src)
return
M.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been attacked with [src.name] by [user.name] ([user.ckey])</font>")
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Used the [src.name] to attack [M.name] ([M.ckey])</font>")
msg_admin_attack("[user.name] ([user.ckey]) used the [src.name] to attack [M.name] ([M.ckey]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
user.visible_message("<span class='danger'>\The [user] has prodded \the [M] with \a [src]!</span>")
if(!user.cell || !user.cell.checked_use(1250)) //Slightly more than a baton.
user.visible_message("<span class='danger'>\The [user] has prodded \the [M] with its arm!</span>")
return
if (M.stuttering < 5)
M.stuttering = 5
M.stun_effect_act(0, 70, check_zone(user.zone_sel.selecting), src)
user.visible_message("<span class='danger'>\The [user] has prodded \the [M] with \a [src]!</span>")
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
M.apply_effect(5, STUTTER)
M.stun_effect_act(0, 70, check_zone(hit_zone), src)
if(ishuman(M))
var/mob/living/carbon/human/H = M
H.forcesay(hit_appends)

View File

@@ -65,24 +65,42 @@
var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting)
if(affecting.open == 0)
if(!affecting.bandage())
if(affecting.is_bandaged())
user << "<span class='warning'>The wounds on [M]'s [affecting.name] have already been bandaged.</span>"
return 1
else
user.visible_message("<span class='notice'>\The [user] starts treating [M]'s [affecting.name].</span>", \
"<span class='notice'>You start treating [M]'s [affecting.name].</span>" )
var/used = 0
for (var/datum/wound/W in affecting.wounds)
if (W.internal)
continue
if(W.bandaged)
continue
if(used == amount)
break
if(!do_mob(user, M, W.damage/5))
user << "<span class='notice'>You must stand still to bandage wounds.</span>"
break
if (W.current_stage <= W.max_bleeding_stage)
user.visible_message("<span class='notice'>\The [user] bandages [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You bandage [W.desc] on [M]'s [affecting.name].</span>" )
user.visible_message("<span class='notice'>\The [user] bandages \a [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You bandage \a [W.desc] on [M]'s [affecting.name].</span>" )
//H.add_side_effect("Itch")
else if (istype(W,/datum/wound/bruise))
user.visible_message("<span class='notice'>\The [user] places a bruise patch over [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You place a bruise patch over [W.desc] on [M]'s [affecting.name].</span>" )
user.visible_message("<span class='notice'>\The [user] places a bruise patch over \a [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You place a bruise patch over \a [W.desc] on [M]'s [affecting.name].</span>" )
else
user.visible_message("<span class='notice'>\The [user] places a bandaid over [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You place a bandaid over [W.desc] on [M]'s [affecting.name].</span>" )
use(1)
user.visible_message("<span class='notice'>\The [user] places a bandaid over \a [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You place a bandaid over \a [W.desc] on [M]'s [affecting.name].</span>" )
W.bandage()
used++
affecting.update_damages()
if(used == amount)
if(affecting.is_bandaged())
user << "<span class='warning'>\The [src] is used up.</span>"
else
user << "<span class='warning'>\The [src] is used up, but there are more wounds to treat on \the [affecting.name].</span>"
use(used)
else
if (can_operate(H)) //Checks if mob is lying down on table for surgery
if (do_surgery(H,user,src))
@@ -108,13 +126,19 @@
var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting)
if(affecting.open == 0)
if(!affecting.salve())
if(affecting.is_salved())
user << "<span class='warning'>The wounds on [M]'s [affecting.name] have already been salved.</span>"
return 1
else
user.visible_message("<span class='notice'>[user] salves wounds on [M]'s [affecting.name].</span>", \
"<span class='notice'>You salve wounds on [M]'s [affecting.name].</span>" )
user.visible_message("<span class='notice'>\The [user] starts salving wounds on [M]'s [affecting.name].</span>", \
"<span class='notice'>You start salving the wounds on [M]'s [affecting.name].</span>" )
if(!do_mob(user, M, 10))
user << "<span class='notice'>You must stand still to salve wounds.</span>"
return 1
user.visible_message("<span class='notice'>[user] salved wounds on [M]'s [affecting.name].</span>", \
"<span class='notice'>You salved wounds on [M]'s [affecting.name].</span>" )
use(1)
affecting.salve()
else
if (can_operate(H)) //Checks if mob is lying down on table for surgery
if (do_surgery(H,user,src))
@@ -127,7 +151,7 @@
singular_name = "advanced trauma kit"
desc = "An advanced trauma kit for severe injuries."
icon_state = "traumakit"
heal_brute = 12
heal_brute = 0
origin_tech = list(TECH_BIO = 1)
/obj/item/stack/medical/advanced/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob)
@@ -139,29 +163,44 @@
var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting)
if(affecting.open == 0)
var/bandaged = affecting.bandage()
var/disinfected = affecting.disinfect()
if(!(bandaged || disinfected))
if(affecting.is_bandaged() && affecting.is_disinfected())
user << "<span class='warning'>The wounds on [M]'s [affecting.name] have already been treated.</span>"
return 1
else
user.visible_message("<span class='notice'>\The [user] starts treating [M]'s [affecting.name].</span>", \
"<span class='notice'>You start treating [M]'s [affecting.name].</span>" )
var/used = 0
for (var/datum/wound/W in affecting.wounds)
if (W.internal)
continue
if (W.bandaged && W.disinfected)
continue
if(used == amount)
break
if(!do_mob(user, M, W.damage/5))
user << "<span class='notice'>You must stand still to bandage wounds.</span>"
break
if (W.current_stage <= W.max_bleeding_stage)
user.visible_message("<span class='notice'>\The [user] cleans [W.desc] on [M]'s [affecting.name] and seals the edges with bioglue.</span>", \
"<span class='notice'>You clean and seal [W.desc] on [M]'s [affecting.name].</span>" )
user.visible_message("<span class='notice'>\The [user] cleans \a [W.desc] on [M]'s [affecting.name] and seals the edges with bioglue.</span>", \
"<span class='notice'>You clean and seal \a [W.desc] on [M]'s [affecting.name].</span>" )
//H.add_side_effect("Itch")
else if (istype(W,/datum/wound/bruise))
user.visible_message("<span class='notice'>\The [user] places a medical patch over [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You place a medical patch over [W.desc] on [M]'s [affecting.name].</span>" )
user.visible_message("<span class='notice'>\The [user] places a medical patch over \a [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You place a medical patch over \a [W.desc] on [M]'s [affecting.name].</span>" )
else
user.visible_message("<span class='notice'>\The [user] smears some bioglue over [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You smear some bioglue over [W.desc] on [M]'s [affecting.name].</span>" )
if (bandaged)
affecting.heal_damage(heal_brute,0)
use(1)
user.visible_message("<span class='notice'>\The [user] smears some bioglue over \a [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You smear some bioglue over \a [W.desc] on [M]'s [affecting.name].</span>" )
W.bandage()
W.disinfect()
W.heal_damage(heal_brute)
used++
affecting.update_damages()
if(used == amount)
if(affecting.is_bandaged())
user << "<span class='warning'>\The [src] is used up.</span>"
else
user << "<span class='warning'>\The [src] is used up, but there are more wounds to treat on \the [affecting.name].</span>"
use(used)
else
if (can_operate(H)) //Checks if mob is lying down on table for surgery
if (do_surgery(H,user,src))
@@ -174,7 +213,7 @@
singular_name = "advanced burn kit"
desc = "An advanced treatment kit for severe burns."
icon_state = "burnkit"
heal_burn = 12
heal_burn = 0
origin_tech = list(TECH_BIO = 1)
@@ -187,14 +226,20 @@
var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting)
if(affecting.open == 0)
if(!affecting.salve())
if(affecting.is_salved())
user << "<span class='warning'>The wounds on [M]'s [affecting.name] have already been salved.</span>"
return 1
else
user.visible_message("<span class='notice'>\The [user] starts salving wounds on [M]'s [affecting.name].</span>", \
"<span class='notice'>You start salving the wounds on [M]'s [affecting.name].</span>" )
if(!do_mob(user, M, 10))
user << "<span class='notice'>You must stand still to salve wounds.</span>"
return 1
user.visible_message( "<span class='notice'>[user] covers wounds on [M]'s [affecting.name] with regenerative membrane.</span>", \
"<span class='notice'>You cover wounds on [M]'s [affecting.name] with regenerative membrane.</span>" )
affecting.heal_damage(0,heal_burn)
use(1)
affecting.salve()
else
if (can_operate(H)) //Checks if mob is lying down on table for surgery
if (do_surgery(H,user,src))

View File

@@ -137,6 +137,7 @@
if (istype(O, /obj/item/stack))
var/obj/item/stack/S = O
S.amount = produced
S.add_to_stacks(user)
if (istype(O, /obj/item/weapon/storage)) //BubbleWrap - so newly formed boxes are empty
for (var/obj/item/I in O)
@@ -191,7 +192,7 @@
else
if(get_amount() < used)
return 0
for(var/i = 1 to uses_charge)
for(var/i = 1 to charge_costs.len)
var/datum/matter_synth/S = synths[i]
S.use_charge(charge_costs[i] * used) // Doesn't need to be deleted
return 1
@@ -264,8 +265,8 @@
return 0
var/datum/matter_synth/S = synths[1]
. = round(S.get_charge() / charge_costs[1])
if(uses_charge > 1)
for(var/i = 2 to uses_charge)
if(charge_costs.len > 1)
for(var/i = 2 to charge_costs.len)
S = synths[i]
. = min(., round(S.get_charge() / charge_costs[i]))
return
@@ -284,13 +285,13 @@
return
return max_amount
/obj/item/stack/proc/add_to_stacks(mob/usr as mob)
for (var/obj/item/stack/item in usr.loc)
/obj/item/stack/proc/add_to_stacks(mob/user as mob)
for (var/obj/item/stack/item in user.loc)
if (item==src)
continue
var/transfer = src.transfer_to(item)
if (transfer)
usr << "You add a new [item.singular_name] to the stack. It now contains [item.amount] [item.singular_name]\s."
user << "<span class='notice'>You add a new [item.singular_name] to the stack. It now contains [item.amount] [item.singular_name]\s.</span>"
if(!amount)
break

View File

@@ -71,7 +71,7 @@
var/uses = 10
/obj/item/weapon/card/emag/resolve_attackby(atom/A, mob/user)
var/used_uses = A.emag_act(uses, user)
var/used_uses = A.emag_act(uses, user, src)
if(used_uses < 0)
return ..(A, user)

View File

@@ -5,23 +5,131 @@
/obj/item/weapon/circuitboard/air_management
name = T_BOARD("atmosphere monitoring console")
build_path = /obj/machinery/computer/general_air_control
var/console_name
var/frequency = 1439
var/list/sensors = list()
var/list/sensor_information = list()
/obj/item/weapon/circuitboard/air_management/tank_control
name = T_BOARD("tank control")
build_path = /obj/machinery/computer/general_air_control/large_tank_control
frequency = 1441
var/input_tag
var/output_tag
var/list/input_info = list()
var/list/output_info = list()
var/input_flow_setting = 200
var/pressure_setting = ONE_ATMOSPHERE * 45
/obj/item/weapon/circuitboard/air_management/supermatter_core
name = T_BOARD("core control")
build_path = /obj/machinery/computer/general_air_control/supermatter_core
frequency = 1438
var/input_tag
var/output_tag
var/list/input_info = list()
var/list/output_info = list()
var/input_flow_setting = 700
var/pressure_setting = 100
/obj/item/weapon/circuitboard/air_management/injector_control
name = T_BOARD("injector control")
build_path = /obj/machinery/computer/general_air_control/fuel_injection
var/device_tag
var/list/device_info
var/automation = 0
var/cutoff_temperature = 2000
var/on_temperature = 1200
/************
* Construct *
************/
/obj/item/weapon/circuitboard/air_management/construct(var/obj/machinery/computer/general_air_control/C)
if (..(C))
C.frequency = frequency
if(console_name)
C.name = console_name
C.set_frequency(frequency)
C.sensors = sensors.Copy()
C.sensor_information = sensor_information.Copy()
return 1
/obj/item/weapon/circuitboard/air_management/tank_control/construct(var/obj/machinery/computer/general_air_control/large_tank_control/LTC)
if(..(LTC))
LTC.input_tag = input_tag
LTC.output_tag = output_tag
LTC.input_info = input_info.Copy()
LTC.output_info = output_info.Copy()
LTC.input_flow_setting = input_flow_setting
LTC.pressure_setting = pressure_setting
return 1
/obj/item/weapon/circuitboard/air_management/supermatter_core/construct(var/obj/machinery/computer/general_air_control/supermatter_core/SC)
if(..(SC))
SC.input_tag = input_tag
SC.output_tag = output_tag
SC.input_info = input_info.Copy()
SC.output_info = output_info.Copy()
SC.input_flow_setting = input_flow_setting
SC.pressure_setting = input_flow_setting
return 1
/obj/item/weapon/circuitboard/air_management/injector_control/construct(var/obj/machinery/computer/general_air_control/fuel_injection/FI)
if(..(FI))
FI.device_tag = device_tag
FI.device_info = device_info.Copy()
FI.automation = automation
FI.cutoff_temperature = cutoff_temperature
FI.on_temperature = on_temperature
return 1
/**************
* Deconstruct *
**************/
/obj/item/weapon/circuitboard/air_management/deconstruct(var/obj/machinery/computer/general_air_control/C)
if (..(C))
console_name = C.name
frequency = C.frequency
sensors = C.sensors.Copy()
sensor_information = C.sensor_information.Copy()
return 1
/obj/item/weapon/circuitboard/air_management/tank_control/deconstruct(var/obj/machinery/computer/general_air_control/large_tank_control/LTC)
if(..(LTC))
input_tag = LTC.input_tag
output_tag = LTC.output_tag
input_info = LTC.input_info.Copy()
output_info = LTC.output_info.Copy()
input_flow_setting = LTC.input_flow_setting
pressure_setting = LTC.pressure_setting
return 1
/obj/item/weapon/circuitboard/air_management/supermatter_core/deconstruct(var/obj/machinery/computer/general_air_control/supermatter_core/SC)
if(..(SC))
input_tag = SC.input_tag
output_tag = SC.output_tag
input_info = SC.input_info.Copy()
output_info = SC.output_info.Copy()
input_flow_setting = SC.input_flow_setting
pressure_setting = SC.input_flow_setting
return 1
/obj/item/weapon/circuitboard/air_management/injector_control/deconstruct(var/obj/machinery/computer/general_air_control/fuel_injection/FI)
if(..(FI))
device_tag = FI.device_tag
device_info = FI.device_info.Copy()
automation = FI.automation
cutoff_temperature = FI.cutoff_temperature
on_temperature = FI.on_temperature
return 1

View File

@@ -0,0 +1,31 @@
#ifndef T_BOARD
#error T_BOARD macro is not defined but we need it!
#endif
/obj/item/weapon/circuitboard/holodeckcontrol
name = T_BOARD("holodeck control console")
build_path = /obj/machinery/computer/HolodeckControl
origin_tech = "programming=2;bluespace=2"
var/last_to_emag
var/linkedholodeck_area
var/list/supported_programs
var/list/restricted_programs
/obj/item/weapon/circuitboard/holodeckcontrol/construct(var/obj/machinery/computer/HolodeckControl/HC)
if (..(HC))
HC.supported_programs = supported_programs.Copy()
HC.restricted_programs = restricted_programs.Copy()
if(linkedholodeck_area)
HC.linkedholodeck = locate(linkedholodeck_area)
if(last_to_emag)
HC.last_to_emag = last_to_emag
HC.emagged = 1
HC.safety_disabled = 1
/obj/item/weapon/circuitboard/holodeckcontrol/deconstruct(var/obj/machinery/computer/HolodeckControl/HC)
if (..(HC))
linkedholodeck_area = HC.linkedholodeck_area
supported_programs = HC.supported_programs.Copy()
restricted_programs = HC.restricted_programs.Copy()
last_to_emag = HC.last_to_emag
HC.emergencyShutdown()

View File

@@ -38,9 +38,11 @@
target.clean_blood()
return
/obj/item/weapon/soap/attack(mob/target as mob, mob/user as mob)
//attack_as_weapon
/obj/item/weapon/soap/attack(mob/living/target, mob/living/user, var/target_zone)
if(target && user && ishuman(target) && ishuman(user) && !target.stat && !user.stat && user.zone_sel &&user.zone_sel.selecting == "mouth" )
user.visible_message("<span class='danger'>\The [user] washes \the [target]'s mouth out with soap!</span>")
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //prevent spam
return
..()

View File

@@ -114,6 +114,9 @@
if(!do_after(user,50))
return
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN)
user.do_attack_animation(M)
M.visible_message("<span class='danger'>\The [M] has been injected with \the [src] by \the [user].</span>")
var/mob/living/carbon/human/H = M

View File

@@ -46,6 +46,7 @@
if (ismob(target) || istype(target, /turf/unsimulated) || istype(target, /turf/simulated/shuttle) || istype(target, /obj/item/weapon/storage/) || istype(target, /obj/item/clothing/accessory/storage/) || istype(target, /obj/item/clothing/under))
return
user << "Planting explosives..."
user.do_attack_animation(target)
if(do_after(user, 50) && in_range(user, target))
user.drop_item()

View File

@@ -61,7 +61,7 @@
qdel(src)
/obj/item/weapon/a_gift/attack_self(mob/M as mob)
var/gift_type = pick(/obj/item/weapon/sord,
var/gift_type = pick(
/obj/item/weapon/storage/wallet,
/obj/item/weapon/storage/photo_album,
/obj/item/weapon/storage/box/snappops,
@@ -80,7 +80,6 @@
/obj/item/weapon/bikehorn,
/obj/item/weapon/beach_ball,
/obj/item/weapon/beach_ball/holoball,
/obj/item/weapon/banhammer,
/obj/item/toy/balloon,
/obj/item/toy/blink,
/obj/item/toy/crossbow,

View File

@@ -49,7 +49,7 @@
ear_safety += 1
//Flashing everyone
if(eye_safety < 1)
if(eye_safety < FLASH_PROTECTION_MODERATE)
flick("e_flash", M.flash)
M.Stun(2)
M.Weaken(10)

View File

@@ -16,7 +16,7 @@
var/turf/T = get_turf(src)
playsound(T, 'sound/effects/phasein.ogg', 100, 1)
for(var/mob/living/carbon/human/M in viewers(T, null))
if(M:eyecheck() <= 0)
if(M.eyecheck() < FLASH_PROTECTION_MODERATE)
flick("e_flash", M.flash)
for(var/i=1, i<=deliveryamt, i++)

View File

@@ -73,6 +73,9 @@
msg_admin_attack("[key_name(user)] attempted to handcuff [key_name(H)]")
feedback_add_details("handcuffs","H")
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(H)
user.visible_message("<span class='danger'>\The [user] has put [cuff_type] on \the [H]!</span>")
// Apply cuffs.

View File

@@ -30,18 +30,18 @@
if (!istype(M, /mob/living/carbon))
return
if (user && src.imp)
for (var/mob/O in viewers(M, null))
O.show_message("<span class='warning'>[user] is attemping to implant [M].</span>", 1)
M.visible_message("<span class='warning'>[user] is attemping to implant [M].</span>")
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN)
user.do_attack_animation(M)
var/turf/T1 = get_turf(M)
if (T1 && ((M == user) || do_after(user, 50)))
if(user && M && (get_turf(M) == T1) && src && src.imp)
for (var/mob/O in viewers(M, null))
O.show_message("<span class='warning'>[M] has been implanted by [user].</span>", 1)
M.visible_message("<span class='warning'>[M] has been implanted by [user].</span>")
admin_attack_log(user, M, "Implanted using \the [src.name] ([src.imp.name])", "Implanted with \the [src.name] ([src.imp.name])", "used an implanter, [src.name] ([src.imp.name]), on")
user.show_message("<span class='warning'>You implanted the implant into [M].</span>")
if(src.imp.implanted(M))
src.imp.loc = M
src.imp.imp_in = M

View File

@@ -34,6 +34,9 @@
else
return ..()
if(!M.can_eat(src))
return
if (reagents.total_volume > 0)
reagents.trans_to_mob(M, reagents.total_volume, CHEM_INGEST)
if(M == user)
@@ -82,13 +85,6 @@
return
return ..()
/obj/item/weapon/material/kitchen/utensil/knife/attack(target as mob, mob/living/user as mob)
if ((CLUMSY in user.mutations) && prob(50))
user << "<span class='warning'>You somehow managed to cut yourself with the [src].</span>"
user.take_organ_damage(20)
return
return ..()
/obj/item/weapon/material/kitchen/utensil/knife/plastic
default_material = "plastic"
@@ -108,6 +104,7 @@
/obj/item/weapon/material/kitchen/rollingpin/attack(mob/living/M as mob, mob/living/user as mob)
if ((CLUMSY in user.mutations) && prob(50))
user << "<span class='warning'>The [src] slips out of your hand and hits your head.</span>"
user.drop_from_inventory(src)
user.take_organ_damage(10)
user.Paralyse(2)
return

View File

@@ -64,9 +64,8 @@
processing_objects -= src
..()
/obj/item/weapon/material/attack()
if(!..())
return
/obj/item/weapon/material/apply_hit_effect()
..()
if(!unbreakable)
if(material.is_brittle())
health = 0

View File

@@ -55,7 +55,6 @@
icon = 'icons/obj/weapons.dmi'
icon_state = "hoe"
item_state = "hoe"
flags = CONDUCT | NOBLUDGEON
force_divisor = 0.25 // 5 with weight 20 (steel)
thrown_force_divisor = 0.25 // as above
w_class = 2

View File

@@ -18,6 +18,7 @@ var/list/tape_roll_applications = list()
anchored = 1
var/lifted = 0
var/crumpled = 0
var/tape_dir = 0
var/icon_base
/obj/item/tape/New()
@@ -32,7 +33,7 @@ var/list/tape_roll_applications = list()
/obj/item/taperoll/police
name = "police tape"
desc = "A roll of police tape used to block off crime scenes from the public."
icon_state = "police_start"
icon_state = "police"
tape_type = /obj/item/tape/police
icon_base = "police"
@@ -45,7 +46,7 @@ var/list/tape_roll_applications = list()
/obj/item/taperoll/engineering
name = "engineering tape"
desc = "A roll of engineering tape used to block off working areas from the public."
icon_state = "engineering_start"
icon_state = "engineering"
tape_type = /obj/item/tape/engineering
icon_base = "engineering"
@@ -55,58 +56,146 @@ var/list/tape_roll_applications = list()
req_one_access = list(access_engine,access_atmospherics)
icon_base = "engineering"
/obj/item/taperoll/atmos
name = "atmospherics tape"
desc = "A roll of atmospherics tape used to block off working areas from the public."
icon_state = "atmos"
tape_type = /obj/item/tape/atmos
icon_base = "atmos"
/obj/item/tape/atmos
name = "atmospherics tape"
desc = "A length of atmospherics tape. Better not cross it."
req_one_access = list(access_engine,access_atmospherics)
icon_base = "atmos"
/obj/item/taperoll/update_icon()
overlays.Cut()
if(ismob(loc))
if(!start)
overlays += "start"
else
overlays += "stop"
/obj/item/taperoll/dropped(mob/user)
..()
update_icon()
/obj/item/taperoll/pickup(mob/user)
..()
update_icon()
/obj/item/taperoll/attack_self(mob/user as mob)
if(icon_state == "[icon_base]_start")
if(!start)
start = get_turf(src)
usr << "<span class='notice'>You place the first end of \the [src].</span>"
icon_state = "[icon_base]_stop"
update_icon()
else
icon_state = "[icon_base]_start"
end = get_turf(src)
if(start.y != end.y && start.x != end.x || start.z != end.z)
start = null
update_icon()
usr << "<span class='notice'>\The [src] can only be laid horizontally or vertically.</span>"
return
var/turf/cur = start
var/dir
if (start.x == end.x)
var/d = end.y-start.y
if(d) d = d/abs(d)
end = get_turf(locate(end.x,end.y+d,end.z))
dir = "v"
if(start == end)
// spread tape in all directions, provided there is a wall/window
var/turf/T
var/possible_dirs = 0
for(var/dir in cardinal)
T = get_step(start, dir)
if(T && T.density)
possible_dirs += dir
else
var/d = end.x-start.x
if(d) d = d/abs(d)
end = get_turf(locate(end.x+d,end.y,end.z))
dir = "h"
for(var/obj/structure/window/W in T)
if(W.is_fulltile() || W.dir == reverse_dir[dir])
possible_dirs += dir
if(!possible_dirs)
start = null
update_icon()
usr << "<span class='notice'>You can't place \the [src] here.</span>"
return
if(possible_dirs & (NORTH|SOUTH))
var/obj/item/tape/TP = new tape_type(start)
for(var/dir in list(NORTH, SOUTH))
if (possible_dirs & dir)
TP.tape_dir += dir
TP.icon_state = "[TP.icon_base]_[TP.tape_dir]"
if(possible_dirs & (EAST|WEST))
var/obj/item/tape/TP = new tape_type(start)
for(var/dir in list(EAST, WEST))
if (possible_dirs & dir)
TP.tape_dir += dir
TP.icon_state = "[TP.icon_base]_[TP.tape_dir]"
start = null
update_icon()
usr << "<span class='notice'>You finish placing \the [src].</span>"
return
var/turf/cur = start
var/orientation = get_dir(start, end)
var/dir = 0
switch(orientation)
if(NORTH, SOUTH) dir = NORTH|SOUTH // North-South taping
if(EAST, WEST) dir = EAST|WEST // East-West taping
var/can_place = 1
while (cur!=end && can_place)
while (can_place)
if(cur.density == 1)
can_place = 0
else if (istype(cur, /turf/space))
can_place = 0
else
for(var/obj/O in cur)
if(!istype(O, /obj/item/tape) && O.density)
if(O.density)
can_place = 0
break
if(cur == end)
break
cur = get_step_towards(cur,end)
if (!can_place)
start = null
update_icon()
usr << "<span class='warning'>You can't run \the [src] through that!</span>"
return
cur = start
var/tapetest = 0
while (cur!=end)
for(var/obj/item/tape/Ptest in cur)
if(Ptest.icon_state == "[Ptest.icon_base]_[dir]")
var/tapetest
var/tape_dir
while (1)
tapetest = 0
tape_dir = dir
if(cur == start)
var/turf/T = get_step(start, reverse_dir[orientation])
if(T && !T.density)
tape_dir = orientation
for(var/obj/structure/window/W in T)
if(W.is_fulltile() || W.dir == orientation)
tape_dir = dir
else if(cur == end)
var/turf/T = get_step(end, orientation)
if(T && !T.density)
tape_dir = reverse_dir[orientation]
for(var/obj/structure/window/W in T)
if(W.is_fulltile() || W.dir == reverse_dir[orientation])
tape_dir = dir
for(var/obj/item/tape/T in cur)
if((T.tape_dir == tape_dir) && (T.icon_base == icon_base))
tapetest = 1
if(tapetest != 1)
var/obj/item/tape/P = new tape_type(cur)
P.icon_state = "[P.icon_base]_[dir]"
break
if(!tapetest)
var/obj/item/tape/T = new tape_type(cur)
T.icon_state = "[T.icon_base]_[tape_dir]"
T.tape_dir = tape_dir
if(tape_dir & SOUTH)
T.layer += 0.1 // Must always show above other tapes
if(cur == end)
break
cur = get_step_towards(cur,end)
start = null
update_icon()
usr << "<span class='notice'>You finish placing \the [src].</span>"
return
/obj/item/taperoll/afterattack(var/atom/A, mob/user as mob, proximity)
if(!proximity)
@@ -149,6 +238,8 @@ var/list/tape_roll_applications = list()
add_fingerprint(M)
if (!allowed(M)) //only select few learn art of not crumpling the tape
M << "<span class='warning'>You are not supposed to go past [src]...</span>"
if(M.a_intent == I_HELP)
return 0
crumple()
return ..(mover)
@@ -158,13 +249,45 @@ var/list/tape_roll_applications = list()
/obj/item/tape/attack_hand(mob/user as mob)
if (user.a_intent == I_HELP && src.allowed(user))
user.show_viewers("<span class='notice'>\The [user] lifts \the [src], allowing passage.</span>")
crumple()
lifted = 1
spawn(200)
lifted = 0
for(var/obj/item/tape/T in gettapeline())
T.lift(100) //~10 seconds
else
breaktape(null, user)
/obj/item/tape/proc/lift(time)
lifted = 1
layer = 8
spawn(time)
lifted = 0
layer = initial(layer)
// Returns a list of all tape objects connected to src, including itself.
/obj/item/tape/proc/gettapeline()
var/list/dirs = list()
if(tape_dir & NORTH)
dirs += NORTH
if(tape_dir & SOUTH)
dirs += SOUTH
if(tape_dir & WEST)
dirs += WEST
if(tape_dir & EAST)
dirs += EAST
var/list/obj/item/tape/tapeline = list()
for (var/obj/item/tape/T in get_turf(src))
tapeline += T
for(var/dir in dirs)
var/turf/cur = get_step(src, dir)
var/not_found = 0
while (!not_found)
not_found = 1
for (var/obj/item/tape/T in cur)
tapeline += T
not_found = 0
cur = get_step(cur, dir)
return tapeline
/obj/item/tape/proc/breaktape(obj/item/weapon/W as obj, mob/user as mob)
@@ -173,27 +296,11 @@ var/list/tape_roll_applications = list()
return
user.show_viewers("<span class='notice'>\The [user] breaks \the [src]!</span>")
var/dir[2]
var/icon_dir = src.icon_state
if(icon_dir == "[src.icon_base]_h")
dir[1] = EAST
dir[2] = WEST
if(icon_dir == "[src.icon_base]_v")
dir[1] = NORTH
dir[2] = SOUTH
for (var/obj/item/tape/T in gettapeline())
if(T == src)
continue
if(T.tape_dir & get_dir(T, src))
qdel(T)
for(var/i=1;i<3;i++)
var/N = 0
var/turf/cur = get_step(src,dir[i])
while(N != 1)
N = 1
for (var/obj/item/tape/P in cur)
if(P.icon_state == icon_dir)
N = 0
qdel(P)
cur = get_step(cur,dir[i])
qdel(src)
qdel(src) //TODO: Dropping a trash item holding fibers/fingerprints of all broken tape parts
return

View File

@@ -639,35 +639,36 @@
icon_state = "light"
desc = "This box is shaped on the inside so that only light tubes and bulbs fit."
item_state = "syringe_kit"
storage_slots=21
can_hold = list(/obj/item/weapon/light/tube, /obj/item/weapon/light/bulb)
max_storage_space = 42 //holds 21 items of w_class 2
use_to_pickup = 1 // for picking up broken bulbs, not that most people will try
/obj/item/weapon/storage/box/lights/bulbs/New()
/obj/item/weapon/storage/box/lights/New()
..()
make_exact_fit()
/obj/item/weapon/storage/box/lights/bulbs/New()
for(var/i = 0; i < 21; i++)
new /obj/item/weapon/light/bulb(src)
..()
/obj/item/weapon/storage/box/lights/tubes
name = "box of replacement tubes"
icon_state = "lighttube"
/obj/item/weapon/storage/box/lights/tubes/New()
..()
for(var/i = 0; i < 21; i++)
new /obj/item/weapon/light/tube(src)
..()
/obj/item/weapon/storage/box/lights/mixed
name = "box of replacement lights"
icon_state = "lightmixed"
/obj/item/weapon/storage/box/lights/mixed/New()
..()
for(var/i = 0; i < 14; i++)
new /obj/item/weapon/light/tube(src)
for(var/i = 0; i < 7; i++)
new /obj/item/weapon/light/bulb(src)
..()
/obj/item/weapon/storage/box/freezer
name = "portable freezer"

View File

@@ -130,8 +130,7 @@
/obj/item/weapon/storage/firstaid/surgery
name = "surgery kit"
desc = "Contains tools for surgery."
storage_slots = 10
desc = "Contains tools for surgery. Has precise foam fitting for safe transport."
/obj/item/weapon/storage/firstaid/surgery/New()
..()
@@ -146,7 +145,8 @@
new /obj/item/weapon/bonegel(src)
new /obj/item/weapon/FixOVein(src)
new /obj/item/stack/medical/advanced/bruise_pack(src)
return
make_exact_fit()
/*
* Pill Bottles

View File

@@ -0,0 +1,87 @@
// -----------------------------
// Laundry Basket
// -----------------------------
// An item designed for hauling the belongings of a character.
// So this cannot be abused for other uses, we make it two-handed and inable to have its storage looked into.
/obj/item/weapon/storage/laundry_basket
name = "laundry basket"
icon = 'icons/obj/janitor.dmi'
icon_state = "laundry-empty"
item_state = "laundry"
desc = "The peak of thousands of years of laundry evolution."
w_class = 5
max_w_class = 4
max_storage_space = 25 //20 for clothes + a bit of additional space for non-clothing items that were worn on body
storage_slots = 14
use_to_pickup = 1
allow_quick_empty = 1
allow_quick_gather = 1
collection_mode = 1
var/linked
/obj/item/weapon/storage/laundry_basket/attack_hand(mob/user as mob)
if(ishuman(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/external/temp = H.get_organ("r_hand")
if (user.hand)
temp = H.get_organ("l_hand")
if(!temp)
user << "<span class='warning'>You need two hands to pick this up!</span>"
return
if(user.get_inactive_hand())
user << "<span class='warning'>You need your other hand to be empty</span>"
return
return ..()
/obj/item/weapon/storage/laundry_basket/attack_self(mob/user as mob)
var/turf/T = get_turf(user)
user << "<span class='notice'>You dump the [src]'s contents onto \the [T].</span>"
return ..()
/obj/item/weapon/storage/laundry_basket/pickup(mob/user)
var/obj/item/weapon/storage/laundry_basket/offhand/O = new(user)
O.name = "[name] - second hand"
O.desc = "Your second grip on the [name]."
O.linked = src
user.put_in_inactive_hand(O)
linked = O
return
/obj/item/weapon/storage/laundry_basket/update_icon()
if(contents.len)
icon_state = "laundry-full"
else
icon_state = "laundry-empty"
return
/obj/item/weapon/storage/laundry_basket/MouseDrop(obj/over_object as obj)
if(over_object == usr)
return
else
return ..()
/obj/item/weapon/storage/laundry_basket/dropped(mob/user as mob)
qdel(linked)
return ..()
/obj/item/weapon/storage/laundry_basket/show_to(mob/user as mob)
return
/obj/item/weapon/storage/laundry_basket/open(mob/user as mob)
//Offhand
/obj/item/weapon/storage/laundry_basket/offhand
icon = 'icons/obj/weapons.dmi'
icon_state = "offhand"
name = "second hand"
use_to_pickup = 0
/obj/item/weapon/storage/laundry_basket/offhand/dropped(mob/user as mob)
user.drop_from_inventory(linked)
return

View File

@@ -235,11 +235,15 @@
usr << "<span class='notice'>[src] is full, make some space.</span>"
return 0 //Storage item is full
if(can_hold.len && !is_type_in_list(W, can_hold))
if(!stop_messages)
if (istype(W, /obj/item/weapon/hand_labeler))
if(can_hold.len)
if(!is_type_in_list(W, can_hold))
if(!stop_messages && ! istype(W, /obj/item/weapon/hand_labeler))
usr << "<span class='notice'>[src] cannot hold \the [W].</span>"
return 0
usr << "<span class='notice'>[src] cannot hold [W].</span>"
var/max_instances = can_hold[W.type]
if(max_instances && instances_of_type_in_list(W, contents) >= max_instances)
if(!stop_messages && !istype(W, /obj/item/weapon/hand_labeler))
usr << "<span class='notice'>[src] has no more space specifically for \the [W].</span>"
return 0
if(cant_hold.len && is_type_in_list(W, cant_hold))
@@ -461,6 +465,17 @@
src.quick_empty()
return 1
/obj/item/weapon/storage/proc/make_exact_fit()
storage_slots = contents.len
can_hold.Cut()
max_w_class = 0
max_storage_space = 0
for(var/obj/item/I in src)
can_hold[I.type]++
max_w_class = max(I.w_class, max_w_class)
max_storage_space += I.get_storage_cost()
//Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area).
//Returns -1 if the atom was not found on container.
/atom/proc/storage_depth(atom/container)

View File

@@ -102,71 +102,54 @@
user << "<span class='warning'>[src] is out of charge.</span>"
add_fingerprint(user)
/obj/item/weapon/melee/baton/attack(mob/M, mob/user)
if(status && (CLUMSY in user.mutations) && prob(50))
user << "<span class='danger'>You accidentally hit yourself with the [src]!</span>"
user.Weaken(30)
deductcharge(hitcost)
return
return ..()
if(isrobot(M))
..()
return
/obj/item/weapon/melee/baton/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone)
if(isrobot(target))
return ..()
var/agony = agonyforce
var/stun = stunforce
var/mob/living/L = M
var/obj/item/organ/external/affecting = null
if(ishuman(target))
var/mob/living/carbon/human/H = target
affecting = H.get_organ(hit_zone)
var/target_zone = check_zone(user.zone_sel.selecting)
if(user.a_intent == I_HURT)
if (!..()) //item/attack() does it's own messaging and logs
return 0 // item/attack() will return 1 if they hit, 0 if they missed.
agony *= 0.5 //whacking someone causes a much poorer contact than prodding them.
. = ..()
//whacking someone causes a much poorer electrical contact than deliberately prodding them.
agony *= 0.5
stun *= 0.5
//we can't really extract the actual hit zone from ..(), unfortunately. Just act like they attacked the area they intended to.
else
//copied from human_defense.dm - human defence code should really be refactored some time.
if (ishuman(L))
user.lastattacked = L //are these used at all, if we have logs?
L.lastattacker = user
if (user != L) // Attacking yourself can't miss
target_zone = get_zone_with_miss_chance(user.zone_sel.selecting, L)
if(!target_zone)
L.visible_message("<span class='danger'>\The [user] misses [L] with \the [src]!</span>")
return 0
var/mob/living/carbon/human/H = L
var/obj/item/organ/external/affecting = H.get_organ(target_zone)
else if(!status)
if(affecting)
if(!status)
L.visible_message("<span class='warning'>[L] has been prodded in the [affecting.name] with [src] by [user]. Luckily it was off.</span>")
return 1
target.visible_message("<span class='warning'>[target] has been prodded in the [affecting.name] with [src] by [user]. Luckily it was off.</span>")
else
H.visible_message("<span class='danger'>[L] has been prodded in the [affecting.name] with [src] by [user]!</span>")
target.visible_message("<span class='warning'>[target] has been prodded with [src] by [user]. Luckily it was off.</span>")
else
if(!status)
L.visible_message("<span class='warning'>[L] has been prodded with [src] by [user]. Luckily it was off.</span>")
return 1
if(affecting)
target.visible_message("<span class='danger'>[target] has been prodded in the [affecting.name] with [src] by [user]!</span>")
else
L.visible_message("<span class='danger'>[L] has been prodded with [src] by [user]!</span>")
target.visible_message("<span class='danger'>[target] has been prodded with [src] by [user]!</span>")
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
//stun effects
L.stun_effect_act(stun, agony, target_zone, src)
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
msg_admin_attack("[key_name(user)] stunned [key_name(L)] with the [src].")
if(status)
target.stun_effect_act(stun, agony, hit_zone, src)
msg_admin_attack("[key_name(user)] stunned [key_name(target)] with the [src].")
deductcharge(hitcost)
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(ishuman(target))
var/mob/living/carbon/human/H = target
H.forcesay(hit_appends)
return 1
/obj/item/weapon/melee/baton/emp_act(severity)
if(bcell)
bcell.emp_act(severity) //let's not duplicate code everywhere if we don't have to please.

View File

@@ -1,17 +1,9 @@
/* Weapons
* Contains:
* Banhammer
* Sword
* Classic Baton
*/
/*
* Banhammer
*/
/obj/item/weapon/banhammer/attack(mob/M as mob, mob/user as mob)
M << "<font color='red'><b> You have been banned FOR NO REISIN by [user]</b></font>"
user << "<font color='red'> You have <b>BANNED</b> [M]</font>"
/*
* Classic Baton
*/
@@ -34,33 +26,7 @@
else
user.take_organ_damage(2*force)
return
/*this is already called in ..()
src.add_fingerprint(user)
M.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been attacked with [src.name] by [user.name] ([user.ckey])</font>")
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Used the [src.name] to attack [M.name] ([M.ckey])</font>")
log_attack("<font color='red'>[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])</font>")
*/
if (user.a_intent == I_HURT)
if(!..()) return
//playsound(src.loc, "swing_hit", 50, 1, -1)
if (M.stuttering < 8 && (!(HULK in M.mutations)) /*&& (!istype(H:wear_suit, /obj/item/clothing/suit/judgerobe))*/)
M.stuttering = 8
M.Stun(8)
M.Weaken(8)
for(var/mob/O in viewers(M))
if (O.client) O.show_message("<span class='danger'>\The [M] has been beaten with \the [src] by [user]!</span>", 1, "<span class='warning'>You hear someone fall</span>", 2)
else
playsound(src.loc, 'sound/weapons/Genhit.ogg', 50, 1, -1)
M.Stun(5)
M.Weaken(5)
M.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been attacked with [src.name] by [user.name] ([user.ckey])</font>")
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Used the [src.name] to attack [M.name] ([M.ckey])</font>")
msg_admin_attack("[key_name(user)] attacked [key_name(user)] with [src.name] (INTENT: [uppertext(user.a_intent)])")
src.add_fingerprint(user)
for(var/mob/O in viewers(M))
if (O.client) O.show_message("<span class='danger'>\The [M] has been stunned with \the [src] by [user]!</span>", 1, "<span class='warning'>You hear someone fall</span>", 2)
return ..()
//Telescopic baton
/obj/item/weapon/melee/telebaton

View File

@@ -41,6 +41,10 @@
processing_objects.Remove(src)
if(istype(loc, /obj/item/device/transfer_valve))
var/obj/item/device/transfer_valve/TTV = loc
TTV.remove_tank(src)
..()
/obj/item/weapon/tank/examine(mob/user)
@@ -249,7 +253,10 @@
qdel(src)
else if(pressure > TANK_RUPTURE_PRESSURE)
//world << "<span class='notice'>[x],[y] tank is rupturing: [pressure] kPa, integrity [integrity]</span>"
#ifdef FIREDBG
log_debug("<span class='warning'>[x],[y] tank is rupturing: [pressure] kPa, integrity [integrity]</span>")
#endif
if(integrity <= 0)
var/turf/simulated/T = get_turf(src)
if(!T)
@@ -261,7 +268,10 @@
integrity--
else if(pressure > TANK_LEAK_PRESSURE)
//world << "<span class='notice'>[x],[y] tank is leaking: [pressure] kPa, integrity [integrity]</span>"
#ifdef FIREDBG
log_debug("<span class='warning'>[x],[y] tank is leaking: [pressure] kPa, integrity [integrity]</span>")
#endif
if(integrity <= 0)
var/turf/simulated/T = get_turf(src)
if(!T)

View File

@@ -115,7 +115,7 @@
item_state = "cutters_yellow"
/obj/item/weapon/wirecutters/attack(mob/living/carbon/C as mob, mob/user as mob)
if((C.handcuffed) && (istype(C.handcuffed, /obj/item/weapon/handcuffs/cable)))
if(user.a_intent == I_HELP && (C.handcuffed) && (istype(C.handcuffed, /obj/item/weapon/handcuffs/cable)))
usr.visible_message("\The [usr] cuts \the [C]'s restraints with \the [src]!",\
"You cut \the [C]'s restraints with \the [src]!",\
"You hear cable being cut.")
@@ -337,18 +337,18 @@
if(!E)
return
switch(safety)
if(1)
if(FLASH_PROTECTION_MODERATE)
usr << "<span class='warning'>Your eyes sting a little.</span>"
E.damage += rand(1, 2)
if(E.damage > 12)
user.eye_blurry += rand(3,6)
if(0)
if(FLASH_PROTECTION_NONE)
usr << "<span class='warning'>Your eyes burn.</span>"
E.damage += rand(2, 4)
if(E.damage > 10)
E.damage += rand(4,10)
if(-1)
usr << "<span class='danger'>Your thermals intensify the welder's glow. Your eyes itch and burn severely.</span>"
if(FLASH_PROTECTION_REDUCED)
usr << "<span class='danger'>Your equipment intensify the welder's glow. Your eyes itch and burn severely.</span>"
user.eye_blurry += rand(12,20)
E.damage += rand(12, 16)
if(safety<2)

View File

@@ -1,18 +1,3 @@
/obj/item/weapon/banhammer
desc = "banhammer"
name = "banhammer"
icon = 'icons/obj/items.dmi'
icon_state = "toyhammer"
slot_flags = SLOT_BELT
throwforce = 0
w_class = 2.0
throw_speed = 7
throw_range = 15
attack_verb = list("banned")
suicide_act(mob/user)
viewers(user) << "<span class='danger'>[user] is hitting \himself with the [src.name]! It looks like \he's trying to ban \himself from life.</span>"
return (BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS)
/obj/item/weapon/nullrod
name = "null rod"
@@ -37,6 +22,9 @@
msg_admin_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(M)
if (!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
user << "<span class='danger'>You don't have the dexterity to do this!</span>"
return
@@ -68,27 +56,6 @@
user << "<span class='notice'>You hit the floor with the [src].</span>"
call(/obj/effect/rune/proc/revealrunes)(src)
/obj/item/weapon/sord
name = "\improper SORD"
desc = "This thing is so unspeakably shitty you are having a hard time even holding it."
icon_state = "sord"
item_state = "sord"
slot_flags = SLOT_BELT
force = 2
throwforce = 1
sharp = 1
edge = 1
w_class = 3
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
suicide_act(mob/user)
viewers(user) << "<span class='danger'>[user] is impaling \himself with the [src.name]! It looks like \he's trying to commit suicide.</span>"
return(BRUTELOSS)
/obj/item/weapon/sord/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
playsound(loc, 'sound/weapons/bladeslice.ogg', 50, 1, -1)
return ..()
/obj/item/weapon/energy_net
name = "energy net"
desc = "It's a net made of green energy."

View File

@@ -46,6 +46,10 @@
/obj/proc/CouldUseTopic(var/mob/user)
var/atom/host = nano_host()
if(!isAI(user) && host.Adjacent(user))
// We are -probably- in physical contact with the object, better than how Topics() previously did it and always applied fingerprints.
host.add_fingerprint(user)
else
host.add_hiddenprint(user)
/obj/proc/CouldNotUseTopic(var/mob/user)

View File

@@ -0,0 +1,61 @@
/obj/structure/alien
name = "alien thing"
desc = "There's something alien about this."
icon = 'icons/mob/alien.dmi'
var/health = 50
/obj/structure/alien/proc/healthcheck()
if(health <=0)
density = 0
qdel(src)
return
/obj/structure/alien/bullet_act(var/obj/item/projectile/Proj)
health -= Proj.damage
..()
healthcheck()
return
/obj/structure/alien/ex_act(severity)
switch(severity)
if(1.0)
health-=50
if(2.0)
health-=50
if(3.0)
if (prob(50))
health-=50
else
health-=25
healthcheck()
return
/obj/structure/alien/hitby(AM as mob|obj)
..()
visible_message("<span class='danger'>\The [src] was hit by \the [AM].</span>")
var/tforce = 0
if(ismob(AM))
tforce = 10
else
tforce = AM:throwforce
playsound(loc, 'sound/effects/attackblob.ogg', 100, 1)
health = max(0, health - tforce)
healthcheck()
..()
return
/obj/structure/alien/attack_generic()
attack_hand(usr)
/obj/structure/alien/attackby(var/obj/item/weapon/W, var/mob/user)
health = max(0, health - W.force)
playsound(loc, 'sound/effects/attackblob.ogg', 100, 1)
healthcheck()
..()
return
/obj/structure/alien/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group) return 0
if(istype(mover) && mover.checkpass(PASSGLASS))
return !opacity
return !density

View File

@@ -0,0 +1,96 @@
#define MAX_PROGRESS 100
/obj/structure/alien/egg
desc = "It looks like a weird egg."
name = "egg"
icon_state = "egg_growing"
density = 0
anchored = 1
var/progress = 0
/obj/structure/alien/egg/New()
..()
processing_objects += src
/obj/structure/alien/egg/Destroy()
processing_objects -= src
..()
/obj/structure/alien/egg/CanUseTopic(var/mob/user)
return isobserver(user) ? STATUS_INTERACTIVE : STATUS_CLOSE
/obj/structure/alien/egg/Topic(href, href_list)
if(..())
return 1
if(href_list["spawn"])
attack_ghost(usr)
/obj/structure/alien/egg/process()
progress++
if(progress >= MAX_PROGRESS)
for(var/mob/M in dead_mob_list)
if(istype(M,/mob/dead) && M.client && M.client.prefs && (M.client.prefs.be_special & BE_ALIEN))
M << "<span class='notice'>An alien is ready to hatch! ([ghost_follow_link(src, M)]) (<a href='byond://?src=\ref[src];spawn=1'>spawn</a>)</span>"
processing_objects -= src
update_icon()
/obj/structure/alien/egg/update_icon()
if(progress == -1)
icon_state = "egg_hatched"
else if(progress < MAX_PROGRESS)
icon_state = "egg_growing"
else
icon_state = "egg"
/obj/structure/alien/egg/attack_ghost(var/mob/dead/observer/user)
if(progress == -1) //Egg has been hatched.
return
if(progress < MAX_PROGRESS)
user << "\The [src] has not yet matured."
return
if(!user.MayRespawn(1))
return
// Check for bans properly.
if(jobban_isbanned(user, "Xenomorph"))
user << "<span class='danger'>You are banned from playing a Xenomorph.</span>"
return
var/confirm = alert(user, "Are you sure you want to join as a Xenomorph larva?", "Become Larva", "No", "Yes")
if(!src || confirm != "Yes")
return
if(!user || !user.ckey)
return
if(progress == -1) //Egg has been hatched.
user << "Too slow..."
return
flick("egg_opening",src)
progress = -1 // No harvesting pls.
sleep(5)
if(!src || !user)
visible_message("<span class='alium'>\The [src] writhes with internal motion, but nothing comes out.</span>")
progress = MAX_PROGRESS // Someone else can have a go.
return // What a pain.
// Create the mob, transfer over key.
var/mob/living/carbon/alien/larva/larva = new(get_turf(src))
larva.ckey = user.ckey
spawn(-1)
if(user) qdel(user) // Remove the keyless ghost if it exists.
visible_message("<span class='alium'>\The [src] splits open with a wet slithering noise, and \the [larva] writhes free!</span>")
// Turn us into a hatched egg.
name = "hatched alien egg"
desc += " This one has hatched."
update_icon()
#undef MAX_PROGRESS

View File

@@ -0,0 +1,19 @@
/obj/structure/alien/node
name = "alien weed node"
desc = "Some kind of strange, pulsating structure."
icon_state = "weednode"
health = 100
layer = 3.1
/obj/structure/alien/node/New()
..()
processing_objects += src
/obj/structure/alien/node/Destroy()
processing_objects -= src
..()
/obj/structure/alien/node/process()
if(locate(/obj/effect/plant) in loc)
return
new /obj/effect/plant(get_turf(src), plant_controller.seeds["xenomorph"])

View File

@@ -0,0 +1,50 @@
/obj/structure/alien/resin
name = "resin"
desc = "Looks like some kind of slimy growth."
icon_state = "resin"
density = 1
opacity = 1
anchored = 1
health = 200
/obj/structure/alien/resin/wall
name = "resin wall"
desc = "Purple slime solidified into a wall."
icon_state = "resinwall"
/obj/structure/alien/resin/membrane
name = "resin membrane"
desc = "Purple slime just thin enough to let light pass through."
icon_state = "resinmembrane"
opacity = 0
health = 120
/obj/structure/alien/resin/New()
..()
var/turf/T = get_turf(src)
T.thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT
/obj/structure/alien/resin/Destroy()
var/turf/T = get_turf(src)
T.thermal_conductivity = initial(T.thermal_conductivity)
..()
/obj/structure/alien/resin/attack_hand(var/mob/user)
if (HULK in user.mutations)
visible_message("<span class='danger'>\The [user] destroys \the [name]!</span>")
health = 0
else
// Aliens can get straight through these.
if(istype(user,/mob/living/carbon))
var/mob/living/carbon/M = user
if(locate(/obj/item/organ/xenos/hivenode) in M.internal_organs)
visible_message("<span class='alium'>\The [user] strokes \the [name] and it melts away!</span>")
health = 0
healthcheck()
return
visible_message("<span class='danger'>\The [user] claws at \the [src]!</span>")
// Todo check attack datums.
health -= rand(5,10)
healthcheck()
return

View File

@@ -217,6 +217,15 @@
M.show_message("<span class='notice'>\The [src] has been cut apart by [user] with \the [WT].</span>", 3, "You hear welding.", 2)
qdel(src)
return
if(istype(W, /obj/item/weapon/storage/laundry_basket) && W.contents.len)
var/obj/item/weapon/storage/laundry_basket/LB = W
var/turf/T = get_turf(src)
for(var/obj/item/I in LB.contents)
LB.remove_from_storage(I, T)
user.visible_message("<span class='notice'>[user] empties \the [LB] into \the [src].</span>", \
"<span class='notice'>You empty \the [LB] into \the [src].</span>", \
"<span class='notice'>You hear rustling of clothes.</span>")
return
if(isrobot(user))
return
if(W.loc != user) // This should stop mounted modules ending up outside the module.

View File

@@ -160,6 +160,6 @@
new /obj/item/clothing/suit/storage/hazardvest(src)
new /obj/item/clothing/mask/gas(src)
new /obj/item/weapon/cartridge/atmos(src)
new /obj/item/taperoll/engineering(src)
new /obj/item/taperoll/atmos(src)
new /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/atmos(src)
return

View File

@@ -65,6 +65,8 @@
/obj/structure/closet/secure_closet/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(src.opened)
if(istype(W, /obj/item/weapon/storage/laundry_basket))
return ..(W,user)
if(istype(W, /obj/item/weapon/grab))
var/obj/item/weapon/grab/G = W
if(src.large)

View File

@@ -82,6 +82,9 @@
if(health <= 0)
deflate(1)
/obj/structure/inflatable/CtrlClick()
hand_deflate()
/obj/structure/inflatable/proc/deflate(var/violent=0)
playsound(loc, 'sound/machines/hiss.ogg', 75, 1)
if(violent)
@@ -102,7 +105,7 @@
set category = "Object"
set src in oview(1)
if(isobserver(usr)) //to stop ghosts from deflating
if(isobserver(usr) || usr.restrained() || !usr.Adjacent(src))
return
verbs -= /obj/structure/inflatable/verb/hand_deflate

View File

@@ -1,87 +0,0 @@
/obj/structure/lamarr
name = "lab cage"
icon = 'icons/obj/stationobjs.dmi'
icon_state = "labcage1"
desc = "A glass lab container for storing interesting creatures."
density = 1
anchored = 1
unacidable = 1//Dissolving the case would also delete Lamarr
var/health = 30
var/occupied = 1
var/destroyed = 0
/obj/structure/lamarr/ex_act(severity)
switch(severity)
if (1)
new /obj/item/weapon/material/shard( src.loc )
Break()
qdel(src)
if (2)
if (prob(50))
src.health -= 15
src.healthcheck()
if (3)
if (prob(50))
src.health -= 5
src.healthcheck()
/obj/structure/lamarr/bullet_act(var/obj/item/projectile/Proj)
health -= Proj.damage
..()
src.healthcheck()
return
/obj/structure/lamarr/proc/healthcheck()
if (src.health <= 0)
if (!( src.destroyed ))
src.density = 0
src.destroyed = 1
new /obj/item/weapon/material/shard( src.loc )
playsound(src, "shatter", 70, 1)
Break()
else
playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1)
return
/obj/structure/lamarr/update_icon()
if(src.destroyed)
src.icon_state = "labcageb[src.occupied]"
else
src.icon_state = "labcage[src.occupied]"
return
/obj/structure/lamarr/attackby(obj/item/weapon/W as obj, mob/user as mob)
src.health -= W.force
src.healthcheck()
..()
return
/obj/structure/lamarr/attack_hand(mob/user as mob)
if (src.destroyed)
return
else
usr << "<span class='notice'>You kick the lab cage.</span>"
for(var/mob/O in oviewers())
if ((O.client && !( O.blinded )))
O << "<span class='warning'>[usr] kicks the lab cage.</span>"
src.health -= 2
healthcheck()
return
/obj/structure/lamarr/proc/Break()
if(occupied)
new /obj/item/clothing/mask/facehugger/lamarr(src.loc)
occupied = 0
update_icon()
return
/obj/item/clothing/mask/facehugger/lamarr
name = "Lamarr"
desc = "The worst she might do is attempt to... couple with your head."//hope we don't get sued over a harmless reference, rite?
sterile = 1
gender = FEMALE
/obj/item/clothing/mask/facehugger/lamarr/New()//to prevent deleting it if aliums are disabled
return

View File

@@ -72,6 +72,9 @@ var/global/list/stool_cache = list() //haha stool
/obj/item/weapon/stool/attack(mob/M as mob, mob/user as mob)
if (prob(5) && istype(M,/mob/living))
user.visible_message("<span class='danger'>[user] breaks [src] over [M]'s back!</span>")
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(M)
user.remove_from_mob(src)
dismantle()
qdel(src)

View File

@@ -491,7 +491,23 @@
animate(src, color="#222222", time=5)
set_opacity(1)
/obj/structure/window/reinforced/crescent/attack_hand()
return
/obj/structure/window/reinforced/crescent/attackby()
return
/obj/structure/window/reinforced/crescent/ex_act()
return
/obj/structure/window/reinforced/crescent/hitby()
return
/obj/structure/window/reinforced/crescent/take_damage()
return
/obj/structure/window/reinforced/crescent/shatter()
return
/obj/machinery/button/windowtint
name = "window tint control"

Some files were not shown because too many files have changed in this diff Show More