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: Making Bombs with ZAS:
Make burny fire with lots of burning Get gas to react in an air tank so that it gains pressure. If it gains enough pressure, it goes boom.
Draw off 5000K gas from burny fire The more pressure, the more boom.
Separate gas into oxygen and phoron components If it gains pressure too slowly, it may leak or just rupture instead of exploding.
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.
*/ */
//#define FIREDBG //#define FIREDBG
@@ -268,16 +264,16 @@ turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
//determine how far the reaction can progress //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 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).) //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/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. //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/total_reaction_progress = gas_reaction_progress + liquid_reaction_progress
var/used_fuel = min(total_reaction_progress, reaction_limit) 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 #ifdef FIREDBG
log_debug("gas_fuel = [gas_fuel], liquid_fuel = [liquid_fuel], total_oxidizers = [total_oxidizers]") 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("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("liquid_reaction_progress = [liquid_reaction_progress]")
log_debug("gas_reaction_progress = [gas_reaction_progress]") log_debug("gas_reaction_progress = [gas_reaction_progress]")
log_debug("total_reaction_progress = [total_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 //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() temperature = (starting_energy + vsc.fire_fuel_energy_release * (used_gas_fuel + used_liquid_fuel)) / heat_capacity()
update_values()
#ifdef FIREDBG #ifdef FIREDBG
log_debug("used_gas_fuel = [used_gas_fuel]; used_liquid_fuel = [used_liquid_fuel]; total = [used_fuel]") 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 #endif
update_values()
return firelevel return firelevel
datum/gas_mixture/proc/check_recombustability(list/fuel_objs) datum/gas_mixture/proc/check_recombustability(list/fuel_objs)
@@ -363,27 +359,31 @@ datum/gas_mixture/proc/check_recombustability(list/fuel_objs)
break break
//returns a value between 0 and vsc.fire_firelevel_multiplier //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. //Calculates the firelevel based on one equation instead of having to do this multiple times in different areas.
var/firelevel = 0 var/firelevel = 0
var/total_combustables = (total_fuel + total_oxidizers) 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) if(total_combustables > 0)
//slows down the burning when the concentration of the reactants is low //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 //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))) var/mix_multiplier = 1 / (1 + (5 * ((total_fuel / total_combustables) ** 2)))
#ifdef FIREDBG #ifdef FIREDBG
ASSERT(dampening_multiplier <= 1) ASSERT(damping_multiplier <= 1)
ASSERT(mix_multiplier <= 1) ASSERT(mix_multiplier <= 1)
#endif #endif
//toss everything together -- should produce a value between 0 and fire_firelevel_multiplier //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) 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_NAME = "Fire - Firelevel Constant"
var/fire_firelevel_multiplier_DESC = "Multiplied by the equation for firelevel, affects mainly the extingiushing of fires." 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_NAME = "Fire - Fuel energy release"
var/fire_fuel_energy_release_DESC = "The energy in joule released when burning one mol of a burnable substance" 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 //These control the speed at which fire burns
#define FIRE_GAS_BURNRATE_MULT 1 #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. //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. //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_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 //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. // XGM gas flags.
#define XGM_GAS_FUEL 1 #define XGM_GAS_FUEL 1
@@ -92,4 +92,4 @@
#define ATMOSTANK_OXYGEN 40000 // O2 is also important for airmix, but not as much as N2 as it's only 21% of it. #define ATMOSTANK_OXYGEN 40000 // O2 is also important for airmix, but not as much as N2 as it's only 21% of it.
#define ATMOSTANK_CO2 25000 // CO2 and PH are not critically important for station, only for toxins and alternative coolants, no need to store a lot of those. #define ATMOSTANK_CO2 25000 // CO2 and PH are not critically important for station, only for toxins and alternative coolants, no need to store a lot of those.
#define ATMOSTANK_PHORON 25000 #define ATMOSTANK_PHORON 25000
#define ATMOSTANK_NITROUSOXIDE 10000 // N2O doesn't have a real useful use, i guess it's on station just to allow refilling of sec's riot control canisters? #define ATMOSTANK_NITROUSOXIDE 10000 // N2O doesn't have a real useful use, i guess it's on station just to allow refilling of sec's riot control canisters?

View File

@@ -82,6 +82,7 @@
// Click cooldown // Click cooldown
#define DEFAULT_ATTACK_COOLDOWN 8 //Default timeout for aggressive actions #define DEFAULT_ATTACK_COOLDOWN 8 //Default timeout for aggressive actions
#define DEFAULT_QUICK_COOLDOWN 4
#define MIN_SUPPLIED_LAW_NUMBER 15 #define MIN_SUPPLIED_LAW_NUMBER 15
@@ -114,3 +115,13 @@
#define MOB_SMALL 10 #define MOB_SMALL 10
#define MOB_TINY 5 #define MOB_TINY 5
#define MOB_MINISCULE 1 #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 1
return 0 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 //Checks for specific paths in a list
/proc/is_path_in_list(var/atom/A, var/list/L) /proc/is_path_in_list(var/atom/A, var/list/L)
for(var/path in L) for(var/path in L)
@@ -78,11 +84,11 @@ proc/isemptylist(list/list)
return 1 return 1
return 0 return 0
//Empties the list by setting the length to 0. Hopefully the elements get garbage collected
proc/clearlist(list/list) //Empties the list by .Cut(). Setting lenght = 0 has been confirmed to leak references.
if(istype(list)) proc/clearlist(var/list/L)
list.len = 0 if(islist(L))
return L.Cut()
//Removes any null entries from the list //Removes any null entries from the list
proc/listclearnulls(list/list) proc/listclearnulls(list/list)

View File

@@ -32,3 +32,12 @@
if(!available_turfs.len) if(!available_turfs.len)
available_turfs = start_turfs available_turfs = start_turfs
return pick(available_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

@@ -229,14 +229,14 @@ Turf and target are seperate in case you want to teleport some distance from a t
#define LOCATE_COORDS(X, Y, Z) locate(between(1, X, world.maxx), between(1, Y, world.maxy), Z) #define LOCATE_COORDS(X, Y, Z) locate(between(1, X, world.maxx), between(1, Y, world.maxy), Z)
/proc/getcircle(turf/center, var/radius) //Uses a fast Bresenham rasterization algorithm to return the turfs in a thin circle. /proc/getcircle(turf/center, var/radius) //Uses a fast Bresenham rasterization algorithm to return the turfs in a thin circle.
if(!radius) return list(center) if(!radius) return list(center)
var/x = 0 var/x = 0
var/y = radius var/y = radius
var/p = 3 - 2 * radius var/p = 3 - 2 * radius
. = list() . = list()
while(y >= x) // only formulate 1/8 of circle while(y >= x) // only formulate 1/8 of circle
. += LOCATE_COORDS(center.x - x, center.y - y, center.z) //upper left left . += LOCATE_COORDS(center.x - x, center.y - y, center.z) //upper left left
. += LOCATE_COORDS(center.x - y, center.y - x, center.z) //upper upper left . += LOCATE_COORDS(center.x - y, center.y - x, center.z) //upper upper left
. += LOCATE_COORDS(center.x + y, center.y - x, center.z) //upper upper right . += LOCATE_COORDS(center.x + y, center.y - x, center.z) //upper upper right
@@ -247,7 +247,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
. += LOCATE_COORDS(center.x + x, center.y + y, center.z) //lower right right . += LOCATE_COORDS(center.x + x, center.y + y, center.z) //lower right right
if(p < 0) if(p < 0)
p += 4*x++ + 6; p += 4*x++ + 6;
else else
p += 4*(x++ - y--) + 10; p += 4*(x++ - y--) + 10;
@@ -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)) var/y = min(world.maxy, max(1, A.y + dy))
return locate(x,y,A.z) 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) /proc/between(var/low, var/middle, var/high)
return max(min(middle, high), low) return max(min(middle, high), low)
@@ -718,21 +718,6 @@ proc/GaussRandRound(var/sigma,var/roundto)
if(istype(N, areatype)) areas += N if(istype(N, areatype)) areas += N
return areas 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. //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. //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) /proc/get_area_all_atoms(var/areatype)

View File

@@ -1,40 +1,59 @@
/*
=== 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. // 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) /obj/item/proc/attack_self(mob/user)
return 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 // No comment
/atom/proc/attackby(obj/item/W, mob/user) /atom/proc/attackby(obj/item/W, mob/user)
return return
/atom/movable/attackby(obj/item/W, mob/user) /atom/movable/attackby(obj/item/W, mob/user)
if(!(W.flags&NOBLUDGEON)) if(!(W.flags & NOBLUDGEON))
visible_message("<span class='danger'>[src] has been hit by [user] with [W].</span>") visible_message("<span class='danger'>[src] has been hit by [user] with [W].</span>")
/mob/living/attackby(obj/item/I, mob/user) /mob/living/attackby(obj/item/I, mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(!ismob(user))
if(istype(I) && ismob(user)) return 0
I.attack(src, user) 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. // 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. // 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) /obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
return return
//TODO: refactor mob attack code. //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)
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. if(!force || (flags & NOBLUDGEON))
leave attackby() as handling the general case of "using an item on a mob" return 0
attackby() will decide to call attacked_by() or not. if(M == user && user.a_intent != I_HURT)
attacked_by() will be made a living level proc and handle the specific case of "attacking with an item to cause harm" return 0
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
///////////////////////// /////////////////////////
user.lastattacked = M 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)])" ) 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 user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(user.a_intent == I_HURT) user.do_attack_animation(M)
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)
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 var/power = force
if(HULK in user.mutations) if(HULK in user.mutations)
power *= 2 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() affected_mob.updatehealth()
if(prob(40)) if(prob(40))
if(gibbed != 0) return 0 if(gibbed != 0) return 0
var/turf/T = find_loc(affected_mob)
gibs(T)
src.cure(0) src.cure(0)
gibbed = 1 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.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!"), ((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!"), ((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.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.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."), ((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 /decl/xgm_gas/phoron
id = "phoron" id = "phoron"
name = "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) specific_heat = 200 // J/(mol*K)
//Hypothetical group 14 (same as carbon), period 8 element. //Hypothetical group 14 (same as carbon), period 8 element.

View File

@@ -61,7 +61,7 @@ proc/can_process_hud(var/mob/M)
return 1 return 1
//Deletes the current HUD images so they can be refreshed with new ones. //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) if(client)
for(var/image/hud in client.images) for(var/image/hud in client.images)
if(copytext(hud.icon_state,1,4) == "hud") 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_RANDSPAWN 256 // Potentially randomly spawns due to events.
#define ANTAG_VOTABLE 512 // Can be voted as an additional antagonist before roundstart. #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_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. // Globals.
var/global/list/all_antag_types = list() 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." welcome_text = "Hiss! You are a larval alien. Hide and bide your time until you are ready to evolve."
antaghud_indicator = "hudalien" 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 = 5
hard_cap_round = 8 hard_cap_round = 8
initial_spawn_req = 4 initial_spawn_req = 4

View File

@@ -86,7 +86,8 @@
/datum/antagonist/proc/tick() /datum/antagonist/proc/tick()
return 1 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. candidates = list() // Clear.
// Prune restricted status. Broke it up for readability. // Prune restricted status. Broke it up for readability.
@@ -108,46 +109,57 @@
return candidates return candidates
/datum/antagonist/proc/attempt_random_spawn() /datum/antagonist/proc/attempt_random_spawn()
update_current_antag_max()
build_candidate_list(flags & (ANTAG_OVERRIDE_MOB|ANTAG_OVERRIDE_JOB)) build_candidate_list(flags & (ANTAG_OVERRIDE_MOB|ANTAG_OVERRIDE_JOB))
attempt_spawn() attempt_spawn()
finalize_spawn() finalize_spawn()
/datum/antagonist/proc/attempt_late_spawn(var/datum/mind/player) /datum/antagonist/proc/attempt_auto_spawn()
if(!can_late_spawn()) if(!can_late_spawn())
return 0 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() 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 //Selects players that will be spawned in the antagonist role from the potential candidates
//Selected players are added to the pending_antagonists lists. //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, //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 //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. //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. // Update our boundaries.
if(!candidates.len) if(!candidates.len)
return 0 return 0
//Grab candidates randomly until we have enough. //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) var/datum/mind/player = pick(candidates)
candidates -= player candidates -= player
draft_antagonist(player) draft_antagonist(player)
@@ -186,11 +198,16 @@
for(var/datum/mind/player in pending_antagonists) for(var/datum/mind/player in pending_antagonists)
pending_antagonists -= player pending_antagonists -= player
add_antagonist(player,0,0,1) add_antagonist(player,0,0,1)
reset_antag_selection()
//Resets all pending_antagonists, clearing their special_role (and assigned_role if ANTAG_OVERRIDE_JOB is set) //Resets the antag selection, clearing all pending_antagonists and their special_role
/datum/antagonist/proc/reset() //(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) for(var/datum/mind/player in pending_antagonists)
if(flags & ANTAG_OVERRIDE_JOB) if(flags & ANTAG_OVERRIDE_JOB)
player.assigned_role = null player.assigned_role = null
player.special_role = null player.special_role = null
pending_antagonists.Cut() pending_antagonists.Cut()
candidates.Cut()

View File

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

View File

@@ -20,6 +20,17 @@
/datum/antagonist/proc/get_antag_count() /datum/antagonist/proc/get_antag_count()
return current_antagonists ? current_antagonists.len : 0 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) /datum/antagonist/proc/is_antagonist(var/datum/mind/player)
if(player in current_antagonists) if(player in current_antagonists)
return 1 return 1
@@ -33,10 +44,15 @@
return (flags & ANTAG_VOTABLE) return (flags & ANTAG_VOTABLE)
/datum/antagonist/proc/can_late_spawn() /datum/antagonist/proc/can_late_spawn()
update_current_antag_max()
if(get_antag_count() >= cur_max)
return 0
return 1 return 1
/datum/antagonist/proc/is_latejoin_template() /datum/antagonist/proc/is_latejoin_template()
return (flags & (ANTAG_OVERRIDE_MOB|ANTAG_OVERRIDE_JOB)) 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) /datum/antagonist/proc/place_mob(var/mob/living/mob)
if(!starting_locations || !starting_locations.len) if(!starting_locations || !starting_locations.len)
return return
var/turf/T = pick_mobless_turf_if_exists(mob) var/turf/T = pick_mobless_turf_if_exists(starting_locations)
mob.forceMove(T) mob.forceMove(T)

View File

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

View File

@@ -11,7 +11,7 @@ var/datum/antagonist/ert/ert
landmark_id = "Response Team" landmark_id = "Response Team"
id_type = /obj/item/weapon/card/id/centcom/ERT 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" antaghud_indicator = "hudloyalist"
hard_cap = 5 hard_cap = 5

View File

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

View File

@@ -61,8 +61,8 @@ var/list/teleportlocs = list()
for(var/area/AR in world) for(var/area/AR in world)
if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue
if(teleportlocs.Find(AR.name)) continue if(teleportlocs.Find(AR.name)) continue
var/turf/picked = pick(get_area_turfs(AR.type)) var/turf/picked = pick_area_turf(AR.type, list(/proc/is_station_turf))
if (picked.z in config.station_levels) if (picked)
teleportlocs += AR.name teleportlocs += AR.name
teleportlocs[AR.name] = AR 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)) 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
ghostteleportlocs[AR.name] = AR ghostteleportlocs[AR.name] = AR
var/turf/picked = pick(get_area_turfs(AR.type)) var/turf/picked = pick_area_turf(AR.type, list(/proc/is_station_turf))
if (picked.z in config.player_levels) if (picked)
ghostteleportlocs += AR.name ghostteleportlocs += AR.name
ghostteleportlocs[AR.name] = AR ghostteleportlocs[AR.name] = AR

View File

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

View File

@@ -11,18 +11,17 @@
event_delay_mod_major = 0.75 event_delay_mod_major = 0.75
/datum/game_mode/calamity/create_antagonists() /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 var/grab_antags = round(num_players()/ANTAG_TYPE_RATIO)+1
for(var/antag_id in all_antag_types) while(antag_candidates.len && antag_tags.len < grab_antags)
if(i > grab_antags) var/antag_id = pick(antag_candidates)
break antag_candidates -= antag_id
antag_tags |= antag_id antag_tags |= antag_id
i++
..() ..()
/datum/game_mode/calamity/check_victory() /datum/game_mode/calamity/check_victory()
world << "<font size = 3><b>This terrible, terrible day has finally ended!</b></font>" world << "<font size = 3><b>This terrible, terrible day has finally ended!</b></font>"
#undef ANTAG_TYPE_RATIO #undef ANTAG_TYPE_RATIO

View File

@@ -12,7 +12,6 @@
certain though... there is never just one of them. Good luck." certain though... there is never just one of them. Good luck."
config_tag = "changeling" config_tag = "changeling"
required_players = 2 required_players = 2
required_players_secret = 10
required_enemies = 1 required_enemies = 1
end_on_antag_death = 0 end_on_antag_death = 0
antag_scaling_coeff = 10 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!" 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" config_tag = "cult"
required_players = 5 required_players = 5
required_players_secret = 15
required_enemies = 3 required_enemies = 3
end_on_antag_death = 0 end_on_antag_death = 0
antag_tags = list(MODE_CULTIST) antag_tags = list(MODE_CULTIST)

View File

@@ -6,23 +6,35 @@
w_class = 4 w_class = 4
force = 30 force = 30
throwforce = 10 throwforce = 10
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
/obj/item/weapon/melee/cultblade/cultify() /obj/item/weapon/melee/cultblade/cultify()
return 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)) if(iscultist(user))
playsound(loc, 'sound/weapons/bladeslice.ogg', 50, 1, -1)
return ..() 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 else
user.Paralyse(5) user << "<span class='danger'>An unexplicable force rips through you, tearing the sword from your grasp!</span>"
user << "<span class='warning'>An unexplicable force powerfully repels the sword from [target]!</span>"
var/organ = ((user.hand ? "l_":"r_") + "arm") //random amount of damage between half of the blade's force and the full force of the blade.
var/obj/item/organ/external/affecting = user.get_organ(organ) user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1)
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.Weaken(5)
user.UpdateDamageIcon()
return 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) /obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob)
if(!iscultist(user)) 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>" 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") 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. 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)) if(istype(user, /mob/living/carbon/human))
var/mob/living/carbon/human/H = user var/mob/living/carbon/human/H = user
R.blood_DNA = list() R.blood_DNA = list()

View File

@@ -2,7 +2,6 @@
name = "epidemic" name = "epidemic"
config_tag = "epidemic" config_tag = "epidemic"
required_players = 1 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!" 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 var/cruiser_arrival

View File

@@ -10,7 +10,6 @@ var/global/list/additional_antag_types = list()
var/probability = 0 var/probability = 0
var/required_players = 0 // Minimum players for round to start if voted in. 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/required_enemies = 0 // Minimum antagonists for round to start.
var/newscaster_announcements = null var/newscaster_announcements = null
var/end_on_antag_death = 0 // Round will end when all antagonists are dead. 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_tags = list() // Core antag templates to spawn.
var/list/antag_templates // Extra antagonist types to include. 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/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/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. var/require_all_templates = 0 // Will only start if all templates are checked and can spawn.
@@ -147,12 +145,8 @@ var/global/list/additional_antag_types = list()
if((player.client)&&(player.ready)) if((player.client)&&(player.ready))
playerC++ playerC++
if(master_mode=="secret") if(playerC < required_players)
if(playerC < required_players_secret) return 0
return 0
else
if(playerC < required_players)
return 0
if(!(antag_templates && antag_templates.len)) if(!(antag_templates && antag_templates.len))
return 1 return 1
@@ -188,6 +182,7 @@ var/global/list/additional_antag_types = list()
/datum/game_mode/proc/pre_setup() /datum/game_mode/proc/pre_setup()
for(var/datum/antagonist/antag in antag_templates) 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.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. //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)) if(!(antag.flags & ANTAG_OVERRIDE_JOB))
antag.attempt_spawn() //select antags to be spawned antag.attempt_spawn() //select antags to be spawned
antag.finalize_spawn() //actually spawn antags antag.finalize_spawn() //actually spawn antags
if(antag.is_latejoin_template())
latejoin_templates |= antag
if(emergency_shuttle && auto_recall_shuttle) if(emergency_shuttle && auto_recall_shuttle)
emergency_shuttle.auto_recall = 1 emergency_shuttle.auto_recall = 1
@@ -226,7 +219,7 @@ var/global/list/additional_antag_types = list()
/datum/game_mode/proc/fail_setup() /datum/game_mode/proc/fail_setup()
for(var/datum/antagonist/antag in antag_templates) for(var/datum/antagonist/antag in antag_templates)
antag.reset() antag.reset_antag_selection()
/datum/game_mode/proc/announce_ert_disabled() /datum/game_mode/proc/announce_ert_disabled()
if(!ert_disabled) if(!ert_disabled)
@@ -498,6 +491,7 @@ var/global/list/additional_antag_types = list()
if(antag) if(antag)
antag_templates |= antag antag_templates |= antag
shuffle(antag_templates) //In the case of multiple antag types
newscaster_announcements = pick(newscaster_standard_feeds) newscaster_announcements = pick(newscaster_standard_feeds)
/datum/game_mode/proc/check_victory() /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/max_autotraitor_delay = 12000 // Approx 20 minutes.
/datum/game_mode/var/process_count = 0 /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() ///process()
///Called by the gameticker ///Called by the gameticker
/datum/game_mode/proc/process() /datum/game_mode/proc/process()
// Slow this down a bit so latejoiners have a chance of being antags. if(round_autoantag && world.time < next_spawn && !emergency_shuttle.departed)
process_count++ process_autoantag()
if(process_count >= 10)
process_count = 0
try_latespawn()
/datum/game_mode/proc/latespawn(var/mob/living/carbon/human/character) //This can be overriden in case a game mode needs to do stuff when a player latejoins
if(!character.mind) /datum/game_mode/proc/handle_latejoin(var/mob/living/carbon/human/character)
return
try_latespawn(character.mind)
return 0 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) var/list/usable_templates = list()
return for(var/datum/antagonist/A in antag_templates)
if(A.can_late_spawn())
if(world.time < next_spawn) message_admins("[uppertext(name)]: [A.id] selected for spawn attempt.")
return usable_templates |= A
message_admins("AUTO[uppertext(name)]: Attempting spawn.") if(!usable_templates.len)
message_admins("[uppertext(name)]: Failed to find configured mode spawn templates, please re-enable auto-antagonists after one is added.")
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.")
round_autoantag = 0 round_autoantag = 0
return return
while(usable_templates.len) while(usable_templates.len)
var/datum/antagonist/spawn_antag = pick(usable_templates) var/datum/antagonist/spawn_antag = pick(usable_templates)
usable_templates -= spawn_antag 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) next_spawn = world.time + rand(min_autotraitor_delay, max_autotraitor_delay)
return 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" name = "heist"
config_tag = "heist" config_tag = "heist"
required_players = 15 required_players = 15
required_players_secret = 25
required_enemies = 4 required_enemies = 4
round_description = "An unidentified bluespace signature has slipped past the Icarus and is approaching the station!" round_description = "An unidentified bluespace signature has slipped past the Icarus and is approaching the station!"
end_on_antag_death = 0 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." 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" config_tag = "malfunction"
required_players = 2 required_players = 2
required_players_secret = 7
required_enemies = 1 required_enemies = 1
end_on_antag_death = 0 end_on_antag_death = 0
auto_recall_shuttle = 0 auto_recall_shuttle = 0

View File

@@ -85,7 +85,7 @@
/datum/game_mode/malfunction/verb/advanced_encryption_hack() /datum/game_mode/malfunction/verb/advanced_encryption_hack()
set category = "Software" 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." 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/price = 75
var/mob/living/silicon/ai/user = usr var/mob/living/silicon/ai/user = usr
@@ -106,15 +106,12 @@
announce_hack_failure(user, "quantum message relay") announce_hack_failure(user, "quantum message relay")
return return
var/datum/announcement/priority/command/AN = new/datum/announcement/priority/command() command_announcement.Announce(text, title)
AN.title = title
AN.Announce(text)
/datum/game_mode/malfunction/verb/elite_encryption_hack() /datum/game_mode/malfunction/verb/elite_encryption_hack()
set category = "Software" set category = "Software"
set name = "Elite Encryption Hack" 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/price = 200
var/mob/living/silicon/ai/user = usr var/mob/living/silicon/ai/user = usr
if(!ability_prechecks(user, price)) if(!ability_prechecks(user, price))

View File

@@ -6,7 +6,6 @@
name = "Memetic Anomaly" name = "Memetic Anomaly"
config_tag = "meme" config_tag = "meme"
required_players = 3 required_players = 3
required_players_secret = 10
restricted_jobs = list("AI", "Cyborg") restricted_jobs = list("AI", "Cyborg")
recommended_enemies = 2 // need at least a meme and a host recommended_enemies = 2 // need at least a meme and a host
votable = 0 // temporarily disable this mode for voting 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!" round_description = "A mercenary strike force is approaching the station to eradicate a xenomorph infestation!"
config_tag = "bughunt" config_tag = "bughunt"
required_players = 15 required_players = 15
required_players_secret = 25
required_enemies = 1 required_enemies = 1
end_on_antag_death = 1 end_on_antag_death = 1
antag_tags = list(MODE_XENOMORPH, MODE_DEATHSQUAD) antag_tags = list(MODE_XENOMORPH, MODE_DEATHSQUAD)

View File

@@ -4,7 +4,6 @@
extended_round_description = "Cultists and wizards spawn during this round." extended_round_description = "Cultists and wizards spawn during this round."
config_tag = "conflux" config_tag = "conflux"
required_players = 15 required_players = 15
required_players_secret = 15
required_enemies = 5 required_enemies = 5
end_on_antag_death = 1 end_on_antag_death = 1
antag_tags = list(MODE_WIZARD, MODE_CULTIST) 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." extended_round_description = "Rampant AIs, renegades and changelings spawn in this mode."
config_tag = "paranoia" config_tag = "paranoia"
required_players = 2 required_players = 2
required_players_secret = 7
required_enemies = 1 required_enemies = 1
end_on_antag_death = 1 end_on_antag_death = 1
require_all_templates = 1 require_all_templates = 1

View File

@@ -4,7 +4,6 @@
extended_round_description = "Traitors and changelings both spawn during this mode." extended_round_description = "Traitors and changelings both spawn during this mode."
config_tag = "traitorling" config_tag = "traitorling"
required_players = 10 required_players = 10
required_players_secret = 15
required_enemies = 5 required_enemies = 5
end_on_antag_death = 1 end_on_antag_death = 1
require_all_templates = 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!" 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." extended_round_description = "Cultists and revolutionaries spawn in this round."
required_players = 15 required_players = 15
required_players_secret = 15
required_enemies = 3 required_enemies = 3
end_on_antag_death = 1 end_on_antag_death = 1
antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST, MODE_CULTIST) 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." only hope this unknown assassin isn't here for you."
config_tag = "ninja" config_tag = "ninja"
required_players = 1 required_players = 1
required_players_secret = 10
required_enemies = 1 required_enemies = 1
end_on_antag_death = 0 end_on_antag_death = 0
antag_tags = list(MODE_NINJA) 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!" round_description = "A mercenary strike force is approaching the station!"
config_tag = "mercenary" config_tag = "mercenary"
required_players = 15 required_players = 15
required_players_secret = 25 // 25 players - 5 players to be the nuke ops = 20 players remaining
required_enemies = 1 required_enemies = 1
end_on_antag_death = 0 end_on_antag_death = 0
var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station 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!" 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." 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 = 4
required_players_secret = 15
required_enemies = 3 required_enemies = 3
auto_recall_shuttle = 0 //NO THANKS auto_recall_shuttle = 0 //NO THANKS
end_on_antag_death = 0 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." 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" config_tag = "wizard"
required_players = 1 required_players = 1
required_players_secret = 10
required_enemies = 1 required_enemies = 1
end_on_antag_death = 0 end_on_antag_death = 0
antag_tags = list(MODE_WIZARD) antag_tags = list(MODE_WIZARD)

View File

@@ -127,11 +127,4 @@
M.deconstruct(src) M.deconstruct(src)
qdel(src) qdel(src)
else 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) /obj/machinery/door/airlock/emp_act(var/severity)
if(prob(40/severity)) if(prob(40/severity))
var/duration = world.time + SecondsToTicks(30 / severity) var/duration = SecondsToTicks(30 / severity)
if(duration > electrified_until) if(electrified_until > -1 && (duration + world.time) > electrified_until)
electrify(duration) electrify(duration)
..() ..()

View File

@@ -191,7 +191,10 @@ Class Procs:
return (stat & (NOPOWER|BROKEN|additional_flags)) return (stat & (NOPOWER|BROKEN|additional_flags))
/obj/machinery/CanUseTopic(var/mob/user) /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 STATUS_CLOSE
return ..() return ..()

View File

@@ -418,10 +418,14 @@ obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob)
nuke_disks |= src nuke_disks |= src
/obj/item/weapon/disk/nuclear/Destroy() /obj/item/weapon/disk/nuclear/Destroy()
if(!nuke_disks.len && blobstart.len > 0) nuke_disks -= src
var/obj/D = new /obj/item/weapon/disk/nuclear(pick(blobstart)) if(!nuke_disks.len)
message_admins("[src], the last authentication disk, has been destroyed. Spawning [D] at ([D.x], [D.y], [D.z]).") var/turf/T = pick_area_turf(/area/maintenance, list(/proc/is_station_turf, /proc/not_turf_contains_dense_objects))
log_game("[src], the last authentication disk, has been destroyed. Spawning [D] at ([D.x], [D.y], [D.z]).") 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() /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 * Acid
*/ */
/obj/effect/alien/acid /obj/effect/acid
name = "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_state = "acid"
icon = 'icons/mob/alien.dmi'
density = 0 density = 0
opacity = 0 opacity = 0
@@ -275,9 +15,9 @@ Alien plants should do something if theres a lot of poison
var/ticks = 0 var/ticks = 0
var/target_strength = 0 var/target_strength = 0
/obj/effect/alien/acid/New(loc, target) /obj/effect/acid/New(loc, supplied_target)
..(loc) ..(loc)
src.target = target target = supplied_target
if(isturf(target)) // Turf take twice as long to take down. if(isturf(target)) // Turf take twice as long to take down.
target_strength = 8 target_strength = 8
@@ -285,17 +25,13 @@ Alien plants should do something if theres a lot of poison
target_strength = 4 target_strength = 4
tick() tick()
/obj/effect/alien/acid/proc/tick() /obj/effect/acid/proc/tick()
if(!target) if(!target)
qdel(src) qdel(src)
ticks += 1 ticks++
if(ticks >= target_strength) if(ticks >= target_strength)
target.visible_message("<span class='alium'>\The [target] collapses under its own weight into a puddle of goop and undigested debris!</span>")
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)
if(istype(target, /turf/simulated/wall)) // I hate turf code. if(istype(target, /turf/simulated/wall)) // I hate turf code.
var/turf/simulated/wall/W = target var/turf/simulated/wall/W = target
W.dismantle_wall(1) W.dismantle_wall(1)
@@ -306,135 +42,11 @@ Alien plants should do something if theres a lot of poison
switch(target_strength - ticks) switch(target_strength - ticks)
if(6) 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) 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) 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) 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() 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 /obj/effect/effect/smoke/chem
icon = 'icons/effects/chemsmoke.dmi' icon = 'icons/effects/chemsmoke.dmi'
opacity = 0 opacity = 0
layer = 6
time_to_live = 300 time_to_live = 300
pass_flags = PASSTABLE | PASSGRILLE | PASSGLASS //PASSGLASS is fine here, it's just so the visual effect can "flow" around glass 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() /obj/effect/effect/smoke/chem/New(var/newloc, smoke_duration, turf/dest_turf = null, icon/cached_icon = null)
time_to_live = smoke_duration
..() ..()
create_reagents(500) create_reagents(500)
return
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 /datum/effect/effect/system/smoke_spread/chem
smoke_type = /obj/effect/effect/smoke/chem smoke_type = /obj/effect/effect/smoke/chem
var/obj/chemholder var/obj/chemholder
@@ -115,13 +177,21 @@
else else
I = icon('icons/effects/96x96.dmi', "smoke") 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 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 for(var/i = 0, i < range, i++) //calculate positions for smoke coverage - then spawn smoke
var/radius = i * 1.5 var/radius = i * 1.5
if(!radius) if(!radius)
spawn(0) spawn(0)
spawnSmoke(location, I, 1) spawnSmoke(location, I, 1, 1)
continue continue
var/offset = 0 var/offset = 0
@@ -146,43 +216,26 @@
// Randomizes and spawns the smoke effect. // Randomizes and spawns the smoke effect.
// Also handles deleting the smoke once the effect is finished. // 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 var/obj/effect/effect/smoke/chem/smoke
if(passed_smoke) if(passed_smoke)
smoke = passed_smoke smoke = passed_smoke
else 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) 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 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) var/obj/effect/effect/smoke/chem/spores = PoolOrNew(/obj/effect/effect/smoke/chem, location)
spores.name = "cloud of [seed.seed_name] [seed.seed_noun]" spores.name = "cloud of [seed.seed_name] [seed.seed_noun]"
..(T, I, dist, spores) ..(T, I, smoke_duration, 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
/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. /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 prisonsecuritywarp += loc
qdel(src) qdel(src)
return return
if("blobstart")
blobstart += loc
qdel(src)
return
if("xeno_spawn") if("xeno_spawn")
xeno_spawn += loc xeno_spawn += loc
qdel(src) qdel(src)

View File

@@ -105,27 +105,61 @@
qdel(src) 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 /obj/effect/spawner/newbomb
name = "bomb" name = "TTV bomb"
icon = 'icons/mob/screen1.dmi' icon = 'icons/mob/screen1.dmi'
icon_state = "x" icon_state = "x"
var/btype = 0 // 0=radio, 1=prox, 2=time
var/assembly_type = /obj/item/device/assembly/signaler
//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
timer /obj/effect/spawner/newbomb/timer
btype = 2 name = "TTV bomb - timer"
assembly_type = /obj/item/device/assembly/timer
syndicate /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
proximity /obj/effect/spawner/newbomb/proximity
btype = 1 name = "TTV bomb - proximity"
assembly_type = /obj/item/device/assembly/prox_sensor
radio /obj/effect/spawner/newbomb/radio/custom/New(var/newloc, ph, ox, co)
btype = 0 if(ph != null) phoron_amt = ph
if(ox != null) oxygen_amt = ox
if(co != null) carbon_amt = co
/obj/effect/spawner/newbomb/New()
..() ..()
/obj/effect/spawner/newbomb/New(newloc)
..(newloc)
var/obj/item/device/transfer_valve/V = new(src.loc) var/obj/item/device/transfer_valve/V = new(src.loc)
var/obj/item/weapon/tank/phoron/PT = new(V) var/obj/item/weapon/tank/phoron/PT = new(V)
var/obj/item/weapon/tank/oxygen/OT = new(V) var/obj/item/weapon/tank/oxygen/OT = new(V)
@@ -137,28 +171,15 @@
OT.master = V OT.master = V
PT.air_contents.temperature = PHORON_FLASHPOINT 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.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 var/obj/item/device/assembly/S = new assembly_type(V)
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)
V.attached_device = S 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>" 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 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) src.add_fingerprint(user)
//if((CLUMSY in user.mutations) && prob(50)) //if((CLUMSY in user.mutations) && prob(50))
// M = user // M = user
@@ -610,8 +613,4 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
return return
/obj/item/proc/pwr_drain() /obj/item/proc/pwr_drain()
return 0 // Process Kill 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") if("Message")
var/obj/item/device/pda/P = locate(href_list["target"]) 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(mode == 2)
if(href_list["target"] in conversations) // Need to make sure the message went through, if not welp. if(href_list["target"] in conversations) // Need to make sure the message went through, if not welp.
active_conversation = href_list["target"] 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) 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) /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) new_info(message_silent, ttone, reception_message)
log_pda("[usr] (PDA: [sending_unit]) sent \"[message]\" to [name]") 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)) 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>)" 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) new_info(message_silent, newstone, reception_message)
log_pda("[usr] (PDA: [sending_unit]) sent \"[message]\" to [name]") log_pda("[usr] (PDA: [sending_unit]) sent \"[message]\" to [name]")

View File

@@ -31,14 +31,17 @@
last_used = world.time last_used = world.time
times_used = max(0,round(times_used)) //sanity times_used = max(0,round(times_used)) //sanity
//attack_as_weapon
/obj/item/device/flash/attack(mob/living/M as mob, mob/user as mob) /obj/item/device/flash/attack(mob/living/M, mob/living/user, var/target_zone)
if(!user || !M) return //sanity 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>") 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>") 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>)") 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(!clown_check(user)) return
if(broken) if(broken)
user << "<span class='warning'>\The [src] is broken.</span>" user << "<span class='warning'>\The [src] is broken.</span>"
@@ -65,8 +68,9 @@
if(iscarbon(M)) if(iscarbon(M))
if(M.stat!=DEAD) if(M.stat!=DEAD)
var/safety = M:eyecheck() var/mob/living/carbon/C = M
if(safety <= 0) var/safety = C.eyecheck()
if(safety < FLASH_PROTECTION_MODERATE)
var/flash_strength = 10 var/flash_strength = 10
if(ishuman(M)) if(ishuman(M))
var/mob/living/carbon/human/H = 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) /obj/item/device/flash/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0)
if(!user || !clown_check(user)) return if(!user || !clown_check(user)) return
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(broken) if(broken)
user.show_message("<span class='warning'>The [src.name] is broken</span>", 2) user.show_message("<span class='warning'>The [src.name] is broken</span>", 2)
return return
@@ -150,8 +157,8 @@
for(var/obj/item/weapon/cloaking_device/S in M) for(var/obj/item/weapon/cloaking_device/S in M)
S.active = 0 S.active = 0
S.icon_state = "shield0" S.icon_state = "shield0"
var/safety = M:eyecheck() var/safety = M.eyecheck()
if(!safety) if(safety < FLASH_PROTECTION_MODERATE)
if(!M.blinded) if(!M.blinded)
flick("flash", M.flash) flick("flash", M.flash)
@@ -170,7 +177,7 @@
if(istype(loc, /mob/living/carbon)) if(istype(loc, /mob/living/carbon))
var/mob/living/carbon/M = loc var/mob/living/carbon/M = loc
var/safety = M.eyecheck() var/safety = M.eyecheck()
if(safety <= 0) if(safety < FLASH_PROTECTION_MODERATE)
M.Weaken(10) M.Weaken(10)
flick("e_flash", M.flash) flick("e_flash", M.flash)
for(var/mob/O in viewers(M, null)) for(var/mob/O in viewers(M, null))
@@ -183,7 +190,8 @@
icon_state = "sflash" icon_state = "sflash"
origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1) 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) if(!broken)
broken = 1 broken = 1

View File

@@ -79,6 +79,8 @@
user << "<span class='notice'>\The [M]'s pupils narrow slightly, but are still very dilated.</span>" user << "<span class='notice'>\The [M]'s pupils narrow slightly, but are still very dilated.</span>"
else else
user << "<span class='notice'>\The [M]'s pupils narrow.</span>" user << "<span class='notice'>\The [M]'s pupils narrow.</span>"
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) //can be used offensively
flick("flash", M.flash) flick("flash", M.flash)
else else
return ..() 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) secure_radio_connections[ch_name] = radio_controller.add_object(src, radiochannels[ch_name], RADIO_CHAT)
/obj/item/device/radio/attack_ghost(mob/user) /obj/item/device/radio/attack_ghost(mob/user)
interact(user) return ui_interact(user)
/obj/item/device/radio/attack_self(mob/user as mob) /obj/item/device/radio/attack_self(mob/user as mob)
user.set_machine(src) user.set_machine(src)
interact(user) interact(user)
/obj/item/device/radio/interact(mob/user as mob) /obj/item/device/radio/interact(mob/user)
if(!on) if(!user)
return return 0
if(active_uplink_check(user)) if(b_stat)
return 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 /obj/item/device/radio/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
dat += "Microphone: [broadcasting ? "<A href='byond://?src=\ref[src];talk=0'>Engaged</A>" : "<A href='byond://?src=\ref[src];talk=1'>Disengaged</A>"]<BR>" var/data[0]
dat += {" data["mic_status"] = broadcasting
Speaker: [listening ? "<A href='byond://?src=\ref[src];listen=0'>Engaged</A>" : "<A href='byond://?src=\ref[src];listen=1'>Disengaged</A>"]<BR> data["speaker"] = listening
Frequency: data["freq"] = format_frequency(frequency)
<A href='byond://?src=\ref[src];freq=-10'>-</A> data["rawfreq"] = num2text(frequency)
<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>
"}
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>"} var/list/chanlist = list_channels(user)
user << browse(dat, "window=radio") if(islist(chanlist) && chanlist.len)
onclose(user, "radio") data["chan_list"] = chanlist
return 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) /obj/item/device/radio/proc/list_channels(var/mob/user)
return list_internal_channels(user) return list_internal_channels(user)
/obj/item/device/radio/proc/list_secure_channels(var/mob/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]) for(var/ch_name in channels)
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 return dat
/obj/item/device/radio/proc/list_internal_channels(var/mob/user) /obj/item/device/radio/proc/list_internal_channels(var/mob/user)
var/dat = "" var/dat[0]
for (var/internal_chan in internal_channels) for(var/internal_chan in internal_channels)
if(has_channel_access(user, internal_chan)) 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 return dat
/obj/item/device/radio/proc/has_channel_access(var/mob/user, var/freq) /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() /obj/item/device/radio/proc/ToggleReception()
listening = !listening && !(wires.IsIndexCut(WIRE_RECEIVE) || wires.IsIndexCut(WIRE_SIGNAL)) 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) /obj/item/device/radio/Topic(href, href_list)
if(..() || !on) if(..())
usr << browse(null, "window=radio")
return 1 return 1
usr.set_machine(src) usr.set_machine(src)
@@ -216,7 +228,7 @@ var/global/list/default_medbay_channels = list(
return 1 return 1
if(.) if(.)
interact(usr) nanomanager.update_uis(src)
/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel) //BS12 EDIT /obj/item/device/radio/proc/autosay(var/message, var/from, var/channel) //BS12 EDIT
var/datum/radio_frequency/connection = null var/datum/radio_frequency/connection = null
@@ -683,31 +695,40 @@ var/global/list/default_medbay_channels = list(
. = 1 . = 1
if(.) if(.)
interact(usr) nanomanager.update_uis(src)
/obj/item/device/radio/borg/interact(mob/user as mob) /obj/item/device/radio/borg/interact(mob/user as mob)
if(!on) if(!on)
return 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 /obj/item/device/radio/borg/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
dat+=list_channels(user) var/data[0]
dat+={"[text_wires()]</TT></body></html>"}
user << browse(dat, "window=radio") data["mic_status"] = broadcasting
onclose(user, "radio") data["speaker"] = listening
return 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) /obj/item/device/radio/proc/config(op)
if(radio_controller) if(radio_controller)

View File

@@ -100,17 +100,9 @@
if (src.loc != usr) if (src.loc != usr)
return 0 return 0
if(tank_one && href_list["tankone"]) if(tank_one && href_list["tankone"])
split_gases() remove_tank(tank_one)
valve_open = 0
tank_one.loc = get_turf(src)
tank_one = null
update_icon()
else if(tank_two && href_list["tanktwo"]) else if(tank_two && href_list["tanktwo"])
split_gases() remove_tank(tank_two)
valve_open = 0
tank_two.loc = get_turf(src)
tank_two = null
update_icon()
else if(href_list["open"]) else if(href_list["open"])
toggle_valve() toggle_valve()
else if(attached_device) else if(attached_device)
@@ -149,20 +141,43 @@
if(attached_device) if(attached_device)
overlays += "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() /obj/item/device/transfer_valve/proc/merge_gases()
if(valve_open)
return
tank_two.air_contents.volume += tank_one.air_contents.volume tank_two.air_contents.volume += tank_one.air_contents.volume
var/datum/gas_mixture/temp var/datum/gas_mixture/temp
temp = tank_one.air_contents.remove_ratio(1) temp = tank_one.air_contents.remove_ratio(1)
tank_two.air_contents.merge(temp) tank_two.air_contents.merge(temp)
valve_open = 1
/obj/item/device/transfer_valve/proc/split_gases() /obj/item/device/transfer_valve/proc/split_gases()
if (!valve_open || !tank_one || !tank_two) if(!valve_open)
return 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/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume
var/datum/gas_mixture/temp var/datum/gas_mixture/temp
temp = tank_two.air_contents.remove_ratio(ratio1) temp = tank_two.air_contents.remove_ratio(ratio1)
tank_one.air_contents.merge(temp) tank_one.air_contents.merge(temp)
tank_two.air_contents.volume -= tank_one.air_contents.volume 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 Exadv1: I know this isn't how it's going to work, but this was just to check
@@ -170,8 +185,7 @@
*/ */
/obj/item/device/transfer_valve/proc/toggle_valve() /obj/item/device/transfer_valve/proc/toggle_valve()
if(valve_open==0 && (tank_one && tank_two)) if(!valve_open && (tank_one && tank_two))
valve_open = 1
var/turf/bombturf = get_turf(src) var/turf/bombturf = get_turf(src)
var/area/A = get_area(bombturf) var/area/A = get_area(bombturf)
@@ -197,16 +211,11 @@
message_admins(log_str, 0, 1) message_admins(log_str, 0, 1)
log_game(log_str) log_game(log_str)
merge_gases() 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)) else if(valve_open==1 && (tank_one && tank_two))
split_gases() split_gases()
valve_open = 0
src.update_icon() src.update_icon()
// this doesn't do anything but the timer etc. expects it to be here // this doesn't do anything but the timer etc. expects it to be here
// eventually maybe have it update icon to show state (timer, prox etc.) like old bombs // eventually maybe have it update icon to show state (timer, prox etc.) like old bombs

View File

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

View File

@@ -65,24 +65,42 @@
var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting)
if(affecting.open == 0) 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>" user << "<span class='warning'>The wounds on [M]'s [affecting.name] have already been bandaged.</span>"
return 1 return 1
else 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) for (var/datum/wound/W in affecting.wounds)
if (W.internal) if (W.internal)
continue 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) 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>", \ user.visible_message("<span class='notice'>\The [user] bandages \a [W.desc] on [M]'s [affecting.name].</span>", \
"<span class='notice'>You bandage [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") //H.add_side_effect("Itch")
else if (istype(W,/datum/wound/bruise)) 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>", \ 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 [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 else
user.visible_message("<span class='notice'>\The [user] places a bandaid over [W.desc] on [M]'s [affecting.name].</span>", \ 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 [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>" )
use(1) 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 else
if (can_operate(H)) //Checks if mob is lying down on table for surgery if (can_operate(H)) //Checks if mob is lying down on table for surgery
if (do_surgery(H,user,src)) if (do_surgery(H,user,src))
@@ -108,13 +126,19 @@
var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting)
if(affecting.open == 0) 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>" user << "<span class='warning'>The wounds on [M]'s [affecting.name] have already been salved.</span>"
return 1 return 1
else else
user.visible_message("<span class='notice'>[user] salves 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 salve 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) use(1)
affecting.salve()
else else
if (can_operate(H)) //Checks if mob is lying down on table for surgery if (can_operate(H)) //Checks if mob is lying down on table for surgery
if (do_surgery(H,user,src)) if (do_surgery(H,user,src))
@@ -127,7 +151,7 @@
singular_name = "advanced trauma kit" singular_name = "advanced trauma kit"
desc = "An advanced trauma kit for severe injuries." desc = "An advanced trauma kit for severe injuries."
icon_state = "traumakit" icon_state = "traumakit"
heal_brute = 12 heal_brute = 0
origin_tech = list(TECH_BIO = 1) origin_tech = list(TECH_BIO = 1)
/obj/item/stack/medical/advanced/bruise_pack/attack(mob/living/carbon/M as mob, mob/user as mob) /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) var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting)
if(affecting.open == 0) if(affecting.open == 0)
var/bandaged = affecting.bandage() if(affecting.is_bandaged() && affecting.is_disinfected())
var/disinfected = affecting.disinfect()
if(!(bandaged || disinfected))
user << "<span class='warning'>The wounds on [M]'s [affecting.name] have already been treated.</span>" user << "<span class='warning'>The wounds on [M]'s [affecting.name] have already been treated.</span>"
return 1 return 1
else 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) for (var/datum/wound/W in affecting.wounds)
if (W.internal) if (W.internal)
continue 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) 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>", \ 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 [W.desc] on [M]'s [affecting.name].</span>" ) "<span class='notice'>You clean and seal \a [W.desc] on [M]'s [affecting.name].</span>" )
//H.add_side_effect("Itch") //H.add_side_effect("Itch")
else if (istype(W,/datum/wound/bruise)) 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>", \ 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 [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 else
user.visible_message("<span class='notice'>\The [user] smears some bioglue over [W.desc] on [M]'s [affecting.name].</span>", \ 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 [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>" )
if (bandaged) W.bandage()
affecting.heal_damage(heal_brute,0) W.disinfect()
use(1) 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 else
if (can_operate(H)) //Checks if mob is lying down on table for surgery if (can_operate(H)) //Checks if mob is lying down on table for surgery
if (do_surgery(H,user,src)) if (do_surgery(H,user,src))
@@ -174,7 +213,7 @@
singular_name = "advanced burn kit" singular_name = "advanced burn kit"
desc = "An advanced treatment kit for severe burns." desc = "An advanced treatment kit for severe burns."
icon_state = "burnkit" icon_state = "burnkit"
heal_burn = 12 heal_burn = 0
origin_tech = list(TECH_BIO = 1) origin_tech = list(TECH_BIO = 1)
@@ -187,14 +226,20 @@
var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting)
if(affecting.open == 0) 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>" user << "<span class='warning'>The wounds on [M]'s [affecting.name] have already been salved.</span>"
return 1 return 1
else 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>", \ 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>" ) "<span class='notice'>You cover wounds on [M]'s [affecting.name] with regenerative membrane.</span>" )
affecting.heal_damage(0,heal_burn) affecting.heal_damage(0,heal_burn)
use(1) use(1)
affecting.salve()
else else
if (can_operate(H)) //Checks if mob is lying down on table for surgery if (can_operate(H)) //Checks if mob is lying down on table for surgery
if (do_surgery(H,user,src)) if (do_surgery(H,user,src))

View File

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

View File

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

View File

@@ -1,27 +1,135 @@
#ifndef T_BOARD #ifndef T_BOARD
#error T_BOARD macro is not defined but we need it! #error T_BOARD macro is not defined but we need it!
#endif #endif
/obj/item/weapon/circuitboard/air_management /obj/item/weapon/circuitboard/air_management
name = T_BOARD("atmosphere monitoring console") name = T_BOARD("atmosphere monitoring console")
build_path = /obj/machinery/computer/general_air_control build_path = /obj/machinery/computer/general_air_control
var/console_name
var/frequency = 1439 var/frequency = 1439
var/list/sensors = list()
var/list/sensor_information = list()
/obj/item/weapon/circuitboard/air_management/tank_control /obj/item/weapon/circuitboard/air_management/tank_control
name = T_BOARD("tank control") name = T_BOARD("tank control")
build_path = /obj/machinery/computer/general_air_control/large_tank_control build_path = /obj/machinery/computer/general_air_control/large_tank_control
frequency = 1441 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 /obj/item/weapon/circuitboard/air_management/supermatter_core
name = T_BOARD("core control") name = T_BOARD("core control")
build_path = /obj/machinery/computer/general_air_control/supermatter_core build_path = /obj/machinery/computer/general_air_control/supermatter_core
frequency = 1438 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 /obj/item/weapon/circuitboard/air_management/injector_control
name = T_BOARD("injector control") name = T_BOARD("injector control")
build_path = /obj/machinery/computer/general_air_control/fuel_injection 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) /obj/item/weapon/circuitboard/air_management/construct(var/obj/machinery/computer/general_air_control/C)
if (..(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) /obj/item/weapon/circuitboard/air_management/deconstruct(var/obj/machinery/computer/general_air_control/C)
if (..(C)) if (..(C))
console_name = C.name
frequency = C.frequency 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() target.clean_blood()
return 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" ) 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.visible_message("<span class='danger'>\The [user] washes \the [target]'s mouth out with soap!</span>")
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //prevent spam
return return
..() ..()

View File

@@ -114,6 +114,9 @@
if(!do_after(user,50)) if(!do_after(user,50))
return 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>") 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 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)) 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 return
user << "Planting explosives..." user << "Planting explosives..."
user.do_attack_animation(target)
if(do_after(user, 50) && in_range(user, target)) if(do_after(user, 50) && in_range(user, target))
user.drop_item() user.drop_item()

View File

@@ -61,7 +61,7 @@
qdel(src) qdel(src)
/obj/item/weapon/a_gift/attack_self(mob/M as mob) /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/wallet,
/obj/item/weapon/storage/photo_album, /obj/item/weapon/storage/photo_album,
/obj/item/weapon/storage/box/snappops, /obj/item/weapon/storage/box/snappops,
@@ -80,7 +80,6 @@
/obj/item/weapon/bikehorn, /obj/item/weapon/bikehorn,
/obj/item/weapon/beach_ball, /obj/item/weapon/beach_ball,
/obj/item/weapon/beach_ball/holoball, /obj/item/weapon/beach_ball/holoball,
/obj/item/weapon/banhammer,
/obj/item/toy/balloon, /obj/item/toy/balloon,
/obj/item/toy/blink, /obj/item/toy/blink,
/obj/item/toy/crossbow, /obj/item/toy/crossbow,

View File

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

View File

@@ -16,7 +16,7 @@
var/turf/T = get_turf(src) var/turf/T = get_turf(src)
playsound(T, 'sound/effects/phasein.ogg', 100, 1) playsound(T, 'sound/effects/phasein.ogg', 100, 1)
for(var/mob/living/carbon/human/M in viewers(T, null)) 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) flick("e_flash", M.flash)
for(var/i=1, i<=deliveryamt, i++) 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)]") msg_admin_attack("[key_name(user)] attempted to handcuff [key_name(H)]")
feedback_add_details("handcuffs","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>") user.visible_message("<span class='danger'>\The [user] has put [cuff_type] on \the [H]!</span>")
// Apply cuffs. // Apply cuffs.

View File

@@ -30,18 +30,18 @@
if (!istype(M, /mob/living/carbon)) if (!istype(M, /mob/living/carbon))
return return
if (user && src.imp) if (user && src.imp)
for (var/mob/O in viewers(M, null)) M.visible_message("<span class='warning'>[user] is attemping to implant [M].</span>")
O.show_message("<span class='warning'>[user] is attemping to implant [M].</span>", 1)
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN)
user.do_attack_animation(M)
var/turf/T1 = get_turf(M) var/turf/T1 = get_turf(M)
if (T1 && ((M == user) || do_after(user, 50))) if (T1 && ((M == user) || do_after(user, 50)))
if(user && M && (get_turf(M) == T1) && src && src.imp) if(user && M && (get_turf(M) == T1) && src && src.imp)
for (var/mob/O in viewers(M, null)) M.visible_message("<span class='warning'>[M] has been implanted by [user].</span>")
O.show_message("<span class='warning'>[M] has been implanted by [user].</span>", 1)
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") 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)) if(src.imp.implanted(M))
src.imp.loc = M src.imp.loc = M
src.imp.imp_in = M src.imp.imp_in = M

View File

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

View File

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

View File

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

View File

@@ -18,6 +18,7 @@ var/list/tape_roll_applications = list()
anchored = 1 anchored = 1
var/lifted = 0 var/lifted = 0
var/crumpled = 0 var/crumpled = 0
var/tape_dir = 0
var/icon_base var/icon_base
/obj/item/tape/New() /obj/item/tape/New()
@@ -32,7 +33,7 @@ var/list/tape_roll_applications = list()
/obj/item/taperoll/police /obj/item/taperoll/police
name = "police tape" name = "police tape"
desc = "A roll of police tape used to block off crime scenes from the public." 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 tape_type = /obj/item/tape/police
icon_base = "police" icon_base = "police"
@@ -45,7 +46,7 @@ var/list/tape_roll_applications = list()
/obj/item/taperoll/engineering /obj/item/taperoll/engineering
name = "engineering tape" name = "engineering tape"
desc = "A roll of engineering tape used to block off working areas from the public." 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 tape_type = /obj/item/tape/engineering
icon_base = "engineering" icon_base = "engineering"
@@ -55,58 +56,146 @@ var/list/tape_roll_applications = list()
req_one_access = list(access_engine,access_atmospherics) req_one_access = list(access_engine,access_atmospherics)
icon_base = "engineering" 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) /obj/item/taperoll/attack_self(mob/user as mob)
if(icon_state == "[icon_base]_start") if(!start)
start = get_turf(src) start = get_turf(src)
usr << "<span class='notice'>You place the first end of \the [src].</span>" usr << "<span class='notice'>You place the first end of \the [src].</span>"
icon_state = "[icon_base]_stop" update_icon()
else else
icon_state = "[icon_base]_start"
end = get_turf(src) end = get_turf(src)
if(start.y != end.y && start.x != end.x || start.z != end.z) 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>" usr << "<span class='notice'>\The [src] can only be laid horizontally or vertically.</span>"
return return
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
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/turf/cur = start
var/dir var/orientation = get_dir(start, end)
if (start.x == end.x) var/dir = 0
var/d = end.y-start.y switch(orientation)
if(d) d = d/abs(d) if(NORTH, SOUTH) dir = NORTH|SOUTH // North-South taping
end = get_turf(locate(end.x,end.y+d,end.z)) if(EAST, WEST) dir = EAST|WEST // East-West taping
dir = "v"
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"
var/can_place = 1 var/can_place = 1
while (cur!=end && can_place) while (can_place)
if(cur.density == 1) if(cur.density == 1)
can_place = 0 can_place = 0
else if (istype(cur, /turf/space)) else if (istype(cur, /turf/space))
can_place = 0 can_place = 0
else else
for(var/obj/O in cur) for(var/obj/O in cur)
if(!istype(O, /obj/item/tape) && O.density) if(O.density)
can_place = 0 can_place = 0
break break
if(cur == end)
break
cur = get_step_towards(cur,end) cur = get_step_towards(cur,end)
if (!can_place) if (!can_place)
start = null
update_icon()
usr << "<span class='warning'>You can't run \the [src] through that!</span>" usr << "<span class='warning'>You can't run \the [src] through that!</span>"
return return
cur = start cur = start
var/tapetest = 0 var/tapetest
while (cur!=end) var/tape_dir
for(var/obj/item/tape/Ptest in cur) while (1)
if(Ptest.icon_state == "[Ptest.icon_base]_[dir]") 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 tapetest = 1
if(tapetest != 1) break
var/obj/item/tape/P = new tape_type(cur) if(!tapetest)
P.icon_state = "[P.icon_base]_[dir]" 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) cur = get_step_towards(cur,end)
start = null
update_icon()
usr << "<span class='notice'>You finish placing \the [src].</span>" usr << "<span class='notice'>You finish placing \the [src].</span>"
return
/obj/item/taperoll/afterattack(var/atom/A, mob/user as mob, proximity) /obj/item/taperoll/afterattack(var/atom/A, mob/user as mob, proximity)
if(!proximity) if(!proximity)
@@ -149,6 +238,8 @@ var/list/tape_roll_applications = list()
add_fingerprint(M) add_fingerprint(M)
if (!allowed(M)) //only select few learn art of not crumpling the tape 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>" M << "<span class='warning'>You are not supposed to go past [src]...</span>"
if(M.a_intent == I_HELP)
return 0
crumple() crumple()
return ..(mover) return ..(mover)
@@ -158,13 +249,45 @@ var/list/tape_roll_applications = list()
/obj/item/tape/attack_hand(mob/user as mob) /obj/item/tape/attack_hand(mob/user as mob)
if (user.a_intent == I_HELP && src.allowed(user)) if (user.a_intent == I_HELP && src.allowed(user))
user.show_viewers("<span class='notice'>\The [user] lifts \the [src], allowing passage.</span>") user.show_viewers("<span class='notice'>\The [user] lifts \the [src], allowing passage.</span>")
crumple() for(var/obj/item/tape/T in gettapeline())
lifted = 1 T.lift(100) //~10 seconds
spawn(200)
lifted = 0
else else
breaktape(null, user) 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) /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 return
user.show_viewers("<span class='notice'>\The [user] breaks \the [src]!</span>") user.show_viewers("<span class='notice'>\The [user] breaks \the [src]!</span>")
var/dir[2] for (var/obj/item/tape/T in gettapeline())
var/icon_dir = src.icon_state if(T == src)
if(icon_dir == "[src.icon_base]_h") continue
dir[1] = EAST if(T.tape_dir & get_dir(T, src))
dir[2] = WEST qdel(T)
if(icon_dir == "[src.icon_base]_v")
dir[1] = NORTH
dir[2] = SOUTH
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)
return
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" icon_state = "light"
desc = "This box is shaped on the inside so that only light tubes and bulbs fit." desc = "This box is shaped on the inside so that only light tubes and bulbs fit."
item_state = "syringe_kit" 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 use_to_pickup = 1 // for picking up broken bulbs, not that most people will try
/obj/item/weapon/storage/box/lights/New()
..()
make_exact_fit()
/obj/item/weapon/storage/box/lights/bulbs/New() /obj/item/weapon/storage/box/lights/bulbs/New()
..()
for(var/i = 0; i < 21; i++) for(var/i = 0; i < 21; i++)
new /obj/item/weapon/light/bulb(src) new /obj/item/weapon/light/bulb(src)
..()
/obj/item/weapon/storage/box/lights/tubes /obj/item/weapon/storage/box/lights/tubes
name = "box of replacement tubes" name = "box of replacement tubes"
icon_state = "lighttube" icon_state = "lighttube"
/obj/item/weapon/storage/box/lights/tubes/New() /obj/item/weapon/storage/box/lights/tubes/New()
..()
for(var/i = 0; i < 21; i++) for(var/i = 0; i < 21; i++)
new /obj/item/weapon/light/tube(src) new /obj/item/weapon/light/tube(src)
..()
/obj/item/weapon/storage/box/lights/mixed /obj/item/weapon/storage/box/lights/mixed
name = "box of replacement lights" name = "box of replacement lights"
icon_state = "lightmixed" icon_state = "lightmixed"
/obj/item/weapon/storage/box/lights/mixed/New() /obj/item/weapon/storage/box/lights/mixed/New()
..()
for(var/i = 0; i < 14; i++) for(var/i = 0; i < 14; i++)
new /obj/item/weapon/light/tube(src) new /obj/item/weapon/light/tube(src)
for(var/i = 0; i < 7; i++) for(var/i = 0; i < 7; i++)
new /obj/item/weapon/light/bulb(src) new /obj/item/weapon/light/bulb(src)
..()
/obj/item/weapon/storage/box/freezer /obj/item/weapon/storage/box/freezer
name = "portable freezer" name = "portable freezer"

View File

@@ -130,8 +130,7 @@
/obj/item/weapon/storage/firstaid/surgery /obj/item/weapon/storage/firstaid/surgery
name = "surgery kit" name = "surgery kit"
desc = "Contains tools for surgery." desc = "Contains tools for surgery. Has precise foam fitting for safe transport."
storage_slots = 10
/obj/item/weapon/storage/firstaid/surgery/New() /obj/item/weapon/storage/firstaid/surgery/New()
..() ..()
@@ -146,7 +145,8 @@
new /obj/item/weapon/bonegel(src) new /obj/item/weapon/bonegel(src)
new /obj/item/weapon/FixOVein(src) new /obj/item/weapon/FixOVein(src)
new /obj/item/stack/medical/advanced/bruise_pack(src) new /obj/item/stack/medical/advanced/bruise_pack(src)
return
make_exact_fit()
/* /*
* Pill Bottles * 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,12 +235,16 @@
usr << "<span class='notice'>[src] is full, make some space.</span>" usr << "<span class='notice'>[src] is full, make some space.</span>"
return 0 //Storage item is full return 0 //Storage item is full
if(can_hold.len && !is_type_in_list(W, can_hold)) if(can_hold.len)
if(!stop_messages) if(!is_type_in_list(W, can_hold))
if (istype(W, /obj/item/weapon/hand_labeler)) if(!stop_messages && ! istype(W, /obj/item/weapon/hand_labeler))
return 0 usr << "<span class='notice'>[src] cannot hold \the [W].</span>"
usr << "<span class='notice'>[src] cannot hold [W].</span>" return 0
return 0 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)) if(cant_hold.len && is_type_in_list(W, cant_hold))
if(!stop_messages) if(!stop_messages)
@@ -461,6 +465,17 @@
src.quick_empty() src.quick_empty()
return 1 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 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. //Returns -1 if the atom was not found on container.
/atom/proc/storage_depth(atom/container) /atom/proc/storage_depth(atom/container)

View File

@@ -102,70 +102,53 @@
user << "<span class='warning'>[src] is out of charge.</span>" user << "<span class='warning'>[src] is out of charge.</span>"
add_fingerprint(user) add_fingerprint(user)
/obj/item/weapon/melee/baton/attack(mob/M, mob/user) /obj/item/weapon/melee/baton/attack(mob/M, mob/user)
if(status && (CLUMSY in user.mutations) && prob(50)) if(status && (CLUMSY in user.mutations) && prob(50))
user << "<span class='danger'>You accidentally hit yourself with the [src]!</span>" user << "<span class='danger'>You accidentally hit yourself with the [src]!</span>"
user.Weaken(30) user.Weaken(30)
deductcharge(hitcost) deductcharge(hitcost)
return return
return ..()
if(isrobot(M)) /obj/item/weapon/melee/baton/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone)
..() if(isrobot(target))
return return ..()
var/agony = agonyforce var/agony = agonyforce
var/stun = stunforce 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(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. //whacking someone causes a much poorer electrical contact than deliberately prodding them.
agony *= 0.5 //whacking someone causes a much poorer contact than prodding them. agony *= 0.5
stun *= 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. //we can't really extract the actual hit zone from ..(), unfortunately. Just act like they attacked the area they intended to.
else else if(!status)
//copied from human_defense.dm - human defence code should really be refactored some time. if(affecting)
if (ishuman(L)) target.visible_message("<span class='warning'>[target] has been prodded in the [affecting.name] with [src] by [user]. Luckily it was off.</span>")
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)
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
else
H.visible_message("<span class='danger'>[L] has been prodded in the [affecting.name] with [src] by [user]!</span>")
else else
if(!status) target.visible_message("<span class='warning'>[target] has been prodded with [src] by [user]. Luckily it was off.</span>")
L.visible_message("<span class='warning'>[L] has been prodded with [src] by [user]. Luckily it was off.</span>") else
return 1 if(affecting)
else target.visible_message("<span class='danger'>[target] has been prodded in the [affecting.name] with [src] by [user]!</span>")
L.visible_message("<span class='danger'>[L] has been prodded with [src] by [user]!</span>") else
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 //stun effects
L.stun_effect_act(stun, agony, target_zone, 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].")
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) deductcharge(hitcost)
msg_admin_attack("[key_name(user)] stunned [key_name(L)] with the [src].")
deductcharge(hitcost) if(ishuman(target))
var/mob/living/carbon/human/H = target
if(ishuman(L)) H.forcesay(hit_appends)
var/mob/living/carbon/human/H = L
H.forcesay(hit_appends)
return 1
/obj/item/weapon/melee/baton/emp_act(severity) /obj/item/weapon/melee/baton/emp_act(severity)
if(bcell) if(bcell)

View File

@@ -1,18 +1,10 @@
/* Weapons /* Weapons
* Contains: * Contains:
* Banhammer
* Sword * Sword
* Classic Baton * 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 * Classic Baton
*/ */
/obj/item/weapon/melee/classic_baton /obj/item/weapon/melee/classic_baton
@@ -34,33 +26,7 @@
else else
user.take_organ_damage(2*force) user.take_organ_damage(2*force)
return return
/*this is already called in ..() return ..()
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)
//Telescopic baton //Telescopic baton
/obj/item/weapon/melee/telebaton /obj/item/weapon/melee/telebaton

View File

@@ -41,6 +41,10 @@
processing_objects.Remove(src) 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) /obj/item/weapon/tank/examine(mob/user)
@@ -249,7 +253,10 @@
qdel(src) qdel(src)
else if(pressure > TANK_RUPTURE_PRESSURE) 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) if(integrity <= 0)
var/turf/simulated/T = get_turf(src) var/turf/simulated/T = get_turf(src)
if(!T) if(!T)
@@ -261,7 +268,10 @@
integrity-- integrity--
else if(pressure > TANK_LEAK_PRESSURE) 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) if(integrity <= 0)
var/turf/simulated/T = get_turf(src) var/turf/simulated/T = get_turf(src)
if(!T) if(!T)

View File

@@ -115,7 +115,7 @@
item_state = "cutters_yellow" item_state = "cutters_yellow"
/obj/item/weapon/wirecutters/attack(mob/living/carbon/C as mob, mob/user as mob) /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]!",\ usr.visible_message("\The [usr] cuts \the [C]'s restraints with \the [src]!",\
"You cut \the [C]'s restraints with \the [src]!",\ "You cut \the [C]'s restraints with \the [src]!",\
"You hear cable being cut.") "You hear cable being cut.")
@@ -337,18 +337,18 @@
if(!E) if(!E)
return return
switch(safety) switch(safety)
if(1) if(FLASH_PROTECTION_MODERATE)
usr << "<span class='warning'>Your eyes sting a little.</span>" usr << "<span class='warning'>Your eyes sting a little.</span>"
E.damage += rand(1, 2) E.damage += rand(1, 2)
if(E.damage > 12) if(E.damage > 12)
user.eye_blurry += rand(3,6) user.eye_blurry += rand(3,6)
if(0) if(FLASH_PROTECTION_NONE)
usr << "<span class='warning'>Your eyes burn.</span>" usr << "<span class='warning'>Your eyes burn.</span>"
E.damage += rand(2, 4) E.damage += rand(2, 4)
if(E.damage > 10) if(E.damage > 10)
E.damage += rand(4,10) E.damage += rand(4,10)
if(-1) if(FLASH_PROTECTION_REDUCED)
usr << "<span class='danger'>Your thermals intensify the welder's glow. Your eyes itch and burn severely.</span>" usr << "<span class='danger'>Your equipment intensify the welder's glow. Your eyes itch and burn severely.</span>"
user.eye_blurry += rand(12,20) user.eye_blurry += rand(12,20)
E.damage += rand(12, 16) E.damage += rand(12, 16)
if(safety<2) 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 /obj/item/weapon/nullrod
name = "null rod" 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>)") 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") 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>" user << "<span class='danger'>You don't have the dexterity to do this!</span>"
return return
@@ -68,27 +56,6 @@
user << "<span class='notice'>You hit the floor with the [src].</span>" user << "<span class='notice'>You hit the floor with the [src].</span>"
call(/obj/effect/rune/proc/revealrunes)(src) 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 /obj/item/weapon/energy_net
name = "energy net" name = "energy net"
desc = "It's a net made of green energy." desc = "It's a net made of green energy."

View File

@@ -46,7 +46,11 @@
/obj/proc/CouldUseTopic(var/mob/user) /obj/proc/CouldUseTopic(var/mob/user)
var/atom/host = nano_host() var/atom/host = nano_host()
host.add_hiddenprint(user) 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) /obj/proc/CouldNotUseTopic(var/mob/user)
// Nada // Nada

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) 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) qdel(src)
return 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)) if(isrobot(user))
return return
if(W.loc != user) // This should stop mounted modules ending up outside the module. 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/suit/storage/hazardvest(src)
new /obj/item/clothing/mask/gas(src) new /obj/item/clothing/mask/gas(src)
new /obj/item/weapon/cartridge/atmos(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) new /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/atmos(src)
return return

View File

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

View File

@@ -40,7 +40,7 @@
/obj/structure/inflatable/bullet_act(var/obj/item/projectile/Proj) /obj/structure/inflatable/bullet_act(var/obj/item/projectile/Proj)
var/proj_damage = Proj.get_structure_damage() var/proj_damage = Proj.get_structure_damage()
if(!proj_damage) return if(!proj_damage) return
health -= proj_damage health -= proj_damage
..() ..()
if(health <= 0) if(health <= 0)
@@ -82,6 +82,9 @@
if(health <= 0) if(health <= 0)
deflate(1) deflate(1)
/obj/structure/inflatable/CtrlClick()
hand_deflate()
/obj/structure/inflatable/proc/deflate(var/violent=0) /obj/structure/inflatable/proc/deflate(var/violent=0)
playsound(loc, 'sound/machines/hiss.ogg', 75, 1) playsound(loc, 'sound/machines/hiss.ogg', 75, 1)
if(violent) if(violent)
@@ -102,7 +105,7 @@
set category = "Object" set category = "Object"
set src in oview(1) set src in oview(1)
if(isobserver(usr)) //to stop ghosts from deflating if(isobserver(usr) || usr.restrained() || !usr.Adjacent(src))
return return
verbs -= /obj/structure/inflatable/verb/hand_deflate 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) /obj/item/weapon/stool/attack(mob/M as mob, mob/user as mob)
if (prob(5) && istype(M,/mob/living)) if (prob(5) && istype(M,/mob/living))
user.visible_message("<span class='danger'>[user] breaks [src] over [M]'s back!</span>") 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) user.remove_from_mob(src)
dismantle() dismantle()
qdel(src) qdel(src)

View File

@@ -293,7 +293,7 @@
if(usr.incapacitated()) if(usr.incapacitated())
return 0 return 0
if(anchored) if(anchored)
usr << "It is fastened to the floor therefore you can't rotate it!" usr << "It is fastened to the floor therefore you can't rotate it!"
return 0 return 0
@@ -491,7 +491,23 @@
animate(src, color="#222222", time=5) animate(src, color="#222222", time=5)
set_opacity(1) 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 /obj/machinery/button/windowtint
name = "window tint control" name = "window tint control"

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