mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-30 15:38:02 +01:00
Merge branch 'master' into upstream-merge-5756
This commit is contained in:
@@ -22,9 +22,6 @@ mob/proc/airflow_stun()
|
||||
mob/living/silicon/airflow_stun()
|
||||
return
|
||||
|
||||
mob/living/simple_animal/slime/airflow_stun()
|
||||
return
|
||||
|
||||
mob/living/carbon/human/airflow_stun()
|
||||
if(shoes && (shoes.item_flags & NOSLIP))
|
||||
to_chat(src, "<span class='notice'>Air suddenly rushes past you!</span>")
|
||||
|
||||
@@ -58,7 +58,10 @@ What is the naming convention for planes or layers?
|
||||
#define ATMOS_LAYER 2.4 // Pipe-like atmos machinery that goes on the floor, like filters.
|
||||
#define ABOVE_UTILITY 2.5 // Above stuff like pipes and wires
|
||||
#define TURF_PLANE -45 // Turfs themselves, most flooring
|
||||
#define ABOVE_TURF_LAYER 2.1 // Snow and wallmounted/floormounted equipment
|
||||
#define WATER_FLOOR_LAYER 2.0 // The 'bottom' of water tiles.
|
||||
#define UNDERWATER_LAYER 2.5 // Anything on this layer will render under the water layer.
|
||||
#define WATER_LAYER 3.0 // Layer for water overlays.
|
||||
#define ABOVE_TURF_LAYER 3.1 // Snow and wallmounted/floormounted equipment
|
||||
#define DECAL_PLANE -44 // Permanent decals
|
||||
#define DIRTY_PLANE -43 // Nonpermanent decals
|
||||
#define BLOOD_PLANE -42 // Blood is really dirty, but we can do special stuff if we separate it
|
||||
|
||||
+213
-3
@@ -1,18 +1,228 @@
|
||||
// Credits to Nickr5 for the useful procs I've taken from his library resource.
|
||||
// This file is quadruple wrapped for your pleasure
|
||||
// (
|
||||
|
||||
#define NUM_E 2.71828183
|
||||
|
||||
#define M_PI (3.14159265)
|
||||
#define INFINITY (1.#INF) //closer then enough
|
||||
|
||||
#define SHORT_REAL_LIMIT 16777216
|
||||
|
||||
//"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks
|
||||
//percent_of_tick_used * (ticklag * 100(to convert to ms)) / 100(percent ratio)
|
||||
//collapsed to percent_of_tick_used * tick_lag
|
||||
#define TICK_DELTA_TO_MS(percent_of_tick_used) ((percent_of_tick_used) * world.tick_lag)
|
||||
#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(TICK_USAGE-starting_tickusage))
|
||||
#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(world.tick_usage - starting_tickusage))
|
||||
|
||||
#define PERCENT(val) (round((val)*100, 0.1))
|
||||
#define CLAMP01(x) (CLAMP(x, 0, 1))
|
||||
|
||||
//time of day but automatically adjusts to the server going into the next day within the same round.
|
||||
//for when you need a reliable time number that doesn't depend on byond time.
|
||||
#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK))
|
||||
#define MIDNIGHT_ROLLOVER_CHECK ( rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : midnight_rollovers )
|
||||
|
||||
#define SHORT_REAL_LIMIT 16777216 // 2^24 - Maximum integer that can be exactly represented in a float (BYOND num var)
|
||||
#define SIGN(x) ( (x)!=0 ? (x) / abs(x) : 0 )
|
||||
|
||||
#define CEILING(x, y) ( -round(-(x) / (y)) * (y) )
|
||||
|
||||
// round() acts like floor(x, 1) by default but can't handle other values
|
||||
#define FLOOR(x, y) ( round((x) / (y)) * (y) )
|
||||
// Check if a BYOND dir var is a cardinal direction (power of two)
|
||||
|
||||
#define CLAMP(CLVALUE,CLMIN,CLMAX) ( max( (CLMIN), min((CLVALUE), (CLMAX)) ) )
|
||||
|
||||
// Similar to clamp but the bottom rolls around to the top and vice versa. min is inclusive, max is exclusive
|
||||
#define WRAP(val, min, max) ( min == max ? min : (val) - (round(((val) - (min))/((max) - (min))) * ((max) - (min))) )
|
||||
|
||||
// Real modulus that handles decimals
|
||||
#define MODULUS(x, y) ( (x) - (y) * round((x) / (y)) )
|
||||
|
||||
// Tangent
|
||||
#define TAN(x) (sin(x) / cos(x))
|
||||
|
||||
// Cotangent
|
||||
#define COT(x) (1 / TAN(x))
|
||||
|
||||
// Secant
|
||||
#define SEC(x) (1 / cos(x))
|
||||
|
||||
// Cosecant
|
||||
#define CSC(x) (1 / sin(x))
|
||||
|
||||
#define ATAN2(x, y) ( !(x) && !(y) ? 0 : (y) >= 0 ? arccos((x) / sqrt((x)*(x) + (y)*(y))) : -arccos((x) / sqrt((x)*(x) + (y)*(y))) )
|
||||
|
||||
// Greatest Common Divisor - Euclid's algorithm
|
||||
/proc/GCD(a, b)
|
||||
return b ? GCD(b, (a) % (b)) : a
|
||||
|
||||
// Least Common Multiple
|
||||
#define LCM(a, b) (abs(a) / GCD(a, b) * abs(b))
|
||||
|
||||
#define IS_CARDINAL(x) ((x & (x - 1)) == 0)
|
||||
|
||||
#define INVERSE(x) ( 1/(x) )
|
||||
|
||||
// Used for calculating the radioactive strength falloff
|
||||
#define INVERSE_SQUARE(initial_strength,cur_distance,initial_distance) ( (initial_strength)*((initial_distance)**2/(cur_distance)**2) )
|
||||
|
||||
#define ISABOUTEQUAL(a, b, deviation) (deviation ? abs((a) - (b)) <= deviation : abs((a) - (b)) <= 0.1)
|
||||
|
||||
#define ISEVEN(x) (x % 2 == 0)
|
||||
|
||||
#define ISODD(x) (x % 2 != 0)
|
||||
|
||||
// Returns true if val is from min to max, inclusive.
|
||||
#define ISINRANGE(val, min, max) (min <= val && val <= max)
|
||||
|
||||
// Same as above, exclusive.
|
||||
#define ISINRANGE_EX(val, min, max) (min < val && val > max)
|
||||
|
||||
#define ISINTEGER(x) (round(x) == x)
|
||||
|
||||
#define ISMULTIPLE(x, y) ((x) % (y) == 0)
|
||||
|
||||
// Performs a linear interpolation between a and b.
|
||||
// Note that amount=0 returns a, amount=1 returns b, and
|
||||
// amount=0.5 returns the mean of a and b.
|
||||
#define LERP(a, b, amount) ( amount ? ((a) + ((b) - (a)) * (amount)) : a )
|
||||
|
||||
// Returns the nth root of x.
|
||||
#define ROOT(n, x) ((x) ** (1 / (n)))
|
||||
|
||||
/proc/Mean(...)
|
||||
var/sum = 0
|
||||
for(var/val in args)
|
||||
sum += val
|
||||
return sum / args.len
|
||||
|
||||
// The quadratic formula. Returns a list with the solutions, or an empty list
|
||||
// if they are imaginary.
|
||||
/proc/SolveQuadratic(a, b, c)
|
||||
ASSERT(a)
|
||||
. = list()
|
||||
var/d = b*b - 4 * a * c
|
||||
var/bottom = 2 * a
|
||||
// Return if the roots are imaginary.
|
||||
if(d < 0)
|
||||
return
|
||||
var/root = sqrt(d)
|
||||
. += (-b + root) / bottom
|
||||
// If discriminant == 0, there would be two roots at the same position.
|
||||
if(!d)
|
||||
return
|
||||
. += (-b - root) / bottom
|
||||
|
||||
// 180 / Pi ~ 57.2957795
|
||||
#define TODEGREES(radians) ((radians) * 57.2957795)
|
||||
|
||||
// Pi / 180 ~ 0.0174532925
|
||||
#define TORADIANS(degrees) ((degrees) * 0.0174532925)
|
||||
|
||||
// Will filter out extra rotations and negative rotations
|
||||
// E.g: 540 becomes 180. -180 becomes 180.
|
||||
#define SIMPLIFY_DEGREES(degrees) (MODULUS((degrees), 360))
|
||||
|
||||
#define GET_ANGLE_OF_INCIDENCE(face, input) (MODULUS((face) - (input), 360))
|
||||
|
||||
//Finds the shortest angle that angle A has to change to get to angle B. Aka, whether to move clock or counterclockwise.
|
||||
/proc/closer_angle_difference(a, b)
|
||||
if(!isnum(a) || !isnum(b))
|
||||
return
|
||||
a = SIMPLIFY_DEGREES(a)
|
||||
b = SIMPLIFY_DEGREES(b)
|
||||
var/inc = b - a
|
||||
if(inc < 0)
|
||||
inc += 360
|
||||
var/dec = a - b
|
||||
if(dec < 0)
|
||||
dec += 360
|
||||
. = inc > dec? -dec : inc
|
||||
|
||||
//A logarithm that converts an integer to a number scaled between 0 and 1.
|
||||
//Currently, this is used for hydroponics-produce sprite transforming, but could be useful for other transform functions.
|
||||
#define TRANSFORM_USING_VARIABLE(input, max) ( sin((90*(input))/(max))**2 )
|
||||
|
||||
//converts a uniform distributed random number into a normal distributed one
|
||||
//since this method produces two random numbers, one is saved for subsequent calls
|
||||
//(making the cost negligble for every second call)
|
||||
//This will return +/- decimals, situated about mean with standard deviation stddev
|
||||
//68% chance that the number is within 1stddev
|
||||
//95% chance that the number is within 2stddev
|
||||
//98% chance that the number is within 3stddev...etc
|
||||
#define ACCURACY 10000
|
||||
/proc/gaussian(mean, stddev)
|
||||
var/static/gaussian_next
|
||||
var/R1;var/R2;var/working
|
||||
if(gaussian_next != null)
|
||||
R1 = gaussian_next
|
||||
gaussian_next = null
|
||||
else
|
||||
do
|
||||
R1 = rand(-ACCURACY,ACCURACY)/ACCURACY
|
||||
R2 = rand(-ACCURACY,ACCURACY)/ACCURACY
|
||||
working = R1*R1 + R2*R2
|
||||
while(working >= 1 || working==0)
|
||||
working = sqrt(-2 * log(working) / working)
|
||||
R1 *= working
|
||||
gaussian_next = R2 * working
|
||||
return (mean + stddev * R1)
|
||||
#undef ACCURACY
|
||||
|
||||
/proc/get_turf_in_angle(angle, turf/starting, increments)
|
||||
var/pixel_x = 0
|
||||
var/pixel_y = 0
|
||||
for(var/i in 1 to increments)
|
||||
pixel_x += sin(angle)+16*sin(angle)*2
|
||||
pixel_y += cos(angle)+16*cos(angle)*2
|
||||
var/new_x = starting.x
|
||||
var/new_y = starting.y
|
||||
while(pixel_x > 16)
|
||||
pixel_x -= 32
|
||||
new_x++
|
||||
while(pixel_x < -16)
|
||||
pixel_x += 32
|
||||
new_x--
|
||||
while(pixel_y > 16)
|
||||
pixel_y -= 32
|
||||
new_y++
|
||||
while(pixel_y < -16)
|
||||
pixel_y += 32
|
||||
new_y--
|
||||
new_x = CLAMP(new_x, 0, world.maxx)
|
||||
new_y = CLAMP(new_y, 0, world.maxy)
|
||||
return locate(new_x, new_y, starting.z)
|
||||
|
||||
// Returns a list where [1] is all x values and [2] is all y values that overlap between the given pair of rectangles
|
||||
/proc/get_overlap(x1, y1, x2, y2, x3, y3, x4, y4)
|
||||
var/list/region_x1 = list()
|
||||
var/list/region_y1 = list()
|
||||
var/list/region_x2 = list()
|
||||
var/list/region_y2 = list()
|
||||
|
||||
// These loops create loops filled with x/y values that the boundaries inhabit
|
||||
// ex: list(5, 6, 7, 8, 9)
|
||||
for(var/i in min(x1, x2) to max(x1, x2))
|
||||
region_x1["[i]"] = TRUE
|
||||
for(var/i in min(y1, y2) to max(y1, y2))
|
||||
region_y1["[i]"] = TRUE
|
||||
for(var/i in min(x3, x4) to max(x3, x4))
|
||||
region_x2["[i]"] = TRUE
|
||||
for(var/i in min(y3, y4) to max(y3, y4))
|
||||
region_y2["[i]"] = TRUE
|
||||
|
||||
return list(region_x1 & region_x2, region_y1 & region_y2)
|
||||
|
||||
// )
|
||||
|
||||
#define RAND_F(LOW, HIGH) (rand()*(HIGH-LOW) + LOW)
|
||||
|
||||
#define SQUARE(x) (x*x)
|
||||
|
||||
//Vector Algebra
|
||||
#define SQUAREDNORM(x, y) (x*x+y*y)
|
||||
#define NORM(x, y) (sqrt(SQUAREDNORM(x,y)))
|
||||
#define ISPOWEROFTWO(x) ((x & (x - 1)) == 0)
|
||||
#define ROUNDUPTOPOWEROFTWO(x) (2 ** -round(-log(2,x)))
|
||||
|
||||
#define DEFAULT(a, b) (a? a : b)
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// Math constants.
|
||||
#define M_PI 3.14159265
|
||||
|
||||
#define R_IDEAL_GAS_EQUATION 8.31 // kPa*L/(K*mol).
|
||||
#define ONE_ATMOSPHERE 101.325 // kPa.
|
||||
#define IDEAL_GAS_ENTROPY_CONSTANT 1164 // (mol^3 * s^3) / (kg^3 * L).
|
||||
|
||||
#define T0C 273.15 // 0.0 degrees celcius
|
||||
#define T20C 293.15 // 20.0 degrees celcius
|
||||
#define TCMB 2.7 // -270.3 degrees celcius
|
||||
#define TN60C 213.15 // -60 degrees celcius
|
||||
|
||||
// Radiation constants.
|
||||
#define STEFAN_BOLTZMANN_CONSTANT 5.6704e-8 // W/(m^2*K^4).
|
||||
#define COSMIC_RADIATION_TEMPERATURE 3.15 // K.
|
||||
@@ -15,18 +18,7 @@
|
||||
#define RADIATOR_EXPOSED_SURFACE_AREA_RATIO 0.04 // (3 cm + 100 cm * sin(3deg))/(2*(3+100 cm)). Unitless ratio.
|
||||
#define HUMAN_EXPOSED_SURFACE_AREA 5.2 //m^2, surface area of 1.7m (H) x 0.46m (D) cylinder
|
||||
|
||||
#define T0C 273.15 // 0.0 degrees celcius
|
||||
#define T20C 293.15 // 20.0 degrees celcius
|
||||
#define TCMB 2.7 // -270.3 degrees celcius
|
||||
#define TN60C 213.15 // -60 degrees celcius
|
||||
|
||||
#define CLAMP01(x) max(0, min(1, x))
|
||||
#define QUANTIZE(variable) (round(variable,0.0001))
|
||||
|
||||
#define INFINITY 1.#INF
|
||||
|
||||
#define TICKS_IN_DAY 24*60*60*10
|
||||
#define TICKS_IN_SECOND 10
|
||||
|
||||
#define SIMPLE_SIGN(X) ((X) < 0 ? -1 : 1)
|
||||
#define SIGN(X) ((X) ? SIMPLE_SIGN(X) : 0)
|
||||
#define TICKS_IN_DAY (TICKS_IN_SECOND * 60 * 60 * 24)
|
||||
#define TICKS_IN_SECOND (world.fps)
|
||||
@@ -302,4 +302,4 @@ var/global/list/##LIST_NAME = list();\
|
||||
|
||||
|
||||
#define RCD_SHEETS_PER_MATTER_UNIT 4 // Each physical material sheet is worth four matter units.
|
||||
#define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT
|
||||
#define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
#define BORGXRAY 0x4
|
||||
#define BORGMATERIAL 8
|
||||
|
||||
#define STANCE_ATTACK 11 // Backwards compatability
|
||||
#define STANCE_ATTACKING 12 // Ditto
|
||||
/*
|
||||
#define STANCE_IDLE 1 // Looking for targets if hostile. Does idle wandering.
|
||||
#define STANCE_ALERT 2 // Bears
|
||||
#define STANCE_ATTACK 3 // Attempting to get into attack position
|
||||
@@ -34,6 +37,20 @@
|
||||
#define STANCE_TIRED 5 // Bears
|
||||
#define STANCE_FOLLOW 6 // Following somone
|
||||
#define STANCE_BUSY 7 // Do nothing on life ticks (Other code is running)
|
||||
*/
|
||||
#define STANCE_SLEEP 0 // Doing (almost) nothing, to save on CPU because nobody is around to notice or the mob died.
|
||||
#define STANCE_IDLE 1 // The more or less default state. Wanders around, looks for baddies, and spouts one-liners.
|
||||
#define STANCE_ALERT 2 // A baddie is visible but not too close, and essentially we tell them to go away or die.
|
||||
#define STANCE_APPROACH 3 // Attempting to get into range to attack them.
|
||||
#define STANCE_FIGHT 4 // Actually fighting, with melee or ranged.
|
||||
#define STANCE_BLINDFIGHT 5 // Fighting something that cannot be seen by the mob, from invisibility or out of sight.
|
||||
#define STANCE_REPOSITION 6 // Relocating to a better position while in combat. Also used when moving away from a danger like grenades.
|
||||
#define STANCE_MOVE 7 // Similar to above but for out of combat. If a baddie is seen, they'll cancel and fight them.
|
||||
#define STANCE_FOLLOW 8 // Following somone, without trying to murder them.
|
||||
#define STANCE_FLEE 9 // Run away from the target because they're too spooky/we're dying/some other reason.
|
||||
#define STANCE_DISABLED 10 // Used when the holder is afflicted with certain status effects, such as stuns or confusion.
|
||||
|
||||
#define STANCES_COMBAT list(STANCE_ALERT, STANCE_APPROACH, STANCE_FIGHT, STANCE_BLINDFIGHT, STANCE_REPOSITION)
|
||||
|
||||
#define LEFT 0x1
|
||||
#define RIGHT 0x2
|
||||
@@ -279,11 +296,34 @@
|
||||
#define SA_ROBOTIC 3
|
||||
#define SA_HUMANOID 4
|
||||
|
||||
// More refined version of SA_* ""intelligence"" seperators.
|
||||
// Now includes bitflags, so to target two classes you just do 'MOB_CLASS_ANIMAL|MOB_CLASS_HUMANOID'
|
||||
#define MOB_CLASS_NONE 0 // Default value, and used to invert for _ALL.
|
||||
|
||||
#define MOB_CLASS_PLANT 1 // Unused at the moment.
|
||||
#define MOB_CLASS_ANIMAL 2 // Animals and beasts like spiders, saviks, and bears.
|
||||
#define MOB_CLASS_HUMANOID 4 // Non-robotic humanoids, including /simple_mob and /carbon/humans and their alien variants.
|
||||
#define MOB_CLASS_SYNTHETIC 8 // Silicons, mechanical simple mobs, FBPs, and anything else that would pass is_synthetic()
|
||||
#define MOB_CLASS_SLIME 16 // Everyone's favorite xenobiology specimen (and maybe prometheans?).
|
||||
#define MOB_CLASS_ABERRATION 32 // Weird shit.
|
||||
#define MOB_CLASS_DEMONIC 64 // Cult stuff.
|
||||
#define MOB_CLASS_BOSS 128 // Future megafauna hopefully someday.
|
||||
#define MOB_CLASS_ILLUSION 256 // Fake mobs, e.g. Technomancer illusions.
|
||||
#define MOB_CLASS_PHOTONIC 512 // Holographic mobs like holocarp, similar to _ILLUSION, but that make no attempt to hide their true nature.
|
||||
|
||||
#define MOB_CLASS_ALL (~MOB_CLASS_NONE)
|
||||
|
||||
// For slime commanding. Higher numbers allow for more actions.
|
||||
#define SLIME_COMMAND_OBEY 1 // When disciplined.
|
||||
#define SLIME_COMMAND_FACTION 2 // When in the same 'faction'.
|
||||
#define SLIME_COMMAND_FRIEND 3 // When befriended with a slime friendship agent.
|
||||
|
||||
// Threshold for mobs being able to damage things like airlocks or reinforced glass windows.
|
||||
// If the damage is below this, nothing will happen besides a message saying that the attack was ineffective.
|
||||
// Generally, this was not a define but was commonly set to 10, however 10 may be too low now since simple_mobs now attack twice as fast,
|
||||
// at half damage compared to the old mob system, meaning mobs who could hurt structures may not be able to now, so now it is 5.
|
||||
#define STRUCTURE_MIN_DAMAGE_THRESHOLD 5
|
||||
|
||||
//Vision flags, for dealing with plane visibility
|
||||
#define VIS_FULLBRIGHT 1
|
||||
#define VIS_LIGHTING 2
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Defines used for miscellaneous objects.
|
||||
* Currently only the home of the Multiool.
|
||||
*/
|
||||
|
||||
// Multitool Mode Defines.
|
||||
|
||||
#define MULTITOOL_MODE_STANDARD "Standard"
|
||||
#define MULTITOOL_MODE_INTCIRCUITS "Modular Wiring"
|
||||
#define MULTITOOL_MODE_DOORHACK "Advanced Jacking"
|
||||
@@ -60,6 +60,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
|
||||
#define INIT_ORDER_OVERLAY -6
|
||||
#define INIT_ORDER_XENOARCH -20
|
||||
#define INIT_ORDER_CIRCUIT -21
|
||||
#define INIT_ORDER_AI -22
|
||||
|
||||
|
||||
// Subsystem fire priority, from lowest to highest priority
|
||||
@@ -67,6 +68,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
|
||||
#define FIRE_PRIORITY_SHUTTLES 5
|
||||
#define FIRE_PRIORITY_ORBIT 8
|
||||
#define FIRE_PRIORITY_VOTE 9
|
||||
#define FIRE_PRIORITY_AI 10
|
||||
#define FIRE_PRIORITY_GARBAGE 15
|
||||
#define FIRE_PRIORITY_AIRFLOW 30
|
||||
#define FIRE_PRIORITY_AIR 35
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
GLOBAL_LIST_EMPTY(all_observable_events)
|
||||
GLOBAL_LIST_EMPTY(error_last_seen)
|
||||
GLOBAL_LIST_EMPTY(error_cooldown)
|
||||
|
||||
GLOBAL_DATUM_INIT(all_observable_events, /datum/all_observable_events, new) // This is a datum. It is not a list.
|
||||
GLOBAL_DATUM_INIT(destroyed_event, /decl/observ/destroyed, new())
|
||||
|
||||
GLOBAL_VAR_INIT(timezoneOffset, 0) // The difference betwen midnight (of the host computer) and 0 world.ticks.
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@ GLOBAL_LIST_EMPTY(admins) //all clients whom are admins
|
||||
GLOBAL_PROTECT(admins)
|
||||
GLOBAL_LIST_EMPTY(deadmins) //all ckeys who have used the de-admin verb.
|
||||
GLOBAL_LIST_EMPTY(stealthminID)
|
||||
GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client
|
||||
GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client
|
||||
|
||||
@@ -28,24 +28,17 @@ var/global/list/vantag_choices_list = list(
|
||||
VANTAG_KIDNAP = "Be Kidnapped",
|
||||
VANTAG_KILL = "Be Killed")
|
||||
|
||||
/* Time to finally undo this. Replaced with digest_act on these items.
|
||||
//Important items that are preserved when people are digested, etc.
|
||||
//On Polaris, different from Cryo list as MMIs need to be removed for FBPs to be logged out.
|
||||
var/global/list/important_items = list(
|
||||
//Blacklist to exclude items from object ingestion. Digestion blacklist located in digest_act_vr.dm
|
||||
var/global/list/item_vore_blacklist = list(
|
||||
/obj/item/weapon/hand_tele,
|
||||
/obj/item/weapon/card/id/gold/captain/spare,
|
||||
/obj/item/device/aicard,
|
||||
/obj/item/device/mmi/digital/posibrain,
|
||||
/obj/item/device/paicard,
|
||||
/obj/item/weapon/gun,
|
||||
/obj/item/weapon/pinpointer,
|
||||
/obj/item/clothing/shoes/magboots,
|
||||
/obj/item/blueprints,
|
||||
/obj/item/clothing/head/helmet/space,
|
||||
/obj/item/weapon/disk/nuclear,
|
||||
/obj/item/clothing/suit/storage/hooded/wintercoat/roiz,
|
||||
/obj/item/device/perfect_tele_beacon)
|
||||
*/
|
||||
/obj/item/clothing/suit/storage/hooded/wintercoat/roiz)
|
||||
|
||||
var/global/list/digestion_sounds = list(
|
||||
'sound/vore/digest1.ogg',
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
// Macro functions.
|
||||
#define RAND_F(LOW, HIGH) (rand()*(HIGH-LOW) + LOW)
|
||||
#define ceil(x) (-round(-(x)))
|
||||
|
||||
// min is inclusive, max is exclusive
|
||||
/proc/Wrap(val, min, max)
|
||||
var/d = max - min
|
||||
var/t = Floor((val - min) / d)
|
||||
return val - (t * d)
|
||||
|
||||
/proc/Default(a, b)
|
||||
return a ? a : b
|
||||
|
||||
// Trigonometric functions.
|
||||
/proc/Tan(x)
|
||||
return sin(x) / cos(x)
|
||||
|
||||
/proc/Csc(x)
|
||||
return 1 / sin(x)
|
||||
|
||||
/proc/Sec(x)
|
||||
return 1 / cos(x)
|
||||
|
||||
/proc/Cot(x)
|
||||
return 1 / Tan(x)
|
||||
|
||||
/proc/Atan2(x, y)
|
||||
if(!x && !y) return 0
|
||||
var/a = arccos(x / sqrt(x*x + y*y))
|
||||
return y >= 0 ? a : -a
|
||||
|
||||
/proc/Floor(x)
|
||||
return round(x)
|
||||
|
||||
/proc/Ceiling(x)
|
||||
return -round(-x)
|
||||
|
||||
// Greatest Common Divisor: Euclid's algorithm.
|
||||
/proc/Gcd(a, b)
|
||||
while (1)
|
||||
if (!b) return a
|
||||
a %= b
|
||||
if (!a) return b
|
||||
b %= a
|
||||
|
||||
// Least Common Multiple. The formula is a consequence of: a*b = LCM*GCD.
|
||||
/proc/Lcm(a, b)
|
||||
return abs(a) * abs(b) / Gcd(a, b)
|
||||
|
||||
// Useful in the cases when x is a large expression, e.g. x = 3a/2 + b^2 + Function(c)
|
||||
/proc/Square(x)
|
||||
return x*x
|
||||
|
||||
/proc/Inverse(x)
|
||||
return 1 / x
|
||||
|
||||
// Condition checks.
|
||||
/proc/IsAboutEqual(a, b, delta = 0.1)
|
||||
return abs(a - b) <= delta
|
||||
|
||||
// Returns true if val is from min to max, inclusive.
|
||||
/proc/IsInRange(val, min, max)
|
||||
return (val >= min) && (val <= max)
|
||||
|
||||
/proc/IsInteger(x)
|
||||
return Floor(x) == x
|
||||
|
||||
/proc/IsMultiple(x, y)
|
||||
return x % y == 0
|
||||
|
||||
/proc/IsEven(x)
|
||||
return !(x & 0x1)
|
||||
|
||||
/proc/IsOdd(x)
|
||||
return (x & 0x1)
|
||||
|
||||
// Performs a linear interpolation between a and b.
|
||||
// Note: weight=0 returns a, weight=1 returns b, and weight=0.5 returns the mean of a and b.
|
||||
/proc/Interpolate(a, b, weight = 0.5)
|
||||
return a + (b - a) * weight // Equivalent to: a*(1 - weight) + b*weight
|
||||
|
||||
/proc/Mean(...)
|
||||
var/sum = 0
|
||||
for(var/val in args)
|
||||
sum += val
|
||||
return sum / args.len
|
||||
|
||||
// Returns the nth root of x.
|
||||
/proc/Root(n, x)
|
||||
return x ** (1 / n)
|
||||
|
||||
// The quadratic formula. Returns a list with the solutions, or an empty list
|
||||
// if they are imaginary.
|
||||
/proc/SolveQuadratic(a, b, c)
|
||||
ASSERT(a)
|
||||
|
||||
. = list()
|
||||
var/discriminant = b*b - 4*a*c
|
||||
var/bottom = 2*a
|
||||
|
||||
// Return if the roots are imaginary.
|
||||
if(discriminant < 0)
|
||||
return
|
||||
|
||||
var/root = sqrt(discriminant)
|
||||
. += (-b + root) / bottom
|
||||
|
||||
// If discriminant == 0, there would be two roots at the same position.
|
||||
if(discriminant != 0)
|
||||
. += (-b - root) / bottom
|
||||
|
||||
/proc/ToDegrees(radians)
|
||||
// 180 / Pi ~ 57.2957795
|
||||
return radians * 57.2957795
|
||||
|
||||
/proc/ToRadians(degrees)
|
||||
// Pi / 180 ~ 0.0174532925
|
||||
return degrees * 0.0174532925
|
||||
|
||||
// Vector algebra.
|
||||
/proc/squaredNorm(x, y)
|
||||
return x*x + y*y
|
||||
|
||||
/proc/norm(x, y)
|
||||
return sqrt(squaredNorm(x, y))
|
||||
|
||||
/proc/IsPowerOfTwo(var/val)
|
||||
return (val & (val-1)) == 0
|
||||
|
||||
/proc/RoundUpToPowerOfTwo(var/val)
|
||||
return 2 ** -round(-log(2,val))
|
||||
@@ -239,4 +239,4 @@ var/round_start_time = 0
|
||||
else
|
||||
day = "[truncate ? "day" : "1 day"]"
|
||||
|
||||
return "[day][hour][minute][second]"
|
||||
return "[day][hour][minute][second]"
|
||||
|
||||
@@ -501,7 +501,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
|
||||
moblist.Add(M)
|
||||
for(var/mob/new_player/M in sortmob)
|
||||
moblist.Add(M)
|
||||
for(var/mob/living/simple_animal/M in sortmob)
|
||||
for(var/mob/living/simple_mob/M in sortmob)
|
||||
moblist.Add(M)
|
||||
// for(var/mob/living/silicon/hivebot/M in sortmob)
|
||||
// mob_list.Add(M)
|
||||
|
||||
@@ -78,13 +78,13 @@ return_location()
|
||||
return
|
||||
|
||||
// calculate the angle
|
||||
angle = Atan2(dx, dy) + angle_offset
|
||||
angle = ATAN2(dx, dy) + angle_offset
|
||||
|
||||
// and some rounding to stop the increments jumping whole turfs - because byond favours certain angles
|
||||
if(angle > -135 && angle < 45)
|
||||
angle = Ceiling(angle)
|
||||
angle = CEILING(angle, 1)
|
||||
else
|
||||
angle = Floor(angle)
|
||||
angle = FLOOR(angle, 1)
|
||||
|
||||
// calculate the offset per increment step
|
||||
if(abs(angle) in list(0, 45, 90, 135, 180)) // check if the angle is a cardinal
|
||||
@@ -93,11 +93,11 @@ return_location()
|
||||
if(abs(angle) in list(45, 90, 135))
|
||||
offset_y = sign(dy)
|
||||
else if(abs(dy) > abs(dx))
|
||||
offset_x = Cot(abs(angle)) // otherwise set the offsets
|
||||
offset_x = COT(abs(angle)) // otherwise set the offsets
|
||||
offset_y = sign(dy)
|
||||
else
|
||||
offset_x = sign(dx)
|
||||
offset_y = Tan(angle)
|
||||
offset_y = TAN(angle)
|
||||
if(dx < 0)
|
||||
offset_y = -offset_y
|
||||
|
||||
|
||||
+5
-5
@@ -10,7 +10,7 @@
|
||||
|
||||
#define isalien(A) istype(A, /mob/living/carbon/alien)
|
||||
|
||||
#define isanimal(A) istype(A, /mob/living/simple_animal)
|
||||
#define isanimal(A) istype(A, /mob/living/simple_mob)
|
||||
|
||||
#define isairlock(A) istype(A, /obj/machinery/door/airlock)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#define iscarbon(A) istype(A, /mob/living/carbon)
|
||||
|
||||
#define iscorgi(A) istype(A, /mob/living/simple_animal/corgi)
|
||||
#define iscorgi(A) istype(A, /mob/living/simple_mob/animal/passive/dog/corgi)
|
||||
|
||||
#define isEye(A) istype(A, /mob/observer/eye)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#define isliving(A) istype(A, /mob/living)
|
||||
|
||||
#define ismouse(A) istype(A, /mob/living/simple_animal/mouse)
|
||||
#define ismouse(A) istype(A, /mob/living/simple_mob/animal/passive/mouse)
|
||||
|
||||
#define isnewplayer(A) istype(A, /mob/new_player)
|
||||
|
||||
@@ -42,11 +42,11 @@
|
||||
|
||||
#define isvoice(A) istype(A, /mob/living/voice)
|
||||
|
||||
#define isslime(A) istype(A, /mob/living/simple_animal/slime)
|
||||
#define isslime(A) istype(A, /mob/living/simple_mob/slime)
|
||||
|
||||
#define isbot(A) istype(A, /mob/living/bot)
|
||||
|
||||
#define isxeno(A) istype(A, /mob/living/simple_animal/xeno)
|
||||
#define isxeno(A) istype(A, /mob/living/simple_mob/animal/space/alien)
|
||||
|
||||
#define isopenspace(A) istype(A, /turf/simulated/open)
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
/obj/screen/fullscreen/flash
|
||||
icon = 'icons/mob/screen1.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "flash"
|
||||
icon_state = "flash_static"
|
||||
|
||||
/obj/screen/fullscreen/flash/noise
|
||||
icon_state = "noise"
|
||||
|
||||
@@ -316,8 +316,6 @@ datum/hud/New(mob/owner)
|
||||
mymob.instantiate_hud(src)
|
||||
else if(isalien(mymob))
|
||||
larva_hud()
|
||||
else if(isslime(mymob))
|
||||
slime_hud()
|
||||
else if(isAI(mymob))
|
||||
ai_hud()
|
||||
else if(isobserver(mymob))
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
mymob.client.screen += list(blobpwrdisplay, blobhealthdisplay)
|
||||
mymob.client.screen += mymob.client.void
|
||||
|
||||
/*
|
||||
/datum/hud/proc/slime_hud(ui_style = 'icons/mob/screen1_Midnight.dmi')
|
||||
|
||||
src.adding = list()
|
||||
@@ -92,23 +92,20 @@
|
||||
mymob.client.screen += mymob.client.void
|
||||
|
||||
return
|
||||
|
||||
|
||||
/mob/living/simple_animal/construct/instantiate_hud(var/datum/hud/HUD)
|
||||
..(HUD)
|
||||
*/
|
||||
|
||||
// HUD.construct_hud() //Archaic.
|
||||
/*
|
||||
/datum/hud/proc/construct_hud()
|
||||
var/constructtype
|
||||
|
||||
if(istype(mymob,/mob/living/simple_animal/construct/armoured) || istype(mymob,/mob/living/simple_animal/construct/behemoth))
|
||||
if(istype(mymob,/mob/living/simple_mob/construct/armoured) || istype(mymob,/mob/living/simple_mob/construct/behemoth))
|
||||
constructtype = "juggernaut"
|
||||
else if(istype(mymob,/mob/living/simple_animal/construct/builder))
|
||||
else if(istype(mymob,/mob/living/simple_mob/construct/builder))
|
||||
constructtype = "artificer"
|
||||
else if(istype(mymob,/mob/living/simple_animal/construct/wraith))
|
||||
else if(istype(mymob,/mob/living/simple_mob/construct/wraith))
|
||||
constructtype = "wraith"
|
||||
else if(istype(mymob,/mob/living/simple_animal/construct/harvester))
|
||||
else if(istype(mymob,/mob/living/simple_mob/construct/harvester))
|
||||
constructtype = "harvester"
|
||||
|
||||
if(constructtype)
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/obj/screen/proc/Click_vr(location, control, params)
|
||||
/obj/screen/proc/Click_vr(location, control, params) //VORESTATION AI TEMPORARY REMOVAL
|
||||
if(!usr) return 1
|
||||
switch(name)
|
||||
|
||||
//Shadekin
|
||||
if("darkness")
|
||||
var/mob/living/simple_animal/shadekin/sk = usr
|
||||
var/mob/living/simple_mob/shadekin/sk = usr
|
||||
var/turf/T = get_turf(sk)
|
||||
var/darkness = round(1 - T.get_lumcount(),0.1)
|
||||
to_chat(usr,"<span class='notice'><b>Darkness:</b> [darkness]</span>")
|
||||
if("energy")
|
||||
var/mob/living/simple_animal/shadekin/sk = usr
|
||||
var/mob/living/simple_mob/shadekin/sk = usr
|
||||
to_chat(usr,"<span class='notice'><b>Energy:</b> [sk.energy] ([sk.dark_gains])</span>")
|
||||
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ avoid code duplication. This includes items that may sometimes act as a standard
|
||||
// Same as above but actually does useful things.
|
||||
// W is the item being used in the attack, if any. modifier is if the attack should be longer or shorter than usual, for whatever reason.
|
||||
/mob/living/get_attack_speed(var/obj/item/W)
|
||||
var/speed = DEFAULT_ATTACK_COOLDOWN
|
||||
var/speed = base_attack_cooldown
|
||||
if(W && istype(W))
|
||||
speed = W.attackspeed
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
|
||||
@@ -68,58 +68,3 @@
|
||||
*/
|
||||
/mob/new_player/ClickOn()
|
||||
return
|
||||
|
||||
/*
|
||||
Animals
|
||||
*/
|
||||
/mob/living/simple_animal/UnarmedAttack(var/atom/A, var/proximity)
|
||||
if(!(. = ..()))
|
||||
return
|
||||
|
||||
setClickCooldown(get_attack_speed())
|
||||
|
||||
if(has_hands && istype(A,/obj) && a_intent != I_HURT)
|
||||
var/obj/O = A
|
||||
return O.attack_hand(src)
|
||||
|
||||
switch(a_intent)
|
||||
if(I_HELP)
|
||||
if(isliving(A))
|
||||
custom_emote(1,"[pick(friendly)] [A]!")
|
||||
|
||||
if(I_HURT)
|
||||
if(prob(spattack_prob))
|
||||
if(spattack_min_range <= 1)
|
||||
SpecialAtkTarget()
|
||||
|
||||
|
||||
else if(melee_damage_upper == 0 && istype(A,/mob/living))
|
||||
custom_emote(1,"[pick(friendly)] [A]!")
|
||||
|
||||
|
||||
else
|
||||
DoPunch(A)
|
||||
|
||||
if(I_GRAB)
|
||||
if(has_hands)
|
||||
A.attack_hand(src)
|
||||
|
||||
if(I_DISARM)
|
||||
if(has_hands)
|
||||
A.attack_hand(src)
|
||||
|
||||
/mob/living/simple_animal/RangedAttack(var/atom/A)
|
||||
setClickCooldown(get_attack_speed())
|
||||
var/distance = get_dist(src, A)
|
||||
|
||||
if(prob(spattack_prob) && (distance >= spattack_min_range) && (distance <= spattack_max_range))
|
||||
target_mob = A
|
||||
SpecialAtkTarget()
|
||||
target_mob = null
|
||||
return
|
||||
|
||||
if(ranged && distance <= shoot_range)
|
||||
target_mob = A
|
||||
ShootTarget(A)
|
||||
target_mob = null
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
var/datum/controller/process/alarm/alarm_manager
|
||||
|
||||
/datum/controller/process/alarm
|
||||
var/list/datum/alarm/all_handlers
|
||||
var/list/datum/alarm/all_handlers = list()
|
||||
|
||||
/datum/controller/process/alarm/setup()
|
||||
name = "alarm"
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
// We are being killed. Least we can do is deregister all those events we registered
|
||||
/datum/controller/process/scheduler/onKill()
|
||||
for(var/st in scheduled_tasks)
|
||||
destroyed_event.unregister(st, src)
|
||||
GLOB.destroyed_event.unregister(st, src)
|
||||
|
||||
/datum/controller/process/scheduler/statProcess()
|
||||
..()
|
||||
@@ -137,6 +137,7 @@
|
||||
var/datum/callback/callback
|
||||
|
||||
/datum/scheduled_task/callback/New(var/trigger_time, var/datum/callback, var/proc/task_after_process, var/list/task_after_process_args)
|
||||
src.callback = callback
|
||||
..(trigger_time = trigger_time, task_after_process = task_after_process, task_after_process_args = task_after_process_args)
|
||||
|
||||
/datum/scheduled_task/callback/process()
|
||||
@@ -147,7 +148,7 @@
|
||||
|
||||
/datum/scheduled_task/source/New(var/trigger_time, var/datum/source, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
|
||||
src.source = source
|
||||
destroyed_event.register(src.source, src, /datum/scheduled_task/source/proc/source_destroyed)
|
||||
GLOB.destroyed_event.register(src.source, src, /datum/scheduled_task/source/proc/source_destroyed)
|
||||
..(trigger_time, procedure, arguments, task_after_process, task_after_process_args)
|
||||
|
||||
/datum/scheduled_task/source/Destroy()
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/datum/controller/process/sun/setup()
|
||||
name = "sun"
|
||||
schedule_interval = 20 // every second
|
||||
sun = new
|
||||
|
||||
/datum/controller/process/sun/doWork()
|
||||
sun.calc_position()
|
||||
@@ -228,6 +228,8 @@ var/list/gamemode_cache = list()
|
||||
var/radiation_resistance_multiplier = 8.5 //VOREstation edit
|
||||
var/radiation_lower_limit = 0.35 //If the radiation level for a turf would be below this, ignore it.
|
||||
|
||||
var/random_submap_orientation = FALSE // If true, submaps loaded automatically can be rotated.
|
||||
|
||||
/datum/configuration/New()
|
||||
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
|
||||
for (var/T in L)
|
||||
@@ -748,6 +750,9 @@ var/list/gamemode_cache = list()
|
||||
if ("paranoia_logging")
|
||||
config.paranoia_logging = 1
|
||||
|
||||
if("random_submap_orientation")
|
||||
config.random_submap_orientation = 1
|
||||
|
||||
else
|
||||
log_misc("Unknown setting in configuration: '[name]'")
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
SUBSYSTEM_DEF(ai)
|
||||
name = "AI"
|
||||
init_order = INIT_ORDER_AI
|
||||
priority = FIRE_PRIORITY_AI
|
||||
wait = 5 // This gets run twice a second, however this is technically two loops in one, with the second loop being run every four iterations.
|
||||
flags = SS_NO_INIT|SS_TICKER
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
|
||||
var/list/processing = list()
|
||||
var/list/currentrun = list()
|
||||
|
||||
/datum/controller/subsystem/ai/stat_entry(msg_prefix)
|
||||
var/list/msg = list(msg_prefix)
|
||||
msg += "P:[processing.len]"
|
||||
..(msg.Join())
|
||||
|
||||
/datum/controller/subsystem/ai/fire(resumed = 0)
|
||||
if (!resumed)
|
||||
src.currentrun = processing.Copy()
|
||||
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/list/currentrun = src.currentrun
|
||||
|
||||
while(currentrun.len)
|
||||
// var/mob/living/L = currentrun[currentrun.len]
|
||||
var/datum/ai_holder/A = currentrun[currentrun.len]
|
||||
--currentrun.len
|
||||
if(!A || QDELETED(A)) // Doesn't exist or won't exist soon.
|
||||
continue
|
||||
if(times_fired % 4 == 0 && A.holder.stat != DEAD)
|
||||
A.handle_strategicals()
|
||||
if(A.holder.stat != DEAD) // The /TG/ version checks stat twice, presumably in-case processing somehow got the mob killed in that instant.
|
||||
A.handle_tactics()
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
+7
-7
@@ -1,11 +1,12 @@
|
||||
/datum/controller/process/inactivity/setup()
|
||||
name = "inactivity"
|
||||
schedule_interval = 600 // Once every minute (approx.)
|
||||
SUBSYSTEM_DEF(inactivity)
|
||||
name = "AFK Kick"
|
||||
wait = 600
|
||||
flags = SS_BACKGROUND | SS_NO_TICK_CHECK
|
||||
|
||||
/datum/controller/process/inactivity/doWork()
|
||||
/datum/controller/subsystem/inactivity/fire()
|
||||
if(config.kick_inactive)
|
||||
for(last_object in clients)
|
||||
var/client/C = last_object
|
||||
for(var/i in clients)
|
||||
var/client/C = i
|
||||
if(C.is_afk(config.kick_inactive MINUTES) && !C.holder) // VOREStation Edit - Allow admins to idle
|
||||
to_chat(C,"<span class='warning'>You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected.</span>")
|
||||
var/information
|
||||
@@ -33,4 +34,3 @@
|
||||
log_and_message_admins("being kicked for AFK[information][adminlinks]", C.mob)
|
||||
|
||||
qdel(C)
|
||||
SCHECK
|
||||
@@ -168,7 +168,7 @@ var/global/image/appearance_bro = new() // Temporarily super-global because of B
|
||||
* Adds specific overlay(s) to the atom.
|
||||
* It is designed so any of the types allowed to be added to /atom/overlays can be added here too. More details below.
|
||||
*
|
||||
* @param overlays The overlay(s) to add. These may be
|
||||
* @param overlays The overlay(s) to add. These may be
|
||||
* - A string: In which case it is treated as an icon_state of the atom's icon.
|
||||
* - An icon: It is treated as an icon.
|
||||
* - An atom: Its own overlays are compiled and then it's appearance is added. (Meaning its current apperance is frozen).
|
||||
@@ -205,7 +205,7 @@ var/global/image/appearance_bro = new() // Temporarily super-global because of B
|
||||
/**
|
||||
* Copy the overlays from another atom, either replacing all of ours or appending to our existing overlays.
|
||||
* Note: This copies only the normal overlays, not the "priority" overlays.
|
||||
*
|
||||
*
|
||||
* @param other The atom to copy overlays from.
|
||||
* @param cut_old If true, all of our overlays will be *replaced* by the other's. If other is null, that means cutting all ours.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
SUBSYSTEM_DEF(sun)
|
||||
name = "Sun"
|
||||
wait = 600
|
||||
var/static/datum/sun/sun = new
|
||||
|
||||
/datum/controller/subsystem/sun/fire()
|
||||
sun.calc_position()
|
||||
+373
-373
@@ -1,373 +1,373 @@
|
||||
SUBSYSTEM_DEF(vote)
|
||||
name = "Vote"
|
||||
wait = 10
|
||||
priority = FIRE_PRIORITY_VOTE
|
||||
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
|
||||
flags = SS_KEEP_TIMING | SS_NO_INIT
|
||||
var/list/round_voters = list()
|
||||
|
||||
//Current vote
|
||||
var/initiator
|
||||
var/started_time
|
||||
var/time_remaining
|
||||
var/duration
|
||||
var/mode
|
||||
var/question
|
||||
var/list/choices = list()
|
||||
var/list/gamemode_names = list()
|
||||
var/list/voted = list()
|
||||
var/list/current_votes = list()
|
||||
var/list/additional_text = list()
|
||||
|
||||
/datum/controller/subsystem/vote/fire(resumed)
|
||||
if(mode)
|
||||
time_remaining = round((started_time + duration - world.time)/10)
|
||||
if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP)
|
||||
to_chat(world, "<b>Gamemode vote aborted: Game has already started.</b>")
|
||||
reset()
|
||||
return
|
||||
if(time_remaining <= 0)
|
||||
result()
|
||||
reset()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/autotransfer()
|
||||
initiate_vote(VOTE_CREW_TRANSFER, "the server", 1)
|
||||
log_debug("The server has called a crew transfer vote.")
|
||||
|
||||
/datum/controller/subsystem/vote/proc/autogamemode()
|
||||
initiate_vote(VOTE_GAMEMODE, "the server", 1)
|
||||
log_debug("The server has called a gamemode vote.")
|
||||
|
||||
/datum/controller/subsystem/vote/proc/reset()
|
||||
initiator = null
|
||||
started_time = null
|
||||
duration = null
|
||||
time_remaining = null
|
||||
mode = null
|
||||
question = null
|
||||
choices.Cut()
|
||||
voted.Cut()
|
||||
current_votes.Cut()
|
||||
additional_text.Cut()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/get_result() // Get the highest number of votes
|
||||
var/greatest_votes = 0
|
||||
var/total_votes = 0
|
||||
|
||||
for(var/option in choices)
|
||||
var/votes = choices[option]
|
||||
total_votes += votes
|
||||
if(votes > greatest_votes)
|
||||
greatest_votes = votes
|
||||
|
||||
if(!config.vote_no_default && choices.len) // Default-vote for everyone who didn't vote
|
||||
var/non_voters = (clients.len - total_votes)
|
||||
if(non_voters > 0)
|
||||
if(mode == VOTE_RESTART)
|
||||
choices["Continue Playing"] += non_voters
|
||||
if(choices["Continue Playing"] >= greatest_votes)
|
||||
greatest_votes = choices["Continue Playing"]
|
||||
else if(mode == VOTE_GAMEMODE)
|
||||
if(master_mode in choices)
|
||||
choices[master_mode] += non_voters
|
||||
if(choices[master_mode] >= greatest_votes)
|
||||
greatest_votes = choices[master_mode]
|
||||
else if(mode == VOTE_CREW_TRANSFER)
|
||||
var/factor = 0.5
|
||||
switch(world.time / (10 * 60)) // minutes
|
||||
if(0 to 60)
|
||||
factor = 0.5
|
||||
if(61 to 120)
|
||||
factor = 0.8
|
||||
if(121 to 240)
|
||||
factor = 1
|
||||
if(241 to 300)
|
||||
factor = 1.2
|
||||
else
|
||||
factor = 1.4
|
||||
choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor)
|
||||
world << "<font color='purple'>Crew Transfer Factor: [factor]</font>"
|
||||
greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"])
|
||||
|
||||
. = list() // Get all options with that many votes and return them in a list
|
||||
if(greatest_votes)
|
||||
for(var/option in choices)
|
||||
if(choices[option] == greatest_votes)
|
||||
. += option
|
||||
|
||||
/datum/controller/subsystem/vote/proc/announce_result()
|
||||
var/list/winners = get_result()
|
||||
var/text
|
||||
if(winners.len > 0)
|
||||
if(winners.len > 1)
|
||||
if(mode != VOTE_GAMEMODE || ticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes
|
||||
text = "<b>Vote Tied Between:</b>\n"
|
||||
for(var/option in winners)
|
||||
text += "\t[option]\n"
|
||||
. = pick(winners)
|
||||
|
||||
for(var/key in current_votes)
|
||||
if(choices[current_votes[key]] == .)
|
||||
round_voters += key // Keep track of who voted for the winning round.
|
||||
if(mode != VOTE_GAMEMODE || . == "Extended" || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes
|
||||
text += "<b>Vote Result: [mode == VOTE_GAMEMODE ? gamemode_names[.] : .]</b>"
|
||||
else
|
||||
text += "<b>The vote has ended.</b>"
|
||||
|
||||
else
|
||||
text += "<b>Vote Result: Inconclusive - No Votes!</b>"
|
||||
if(mode == VOTE_ADD_ANTAGONIST)
|
||||
antag_add_failed = 1
|
||||
log_vote(text)
|
||||
to_chat(world, "<font color='purple'>[text]</font>")
|
||||
|
||||
/datum/controller/subsystem/vote/proc/result()
|
||||
. = announce_result()
|
||||
var/restart = 0
|
||||
if(.)
|
||||
switch(mode)
|
||||
if(VOTE_RESTART)
|
||||
if(. == "Restart Round")
|
||||
restart = 1
|
||||
if(VOTE_GAMEMODE)
|
||||
if(master_mode != .)
|
||||
world.save_mode(.)
|
||||
if(ticker && ticker.mode)
|
||||
restart = 1
|
||||
else
|
||||
master_mode = .
|
||||
if(VOTE_CREW_TRANSFER)
|
||||
if(. == "Initiate Crew Transfer")
|
||||
init_shift_change(null, 1)
|
||||
if(VOTE_ADD_ANTAGONIST)
|
||||
if(isnull(.) || . == "None")
|
||||
antag_add_failed = 1
|
||||
else
|
||||
additional_antag_types |= antag_names_to_ids[.]
|
||||
|
||||
if(mode == VOTE_GAMEMODE) //fire this even if the vote fails.
|
||||
if(!round_progressing)
|
||||
round_progressing = 1
|
||||
world << "<font color='red'><b>The round will start soon.</b></font>"
|
||||
|
||||
if(restart)
|
||||
world << "World restarting due to vote..."
|
||||
feedback_set_details("end_error", "restart vote")
|
||||
if(blackbox)
|
||||
blackbox.save_all_data_to_sql()
|
||||
sleep(50)
|
||||
log_game("Rebooting due to restart vote")
|
||||
world.Reboot()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/submit_vote(ckey, newVote)
|
||||
if(mode)
|
||||
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
|
||||
return
|
||||
if(current_votes[ckey])
|
||||
choices[choices[current_votes[ckey]]]--
|
||||
if(newVote && newVote >= 1 && newVote <= choices.len)
|
||||
choices[choices[newVote]]++
|
||||
current_votes[ckey] = newVote
|
||||
else
|
||||
current_votes[ckey] = null
|
||||
|
||||
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period)
|
||||
if(!mode)
|
||||
if(started_time != null && !(check_rights(R_ADMIN) || automatic))
|
||||
var/next_allowed_time = (started_time + config.vote_delay)
|
||||
if(next_allowed_time > world.time)
|
||||
return 0
|
||||
|
||||
reset()
|
||||
|
||||
switch(vote_type)
|
||||
if(VOTE_RESTART)
|
||||
choices.Add("Restart Round", "Continue Playing")
|
||||
if(VOTE_GAMEMODE)
|
||||
if(ticker.current_state >= GAME_STATE_SETTING_UP)
|
||||
return 0
|
||||
choices.Add(config.votable_modes)
|
||||
for(var/F in choices)
|
||||
var/datum/game_mode/M = gamemode_cache[F]
|
||||
if(!M)
|
||||
continue
|
||||
gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works
|
||||
additional_text.Add("<td align = 'center'>[M.required_players]</td>")
|
||||
gamemode_names["secret"] = "Secret"
|
||||
if(VOTE_CREW_TRANSFER)
|
||||
if(!check_rights(R_ADMIN|R_MOD, 0)) // The gods care not for the affairs of the mortals
|
||||
if(get_security_level() == "red" || get_security_level() == "delta")
|
||||
initiator_key << "The current alert status is too high to call for a crew transfer!"
|
||||
return 0
|
||||
if(ticker.current_state <= GAME_STATE_SETTING_UP)
|
||||
initiator_key << "The crew transfer button has been disabled!"
|
||||
return 0
|
||||
question = "End the shift?"
|
||||
choices.Add("Initiate Crew Transfer", "Continue The Round")
|
||||
if(VOTE_ADD_ANTAGONIST)
|
||||
if(!config.allow_extra_antags || ticker.current_state >= GAME_STATE_SETTING_UP)
|
||||
return 0
|
||||
for(var/antag_type in all_antag_types)
|
||||
var/datum/antagonist/antag = all_antag_types[antag_type]
|
||||
if(!(antag.id in additional_antag_types) && antag.is_votable())
|
||||
choices.Add(antag.role_text)
|
||||
choices.Add("None")
|
||||
if(VOTE_CUSTOM)
|
||||
question = sanitizeSafe(input(usr, "What is the vote for?") as text|null)
|
||||
if(!question)
|
||||
return 0
|
||||
for(var/i = 1 to 10)
|
||||
var/option = capitalize(sanitize(input(usr, "Please enter an option or hit cancel to finish") as text|null))
|
||||
if(!option || mode || !usr.client)
|
||||
break
|
||||
choices.Add(option)
|
||||
else
|
||||
return 0
|
||||
|
||||
mode = vote_type
|
||||
initiator = initiator_key
|
||||
started_time = world.time
|
||||
duration = time
|
||||
var/text = "[capitalize(mode)] vote started by [initiator]."
|
||||
if(mode == VOTE_CUSTOM)
|
||||
text += "\n[question]"
|
||||
|
||||
log_vote(text)
|
||||
|
||||
world << "<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=\ref[src]'>here</a> to place your votes.\nYou have [config.vote_period / 10] seconds to vote.</font>"
|
||||
if(vote_type == VOTE_CREW_TRANSFER || vote_type == VOTE_GAMEMODE || vote_type == VOTE_CUSTOM)
|
||||
world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
|
||||
|
||||
if(mode == VOTE_GAMEMODE && round_progressing)
|
||||
round_progressing = 0
|
||||
world << "<font color='red'><b>Round start has been delayed.</b></font>"
|
||||
|
||||
time_remaining = round(config.vote_period / 10)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/controller/subsystem/vote/proc/interface(var/client/C)
|
||||
if(!istype(C))
|
||||
return
|
||||
var/admin = FALSE
|
||||
if(C.holder)
|
||||
if(C.holder.rights & R_ADMIN)
|
||||
admin = TRUE
|
||||
|
||||
. = "<html><head><title>Voting Panel</title></head><body>"
|
||||
if(mode)
|
||||
if(question)
|
||||
. += "<h2>Vote: '[question]'</h2>"
|
||||
else
|
||||
. += "<h2>Vote: [capitalize(mode)]</h2>"
|
||||
. += "Time Left: [time_remaining] s<hr>"
|
||||
. += "<table width = '100%'><tr><td align = 'center'><b>Choices</b></td><td align = 'center'><b>Votes</b></td>"
|
||||
if(mode == VOTE_GAMEMODE)
|
||||
.+= "<td align = 'center'><b>Minimum Players</b></td></tr>"
|
||||
|
||||
for(var/i = 1 to choices.len)
|
||||
var/votes = choices[choices[i]]
|
||||
if(!votes)
|
||||
votes = 0
|
||||
. += "<tr>"
|
||||
var/thisVote = (current_votes[C.ckey] == i)
|
||||
if(mode == VOTE_GAMEMODE)
|
||||
. += "<td>[thisVote ? "<b>" : ""]<a href='?src=\ref[src];vote=[i]'>[gamemode_names[choices[i]]]</a>[thisVote ? "</b>" : ""]</td><td align = 'center'>[votes]</td>"
|
||||
else
|
||||
. += "<td>[thisVote ? "<b>" : ""]<a href='?src=\ref[src];vote=[i]'>[choices[i]]</a>[thisVote ? "</b>" : ""]</td><td align = 'center'>[votes]</td>"
|
||||
if (additional_text.len >= i)
|
||||
. += additional_text[i]
|
||||
. += "</tr>"
|
||||
|
||||
. += "<tr><td><a href='?src=\ref[src];vote=unvote'>Unvote</a></td></tr>"
|
||||
|
||||
. += "</table><hr>"
|
||||
if(admin)
|
||||
. += "(<a href='?src=\ref[src];vote=cancel'>Cancel Vote</a>) "
|
||||
else
|
||||
. += "<h2>Start a vote:</h2><hr><ul><li>"
|
||||
if(admin || config.allow_vote_restart)
|
||||
. += "<a href='?src=\ref[src];vote=restart'>Restart</a>"
|
||||
else
|
||||
. += "<font color='grey'>Restart (Disallowed)</font>"
|
||||
. += "</li><li>"
|
||||
|
||||
if(admin || config.allow_vote_restart)
|
||||
. += "<a href='?src=\ref[src];vote=crew_transfer'>Crew Transfer</a>"
|
||||
else
|
||||
. += "<font color='grey'>Crew Transfer (Disallowed)</font>"
|
||||
|
||||
if(admin)
|
||||
. += "\t(<a href='?src=\ref[src];vote=toggle_restart'>[config.allow_vote_restart ? "Allowed" : "Disallowed"]</a>)"
|
||||
. += "</li><li>"
|
||||
|
||||
if(admin || config.allow_vote_mode)
|
||||
. += "<a href='?src=\ref[src];vote=gamemode'>GameMode</a>"
|
||||
else
|
||||
. += "<font color='grey'>GameMode (Disallowed)</font>"
|
||||
|
||||
if(admin)
|
||||
. += "\t(<a href='?src=\ref[src];vote=toggle_gamemode'>[config.allow_vote_mode ? "Allowed" : "Disallowed"]</a>)"
|
||||
. += "</li><li>"
|
||||
|
||||
if(!antag_add_failed && config.allow_extra_antags)
|
||||
. += "<a href='?src=\ref[src];vote=add_antagonist'>Add Antagonist Type</a>"
|
||||
else
|
||||
. += "<font color='grey'>Add Antagonist (Disallowed)</font>"
|
||||
. += "</li>"
|
||||
|
||||
if(admin)
|
||||
. += "<li><a href='?src=\ref[src];vote=custom'>Custom</a></li>"
|
||||
. += "</ul><hr>"
|
||||
|
||||
. += "<a href='?src=\ref[src];vote=close' style='position:absolute;right:50px'>Close</a></body></html>"
|
||||
|
||||
/datum/controller/subsystem/vote/Topic(href, href_list[])
|
||||
if(!usr || !usr.client)
|
||||
return
|
||||
switch(href_list["vote"])
|
||||
if("close")
|
||||
usr << browse(null, "window=vote")
|
||||
return
|
||||
|
||||
if("cancel")
|
||||
if(usr.client.holder)
|
||||
reset()
|
||||
if("toggle_restart")
|
||||
if(usr.client.holder)
|
||||
config.allow_vote_restart = !config.allow_vote_restart
|
||||
if("toggle_gamemode")
|
||||
if(usr.client.holder)
|
||||
config.allow_vote_mode = !config.allow_vote_mode
|
||||
|
||||
if(VOTE_RESTART)
|
||||
if(config.allow_vote_restart || usr.client.holder)
|
||||
initiate_vote(VOTE_RESTART, usr.key)
|
||||
if(VOTE_GAMEMODE)
|
||||
if(config.allow_vote_mode || usr.client.holder)
|
||||
initiate_vote(VOTE_GAMEMODE, usr.key)
|
||||
if(VOTE_CREW_TRANSFER)
|
||||
if(config.allow_vote_restart || usr.client.holder)
|
||||
initiate_vote(VOTE_CREW_TRANSFER, usr.key)
|
||||
if(VOTE_ADD_ANTAGONIST)
|
||||
if(config.allow_extra_antags || usr.client.holder)
|
||||
initiate_vote(VOTE_ADD_ANTAGONIST, usr.key)
|
||||
if(VOTE_CUSTOM)
|
||||
if(usr.client.holder)
|
||||
initiate_vote(VOTE_CUSTOM, usr.key)
|
||||
|
||||
if("unvote")
|
||||
submit_vote(usr.ckey, null)
|
||||
|
||||
else
|
||||
var/t = round(text2num(href_list["vote"]))
|
||||
if(t) // It starts from 1, so there's no problem
|
||||
submit_vote(usr.ckey, t)
|
||||
usr.client.vote()
|
||||
|
||||
/client/verb/vote()
|
||||
set category = "OOC"
|
||||
set name = "Vote"
|
||||
|
||||
if(SSvote)
|
||||
src << browse(SSvote.interface(src), "window=vote;size=500x[300 + SSvote.choices.len * 25]")
|
||||
SUBSYSTEM_DEF(vote)
|
||||
name = "Vote"
|
||||
wait = 10
|
||||
priority = FIRE_PRIORITY_VOTE
|
||||
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
|
||||
flags = SS_KEEP_TIMING | SS_NO_INIT
|
||||
var/list/round_voters = list()
|
||||
|
||||
//Current vote
|
||||
var/initiator
|
||||
var/started_time
|
||||
var/time_remaining
|
||||
var/duration
|
||||
var/mode
|
||||
var/question
|
||||
var/list/choices = list()
|
||||
var/list/gamemode_names = list()
|
||||
var/list/voted = list()
|
||||
var/list/current_votes = list()
|
||||
var/list/additional_text = list()
|
||||
|
||||
/datum/controller/subsystem/vote/fire(resumed)
|
||||
if(mode)
|
||||
time_remaining = round((started_time + duration - world.time)/10)
|
||||
if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP)
|
||||
to_chat(world, "<b>Gamemode vote aborted: Game has already started.</b>")
|
||||
reset()
|
||||
return
|
||||
if(time_remaining <= 0)
|
||||
result()
|
||||
reset()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/autotransfer()
|
||||
initiate_vote(VOTE_CREW_TRANSFER, "the server", 1)
|
||||
log_debug("The server has called a crew transfer vote.")
|
||||
|
||||
/datum/controller/subsystem/vote/proc/autogamemode()
|
||||
initiate_vote(VOTE_GAMEMODE, "the server", 1)
|
||||
log_debug("The server has called a gamemode vote.")
|
||||
|
||||
/datum/controller/subsystem/vote/proc/reset()
|
||||
initiator = null
|
||||
started_time = null
|
||||
duration = null
|
||||
time_remaining = null
|
||||
mode = null
|
||||
question = null
|
||||
choices.Cut()
|
||||
voted.Cut()
|
||||
current_votes.Cut()
|
||||
additional_text.Cut()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/get_result() // Get the highest number of votes
|
||||
var/greatest_votes = 0
|
||||
var/total_votes = 0
|
||||
|
||||
for(var/option in choices)
|
||||
var/votes = choices[option]
|
||||
total_votes += votes
|
||||
if(votes > greatest_votes)
|
||||
greatest_votes = votes
|
||||
|
||||
if(!config.vote_no_default && choices.len) // Default-vote for everyone who didn't vote
|
||||
var/non_voters = (clients.len - total_votes)
|
||||
if(non_voters > 0)
|
||||
if(mode == VOTE_RESTART)
|
||||
choices["Continue Playing"] += non_voters
|
||||
if(choices["Continue Playing"] >= greatest_votes)
|
||||
greatest_votes = choices["Continue Playing"]
|
||||
else if(mode == VOTE_GAMEMODE)
|
||||
if(master_mode in choices)
|
||||
choices[master_mode] += non_voters
|
||||
if(choices[master_mode] >= greatest_votes)
|
||||
greatest_votes = choices[master_mode]
|
||||
else if(mode == VOTE_CREW_TRANSFER)
|
||||
var/factor = 0.5
|
||||
switch(world.time / (10 * 60)) // minutes
|
||||
if(0 to 60)
|
||||
factor = 0.5
|
||||
if(61 to 120)
|
||||
factor = 0.8
|
||||
if(121 to 240)
|
||||
factor = 1
|
||||
if(241 to 300)
|
||||
factor = 1.2
|
||||
else
|
||||
factor = 1.4
|
||||
choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor)
|
||||
world << "<font color='purple'>Crew Transfer Factor: [factor]</font>"
|
||||
greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"])
|
||||
|
||||
. = list() // Get all options with that many votes and return them in a list
|
||||
if(greatest_votes)
|
||||
for(var/option in choices)
|
||||
if(choices[option] == greatest_votes)
|
||||
. += option
|
||||
|
||||
/datum/controller/subsystem/vote/proc/announce_result()
|
||||
var/list/winners = get_result()
|
||||
var/text
|
||||
if(winners.len > 0)
|
||||
if(winners.len > 1)
|
||||
if(mode != VOTE_GAMEMODE || ticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes
|
||||
text = "<b>Vote Tied Between:</b>\n"
|
||||
for(var/option in winners)
|
||||
text += "\t[option]\n"
|
||||
. = pick(winners)
|
||||
|
||||
for(var/key in current_votes)
|
||||
if(choices[current_votes[key]] == .)
|
||||
round_voters += key // Keep track of who voted for the winning round.
|
||||
if(mode != VOTE_GAMEMODE || . == "Extended" || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes
|
||||
text += "<b>Vote Result: [mode == VOTE_GAMEMODE ? gamemode_names[.] : .]</b>"
|
||||
else
|
||||
text += "<b>The vote has ended.</b>"
|
||||
|
||||
else
|
||||
text += "<b>Vote Result: Inconclusive - No Votes!</b>"
|
||||
if(mode == VOTE_ADD_ANTAGONIST)
|
||||
antag_add_failed = 1
|
||||
log_vote(text)
|
||||
to_chat(world, "<font color='purple'>[text]</font>")
|
||||
|
||||
/datum/controller/subsystem/vote/proc/result()
|
||||
. = announce_result()
|
||||
var/restart = 0
|
||||
if(.)
|
||||
switch(mode)
|
||||
if(VOTE_RESTART)
|
||||
if(. == "Restart Round")
|
||||
restart = 1
|
||||
if(VOTE_GAMEMODE)
|
||||
if(master_mode != .)
|
||||
world.save_mode(.)
|
||||
if(ticker && ticker.mode)
|
||||
restart = 1
|
||||
else
|
||||
master_mode = .
|
||||
if(VOTE_CREW_TRANSFER)
|
||||
if(. == "Initiate Crew Transfer")
|
||||
init_shift_change(null, 1)
|
||||
if(VOTE_ADD_ANTAGONIST)
|
||||
if(isnull(.) || . == "None")
|
||||
antag_add_failed = 1
|
||||
else
|
||||
additional_antag_types |= antag_names_to_ids[.]
|
||||
|
||||
if(mode == VOTE_GAMEMODE) //fire this even if the vote fails.
|
||||
if(!round_progressing)
|
||||
round_progressing = 1
|
||||
world << "<font color='red'><b>The round will start soon.</b></font>"
|
||||
|
||||
if(restart)
|
||||
world << "World restarting due to vote..."
|
||||
feedback_set_details("end_error", "restart vote")
|
||||
if(blackbox)
|
||||
blackbox.save_all_data_to_sql()
|
||||
sleep(50)
|
||||
log_game("Rebooting due to restart vote")
|
||||
world.Reboot()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/submit_vote(ckey, newVote)
|
||||
if(mode)
|
||||
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
|
||||
return
|
||||
if(current_votes[ckey])
|
||||
choices[choices[current_votes[ckey]]]--
|
||||
if(newVote && newVote >= 1 && newVote <= choices.len)
|
||||
choices[choices[newVote]]++
|
||||
current_votes[ckey] = newVote
|
||||
else
|
||||
current_votes[ckey] = null
|
||||
|
||||
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period)
|
||||
if(!mode)
|
||||
if(started_time != null && !(check_rights(R_ADMIN) || automatic))
|
||||
var/next_allowed_time = (started_time + config.vote_delay)
|
||||
if(next_allowed_time > world.time)
|
||||
return 0
|
||||
|
||||
reset()
|
||||
|
||||
switch(vote_type)
|
||||
if(VOTE_RESTART)
|
||||
choices.Add("Restart Round", "Continue Playing")
|
||||
if(VOTE_GAMEMODE)
|
||||
if(ticker.current_state >= GAME_STATE_SETTING_UP)
|
||||
return 0
|
||||
choices.Add(config.votable_modes)
|
||||
for(var/F in choices)
|
||||
var/datum/game_mode/M = gamemode_cache[F]
|
||||
if(!M)
|
||||
continue
|
||||
gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works
|
||||
additional_text.Add("<td align = 'center'>[M.required_players]</td>")
|
||||
gamemode_names["secret"] = "Secret"
|
||||
if(VOTE_CREW_TRANSFER)
|
||||
if(!check_rights(R_ADMIN|R_MOD, 0)) // The gods care not for the affairs of the mortals
|
||||
if(get_security_level() == "red" || get_security_level() == "delta")
|
||||
initiator_key << "The current alert status is too high to call for a crew transfer!"
|
||||
return 0
|
||||
if(ticker.current_state <= GAME_STATE_SETTING_UP)
|
||||
initiator_key << "The crew transfer button has been disabled!"
|
||||
return 0
|
||||
question = "End the shift?"
|
||||
choices.Add("Initiate Crew Transfer", "Continue The Round")
|
||||
if(VOTE_ADD_ANTAGONIST)
|
||||
if(!config.allow_extra_antags || ticker.current_state >= GAME_STATE_SETTING_UP)
|
||||
return 0
|
||||
for(var/antag_type in all_antag_types)
|
||||
var/datum/antagonist/antag = all_antag_types[antag_type]
|
||||
if(!(antag.id in additional_antag_types) && antag.is_votable())
|
||||
choices.Add(antag.role_text)
|
||||
choices.Add("None")
|
||||
if(VOTE_CUSTOM)
|
||||
question = sanitizeSafe(input(usr, "What is the vote for?") as text|null)
|
||||
if(!question)
|
||||
return 0
|
||||
for(var/i = 1 to 10)
|
||||
var/option = capitalize(sanitize(input(usr, "Please enter an option or hit cancel to finish") as text|null))
|
||||
if(!option || mode || !usr.client)
|
||||
break
|
||||
choices.Add(option)
|
||||
else
|
||||
return 0
|
||||
|
||||
mode = vote_type
|
||||
initiator = initiator_key
|
||||
started_time = world.time
|
||||
duration = time
|
||||
var/text = "[capitalize(mode)] vote started by [initiator]."
|
||||
if(mode == VOTE_CUSTOM)
|
||||
text += "\n[question]"
|
||||
|
||||
log_vote(text)
|
||||
|
||||
world << "<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=\ref[src]'>here</a> to place your votes.\nYou have [config.vote_period / 10] seconds to vote.</font>"
|
||||
if(vote_type == VOTE_CREW_TRANSFER || vote_type == VOTE_GAMEMODE || vote_type == VOTE_CUSTOM)
|
||||
world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
|
||||
|
||||
if(mode == VOTE_GAMEMODE && round_progressing)
|
||||
round_progressing = 0
|
||||
world << "<font color='red'><b>Round start has been delayed.</b></font>"
|
||||
|
||||
time_remaining = round(config.vote_period / 10)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/controller/subsystem/vote/proc/interface(var/client/C)
|
||||
if(!istype(C))
|
||||
return
|
||||
var/admin = FALSE
|
||||
if(C.holder)
|
||||
if(C.holder.rights & R_ADMIN)
|
||||
admin = TRUE
|
||||
|
||||
. = "<html><head><title>Voting Panel</title></head><body>"
|
||||
if(mode)
|
||||
if(question)
|
||||
. += "<h2>Vote: '[question]'</h2>"
|
||||
else
|
||||
. += "<h2>Vote: [capitalize(mode)]</h2>"
|
||||
. += "Time Left: [time_remaining] s<hr>"
|
||||
. += "<table width = '100%'><tr><td align = 'center'><b>Choices</b></td><td align = 'center'><b>Votes</b></td>"
|
||||
if(mode == VOTE_GAMEMODE)
|
||||
.+= "<td align = 'center'><b>Minimum Players</b></td></tr>"
|
||||
|
||||
for(var/i = 1 to choices.len)
|
||||
var/votes = choices[choices[i]]
|
||||
if(!votes)
|
||||
votes = 0
|
||||
. += "<tr>"
|
||||
var/thisVote = (current_votes[C.ckey] == i)
|
||||
if(mode == VOTE_GAMEMODE)
|
||||
. += "<td>[thisVote ? "<b>" : ""]<a href='?src=\ref[src];vote=[i]'>[gamemode_names[choices[i]]]</a>[thisVote ? "</b>" : ""]</td><td align = 'center'>[votes]</td>"
|
||||
else
|
||||
. += "<td>[thisVote ? "<b>" : ""]<a href='?src=\ref[src];vote=[i]'>[choices[i]]</a>[thisVote ? "</b>" : ""]</td><td align = 'center'>[votes]</td>"
|
||||
if (additional_text.len >= i)
|
||||
. += additional_text[i]
|
||||
. += "</tr>"
|
||||
|
||||
. += "<tr><td><a href='?src=\ref[src];vote=unvote'>Unvote</a></td></tr>"
|
||||
|
||||
. += "</table><hr>"
|
||||
if(admin)
|
||||
. += "(<a href='?src=\ref[src];vote=cancel'>Cancel Vote</a>) "
|
||||
else
|
||||
. += "<h2>Start a vote:</h2><hr><ul><li>"
|
||||
if(admin || config.allow_vote_restart)
|
||||
. += "<a href='?src=\ref[src];vote=restart'>Restart</a>"
|
||||
else
|
||||
. += "<font color='grey'>Restart (Disallowed)</font>"
|
||||
. += "</li><li>"
|
||||
|
||||
if(admin || config.allow_vote_restart)
|
||||
. += "<a href='?src=\ref[src];vote=crew_transfer'>Crew Transfer</a>"
|
||||
else
|
||||
. += "<font color='grey'>Crew Transfer (Disallowed)</font>"
|
||||
|
||||
if(admin)
|
||||
. += "\t(<a href='?src=\ref[src];vote=toggle_restart'>[config.allow_vote_restart ? "Allowed" : "Disallowed"]</a>)"
|
||||
. += "</li><li>"
|
||||
|
||||
if(admin || config.allow_vote_mode)
|
||||
. += "<a href='?src=\ref[src];vote=gamemode'>GameMode</a>"
|
||||
else
|
||||
. += "<font color='grey'>GameMode (Disallowed)</font>"
|
||||
|
||||
if(admin)
|
||||
. += "\t(<a href='?src=\ref[src];vote=toggle_gamemode'>[config.allow_vote_mode ? "Allowed" : "Disallowed"]</a>)"
|
||||
. += "</li><li>"
|
||||
|
||||
if(!antag_add_failed && config.allow_extra_antags)
|
||||
. += "<a href='?src=\ref[src];vote=add_antagonist'>Add Antagonist Type</a>"
|
||||
else
|
||||
. += "<font color='grey'>Add Antagonist (Disallowed)</font>"
|
||||
. += "</li>"
|
||||
|
||||
if(admin)
|
||||
. += "<li><a href='?src=\ref[src];vote=custom'>Custom</a></li>"
|
||||
. += "</ul><hr>"
|
||||
|
||||
. += "<a href='?src=\ref[src];vote=close' style='position:absolute;right:50px'>Close</a></body></html>"
|
||||
|
||||
/datum/controller/subsystem/vote/Topic(href, href_list[])
|
||||
if(!usr || !usr.client)
|
||||
return
|
||||
switch(href_list["vote"])
|
||||
if("close")
|
||||
usr << browse(null, "window=vote")
|
||||
return
|
||||
|
||||
if("cancel")
|
||||
if(usr.client.holder)
|
||||
reset()
|
||||
if("toggle_restart")
|
||||
if(usr.client.holder)
|
||||
config.allow_vote_restart = !config.allow_vote_restart
|
||||
if("toggle_gamemode")
|
||||
if(usr.client.holder)
|
||||
config.allow_vote_mode = !config.allow_vote_mode
|
||||
|
||||
if(VOTE_RESTART)
|
||||
if(config.allow_vote_restart || usr.client.holder)
|
||||
initiate_vote(VOTE_RESTART, usr.key)
|
||||
if(VOTE_GAMEMODE)
|
||||
if(config.allow_vote_mode || usr.client.holder)
|
||||
initiate_vote(VOTE_GAMEMODE, usr.key)
|
||||
if(VOTE_CREW_TRANSFER)
|
||||
if(config.allow_vote_restart || usr.client.holder)
|
||||
initiate_vote(VOTE_CREW_TRANSFER, usr.key)
|
||||
if(VOTE_ADD_ANTAGONIST)
|
||||
if(config.allow_extra_antags || usr.client.holder)
|
||||
initiate_vote(VOTE_ADD_ANTAGONIST, usr.key)
|
||||
if(VOTE_CUSTOM)
|
||||
if(usr.client.holder)
|
||||
initiate_vote(VOTE_CUSTOM, usr.key)
|
||||
|
||||
if("unvote")
|
||||
submit_vote(usr.ckey, null)
|
||||
|
||||
else
|
||||
var/t = round(text2num(href_list["vote"]))
|
||||
if(t) // It starts from 1, so there's no problem
|
||||
submit_vote(usr.ckey, t)
|
||||
usr.client.vote()
|
||||
|
||||
/client/verb/vote()
|
||||
set category = "OOC"
|
||||
set name = "Vote"
|
||||
|
||||
if(SSvote)
|
||||
src << browse(SSvote.interface(src), "window=vote;size=500x[300 + SSvote.choices.len * 25]")
|
||||
|
||||
@@ -85,9 +85,6 @@
|
||||
if("Jobs")
|
||||
debug_variables(job_master)
|
||||
feedback_add_details("admin_verb","DJobs")
|
||||
if("Sun")
|
||||
debug_variables(sun)
|
||||
feedback_add_details("admin_verb","DSun")
|
||||
if("Radio")
|
||||
debug_variables(radio_controller)
|
||||
feedback_add_details("admin_verb","DRadio")
|
||||
|
||||
+2
-2
@@ -102,11 +102,11 @@
|
||||
//Position the effect so the beam is one continous line
|
||||
var/a
|
||||
if(abs(Pixel_x)>32)
|
||||
a = Pixel_x > 0 ? round(Pixel_x/32) : Ceiling(Pixel_x/32)
|
||||
a = Pixel_x > 0 ? round(Pixel_x/32) : CEILING(Pixel_x/32, 1)
|
||||
X.x += a
|
||||
Pixel_x %= 32
|
||||
if(abs(Pixel_y)>32)
|
||||
a = Pixel_y > 0 ? round(Pixel_y/32) : Ceiling(Pixel_y/32)
|
||||
a = Pixel_y > 0 ? round(Pixel_y/32) : CEILING(Pixel_y/32, 1)
|
||||
X.y += a
|
||||
Pixel_y %= 32
|
||||
|
||||
|
||||
@@ -106,6 +106,13 @@
|
||||
check_bans = list("AI", "Cyborg", "Syndicate")
|
||||
cutoff_number = 1
|
||||
|
||||
/datum/ghost_query/borer
|
||||
role_name = "Cortical Borer"
|
||||
question = "A cortical borer has just been created on the facility. Would you like to play as them?"
|
||||
be_special_flag = BE_ALIEN
|
||||
check_bans = list("Syndicate", "Borer")
|
||||
cutoff_number = 1
|
||||
|
||||
// Surface stuff.
|
||||
/datum/ghost_query/lost_drone
|
||||
role_name = "Lost Drone"
|
||||
|
||||
+9
-8
@@ -502,7 +502,7 @@
|
||||
if(!mind.assigned_role) mind.assigned_role = USELESS_JOB //defualt //VOREStation Edit - Visitor not Assistant
|
||||
|
||||
//slime
|
||||
/mob/living/simple_animal/slime/mind_initialize()
|
||||
/mob/living/simple_mob/slime/mind_initialize()
|
||||
. = ..()
|
||||
mind.assigned_role = "slime"
|
||||
|
||||
@@ -527,29 +527,30 @@
|
||||
mind.special_role = ""
|
||||
|
||||
//Animals
|
||||
/mob/living/simple_animal/mind_initialize()
|
||||
/mob/living/simple_mob/mind_initialize()
|
||||
. = ..()
|
||||
mind.assigned_role = "Animal"
|
||||
mind.assigned_role = "Simple Mob"
|
||||
|
||||
/mob/living/simple_animal/corgi/mind_initialize()
|
||||
/mob/living/simple_mob/animal/passive/dog/corgi/mind_initialize()
|
||||
. = ..()
|
||||
mind.assigned_role = "Corgi"
|
||||
|
||||
/mob/living/simple_animal/shade/mind_initialize()
|
||||
/mob/living/simple_mob/construct/shade/mind_initialize()
|
||||
. = ..()
|
||||
mind.assigned_role = "Shade"
|
||||
mind.special_role = "Cultist"
|
||||
|
||||
/mob/living/simple_animal/construct/builder/mind_initialize()
|
||||
/mob/living/simple_mob/construct/artificer/mind_initialize()
|
||||
. = ..()
|
||||
mind.assigned_role = "Artificer"
|
||||
mind.special_role = "Cultist"
|
||||
|
||||
/mob/living/simple_animal/construct/wraith/mind_initialize()
|
||||
/mob/living/simple_mob/construct/wraith/mind_initialize()
|
||||
. = ..()
|
||||
mind.assigned_role = "Wraith"
|
||||
mind.special_role = "Cultist"
|
||||
|
||||
/mob/living/simple_animal/construct/armoured/mind_initialize()
|
||||
/mob/living/simple_mob/construct/juggernaut/mind_initialize()
|
||||
. = ..()
|
||||
mind.assigned_role = "Juggernaut"
|
||||
mind.special_role = "Cultist"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/****************
|
||||
* Debug Support *
|
||||
****************/
|
||||
var/datum/all_observable_events/all_observable_events = new()
|
||||
|
||||
/datum/all_observable_events
|
||||
var/list/events
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
//
|
||||
// Arguments that the called proc should expect:
|
||||
// /datum/destroyed_instance: The instance that was destroyed.
|
||||
var/decl/observ/destroyed/destroyed_event = new()
|
||||
|
||||
/decl/observ/destroyed
|
||||
name = "Destroyed"
|
||||
|
||||
/datum/Destroy()
|
||||
destroyed_event.raise_event(src)
|
||||
GLOB.destroyed_event.raise_event(src)
|
||||
. = ..()
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
var/list/global_listeners = list() // Associative list of instances that listen to all events of this type (as opposed to events belonging to a specific source) and the proc to call.
|
||||
|
||||
/decl/observ/New()
|
||||
all_observable_events.events += src
|
||||
GLOB.all_observable_events.events += src
|
||||
. = ..()
|
||||
|
||||
/decl/observ/proc/is_listening(var/event_source, var/datum/listener, var/proc_call)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var/list/global_listen_count = list()
|
||||
var/list/event_sources_count = list()
|
||||
var/list/event_listen_count = list()
|
||||
GLOBAL_LIST_EMPTY(global_listen_count)
|
||||
GLOBAL_LIST_EMPTY(event_sources_count)
|
||||
GLOBAL_LIST_EMPTY(event_listen_count)
|
||||
|
||||
/decl/observ/destroyed/raise_event()
|
||||
. = ..()
|
||||
@@ -8,39 +8,39 @@ var/list/event_listen_count = list()
|
||||
return
|
||||
var/source = args[1]
|
||||
|
||||
if(global_listen_count[source])
|
||||
cleanup_global_listener(source, global_listen_count[source])
|
||||
if(event_sources_count[source])
|
||||
cleanup_source_listeners(source, event_sources_count[source])
|
||||
if(event_listen_count[source])
|
||||
cleanup_event_listener(source, event_listen_count[source])
|
||||
if(GLOB.global_listen_count[source])
|
||||
cleanup_global_listener(source, GLOB.global_listen_count[source])
|
||||
if(GLOB.event_sources_count[source])
|
||||
cleanup_source_listeners(source, GLOB.event_sources_count[source])
|
||||
if(GLOB.event_listen_count[source])
|
||||
cleanup_event_listener(source, GLOB.event_listen_count[source])
|
||||
|
||||
|
||||
/decl/observ/register(var/datum/event_source, var/datum/listener, var/proc_call)
|
||||
. = ..()
|
||||
if(.)
|
||||
event_sources_count[event_source] += 1
|
||||
event_listen_count[listener] += 1
|
||||
GLOB.event_sources_count[event_source] += 1
|
||||
GLOB.event_listen_count[listener] += 1
|
||||
|
||||
/decl/observ/unregister(var/datum/event_source, var/datum/listener, var/proc_call)
|
||||
. = ..()
|
||||
if(.)
|
||||
event_sources_count[event_source] -= 1
|
||||
event_listen_count[listener] -= 1
|
||||
GLOB.event_sources_count[event_source] -= 1
|
||||
GLOB.event_listen_count[listener] -= 1
|
||||
|
||||
/decl/observ/register_global(var/datum/listener, var/proc_call)
|
||||
. = ..()
|
||||
if(.)
|
||||
global_listen_count[listener] += 1
|
||||
GLOB.global_listen_count[listener] += 1
|
||||
|
||||
/decl/observ/unregister_global(var/datum/listener, var/proc_call)
|
||||
. = ..()
|
||||
if(.)
|
||||
global_listen_count[listener] -= 1
|
||||
GLOB.global_listen_count[listener] -= 1
|
||||
|
||||
/decl/observ/destroyed/proc/cleanup_global_listener(listener, listen_count)
|
||||
global_listen_count -= listener
|
||||
for(var/entry in all_observable_events.events)
|
||||
GLOB.global_listen_count -= listener
|
||||
for(var/entry in GLOB.all_observable_events.events)
|
||||
var/decl/observ/event = entry
|
||||
if(event.unregister_global(listener))
|
||||
log_debug("[event] - [listener] was deleted while still registered to global events.")
|
||||
@@ -48,8 +48,8 @@ var/list/event_listen_count = list()
|
||||
return
|
||||
|
||||
/decl/observ/destroyed/proc/cleanup_source_listeners(event_source, source_listener_count)
|
||||
event_sources_count -= event_source
|
||||
for(var/entry in all_observable_events.events)
|
||||
GLOB.event_sources_count -= event_source
|
||||
for(var/entry in GLOB.all_observable_events.events)
|
||||
var/decl/observ/event = entry
|
||||
var/proc_owners = event.event_sources[event_source]
|
||||
if(proc_owners)
|
||||
@@ -60,11 +60,11 @@ var/list/event_listen_count = list()
|
||||
return
|
||||
|
||||
/decl/observ/destroyed/proc/cleanup_event_listener(listener, listener_count)
|
||||
event_listen_count -= listener
|
||||
for(var/entry in all_observable_events.events)
|
||||
GLOB.event_listen_count -= listener
|
||||
for(var/entry in GLOB.all_observable_events.events)
|
||||
var/decl/observ/event = entry
|
||||
for(var/event_source in event.event_sources)
|
||||
if(event.unregister(event_source, listener))
|
||||
log_debug("[event] - [listener] was deleted while still listening to [event_source].")
|
||||
if(!(--listener_count))
|
||||
return
|
||||
return
|
||||
+2
-19
@@ -1,38 +1,21 @@
|
||||
#define SOLAR_UPDATE_TIME 600 //duration between two updates of the whole sun/solars positions
|
||||
|
||||
/datum/sun
|
||||
var/angle
|
||||
var/dx
|
||||
var/dy
|
||||
var/rate
|
||||
var/list/solars // for debugging purposes, references solars_list at the constructor
|
||||
var/solar_next_update // last time the sun position was checked and adjusted
|
||||
|
||||
/datum/sun/New()
|
||||
|
||||
solars = solars_list
|
||||
rate = rand(50,200)/100 // 50% - 200% of standard rotation
|
||||
if(prob(50)) // same chance to rotate clockwise than counter-clockwise
|
||||
rate = -rate
|
||||
solar_next_update = world.time // init the timer
|
||||
angle = rand (0,360) // the station position to the sun is randomised at round start
|
||||
|
||||
/*/hook/startup/proc/createSun() // handled in scheduler
|
||||
sun = new /datum/sun()
|
||||
return 1*/
|
||||
|
||||
// calculate the sun's position given the time of day
|
||||
// at the standard rate (100%) the angle is increase/decreased by 6 degrees every minute.
|
||||
// a full rotation thus take a game hour in that case
|
||||
/datum/sun/proc/calc_position()
|
||||
|
||||
if(world.time < solar_next_update) //if less than 60 game secondes have passed, do nothing
|
||||
return;
|
||||
|
||||
angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate
|
||||
|
||||
solar_next_update += SOLAR_UPDATE_TIME // since we updated the angle, set the proper time for the next loop
|
||||
|
||||
// now calculate and cache the (dx,dy) increments for line drawing
|
||||
|
||||
var/s = sin(angle)
|
||||
@@ -51,8 +34,8 @@
|
||||
dy = c / abs(s)
|
||||
|
||||
//now tell the solar control computers to update their status and linked devices
|
||||
for(var/obj/machinery/power/solar_control/SC in solars_list)
|
||||
for(var/obj/machinery/power/solar_control/SC in GLOB.solars_list)
|
||||
if(!SC.powernet)
|
||||
solars_list.Remove(SC)
|
||||
GLOB.solars_list.Remove(SC)
|
||||
continue
|
||||
SC.update()
|
||||
|
||||
@@ -81,4 +81,4 @@
|
||||
containername = "Chinese takeout crate"
|
||||
|
||||
/datum/supply_pack/randomised/hospitality/pizza
|
||||
cost = 50
|
||||
cost = 25
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
containername = "EXTREMELY Dangerous Predator crate"
|
||||
access = access_xenobiology
|
||||
contraband = 1
|
||||
|
||||
/*
|
||||
/datum/supply_pack/sci/otie
|
||||
name = "VARMAcorp adoptable reject (Dangerous!)"
|
||||
cost = 100
|
||||
@@ -35,4 +35,5 @@
|
||||
cost = 200
|
||||
containertype = /obj/structure/largecrate/animal/otie/phoron
|
||||
containername = "VARMAcorp adaptive beta subject (Experimental)"
|
||||
access = access_xenobiology
|
||||
access = access_xenobiology
|
||||
*/ //VORESTATION AI TEMPORARY REMOVAL. Oties commented out cuz broke.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/datum/supply_pack/security/guardbeast
|
||||
/*/datum/supply_pack/security/guardbeast //VORESTATION AI TEMPORARY REMOVAL
|
||||
name = "VARMAcorp autoNOMous security solution"
|
||||
cost = 150
|
||||
containertype = /obj/structure/largecrate/animal/guardbeast
|
||||
@@ -15,6 +15,7 @@
|
||||
access = list(
|
||||
access_security,
|
||||
access_xenobiology)
|
||||
*/
|
||||
|
||||
/datum/supply_pack/security/biosuit
|
||||
contains = list(
|
||||
|
||||
@@ -56,4 +56,8 @@
|
||||
|
||||
/datum/category_item/underwear/socks/fishnet
|
||||
name = "Fishnet"
|
||||
icon_state = "fishnet"
|
||||
icon_state = "fishnet"
|
||||
|
||||
/datum/category_item/underwear/socks/leggings
|
||||
name = "Leggings"
|
||||
icon_state = "leggings"
|
||||
@@ -62,4 +62,12 @@
|
||||
/datum/category_item/underwear/top/striped_bra
|
||||
name = "Striped Bra"
|
||||
icon_state = "striped_bra"
|
||||
has_color = TRUE
|
||||
has_color = TRUE
|
||||
|
||||
/datum/category_item/underwear/top/binder
|
||||
name = "Binder"
|
||||
icon_state = "binder_s"
|
||||
|
||||
/datum/category_item/underwear/top/straplessbinder
|
||||
name = "Binder Strapless"
|
||||
icon_state = "straplessbinder_s"
|
||||
@@ -165,4 +165,16 @@
|
||||
|
||||
/datum/category_item/underwear/undershirt/tiedye
|
||||
name = "Tiedye Shirt"
|
||||
icon_state = "tiedye"
|
||||
icon_state = "tiedye"
|
||||
|
||||
/datum/category_item/underwear/undershirt/longstripe_pink
|
||||
name = "Longsleeve Striped Shirt, Pink"
|
||||
icon_state = "longstripe_pink_s"
|
||||
|
||||
/datum/category_item/underwear/undershirt/wingshirt
|
||||
name = "Pink Wing Shirt"
|
||||
icon_state = "wing_shirt_s"
|
||||
|
||||
/datum/category_item/underwear/undershirt/pinkblack_tshirt
|
||||
name = "Pink and Black T-Shirt"
|
||||
icon_state = "pinkblack_tshirt"
|
||||
@@ -5,7 +5,7 @@ var/datum/antagonist/borer/borers
|
||||
role_type = BE_ALIEN
|
||||
role_text = "Cortical Borer"
|
||||
role_text_plural = "Cortical Borers"
|
||||
mob_path = /mob/living/simple_animal/borer
|
||||
mob_path = /mob/living/simple_mob/animal/borer
|
||||
bantype = "Borer"
|
||||
welcome_text = "Use your Infest power to crawl into the ear of a host and fuse with their brain. You can only take control temporarily, and at risk of hurting your host, so be clever and careful; your host is encouraged to help you however they can. Talk to your fellow borers with :x."
|
||||
antag_indicator = "brainworm"
|
||||
@@ -40,7 +40,7 @@ var/datum/antagonist/borer/borers
|
||||
player.objectives += new /datum/objective/escape()
|
||||
|
||||
/datum/antagonist/borer/place_mob(var/mob/living/mob)
|
||||
var/mob/living/simple_animal/borer/borer = mob
|
||||
var/mob/living/simple_mob/animal/borer/borer = mob
|
||||
if(istype(borer))
|
||||
var/mob/living/carbon/human/host
|
||||
for(var/mob/living/carbon/human/H in mob_list)
|
||||
|
||||
@@ -111,12 +111,12 @@ var/datum/antagonist/cultist/cult
|
||||
. = ..()
|
||||
if(.)
|
||||
player << "You catch a glimpse of the Realm of Nar-Sie, the Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of That Which Waits. Assist your new compatriots in their dark dealings. Their goals are yours, and yours are theirs. You serve the Dark One above all else. Bring It back."
|
||||
if(player.current && !istype(player.current, /mob/living/simple_animal/construct))
|
||||
if(player.current && !istype(player.current, /mob/living/simple_mob/construct))
|
||||
player.current.add_language(LANGUAGE_CULT)
|
||||
|
||||
/datum/antagonist/cultist/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted)
|
||||
. = ..()
|
||||
if(. && player.current && !istype(player.current, /mob/living/simple_animal/construct))
|
||||
if(. && player.current && !istype(player.current, /mob/living/simple_mob/construct))
|
||||
player.current.remove_language(LANGUAGE_CULT)
|
||||
|
||||
/datum/antagonist/cultist/can_become_antag(var/datum/mind/player)
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
// Special handling of windows, which are dense but block only from some directions
|
||||
if(istype(A, /obj/structure/window))
|
||||
var/obj/structure/window/W = A
|
||||
if (!W.is_full_window() && !(turn(src.last_move, 180) & A.dir))
|
||||
if (!W.is_fulltile() && !(turn(src.last_move, 180) & A.dir))
|
||||
continue
|
||||
// Same thing for (closed) windoors, which have the same problem
|
||||
else if(istype(A, /obj/machinery/door/window) && !(turn(src.last_move, 180) & A.dir))
|
||||
|
||||
@@ -476,14 +476,14 @@ proc/findNullRod(var/atom/target)
|
||||
|
||||
/obj/item/weapon/spell/construct/run_checks()
|
||||
if(owner)
|
||||
if((iscultist(owner) || istype(owner, /mob/living/simple_animal/construct)) && (world.time >= (last_castcheck + cooldown))) //Are they a cultist or a construct, and has the cooldown time passed?
|
||||
if((iscultist(owner) || istype(owner, /mob/living/simple_mob/construct)) && (world.time >= (last_castcheck + cooldown))) //Are they a cultist or a construct, and has the cooldown time passed?
|
||||
last_castcheck = world.time
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/item/weapon/spell/construct/pay_energy(var/amount)
|
||||
if(owner)
|
||||
if(istype(owner, /mob/living/simple_animal/construct))
|
||||
if(istype(owner, /mob/living/simple_mob/construct))
|
||||
return 1
|
||||
if(iscultist(owner) && pay_blood(amount))
|
||||
return 1
|
||||
@@ -607,7 +607,7 @@ proc/findNullRod(var/atom/target)
|
||||
name = "sphere of agony"
|
||||
desc = "Call forth a portal to a dimension of naught but pain at your target."
|
||||
|
||||
spawner_type = /obj/effect/temporary_effect/pulsar/agonizing_sphere
|
||||
spawner_type = /obj/effect/temporary_effect/pulse/agonizing_sphere
|
||||
|
||||
/obj/item/weapon/spell/construct/spawner/agonizing_sphere/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(within_range(hit_atom) && pay_energy(10))
|
||||
@@ -619,7 +619,7 @@ proc/findNullRod(var/atom/target)
|
||||
var/mob/living/L = hit_atom
|
||||
L.add_modifier(/datum/modifier/agonize, 10 SECONDS)
|
||||
|
||||
/obj/effect/temporary_effect/pulsar/agonizing_sphere
|
||||
/obj/effect/temporary_effect/pulse/agonizing_sphere
|
||||
name = "agonizing sphere"
|
||||
desc = "A portal to some hellish place. Its screams wrack your body with pain.."
|
||||
icon_state = "red_static_sphere"
|
||||
@@ -628,19 +628,15 @@ proc/findNullRod(var/atom/target)
|
||||
light_power = 5
|
||||
light_color = "#FF0000"
|
||||
pulses_remaining = 10
|
||||
pulse_delay = 1 SECOND
|
||||
|
||||
/obj/effect/temporary_effect/pulsar/agonizing_sphere/pulse_loop()
|
||||
while(pulses_remaining)
|
||||
sleep(1 SECONDS)
|
||||
spawn()
|
||||
for(var/mob/living/L in view(4,src))
|
||||
if(!iscultist(L) && !istype(L, /mob/living/simple_animal/construct))
|
||||
L.add_modifier(/datum/modifier/agonize, 2 SECONDS)
|
||||
if(L.isSynthetic())
|
||||
to_chat(L, "<span class='cult'>Your chassis warps as the [src] pulses!</span>")
|
||||
L.adjustFireLoss(4)
|
||||
pulses_remaining--
|
||||
qdel(src)
|
||||
/obj/effect/temporary_effect/pulse/agonizing_sphere/on_pulse()
|
||||
for(var/mob/living/L in view(4,src))
|
||||
if(!iscultist(L) && !istype(L, /mob/living/simple_mob/construct))
|
||||
L.add_modifier(/datum/modifier/agonize, 2 SECONDS)
|
||||
if(L.isSynthetic())
|
||||
to_chat(L, "<span class='cult'>Your chassis warps as the [src] pulses!</span>")
|
||||
L.adjustFireLoss(4)
|
||||
|
||||
//Artificer Heal
|
||||
|
||||
@@ -659,7 +655,7 @@ proc/findNullRod(var/atom/target)
|
||||
L.add_modifier(/datum/modifier/mend_occult, 150)
|
||||
qdel(src)
|
||||
|
||||
//Juggernaut + Behemoth Slam
|
||||
//Juggernaut Slam
|
||||
/obj/item/weapon/spell/construct/slam
|
||||
name = "slam"
|
||||
desc = "Empower your FIST, to send an opponent flying."
|
||||
@@ -672,8 +668,8 @@ proc/findNullRod(var/atom/target)
|
||||
|
||||
/obj/item/weapon/spell/construct/slam/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
|
||||
var/attack_message = "slams"
|
||||
if(istype(user, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/S = user
|
||||
if(istype(user, /mob/living/simple_mob))
|
||||
var/mob/living/simple_mob/S = user
|
||||
attack_message = pick(S.attacktext)
|
||||
if(isliving(hit_atom))
|
||||
var/mob/living/L = hit_atom
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
return
|
||||
|
||||
/obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone)
|
||||
if(iscultist(user) && !istype(user, /mob/living/simple_animal/construct))
|
||||
if(iscultist(user) && !istype(user, /mob/living/simple_mob/construct))
|
||||
return ..()
|
||||
|
||||
var/zone = (user.hand ? "l_arm":"r_arm")
|
||||
@@ -25,7 +25,7 @@
|
||||
//random amount of damage between half of the blade's force and the full force of the blade.
|
||||
user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1)
|
||||
user.Weaken(5)
|
||||
else if(!istype(user, /mob/living/simple_animal/construct))
|
||||
else if(!istype(user, /mob/living/simple_mob/construct))
|
||||
to_chat(user, "<span class='danger'>An inexplicable force rips through you, tearing the sword from your grasp!</span>")
|
||||
else
|
||||
to_chat(user, "<span class='critical'>The blade hisses, forcing itself from your manipulators. \The [src] will only allow mortals to wield it against foes, not kin.</span>")
|
||||
@@ -39,10 +39,10 @@
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob)
|
||||
if(!iscultist(user) && !istype(user, /mob/living/simple_animal/construct))
|
||||
if(!iscultist(user) && !istype(user, /mob/living/simple_mob/construct))
|
||||
to_chat(user, "<span class='warning'>An overwhelming feeling of dread comes over you as you pick up the cultist's sword. It would be wise to be rid of this blade quickly.</span>")
|
||||
user.make_dizzy(120)
|
||||
if(istype(user, /mob/living/simple_animal/construct))
|
||||
if(istype(user, /mob/living/simple_mob/construct))
|
||||
to_chat(user, "<span class='warning'>\The [src] hisses, as it is discontent with your acquisition of it. It would be wise to return it to a worthy mortal quickly.</span>")
|
||||
|
||||
/obj/item/clothing/head/culthood
|
||||
|
||||
@@ -108,18 +108,18 @@
|
||||
light_range=5
|
||||
light_color="#ff0000"
|
||||
spawnable=list(
|
||||
/mob/living/simple_animal/hostile/scarybat,
|
||||
/mob/living/simple_animal/hostile/creature/vore,
|
||||
/mob/living/simple_animal/hostile/faithless
|
||||
) // Vorestation Edit
|
||||
/mob/living/simple_mob/animal/space/bats,
|
||||
/mob/living/simple_mob/creature,
|
||||
/mob/living/simple_mob/faithless
|
||||
)
|
||||
|
||||
/obj/effect/gateway/active/cult
|
||||
light_range=5
|
||||
light_color="#ff0000"
|
||||
spawnable=list(
|
||||
/mob/living/simple_animal/hostile/scarybat/cult,
|
||||
/mob/living/simple_animal/hostile/creature/cult,
|
||||
/mob/living/simple_animal/hostile/faithless/cult
|
||||
/mob/living/simple_mob/animal/space/bats/cult,
|
||||
/mob/living/simple_mob/creature/cult,
|
||||
/mob/living/simple_mob/faithless/cult
|
||||
)
|
||||
|
||||
/obj/effect/gateway/active/cult/cultify()
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
/mob/living/cultify()
|
||||
if(iscultist(src) && client)
|
||||
var/mob/living/simple_animal/construct/harvester/C = new(get_turf(src))
|
||||
var/mob/living/simple_mob/construct/harvester/C = new(get_turf(src))
|
||||
mind.transfer_to(C)
|
||||
C << "<span class='sinister'>The Geometer of Blood is overjoyed to be reunited with its followers, and accepts your body in sacrifice. As reward, you have been gifted with the shell of an Harvester.<br>Your tendrils can use and draw runes without need for a tome, your eyes can see beings through walls, and your mind can open any door. Use these assets to serve Nar-Sie and bring him any remaining living human in the world.<br>You can teleport yourself back to Nar-Sie along with any being under yourself at any time using your \"Harvest\" spell.</span>"
|
||||
dust()
|
||||
|
||||
+246
-242
@@ -1,242 +1,246 @@
|
||||
/obj/item/device/soulstone/cultify()
|
||||
return
|
||||
|
||||
/obj/item/device/soulstone
|
||||
name = "Soul Stone Shard"
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "soulstone"
|
||||
item_state = "electronic"
|
||||
desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefacts power."
|
||||
w_class = ITEMSIZE_SMALL
|
||||
slot_flags = SLOT_BELT
|
||||
origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4)
|
||||
var/imprinted = "empty"
|
||||
var/possible_constructs = list("Juggernaut","Wraith","Artificer","Harvester")
|
||||
|
||||
//////////////////////////////Capturing////////////////////////////////////////////////////////
|
||||
|
||||
/obj/item/device/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob)
|
||||
if(!istype(M, /mob/living/carbon/human))//If target is not a human.
|
||||
return ..()
|
||||
if(istype(M, /mob/living/carbon/human/dummy))
|
||||
return..()
|
||||
if(jobban_isbanned(M, "cultist"))
|
||||
user << "<span class='warning'>This person's soul is too corrupt and cannot be captured!</span>"
|
||||
return..()
|
||||
|
||||
if(M.has_brain_worms()) //Borer stuff - RR
|
||||
user << "<span class='warning'>This being is corrupted by an alien intelligence and cannot be soul trapped.</span>"
|
||||
return..()
|
||||
|
||||
add_attack_logs(user,M,"Soulstone'd with [src.name]")
|
||||
transfer_soul("VICTIM", M, user)
|
||||
return
|
||||
|
||||
|
||||
///////////////////Options for using captured souls///////////////////////////////////////
|
||||
|
||||
/obj/item/device/soulstone/attack_self(mob/user)
|
||||
if (!in_range(src, user))
|
||||
return
|
||||
user.set_machine(src)
|
||||
var/dat = "<TT><B>Soul Stone</B><BR>"
|
||||
for(var/mob/living/simple_animal/shade/A in src)
|
||||
dat += "Captured Soul: [A.name]<br>"
|
||||
dat += {"<A href='byond://?src=\ref[src];choice=Summon'>Summon Shade</A>"}
|
||||
dat += "<br>"
|
||||
dat += {"<a href='byond://?src=\ref[src];choice=Close'> Close</a>"}
|
||||
user << browse(dat, "window=aicard")
|
||||
onclose(user, "aicard")
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
/obj/item/device/soulstone/Topic(href, href_list)
|
||||
var/mob/U = usr
|
||||
if (!in_range(src, U)||U.machine!=src)
|
||||
U << browse(null, "window=aicard")
|
||||
U.unset_machine()
|
||||
return
|
||||
|
||||
add_fingerprint(U)
|
||||
U.set_machine(src)
|
||||
|
||||
switch(href_list["choice"])//Now we switch based on choice.
|
||||
if ("Close")
|
||||
U << browse(null, "window=aicard")
|
||||
U.unset_machine()
|
||||
return
|
||||
|
||||
if ("Summon")
|
||||
for(var/mob/living/simple_animal/shade/A in src)
|
||||
A.status_flags &= ~GODMODE
|
||||
A.canmove = 1
|
||||
A << "<b>You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs.</b>"
|
||||
A.forceMove(U.loc)
|
||||
A.cancel_camera()
|
||||
src.icon_state = "soulstone"
|
||||
attack_self(U)
|
||||
|
||||
///////////////////////////Transferring to constructs/////////////////////////////////////////////////////
|
||||
/obj/structure/constructshell
|
||||
name = "empty shell"
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "construct"
|
||||
desc = "A wicked machine used by those skilled in magical arts. It is inactive."
|
||||
|
||||
/obj/structure/constructshell/cultify()
|
||||
return
|
||||
|
||||
/obj/structure/constructshell/cult
|
||||
icon_state = "construct-cult"
|
||||
desc = "This eerie contraption looks like it would come alive if supplied with a missing ingredient."
|
||||
|
||||
/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob)
|
||||
if(istype(O, /obj/item/device/soulstone))
|
||||
var/obj/item/device/soulstone/S = O;
|
||||
S.transfer_soul("CONSTRUCT",src,user)
|
||||
|
||||
|
||||
////////////////////////////Proc for moving soul in and out off stone//////////////////////////////////////
|
||||
/obj/item/device/soulstone/proc/transfer_human(var/mob/living/carbon/human/T,var/mob/U)
|
||||
if(!istype(T))
|
||||
return;
|
||||
if(src.imprinted != "empty")
|
||||
U << "<span class='danger'>Capture failed!</span>: The soul stone has already been imprinted with [src.imprinted]'s mind!"
|
||||
return
|
||||
if ((T.health + T.halloss) > config.health_threshold_crit && T.stat != DEAD)
|
||||
U << "<span class='danger'>Capture failed!</span>: Kill or maim the victim first!"
|
||||
return
|
||||
if(T.client == null)
|
||||
U << "<span class='danger'>Capture failed!</span>: The soul has already fled it's mortal frame."
|
||||
return
|
||||
if(src.contents.len)
|
||||
U << "<span class='danger'>Capture failed!</span>: The soul stone is full! Use or free an existing soul to make room."
|
||||
return
|
||||
|
||||
for(var/obj/item/W in T)
|
||||
T.drop_from_inventory(W)
|
||||
|
||||
new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton
|
||||
T.invisibility = 101
|
||||
|
||||
var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc )
|
||||
animation.icon_state = "blank"
|
||||
animation.icon = 'icons/mob/mob.dmi'
|
||||
animation.master = T
|
||||
flick("dust-h", animation)
|
||||
qdel(animation)
|
||||
|
||||
var/mob/living/simple_animal/shade/S = new /mob/living/simple_animal/shade( T.loc )
|
||||
S.forceMove(src) //put shade in stone
|
||||
S.status_flags |= GODMODE //So they won't die inside the stone somehow
|
||||
S.canmove = 0//Can't move out of the soul stone
|
||||
S.name = "Shade of [T.real_name]"
|
||||
S.real_name = "Shade of [T.real_name]"
|
||||
S.icon = T.icon
|
||||
S.icon_state = T.icon_state
|
||||
S.overlays = T.overlays
|
||||
S.color = rgb(254,0,0)
|
||||
S.alpha = 127
|
||||
if (T.client)
|
||||
T.client.mob = S
|
||||
S.cancel_camera()
|
||||
|
||||
|
||||
src.icon_state = "soulstone2"
|
||||
src.name = "Soul Stone: [S.real_name]"
|
||||
to_chat(S, "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs.")
|
||||
to_chat(U, "<span class='notice'>Capture successful!</span> : [T.real_name]'s soul has been ripped from their body and stored within the soul stone.")
|
||||
to_chat(U, "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls.")
|
||||
src.imprinted = "[S.name]"
|
||||
qdel(T)
|
||||
|
||||
/obj/item/device/soulstone/proc/transfer_shade(var/mob/living/simple_animal/shade/T,var/mob/U)
|
||||
if(!istype(T))
|
||||
return;
|
||||
if (T.stat == DEAD)
|
||||
to_chat(U, "<span class='danger'>Capture failed!</span>: The shade has already been banished!")
|
||||
return
|
||||
if(src.contents.len)
|
||||
to_chat(U, "<span class='danger'>Capture failed!</span>: The soul stone is full! Use or free an existing soul to make room.")
|
||||
return
|
||||
if(T.name != src.imprinted)
|
||||
to_chat(U, "<span class='danger'>Capture failed!</span>: The soul stone has already been imprinted with [src.imprinted]'s mind!")
|
||||
return
|
||||
|
||||
T.forceMove(src) //put shade in stone
|
||||
T.status_flags |= GODMODE
|
||||
T.canmove = 0
|
||||
T.health = T.getMaxHealth()
|
||||
src.icon_state = "soulstone2"
|
||||
|
||||
to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form")
|
||||
to_chat(U, "<span class='notice'>Capture successful!</span> : [T.name]'s has been recaptured and stored within the soul stone.")
|
||||
|
||||
/obj/item/device/soulstone/proc/transfer_construct(var/obj/structure/constructshell/T,var/mob/U)
|
||||
var/mob/living/simple_animal/shade/A = locate() in src
|
||||
if(!A)
|
||||
to_chat(U,"<span class='danger'>Capture failed!</span>: The soul stone is empty! Go kill someone!")
|
||||
return;
|
||||
var/construct_class = input(U, "Please choose which type of construct you wish to create.") as null|anything in possible_constructs
|
||||
switch(construct_class)
|
||||
if("Juggernaut")
|
||||
var/mob/living/simple_animal/construct/armoured/Z = new /mob/living/simple_animal/construct/armoured (get_turf(T.loc))
|
||||
Z.key = A.key
|
||||
if(iscultist(U))
|
||||
cult.add_antagonist(Z.mind)
|
||||
qdel(T)
|
||||
to_chat(Z,"<B>You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.</B>")
|
||||
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
|
||||
Z.cancel_camera()
|
||||
qdel(src)
|
||||
if("Wraith")
|
||||
var/mob/living/simple_animal/construct/wraith/Z = new /mob/living/simple_animal/construct/wraith (get_turf(T.loc))
|
||||
Z.key = A.key
|
||||
if(iscultist(U))
|
||||
cult.add_antagonist(Z.mind)
|
||||
qdel(T)
|
||||
to_chat(Z,"<B>You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.</B>")
|
||||
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
|
||||
Z.cancel_camera()
|
||||
qdel(src)
|
||||
if("Artificer")
|
||||
var/mob/living/simple_animal/construct/builder/Z = new /mob/living/simple_animal/construct/builder (get_turf(T.loc))
|
||||
Z.key = A.key
|
||||
if(iscultist(U))
|
||||
cult.add_antagonist(Z.mind)
|
||||
qdel(T)
|
||||
to_chat(Z,"<B>You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs</B>")
|
||||
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
|
||||
Z.cancel_camera()
|
||||
qdel(src)
|
||||
if("Harvester")
|
||||
var/mob/living/simple_animal/construct/harvester/Z = new /mob/living/simple_animal/construct/harvester (get_turf(T.loc))
|
||||
Z.key = A.key
|
||||
if(iscultist(U))
|
||||
cult.add_antagonist(Z.mind)
|
||||
qdel(T)
|
||||
to_chat(Z,"<B>You are playing a Harvester. You are relatively weak, but your physical frailty is made up for by your ranged abilities.</B>")
|
||||
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
|
||||
Z.cancel_camera()
|
||||
qdel(src)
|
||||
if("Behemoth")
|
||||
var/mob/living/simple_animal/construct/behemoth/Z = new /mob/living/simple_animal/construct/behemoth (get_turf(T.loc))
|
||||
Z.key = A.key
|
||||
if(iscultist(U))
|
||||
cult.add_antagonist(Z.mind)
|
||||
qdel(T)
|
||||
to_chat(Z,"<B>You are playing a Behemoth. You are incredibly slow, though your slowness is made up for by the fact your shell is far larger than any of your bretheren. You are the Unstoppable Force, and Immovable Object.</B>")
|
||||
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
|
||||
Z.cancel_camera()
|
||||
qdel(src)
|
||||
|
||||
/obj/item/device/soulstone/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob)
|
||||
switch(choice)
|
||||
if("VICTIM")
|
||||
transfer_human(target,U)
|
||||
if("SHADE")
|
||||
transfer_shade(target,U)
|
||||
if("CONSTRUCT")
|
||||
transfer_construct(target,U)
|
||||
/////////////////////////
|
||||
// Soulstone
|
||||
/////////////////////////
|
||||
|
||||
/obj/item/device/soulstone
|
||||
name = "Soul Stone Shard"
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "soulstone"
|
||||
item_state = "electronic"
|
||||
desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artefacts power."
|
||||
w_class = ITEMSIZE_SMALL
|
||||
slot_flags = SLOT_BELT
|
||||
origin_tech = list(TECH_BLUESPACE = 4, TECH_MATERIAL = 4)
|
||||
var/imprinted = "empty"
|
||||
var/possible_constructs = list("Juggernaut","Wraith","Artificer","Harvester")
|
||||
|
||||
/obj/item/device/soulstone/cultify()
|
||||
return
|
||||
|
||||
//////////////////////////////Capturing////////////////////////////////////////////////////////
|
||||
|
||||
/obj/item/device/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob)
|
||||
if(!istype(M, /mob/living/carbon/human))//If target is not a human.
|
||||
return ..()
|
||||
if(istype(M, /mob/living/carbon/human/dummy))
|
||||
return..()
|
||||
if(jobban_isbanned(M, "cultist"))
|
||||
user << "<span class='warning'>This person's soul is too corrupt and cannot be captured!</span>"
|
||||
return..()
|
||||
|
||||
if(M.has_brain_worms()) //Borer stuff - RR
|
||||
user << "<span class='warning'>This being is corrupted by an alien intelligence and cannot be soul trapped.</span>"
|
||||
return..()
|
||||
|
||||
add_attack_logs(user,M,"Soulstone'd with [src.name]")
|
||||
transfer_soul("VICTIM", M, user)
|
||||
return
|
||||
|
||||
|
||||
///////////////////Options for using captured souls///////////////////////////////////////
|
||||
|
||||
/obj/item/device/soulstone/attack_self(mob/user)
|
||||
if (!in_range(src, user))
|
||||
return
|
||||
user.set_machine(src)
|
||||
var/dat = "<TT><B>Soul Stone</B><BR>"
|
||||
for(var/mob/living/simple_mob/construct/shade/A in src)
|
||||
dat += "Captured Soul: [A.name]<br>"
|
||||
dat += {"<A href='byond://?src=\ref[src];choice=Summon'>Summon Shade</A>"}
|
||||
dat += "<br>"
|
||||
dat += {"<a href='byond://?src=\ref[src];choice=Close'> Close</a>"}
|
||||
user << browse(dat, "window=aicard")
|
||||
onclose(user, "aicard")
|
||||
return
|
||||
|
||||
|
||||
|
||||
|
||||
/obj/item/device/soulstone/Topic(href, href_list)
|
||||
var/mob/U = usr
|
||||
if (!in_range(src, U)||U.machine!=src)
|
||||
U << browse(null, "window=aicard")
|
||||
U.unset_machine()
|
||||
return
|
||||
|
||||
add_fingerprint(U)
|
||||
U.set_machine(src)
|
||||
|
||||
switch(href_list["choice"])//Now we switch based on choice.
|
||||
if ("Close")
|
||||
U << browse(null, "window=aicard")
|
||||
U.unset_machine()
|
||||
return
|
||||
|
||||
if ("Summon")
|
||||
for(var/mob/living/simple_mob/construct/shade/A in src)
|
||||
A.status_flags &= ~GODMODE
|
||||
A.canmove = 1
|
||||
A << "<b>You have been released from your prison, but you are still bound to [U.name]'s will. Help them suceed in their goals at all costs.</b>"
|
||||
A.forceMove(U.loc)
|
||||
A.cancel_camera()
|
||||
src.icon_state = "soulstone"
|
||||
attack_self(U)
|
||||
|
||||
///////////////////////////Transferring to constructs/////////////////////////////////////////////////////
|
||||
/obj/structure/constructshell
|
||||
name = "empty shell"
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "construct"
|
||||
desc = "A wicked machine used by those skilled in magical arts. It is inactive."
|
||||
|
||||
/obj/structure/constructshell/cultify()
|
||||
return
|
||||
|
||||
/obj/structure/constructshell/cult
|
||||
icon_state = "construct-cult"
|
||||
desc = "This eerie contraption looks like it would come alive if supplied with a missing ingredient."
|
||||
|
||||
/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob)
|
||||
if(istype(O, /obj/item/device/soulstone))
|
||||
var/obj/item/device/soulstone/S = O;
|
||||
S.transfer_soul("CONSTRUCT",src,user)
|
||||
|
||||
|
||||
////////////////////////////Proc for moving soul in and out off stone//////////////////////////////////////
|
||||
/obj/item/device/soulstone/proc/transfer_human(var/mob/living/carbon/human/T,var/mob/U)
|
||||
if(!istype(T))
|
||||
return;
|
||||
if(src.imprinted != "empty")
|
||||
U << "<span class='danger'>Capture failed!</span>: The soul stone has already been imprinted with [src.imprinted]'s mind!"
|
||||
return
|
||||
if ((T.health + T.halloss) > config.health_threshold_crit && T.stat != DEAD)
|
||||
U << "<span class='danger'>Capture failed!</span>: Kill or maim the victim first!"
|
||||
return
|
||||
if(T.client == null)
|
||||
U << "<span class='danger'>Capture failed!</span>: The soul has already fled it's mortal frame."
|
||||
return
|
||||
if(src.contents.len)
|
||||
U << "<span class='danger'>Capture failed!</span>: The soul stone is full! Use or free an existing soul to make room."
|
||||
return
|
||||
|
||||
for(var/obj/item/W in T)
|
||||
T.drop_from_inventory(W)
|
||||
|
||||
new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton
|
||||
T.invisibility = 101
|
||||
|
||||
var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc )
|
||||
animation.icon_state = "blank"
|
||||
animation.icon = 'icons/mob/mob.dmi'
|
||||
animation.master = T
|
||||
flick("dust-h", animation)
|
||||
qdel(animation)
|
||||
|
||||
var/mob/living/simple_mob/construct/shade/S = new /mob/living/simple_mob/construct/shade( T.loc )
|
||||
S.forceMove(src) //put shade in stone
|
||||
S.status_flags |= GODMODE //So they won't die inside the stone somehow
|
||||
S.canmove = 0//Can't move out of the soul stone
|
||||
S.name = "Shade of [T.real_name]"
|
||||
S.real_name = "Shade of [T.real_name]"
|
||||
S.icon = T.icon
|
||||
S.icon_state = T.icon_state
|
||||
S.overlays = T.overlays
|
||||
S.color = rgb(254,0,0)
|
||||
S.alpha = 127
|
||||
if (T.client)
|
||||
T.client.mob = S
|
||||
S.cancel_camera()
|
||||
|
||||
|
||||
src.icon_state = "soulstone2"
|
||||
src.name = "Soul Stone: [S.real_name]"
|
||||
to_chat(S, "Your soul has been captured! You are now bound to [U.name]'s will, help them suceed in their goals at all costs.")
|
||||
to_chat(U, "<span class='notice'>Capture successful!</span> : [T.real_name]'s soul has been ripped from their body and stored within the soul stone.")
|
||||
to_chat(U, "The soulstone has been imprinted with [S.real_name]'s mind, it will no longer react to other souls.")
|
||||
src.imprinted = "[S.name]"
|
||||
qdel(T)
|
||||
|
||||
/obj/item/device/soulstone/proc/transfer_shade(var/mob/living/simple_mob/construct/shade/T,var/mob/U)
|
||||
if(!istype(T))
|
||||
return;
|
||||
if (T.stat == DEAD)
|
||||
to_chat(U, "<span class='danger'>Capture failed!</span>: The shade has already been banished!")
|
||||
return
|
||||
if(src.contents.len)
|
||||
to_chat(U, "<span class='danger'>Capture failed!</span>: The soul stone is full! Use or free an existing soul to make room.")
|
||||
return
|
||||
if(T.name != src.imprinted)
|
||||
to_chat(U, "<span class='danger'>Capture failed!</span>: The soul stone has already been imprinted with [src.imprinted]'s mind!")
|
||||
return
|
||||
|
||||
T.forceMove(src) //put shade in stone
|
||||
T.status_flags |= GODMODE
|
||||
T.canmove = 0
|
||||
T.health = T.getMaxHealth()
|
||||
src.icon_state = "soulstone2"
|
||||
|
||||
to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form")
|
||||
to_chat(U, "<span class='notice'>Capture successful!</span> : [T.name]'s has been recaptured and stored within the soul stone.")
|
||||
|
||||
/obj/item/device/soulstone/proc/transfer_construct(var/obj/structure/constructshell/T,var/mob/U)
|
||||
var/mob/living/simple_mob/construct/shade/A = locate() in src
|
||||
if(!A)
|
||||
to_chat(U,"<span class='danger'>Capture failed!</span>: The soul stone is empty! Go kill someone!")
|
||||
return;
|
||||
var/construct_class = input(U, "Please choose which type of construct you wish to create.") as null|anything in possible_constructs
|
||||
switch(construct_class)
|
||||
if("Juggernaut")
|
||||
var/mob/living/simple_mob/construct/juggernaut/Z = new /mob/living/simple_mob/construct/juggernaut (get_turf(T.loc))
|
||||
Z.key = A.key
|
||||
if(iscultist(U))
|
||||
cult.add_antagonist(Z.mind)
|
||||
qdel(T)
|
||||
to_chat(Z,"<B>You are playing a Juggernaut. Though slow, you can withstand extreme punishment, and rip apart enemies and walls alike.</B>")
|
||||
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
|
||||
Z.cancel_camera()
|
||||
qdel(src)
|
||||
if("Wraith")
|
||||
var/mob/living/simple_mob/construct/wraith/Z = new /mob/living/simple_mob/construct/wraith (get_turf(T.loc))
|
||||
Z.key = A.key
|
||||
if(iscultist(U))
|
||||
cult.add_antagonist(Z.mind)
|
||||
qdel(T)
|
||||
to_chat(Z,"<B>You are playing a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.</B>")
|
||||
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
|
||||
Z.cancel_camera()
|
||||
qdel(src)
|
||||
if("Artificer")
|
||||
var/mob/living/simple_mob/construct/artificer/Z = new /mob/living/simple_mob/construct/artificer (get_turf(T.loc))
|
||||
Z.key = A.key
|
||||
if(iscultist(U))
|
||||
cult.add_antagonist(Z.mind)
|
||||
qdel(T)
|
||||
to_chat(Z,"<B>You are playing an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, repair allied constructs (by clicking on them), and even create new constructs</B>")
|
||||
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
|
||||
Z.cancel_camera()
|
||||
qdel(src)
|
||||
if("Harvester")
|
||||
var/mob/living/simple_mob/construct/harvester/Z = new /mob/living/simple_mob/construct/harvester (get_turf(T.loc))
|
||||
Z.key = A.key
|
||||
if(iscultist(U))
|
||||
cult.add_antagonist(Z.mind)
|
||||
qdel(T)
|
||||
to_chat(Z,"<B>You are playing a Harvester. You are relatively weak, but your physical frailty is made up for by your ranged abilities.</B>")
|
||||
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
|
||||
Z.cancel_camera()
|
||||
qdel(src)
|
||||
if("Behemoth")
|
||||
var/mob/living/simple_mob/construct/juggernaut/behemoth/Z = new /mob/living/simple_mob/construct/juggernaut/behemoth (get_turf(T.loc))
|
||||
Z.key = A.key
|
||||
if(iscultist(U))
|
||||
cult.add_antagonist(Z.mind)
|
||||
qdel(T)
|
||||
to_chat(Z,"<B>You are playing a Behemoth. You are incredibly slow, though your slowness is made up for by the fact your shell is far larger than any of your bretheren. You are the Unstoppable Force, and Immovable Object.</B>")
|
||||
to_chat(Z,"<B>You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.</B>")
|
||||
Z.cancel_camera()
|
||||
qdel(src)
|
||||
|
||||
/obj/item/device/soulstone/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob)
|
||||
switch(choice)
|
||||
if("VICTIM")
|
||||
transfer_human(target,U)
|
||||
if("SHADE")
|
||||
transfer_shade(target,U)
|
||||
if("CONSTRUCT")
|
||||
transfer_construct(target,U)
|
||||
@@ -206,7 +206,7 @@ var/hadevent = 0
|
||||
/proc/carp_migration() // -- Darem
|
||||
for(var/obj/effect/landmark/C in landmarks_list)
|
||||
if(C.name == "carpspawn")
|
||||
new /mob/living/simple_animal/hostile/carp(C.loc)
|
||||
new /mob/living/simple_mob/animal/space/carp(C.loc)
|
||||
//sleep(100)
|
||||
spawn(rand(300, 600)) //Delayed announcements to keep the crew on their toes.
|
||||
command_announcement.Announce("Unknown biological entities have been detected near \the [station_name()], please stand-by.", "Lifesign Alert", new_sound = 'sound/AI/commandreport.ogg')
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
for(var/turf/simulated/floor/T in orange(1,xmas))
|
||||
for(var/i=1,i<=rand(1,5),i++)
|
||||
new /obj/item/weapon/a_gift(T)
|
||||
//for(var/mob/living/simple_animal/corgi/Ian/Ian in mob_list)
|
||||
//for(var/mob/living/simple_mob/corgi/Ian/Ian in mob_list)
|
||||
// Ian.place_on_head(new /obj/item/clothing/head/helmet/space/santahat(Ian))
|
||||
|
||||
/proc/ChristmasEvent()
|
||||
for(var/obj/structure/flora/tree/pine/xmas in world)
|
||||
var/mob/living/simple_animal/hostile/tree/evil_tree = new /mob/living/simple_animal/hostile/tree(xmas.loc)
|
||||
var/mob/living/simple_mob/animal/space/tree/evil_tree = new /mob/living/simple_mob/animal/space/tree(xmas.loc)
|
||||
evil_tree.icon_state = xmas.icon_state
|
||||
evil_tree.icon_living = evil_tree.icon_state
|
||||
evil_tree.icon_dead = evil_tree.icon_state
|
||||
|
||||
@@ -808,7 +808,7 @@ datum/objective/heist/salvage
|
||||
|
||||
/datum/objective/borer_survive/check_completion()
|
||||
if(owner)
|
||||
var/mob/living/simple_animal/borer/B = owner
|
||||
var/mob/living/simple_mob/animal/borer/B = owner
|
||||
if(istype(B) && B.stat < 2 && B.host && B.host.stat < 2) return 1
|
||||
return 0
|
||||
|
||||
@@ -817,7 +817,7 @@ datum/objective/heist/salvage
|
||||
|
||||
/datum/objective/borer_reproduce/check_completion()
|
||||
if(owner && owner.current)
|
||||
var/mob/living/simple_animal/borer/B = owner.current
|
||||
var/mob/living/simple_mob/animal/borer/B = owner.current
|
||||
if(istype(B) && B.has_reproduced) return 1
|
||||
return 0
|
||||
|
||||
|
||||
@@ -1,258 +0,0 @@
|
||||
//An AI-controlled 'companion' for the Technomancer. It's tough, strong, and can also use spells.
|
||||
/mob/living/simple_animal/technomancer_golem
|
||||
name = "G.O.L.E.M."
|
||||
desc = "A rather unusual looking synthetic."
|
||||
icon = 'icons/mob/mob.dmi'
|
||||
icon_state = "technomancer_golem"
|
||||
health = 250
|
||||
maxHealth = 250
|
||||
stop_automated_movement = 1
|
||||
wander = 0
|
||||
response_help = "pets"
|
||||
response_disarm = "pushes away"
|
||||
response_harm = "punches"
|
||||
harm_intent_damage = 3
|
||||
|
||||
heat_damage_per_tick = 0
|
||||
cold_damage_per_tick = 0
|
||||
|
||||
min_oxy = 0
|
||||
max_oxy = 0
|
||||
min_tox = 0
|
||||
max_tox = 0
|
||||
min_co2 = 0
|
||||
max_co2 = 0
|
||||
min_n2 = 0
|
||||
max_n2 = 0
|
||||
unsuitable_atoms_damage = 0
|
||||
speed = 0
|
||||
|
||||
melee_damage_lower = 30 // It has a built in esword.
|
||||
melee_damage_upper = 30
|
||||
attack_sound = 'sound/weapons/blade1.ogg'
|
||||
attacktext = list("slashed")
|
||||
friendly = "hugs"
|
||||
resistance = 0
|
||||
melee_miss_chance = 0
|
||||
|
||||
var/obj/item/weapon/technomancer_core/golem/core = null
|
||||
var/obj/item/weapon/spell/active_spell = null // Shield and ranged spells
|
||||
var/mob/living/master = null
|
||||
|
||||
var/list/known_spells = list(
|
||||
"beam" = /obj/item/weapon/spell/projectile/beam,
|
||||
"chain lightning" = /obj/item/weapon/spell/projectile/chain_lightning,
|
||||
"force missile" = /obj/item/weapon/spell/projectile/force_missile,
|
||||
"ionic bolt" = /obj/item/weapon/spell/projectile/ionic_bolt,
|
||||
"lightning" = /obj/item/weapon/spell/projectile/lightning,
|
||||
"blink" = /obj/item/weapon/spell/blink,
|
||||
"dispel" = /obj/item/weapon/spell/dispel,
|
||||
"oxygenate" = /obj/item/weapon/spell/oxygenate,
|
||||
"mend life" = /obj/item/weapon/spell/modifier/mend_life,
|
||||
"mend synthetic" = /obj/item/weapon/spell/modifier/mend_synthetic,
|
||||
"mend organs" = /obj/item/weapon/spell/mend_organs,
|
||||
"purify" = /obj/item/weapon/spell/modifier/purify,
|
||||
"resurrect" = /obj/item/weapon/spell/resurrect,
|
||||
"passwall" = /obj/item/weapon/spell/passwall,
|
||||
"repel missiles" = /obj/item/weapon/spell/modifier/repel_missiles,
|
||||
"corona" = /obj/item/weapon/spell/modifier/corona,
|
||||
"haste" = /obj/item/weapon/spell/modifier/haste
|
||||
)
|
||||
|
||||
// Holds the overlays, when idle or attacking.
|
||||
var/image/sword_image = null
|
||||
var/image/spell_image = null
|
||||
// These contain icon_states for each frame of an attack animation, which is swapped in and out manually, because BYOND.
|
||||
// They are assoc lists, to hold the frame duration and the frame icon_state in one list.
|
||||
var/list/spell_pre_attack_states = list(
|
||||
"golem_spell_attack_1" = 1,
|
||||
"golem_spell_attack_2" = 2,
|
||||
"golem_spell_attack_3" = 2
|
||||
)
|
||||
var/list/spell_post_attack_states = list(
|
||||
"golem_spell_attack_4" = 2,
|
||||
"golem_spell_attack_5" = 3,
|
||||
"golem_spell_attack_6" = 3
|
||||
)
|
||||
var/list/sword_pre_attack_states = list(
|
||||
"golem_sword_attack_1" = 1,
|
||||
"golem_sword_attack_2" = 5
|
||||
)
|
||||
var/list/sword_post_attack_states = list(
|
||||
"golem_sword_attack_3" = 1,
|
||||
"golem_sword_attack_4" = 3
|
||||
)
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/New()
|
||||
..()
|
||||
core = new(src)
|
||||
sword_image = image(icon, src, "golem_sword")
|
||||
spell_image = image(icon, src, "golem_spell")
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/Destroy()
|
||||
qdel(core)
|
||||
qdel(sword_image)
|
||||
qdel(spell_image)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/unref_spell()
|
||||
active_spell = null
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/hivebot/death()
|
||||
..()
|
||||
visible_message("\The [src] disintegrates!")
|
||||
new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc)
|
||||
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/update_icon()
|
||||
overlays.Cut()
|
||||
overlays += sword_image
|
||||
overlays += spell_image
|
||||
update_modifier_visuals()
|
||||
|
||||
// Unfortunately, BYOND does not let you flick() images or other overlays, so we need to do this in a terrible way.
|
||||
/atom/proc/manual_flick(var/list/frames, var/image/I, var/reset_to = null)
|
||||
// Swap in and out each frame manually.
|
||||
for(var/frame in frames)
|
||||
overlays -= I
|
||||
I.icon_state = frame
|
||||
overlays += I
|
||||
sleep(frames[frame])
|
||||
if(reset_to)
|
||||
// One more time to reset it to what it was before.
|
||||
overlays -= I
|
||||
I.icon_state = reset_to
|
||||
overlays += I
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/spellcast_pre_animation()
|
||||
setClickCooldown(5)
|
||||
manual_flick(spell_pre_attack_states, spell_image, reset_to = "golem_spell_attack_3")
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/spellcast_post_animation()
|
||||
setClickCooldown(8)
|
||||
manual_flick(spell_post_attack_states, spell_image, reset_to = "golem_spell")
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/sword_pre_animation()
|
||||
setClickCooldown(6)
|
||||
manual_flick(sword_pre_attack_states, sword_image)
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/sword_post_animation()
|
||||
setClickCooldown(3)
|
||||
manual_flick(sword_post_attack_states, sword_image, reset_to = "golem_sword")
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/DoPunch(var/atom/A)
|
||||
sword_pre_animation()
|
||||
. = ..() // This does the actual attack and will check adjacency again.
|
||||
sword_post_animation()
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/isSynthetic()
|
||||
return TRUE // So Mend Synthetic will work on them.
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/speech_bubble_appearance()
|
||||
return "synthetic_evil"
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/place_spell_in_hand(var/path)
|
||||
if(!path || !ispath(path))
|
||||
return 0
|
||||
|
||||
if(active_spell)
|
||||
qdel(active_spell) // Get rid of our old spell.
|
||||
|
||||
var/obj/item/weapon/spell/S = new path(src)
|
||||
active_spell = S
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/verb/test_giving_spells()
|
||||
var/choice = input(usr, "What spell?", "Give spell") as null|anything in known_spells
|
||||
if(choice)
|
||||
place_spell_in_hand(known_spells[choice])
|
||||
else
|
||||
qdel(active_spell)
|
||||
|
||||
// Used to cast spells.
|
||||
/mob/living/simple_animal/technomancer_golem/RangedAttack(var/atom/A, var/params)
|
||||
if(active_spell)
|
||||
spellcast_pre_animation()
|
||||
if(active_spell.cast_methods & CAST_RANGED)
|
||||
active_spell.on_ranged_cast(A, src)
|
||||
spellcast_post_animation()
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/UnarmedAttack(var/atom/A, var/proximity)
|
||||
if(proximity)
|
||||
if(active_spell)
|
||||
spellcast_pre_animation()
|
||||
if(!Adjacent(A)) // Need to check again since they might've moved while 'warming up'.
|
||||
spellcast_post_animation()
|
||||
return
|
||||
var/effective_cooldown = round(active_spell.cooldown * core.cooldown_modifier, 5)
|
||||
if(active_spell.cast_methods & CAST_MELEE)
|
||||
active_spell.on_melee_cast(A, src)
|
||||
else if(active_spell.cast_methods & CAST_RANGED)
|
||||
active_spell.on_ranged_cast(A, src)
|
||||
spellcast_post_animation()
|
||||
src.setClickCooldown(effective_cooldown)
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/get_technomancer_core()
|
||||
return core
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/bind_to_mob(mob/user)
|
||||
if(!user || master)
|
||||
return
|
||||
master = user
|
||||
name = "[master]'s [initial(name)]"
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/examine(mob/user)
|
||||
..()
|
||||
if(user.mind && technomancers.is_antagonist(user.mind))
|
||||
user << "Your pride and joy. It's a very special synthetic robot, capable of using functions similar to you, and you built it \
|
||||
yourself! It'll always stand by your side, ready to help you out. You have no idea what GOLEM stands for, however..."
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/Life()
|
||||
..()
|
||||
handle_ai()
|
||||
|
||||
// This is where the real spaghetti begins.
|
||||
/mob/living/simple_animal/technomancer_golem/proc/handle_ai()
|
||||
if(!master)
|
||||
return
|
||||
if(get_dist(src, master) > 6 || src.z != master.z)
|
||||
targeted_blink(master)
|
||||
|
||||
// Give our allies buffs and heals.
|
||||
for(var/mob/living/L in view(src))
|
||||
if(L in friends)
|
||||
support_friend(L)
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/support_friend(var/mob/living/L)
|
||||
if(L.getBruteLoss() >= 10 || L.getFireLoss() >= 10)
|
||||
if(L.isSynthetic() && !L.has_modifier_of_type(/datum/modifier/technomancer/mend_synthetic))
|
||||
place_spell_in_hand(known_spells["mend synthetic"])
|
||||
targeted_blink(L)
|
||||
UnarmedAttack(L, 1)
|
||||
else if(!L.has_modifier_of_type(/datum/modifier/technomancer/mend_life))
|
||||
place_spell_in_hand(known_spells["mend life"])
|
||||
targeted_blink(L)
|
||||
UnarmedAttack(L, 1)
|
||||
return
|
||||
|
||||
|
||||
// Give them repel missiles if they lack it.
|
||||
if(!L.has_modifier_of_type(/datum/modifier/technomancer/repel_missiles))
|
||||
place_spell_in_hand(known_spells["repel missiles"])
|
||||
RangedAttack(L)
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/targeted_blink(var/atom/target)
|
||||
var/datum/effect/effect/system/spark_spread/spark_system = new()
|
||||
spark_system.set_up(5, 0, get_turf(src))
|
||||
spark_system.start()
|
||||
src.visible_message("<span class='notice'>\The [src] vanishes!</span>")
|
||||
src.forceMove(get_turf(target))
|
||||
return
|
||||
@@ -130,7 +130,7 @@
|
||||
for(var/mob/living/L in summoned_mobs)
|
||||
summoned_mobs -= L
|
||||
qdel(L)
|
||||
for(var/mob/living/simple_animal/ward/ward in wards_in_use)
|
||||
for(var/mob/living/ward in wards_in_use)
|
||||
wards_in_use -= ward
|
||||
qdel(ward)
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
//Returns 1 if the turf is dense, or if there's dense objects on it, unless told to ignore them.
|
||||
/turf/proc/check_density(var/ignore_objs = 0)
|
||||
//Returns 1 if the turf is dense, or if there's dense objects/mobs on it, unless told to ignore them.
|
||||
/turf/proc/check_density(var/ignore_objs = FALSE, var/ignore_mobs = FALSE)
|
||||
if(density)
|
||||
return 1
|
||||
if(!ignore_objs)
|
||||
return TRUE
|
||||
if(!ignore_objs || !ignore_mobs)
|
||||
for(var/atom/movable/stuff in contents)
|
||||
if(stuff.density)
|
||||
return 1
|
||||
return 0
|
||||
if(ignore_objs && isobj(stuff))
|
||||
continue
|
||||
else if(ignore_mobs && isliving(stuff)) // Ghosts aren't dense but keeping this limited to living type will probably save headaches in the future.
|
||||
continue
|
||||
else
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
// Used to distinguish friend from foe.
|
||||
/obj/item/weapon/spell/proc/is_ally(var/mob/living/L)
|
||||
@@ -14,9 +19,9 @@
|
||||
return 1
|
||||
if(L.mind && technomancers.is_antagonist(L.mind)) // This should be done better since we might want opposing technomancers later.
|
||||
return 1
|
||||
if(istype(L, /mob/living/simple_animal/hostile)) // Mind controlled simple mobs count as allies too.
|
||||
var/mob/living/simple_animal/SA = L
|
||||
if(owner in SA.friends)
|
||||
if(istype(L, /mob/living/simple_mob)) // Mind controlled simple mobs count as allies too.
|
||||
var/mob/living/simple_mob/SM = L
|
||||
if(owner in SM.friends)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
/obj/item/weapon/spell/abjuration/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /mob/living) && pay_energy(500) && within_range(hit_atom))
|
||||
var/mob/living/L = hit_atom
|
||||
var/mob/living/simple_animal/SA = null
|
||||
var/mob/living/simple_mob/SM = null
|
||||
|
||||
//Bit of a roundabout typecheck, in order to test for two variables from two different mob types in one line.
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
SA = L
|
||||
if(L.summoned || (SA && SA.supernatural) )
|
||||
if(istype(L, /mob/living/simple_mob))
|
||||
SM = L
|
||||
if(L.summoned || (SM && SM.supernatural) )
|
||||
if(L.client) // Player-controlled mobs are immune to being killed by this.
|
||||
user << "<span class='warning'>\The [L] resists your attempt to banish it!</span>"
|
||||
L << "<span class='warning'>\The [user] tried to teleport you far away, but failed.</span>"
|
||||
@@ -29,8 +29,8 @@
|
||||
else
|
||||
visible_message("<span class='notice'>\The [L] vanishes!</span>")
|
||||
qdel(L)
|
||||
else if(istype(L, /mob/living/simple_animal/construct))
|
||||
var/mob/living/simple_animal/construct/evil = L
|
||||
else if(istype(L, /mob/living/simple_mob/construct))
|
||||
var/mob/living/simple_mob/construct/evil = L
|
||||
evil << "<span class='danger'>\The [user]'s abjuration purges your form!</span>"
|
||||
evil.purge = 3
|
||||
adjust_instability(5)
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
for(var/mob/living/carbon/human/H in nearby_mobs) //Heal our apprentices
|
||||
if(H.mind && technomancers.is_antagonist(H.mind))
|
||||
mobs_to_heal |= H
|
||||
for(var/mob/living/simple_animal/hostile/SAH in nearby_mobs) //Heal our controlled mobs
|
||||
for(var/mob/living/simple_mob/hostile/SAH in nearby_mobs) //Heal our controlled mobs
|
||||
if(owner in SAH.friends)
|
||||
mobs_to_heal |= SAH
|
||||
else
|
||||
|
||||
@@ -21,91 +21,68 @@
|
||||
aspect = ASPECT_BIOMED //Not sure if this should be something else.
|
||||
var/image/control_overlay = null
|
||||
var/list/controlled_mobs = list()
|
||||
var/list/allowed_mobs = list(
|
||||
/mob/living/bot,
|
||||
/mob/living/simple_animal/cat,
|
||||
/mob/living/simple_animal/chick,
|
||||
/mob/living/simple_animal/chicken,
|
||||
/mob/living/simple_animal/corgi,
|
||||
/mob/living/simple_animal/cow,
|
||||
/mob/living/simple_animal/crab,
|
||||
/mob/living/simple_animal/lizard,
|
||||
/mob/living/simple_animal/mouse,
|
||||
/mob/living/simple_animal/parrot,
|
||||
/mob/living/simple_animal/slime,
|
||||
// /mob/living/simple_animal/adultslime,
|
||||
/mob/living/simple_animal/tindalos,
|
||||
/mob/living/simple_animal/yithian,
|
||||
/mob/living/simple_animal/hostile/scarybat,
|
||||
/mob/living/simple_animal/hostile/viscerator,
|
||||
/mob/living/simple_animal/hostile/malf_drone,
|
||||
/mob/living/simple_animal/hostile/giant_spider,
|
||||
/mob/living/simple_animal/hostile/hivebot,
|
||||
/mob/living/simple_animal/retaliate/diyaab, //Doubt these will get used but might as well,
|
||||
/mob/living/simple_animal/hostile/savik,
|
||||
/mob/living/simple_animal/hostile/shantak
|
||||
)
|
||||
var/allowed_mob_classes = MOB_CLASS_ANIMAL|MOB_CLASS_SYNTHETIC
|
||||
|
||||
//This unfortunately is gonna be rather messy due to the various mobtypes involved.
|
||||
/obj/item/weapon/spell/control/proc/select(var/mob/living/L)
|
||||
if(!(is_type_in_list(L, allowed_mobs)))
|
||||
return 0
|
||||
if(!(L.mob_class & allowed_mob_classes))
|
||||
return FALSE
|
||||
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SA = L
|
||||
SA.ai_inactive = 1
|
||||
SA.friends |= src.owner
|
||||
SA.stance = STANCE_IDLE
|
||||
if(!L.has_AI())
|
||||
return FALSE
|
||||
|
||||
L.overlays |= control_overlay
|
||||
var/datum/ai_holder/AI = L.ai_holder
|
||||
AI.hostile = FALSE // The Technomancer chooses the target, not the AI.
|
||||
AI.retaliate = TRUE
|
||||
AI.wander = FALSE
|
||||
AI.forget_everything()
|
||||
|
||||
if(istype(L, /mob/living/simple_mob))
|
||||
var/mob/living/simple_mob/SM = L
|
||||
SM.friends |= src.owner
|
||||
|
||||
L.add_overlay(control_overlay, TRUE)
|
||||
controlled_mobs |= L
|
||||
|
||||
/obj/item/weapon/spell/control/proc/deselect(var/mob/living/L)
|
||||
if(!(L in controlled_mobs))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SA = L
|
||||
SA.ai_inactive = 1
|
||||
if(istype(SA, /mob/living/simple_animal/hostile))
|
||||
var/mob/living/simple_animal/hostile/SAH = SA
|
||||
SAH.friends.Remove(owner)
|
||||
if(L.has_AI())
|
||||
var/datum/ai_holder/AI = L.ai_holder
|
||||
AI.hostile = initial(AI.hostile)
|
||||
AI.retaliate = initial(AI.retaliate)
|
||||
AI.wander = initial(AI.wander)
|
||||
AI.forget_everything()
|
||||
|
||||
L.overlays.Remove(control_overlay)
|
||||
if(istype(L, /mob/living/simple_mob))
|
||||
var/mob/living/simple_mob/SM = L
|
||||
SM.friends -= owner
|
||||
|
||||
L.cut_overlay(control_overlay, TRUE)
|
||||
controlled_mobs.Remove(L)
|
||||
|
||||
/obj/item/weapon/spell/control/proc/move_all(turf/T)
|
||||
for(var/mob/living/living in controlled_mobs)
|
||||
if(living.stat)
|
||||
deselect(living)
|
||||
for(var/mob/living/L in controlled_mobs)
|
||||
if(!L.has_AI() || L.stat)
|
||||
deselect(L)
|
||||
continue
|
||||
if(istype(living, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SA = living
|
||||
SA.target_mob = null
|
||||
SA.stance = STANCE_IDLE
|
||||
walk_towards(SA,T,SA.speed)
|
||||
else
|
||||
walk_towards(living,T,5)
|
||||
L.ai_holder.give_destination(T, 0, TRUE)
|
||||
|
||||
/obj/item/weapon/spell/control/proc/attack_all(mob/target)
|
||||
for(var/mob/living/L in controlled_mobs)
|
||||
if(L.stat)
|
||||
if(!L.has_AI() || L.stat)
|
||||
deselect(L)
|
||||
continue
|
||||
if(istype(L, /mob/living/simple_animal/hostile))
|
||||
var/mob/living/simple_animal/hostile/SAH
|
||||
SAH.target_mob = target
|
||||
else if(istype(L, /mob/living/bot))
|
||||
var/mob/living/bot/B = L
|
||||
B.UnarmedAttack(L)
|
||||
L.ai_holder.give_target(target)
|
||||
|
||||
/obj/item/weapon/spell/control/New()
|
||||
/obj/item/weapon/spell/control/initialize()
|
||||
control_overlay = image('icons/obj/spells.dmi',"controlled")
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/spell/control/Destroy()
|
||||
for(var/mob/living/simple_animal/hostile/SM in controlled_mobs)
|
||||
deselect(SM)
|
||||
for(var/mob/living/L in controlled_mobs)
|
||||
deselect(L)
|
||||
controlled_mobs = list()
|
||||
return ..()
|
||||
|
||||
@@ -127,11 +104,14 @@
|
||||
trying to use it on yourself, perhaps you're an exception? Regardless, nothing happens.</span>"
|
||||
return 0
|
||||
|
||||
if(is_type_in_list(L, allowed_mobs))
|
||||
if(L.mob_class & allowed_mob_classes)
|
||||
if(!(L in controlled_mobs)) //Selecting
|
||||
if(L.client)
|
||||
user << "<span class='danger'>\The [L] seems to resist you!</span>"
|
||||
return 0
|
||||
if(!L.has_AI())
|
||||
to_chat(user, span("warning", "\The [L] seems too dim for this to work on them."))
|
||||
return FALSE
|
||||
if(pay_energy(500))
|
||||
select(L)
|
||||
user << "<span class='notice'>\The [L] is now under your (limited) control.</span>"
|
||||
|
||||
@@ -75,9 +75,9 @@
|
||||
|
||||
for(var/mob/living/L in view(owner))
|
||||
// Spiders, carp... bears.
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SM = L
|
||||
if(!is_ally(SM) && SM.hostile)
|
||||
if(istype(L, /mob/living/simple_mob))
|
||||
var/mob/living/simple_mob/SM = L
|
||||
if(!is_ally(SM) && SM.has_AI() && SM.ai_holder.hostile)
|
||||
hostile_mobs++
|
||||
if(SM.summoned || SM.supernatural) // Our creations might be trying to kill us.
|
||||
potential_spells |= /obj/item/weapon/spell/abjuration
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
aspect = ASPECT_LIGHT
|
||||
cast_methods = CAST_RANGED | CAST_USE
|
||||
var/atom/movable/copied = null
|
||||
var/mob/living/simple_animal/illusion/illusion = null
|
||||
var/mob/living/simple_mob/illusion/illusion = null
|
||||
|
||||
/obj/item/weapon/spell/illusion/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /atom/movable))
|
||||
@@ -33,17 +33,13 @@
|
||||
if(pay_energy(500))
|
||||
illusion = new(T)
|
||||
illusion.copy_appearance(copied)
|
||||
if(ishuman(copied))
|
||||
var/mob/living/carbon/human/H = copied
|
||||
// This is to try to have the illusion move at the same rate the real mob world.
|
||||
illusion.step_delay = max(H.movement_delay() + 4, 3)
|
||||
user << "<span class='notice'>An illusion of \the [copied] is made on \the [T].</span>"
|
||||
user << 'sound/effects/pop.ogg'
|
||||
return 1
|
||||
else
|
||||
if(pay_energy(100))
|
||||
spawn(1)
|
||||
illusion.walk_loop(T)
|
||||
var/datum/ai_holder/AI = illusion.ai_holder
|
||||
AI.give_destination(T)
|
||||
|
||||
/obj/item/weapon/spell/illusion/on_use_cast(mob/user)
|
||||
if(illusion)
|
||||
@@ -76,116 +72,3 @@
|
||||
temp_image.transform = M
|
||||
// temp_image.pixel_y = 8
|
||||
src.overlays.Add(temp_image)
|
||||
|
||||
|
||||
/mob/living/simple_animal/illusion
|
||||
name = "illusion" // gets overwritten
|
||||
desc = "If you can read me, the game broke. Please report this to a coder."
|
||||
resistance = 1000 // holograms are tough
|
||||
wander = 0
|
||||
response_help = "pushes a hand through"
|
||||
response_disarm = "tried to disarm"
|
||||
response_harm = "tried to punch"
|
||||
var/atom/movable/copying = null
|
||||
universal_speak = 1
|
||||
var/realistic = 0
|
||||
var/list/path = list() //Used for AStar pathfinding.
|
||||
var/walking = 0
|
||||
var/step_delay = 10
|
||||
|
||||
/mob/living/simple_animal/illusion/update_icon() // We don't want the appearance changing AT ALL unless by copy_appearance().
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/illusion/proc/copy_appearance(var/atom/movable/thing_to_copy)
|
||||
if(!thing_to_copy)
|
||||
return 0
|
||||
name = thing_to_copy.name
|
||||
desc = thing_to_copy.desc
|
||||
gender = thing_to_copy.gender
|
||||
appearance = thing_to_copy.appearance
|
||||
copying = thing_to_copy
|
||||
return 1
|
||||
|
||||
// We use special movement code for illusions, because BYOND's default pathfinding will use diagonal movement if it results
|
||||
// in the shortest path. As players are incapable of moving in diagonals, we must do this or else illusions will not be convincing.
|
||||
/mob/living/simple_animal/illusion/proc/calculate_path(var/turf/targeted_loc)
|
||||
if(!path.len || !path)
|
||||
spawn(0)
|
||||
path = AStar(loc, targeted_loc, /turf/proc/CardinalTurfs, /turf/proc/Distance, 0, 10, id = null)
|
||||
if(!path)
|
||||
path = list()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/illusion/proc/walk_path(var/turf/targeted_loc)
|
||||
if(path && path.len)
|
||||
step_to(src, path[1])
|
||||
path -= path[1]
|
||||
return
|
||||
else
|
||||
if(targeted_loc)
|
||||
calculate_path(targeted_loc)
|
||||
|
||||
/mob/living/simple_animal/illusion/proc/walk_loop(var/turf/targeted_loc)
|
||||
if(walking) //Already busy moving somewhere else.
|
||||
return 0
|
||||
walking = 1
|
||||
calculate_path(targeted_loc)
|
||||
if(!targeted_loc)
|
||||
walking = 0
|
||||
return 0
|
||||
if(path.len == 0)
|
||||
calculate_path(targeted_loc)
|
||||
while(loc != targeted_loc)
|
||||
walk_path(targeted_loc)
|
||||
sleep(step_delay)
|
||||
walking = 0
|
||||
return 1
|
||||
|
||||
// Because we can't perfectly duplicate some examine() output, we directly examine the AM it is copying. It's messy but
|
||||
// this is to prevent easy checks from the opposing force.
|
||||
/mob/living/simple_animal/illusion/examine(mob/user)
|
||||
if(copying)
|
||||
copying.examine(user)
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/illusion/bullet_act(var/obj/item/projectile/P)
|
||||
if(!P)
|
||||
return
|
||||
|
||||
if(realistic)
|
||||
return ..()
|
||||
|
||||
return PROJECTILE_FORCE_MISS
|
||||
|
||||
/mob/living/simple_animal/illusion/attack_hand(mob/living/carbon/human/M)
|
||||
if(!realistic)
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='warning'>[M]'s hand goes through \the [src]!</span>")
|
||||
return
|
||||
else
|
||||
switch(M.a_intent)
|
||||
|
||||
if(I_HELP)
|
||||
var/datum/gender/T = gender_datums[src.get_visible_gender()]
|
||||
M.visible_message("<span class='notice'>[M] hugs [src] to make [T.him] feel better!</span>", \
|
||||
"<span class='notice'>You hug [src] to make [T.him] feel better!</span>") // slightly redundant as at the moment most mobs still use the normal gender var, but it works and future-proofs it
|
||||
playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
|
||||
if(I_DISARM)
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] attempted to disarm [src]!</span>")
|
||||
M.do_attack_animation(src)
|
||||
|
||||
if(I_GRAB)
|
||||
..()
|
||||
|
||||
if(I_HURT)
|
||||
adjustBruteLoss(harm_intent_damage)
|
||||
M.visible_message("<font color='red'>[M] [response_harm] \the [src]</font>")
|
||||
M.do_attack_animation(src)
|
||||
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/illusion/ex_act()
|
||||
return
|
||||
|
||||
@@ -15,11 +15,13 @@
|
||||
|
||||
/obj/item/weapon/spell/modifier/on_melee_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /mob/living))
|
||||
on_add_modifier(hit_atom)
|
||||
return on_add_modifier(hit_atom)
|
||||
return FALSE
|
||||
|
||||
/obj/item/weapon/spell/modifier/on_ranged_cast(atom/hit_atom, mob/user)
|
||||
if(istype(hit_atom, /mob/living))
|
||||
on_add_modifier(hit_atom)
|
||||
return on_add_modifier(hit_atom)
|
||||
return FALSE
|
||||
|
||||
|
||||
/obj/item/weapon/spell/modifier/proc/on_add_modifier(var/mob/living/L)
|
||||
@@ -32,6 +34,7 @@
|
||||
MT.spell_power = calculate_spell_power(1)
|
||||
log_and_message_admins("has casted [src] on [L].")
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
// Technomancer specific subtype which keeps track of spell power and gets targeted specificially by Dispel.
|
||||
/datum/modifier/technomancer
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
this point.</span>"
|
||||
return 0
|
||||
user << "<span class='notice'>You stab \the [L] with a hidden integrated hypo, attempting to bring them back...</span>"
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SM = L
|
||||
if(istype(L, /mob/living/simple_mob))
|
||||
var/mob/living/simple_mob/SM = L
|
||||
SM.health = SM.getMaxHealth() / 3
|
||||
SM.stat = CONSCIOUS
|
||||
dead_mob_list -= SM
|
||||
living_mob_list += SM
|
||||
SM.icon_state = SM.icon_living
|
||||
SM.update_icon()
|
||||
adjust_instability(15)
|
||||
else if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
icon_state = "radiance"
|
||||
cast_methods = CAST_RANGED | CAST_THROW
|
||||
aspect = ASPECT_EMP
|
||||
spawner_type = /obj/effect/temporary_effect/pulsar
|
||||
spawner_type = /obj/effect/temporary_effect/pulse/pulsar
|
||||
|
||||
/obj/item/weapon/spell/spawner/pulsar/New()
|
||||
..()
|
||||
@@ -25,7 +25,29 @@
|
||||
/obj/item/weapon/spell/spawner/pulsar/on_throw_cast(atom/hit_atom, mob/user)
|
||||
empulse(hit_atom, 1, 1, 1, 1, log=1)
|
||||
|
||||
/obj/effect/temporary_effect/pulsar
|
||||
// Does something every so often. Deletes itself when pulses_remaining hits zero.
|
||||
/obj/effect/temporary_effect/pulse
|
||||
var/pulses_remaining = 3
|
||||
var/pulse_delay = 2 SECONDS
|
||||
|
||||
/obj/effect/temporary_effect/pulse/initialize()
|
||||
spawn(0)
|
||||
pulse_loop()
|
||||
return ..()
|
||||
|
||||
/obj/effect/temporary_effect/pulse/proc/pulse_loop()
|
||||
while(pulses_remaining)
|
||||
sleep(pulse_delay)
|
||||
on_pulse()
|
||||
pulses_remaining--
|
||||
qdel(src)
|
||||
|
||||
// Override for specific effects.
|
||||
/obj/effect/temporary_effect/pulse/proc/on_pulse()
|
||||
|
||||
|
||||
|
||||
/obj/effect/temporary_effect/pulse/pulsar
|
||||
name = "pulsar"
|
||||
desc = "Not a real pulsar, but still emits loads of EMP."
|
||||
icon_state = "shield2"
|
||||
@@ -33,17 +55,14 @@
|
||||
light_range = 4
|
||||
light_power = 5
|
||||
light_color = "#2ECCFA"
|
||||
var/pulses_remaining = 3
|
||||
pulses_remaining = 3
|
||||
|
||||
/obj/effect/temporary_effect/pulse/pulsar/on_pulse()
|
||||
empulse(src, 1, 1, 2, 2, log = 1)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/obj/effect/temporary_effect/pulsar/New()
|
||||
..()
|
||||
spawn(0)
|
||||
pulse_loop()
|
||||
|
||||
/obj/effect/temporary_effect/pulsar/proc/pulse_loop()
|
||||
while(pulses_remaining)
|
||||
sleep(2 SECONDS)
|
||||
empulse(src, 1, 1, 2, 2, log = 1)
|
||||
pulses_remaining--
|
||||
qdel(src)
|
||||
|
||||
|
||||
@@ -16,34 +16,32 @@
|
||||
desc = "Chitter chitter."
|
||||
summoned_mob_type = null
|
||||
summon_options = list(
|
||||
"Mouse" = /mob/living/simple_animal/mouse,
|
||||
"Lizard" = /mob/living/simple_animal/lizard,
|
||||
"Chicken" = /mob/living/simple_animal/chicken,
|
||||
"Chick" = /mob/living/simple_animal/chick,
|
||||
"Crab" = /mob/living/simple_animal/crab,
|
||||
"Parrot" = /mob/living/simple_animal/parrot,
|
||||
"Goat" = /mob/living/simple_animal/retaliate/goat,
|
||||
"Cat" = /mob/living/simple_animal/cat,
|
||||
"Kitten" = /mob/living/simple_animal/cat/kitten,
|
||||
"Corgi" = /mob/living/simple_animal/corgi,
|
||||
"Corgi Pup" = /mob/living/simple_animal/corgi/puppy,
|
||||
"BAT" = /mob/living/simple_animal/hostile/scarybat,
|
||||
"SPIDER" = /mob/living/simple_animal/hostile/giant_spider,
|
||||
"SPIDER HUNTER" = /mob/living/simple_animal/hostile/giant_spider/hunter,
|
||||
"SPIDER NURSE" = /mob/living/simple_animal/hostile/giant_spider/nurse,
|
||||
"CARP" = /mob/living/simple_animal/hostile/carp,
|
||||
"BEAR" = /mob/living/simple_animal/hostile/bear
|
||||
) // Vorestation edits to add vore versions.
|
||||
"Mouse" = /mob/living/simple_mob/animal/passive/mouse,
|
||||
"Lizard" = /mob/living/simple_mob/animal/passive/lizard,
|
||||
"Chicken" = /mob/living/simple_mob/animal/passive/chicken,
|
||||
"Chick" = /mob/living/simple_mob/animal/passive/chick,
|
||||
"Crab" = /mob/living/simple_mob/animal/passive/crab,
|
||||
"Parrot" = /mob/living/simple_mob/animal/passive/bird/parrot,
|
||||
"Goat" = /mob/living/simple_mob/animal/goat,
|
||||
"Cat" = /mob/living/simple_mob/animal/passive/cat,
|
||||
"Kitten" = /mob/living/simple_mob/animal/passive/cat/kitten,
|
||||
"Corgi" = /mob/living/simple_mob/animal/passive/dog/corgi,
|
||||
"Corgi Pup" = /mob/living/simple_mob/animal/passive/dog/corgi/puppy,
|
||||
"BAT" = /mob/living/simple_mob/animal/space/bats,
|
||||
"SPIDER" = /mob/living/simple_mob/animal/giant_spider,
|
||||
"SPIDER HUNTER" = /mob/living/simple_mob/animal/giant_spider/hunter,
|
||||
"SPIDER NURSE" = /mob/living/simple_mob/animal/giant_spider/nurse,
|
||||
"CARP" = /mob/living/simple_mob/animal/space/carp,
|
||||
"BEAR" = /mob/living/simple_mob/animal/space/bear
|
||||
)
|
||||
cooldown = 30
|
||||
instability_cost = 10
|
||||
energy_cost = 1000
|
||||
|
||||
/obj/item/weapon/spell/summon/summon_creature/on_summon(var/mob/living/simple_animal/summoned)
|
||||
/obj/item/weapon/spell/summon/summon_creature/on_summon(var/mob/living/simple_mob/summoned)
|
||||
if(check_for_scepter())
|
||||
// summoned.faction = "technomancer"
|
||||
if(istype(summoned, /mob/living/simple_animal/hostile))
|
||||
var/mob/living/simple_animal/SA = summoned
|
||||
SA.friends.Add(owner)
|
||||
summoned.friends += owner
|
||||
|
||||
// Makes their new pal big and strong, if they have spell power.
|
||||
summoned.maxHealth = calculate_spell_power(summoned.maxHealth)
|
||||
@@ -51,15 +49,12 @@
|
||||
summoned.melee_damage_lower = calculate_spell_power(summoned.melee_damage_lower)
|
||||
summoned.melee_damage_upper = calculate_spell_power(summoned.melee_damage_upper)
|
||||
// This makes the summon slower, so the crew has a chance to flee from massive monsters.
|
||||
summoned.move_to_delay = calculate_spell_power(round(summoned.move_to_delay))
|
||||
summoned.movement_cooldown = calculate_spell_power(round(summoned.movement_cooldown))
|
||||
|
||||
var/new_size = calculate_spell_power(1)
|
||||
if(new_size != 1)
|
||||
var/matrix/M = matrix()
|
||||
M.Scale(new_size)
|
||||
M.Translate(0, 16*(new_size-1))
|
||||
summoned.transform = M
|
||||
adjust_scale(new_size)
|
||||
|
||||
|
||||
// Now we hurt their new pal, because being forcefully abducted by teleportation can't be healthy.
|
||||
summoned.health = round(summoned.getMaxHealth() * 0.7)
|
||||
summoned.adjustBruteLoss(summoned.getMaxHealth() * 0.3) // Lose 30% of max health on arrival (but could be healed back up).
|
||||
@@ -1,9 +1,8 @@
|
||||
/datum/technomancer/spell/summon_ward
|
||||
name = "Summon Ward"
|
||||
desc = "Teleports a prefabricated 'ward' drone to the target location, which will alert you and your allies when it sees entities \
|
||||
moving around it, or when it is attacked. They can see for up to five meters."
|
||||
enhancement_desc = "Wards can detect invisibile entities, and are more specific in relaying information about what it sees. \
|
||||
Invisible entities that are spotted by it will be decloaked."
|
||||
name = "Summon Monitor Ward"
|
||||
desc = "Teleports a prefabricated 'ward' drone to the target location, which will alert you when it sees entities \
|
||||
moving around it, or when it is attacked. They can see for up to five meters. It can also see invisible entities, and \
|
||||
forcefully decloak them if close enough."
|
||||
cost = 25
|
||||
obj_path = /obj/item/weapon/spell/summon/summon_ward
|
||||
category = UTILITY_SPELLS
|
||||
@@ -12,116 +11,10 @@
|
||||
name = "summon ward"
|
||||
desc = "Finally, someone you can depend on to watch your back."
|
||||
cast_methods = CAST_RANGED
|
||||
summoned_mob_type = /mob/living/simple_animal/ward
|
||||
summoned_mob_type = /mob/living/simple_mob/mechanical/ward/monitor
|
||||
cooldown = 10
|
||||
instability_cost = 5
|
||||
energy_cost = 500
|
||||
|
||||
/obj/item/weapon/spell/summon/summon_ward/on_summon(var/mob/living/simple_animal/ward/ward)
|
||||
ward.creator = owner
|
||||
if(check_for_scepter())
|
||||
ward.true_sight = 1
|
||||
ward.see_invisible = SEE_INVISIBLE_LEVEL_TWO
|
||||
|
||||
/mob/living/simple_animal/ward
|
||||
name = "ward"
|
||||
desc = "It's a little flying drone that seems to be watching you..."
|
||||
icon = 'icons/mob/critter.dmi'
|
||||
icon_state = "ward"
|
||||
resistance = 5
|
||||
wander = 0
|
||||
response_help = "pets the"
|
||||
response_disarm = "swats away"
|
||||
response_harm = "punches"
|
||||
min_oxy = 0
|
||||
max_oxy = 0
|
||||
min_tox = 0
|
||||
max_tox = 0
|
||||
min_co2 = 0
|
||||
max_co2 = 0
|
||||
min_n2 = 0
|
||||
max_n2 = 0
|
||||
minbodytemp = 0
|
||||
maxbodytemp = 0
|
||||
unsuitable_atoms_damage = 0
|
||||
heat_damage_per_tick = 0
|
||||
cold_damage_per_tick = 0
|
||||
|
||||
var/true_sight = 0 // If true, detects more than what the Technomancer normally can't.
|
||||
var/mob/living/carbon/human/creator = null
|
||||
var/list/seen_mobs = list()
|
||||
|
||||
/mob/living/simple_animal/ward/death()
|
||||
if(creator)
|
||||
creator << "<span class='danger'>Your ward inside [get_area(src)] was killed!</span>"
|
||||
..()
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/ward/proc/expire()
|
||||
if(creator && src)
|
||||
creator << "<span class='warning'>Your ward inside [get_area(src)] expired.</span>"
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/ward/Life()
|
||||
..()
|
||||
detect_mobs()
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/ward/proc/detect_mobs()
|
||||
var/list/things_in_sight = view(5,src)
|
||||
var/list/newly_seen_mobs = list()
|
||||
for(var/mob/living/L in things_in_sight)
|
||||
if(L == creator) // I really wish is_ally() was usable here.
|
||||
continue
|
||||
|
||||
if(istype(L, /mob/living/simple_animal/ward))
|
||||
continue
|
||||
|
||||
if(istype(L, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/SA = L
|
||||
if(creator in SA.friends)
|
||||
continue
|
||||
|
||||
if(!true_sight)
|
||||
var/turf/T = get_turf(L)
|
||||
var/light_amount = T.get_lumcount()
|
||||
if(light_amount <= 0.5)
|
||||
continue // Too dark to see.
|
||||
|
||||
if(L.alpha <= 127)
|
||||
continue // Too transparent, as a mercy to camo lings.
|
||||
|
||||
else
|
||||
L.break_cloak()
|
||||
|
||||
// Warn the Technomancer when it sees a new mob.
|
||||
if(!(L in seen_mobs))
|
||||
seen_mobs.Add(L)
|
||||
newly_seen_mobs.Add(L)
|
||||
if(creator)
|
||||
if(true_sight)
|
||||
creator << "<span class='notice'>Your ward at [get_area(src)] detected [english_list(newly_seen_mobs)].</span>"
|
||||
else
|
||||
creator << "<span class='notice'>Your ward at [get_area(src)] detected something.</span>"
|
||||
|
||||
// Now get rid of old mobs that left vision.
|
||||
for(var/mob/living/L in seen_mobs)
|
||||
if(!(L in things_in_sight))
|
||||
seen_mobs.Remove(L)
|
||||
|
||||
|
||||
/mob/living/simple_animal/ward/update_icon()
|
||||
if(seen_mobs.len)
|
||||
icon_state = "ward_spotted"
|
||||
set_light(3, 3, l_color = "FF0000")
|
||||
else
|
||||
icon_state = "ward"
|
||||
set_light(3, 3, l_color = "00FF00")
|
||||
if(true_sight)
|
||||
overlays.Cut()
|
||||
var/image/I = image('icons/mob/critter.dmi',"ward_truesight")
|
||||
overlays.Add(I)
|
||||
|
||||
/mob/living/simple_animal/ward/invisible_detect
|
||||
true_sight = 1
|
||||
see_invisible = SEE_INVISIBLE_LEVEL_TWO
|
||||
/obj/item/weapon/spell/summon/summon_ward/on_summon(var/mob/living/simple_mob/mechanical/ward/monitor/my_ward)
|
||||
my_ward.owner = owner
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//Moved from southern_cross_jobs.vr to fix a runtime
|
||||
var/const/access_explorer = 43
|
||||
/datum/access/explorer
|
||||
id = access_explorer
|
||||
desc = "Explorer"
|
||||
region = ACCESS_REGION_GENERAL
|
||||
|
||||
var/const/access_pilot = 67
|
||||
/datum/access/pilot
|
||||
id = access_pilot
|
||||
desc = "Pilot"
|
||||
region = ACCESS_REGION_SUPPLY
|
||||
@@ -18,7 +18,7 @@
|
||||
access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks)//VOREStation Edit
|
||||
minimal_access = list(access_security, access_eva, access_sec_doors, access_brig, access_armory,
|
||||
access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers,
|
||||
access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting,
|
||||
access_research, access_engine, access_mining, access_construction, access_mailsorting,
|
||||
access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_external_airlocks)//VOREStation Edit
|
||||
minimum_character_age = 25
|
||||
minimal_player_age = 14
|
||||
|
||||
@@ -73,7 +73,7 @@ var/list/medical_positions = list(
|
||||
"Geneticist",
|
||||
"Psychiatrist",
|
||||
"Chemist",
|
||||
"Search and Rescue", // VOREStation Edit - Moved SAR from planetary -> medical
|
||||
"Field Medic", // VOREStation Edit - Moved SAR from planetary -> medical
|
||||
"Paramedic"
|
||||
)
|
||||
|
||||
|
||||
@@ -49,10 +49,9 @@
|
||||
if(occupant)
|
||||
to_chat(user, "<span class='notice'>\The [src] is already occupied!</span>")
|
||||
return
|
||||
for(var/mob/living/simple_animal/slime/M in range(1, H.affecting))
|
||||
if(M.victim == H.affecting)
|
||||
to_chat(user, "<span class='danger'>[H.affecting.name] has a slime attached to them, deal with that first.</span>")
|
||||
return
|
||||
if(H.affecting.has_buckled_mobs())
|
||||
to_chat(user, span("warning", "\The [H.affecting] has other entities attached to it. Remove them first."))
|
||||
return
|
||||
var/mob/M = H.affecting
|
||||
if(M.abiotic())
|
||||
to_chat(user, "<span class='notice'>Subject cannot have abiotic items on.</span>")
|
||||
@@ -86,10 +85,9 @@
|
||||
if(O.abiotic())
|
||||
to_chat(user, "<span class='notice'>Subject cannot have abiotic items on.</span>")
|
||||
return 0
|
||||
for(var/mob/living/simple_animal/slime/M in range(1, O))
|
||||
if(M.victim == O)
|
||||
to_chat(user, "<span class='danger'>[O] has a slime attached to them, deal with that first.</span>")
|
||||
return 0
|
||||
if(O.has_buckled_mobs())
|
||||
to_chat(user, span("warning", "\The [O] has other entities attached to it. Remove them first."))
|
||||
return
|
||||
|
||||
if(O == user)
|
||||
visible_message("[user] climbs into \the [src].")
|
||||
|
||||
@@ -287,7 +287,7 @@
|
||||
|
||||
/obj/machinery/organ_printer/robot/dismantle()
|
||||
if(stored_matter >= matter_amount_per_sheet)
|
||||
new /obj/item/stack/material/steel(get_turf(src), Floor(stored_matter/matter_amount_per_sheet))
|
||||
new /obj/item/stack/material/steel(get_turf(src), FLOOR(stored_matter/matter_amount_per_sheet, 1))
|
||||
return ..()
|
||||
|
||||
/obj/machinery/organ_printer/robot/print_organ(var/choice)
|
||||
@@ -305,7 +305,7 @@
|
||||
return
|
||||
var/obj/item/stack/S = W
|
||||
var/space_left = max_stored_matter - stored_matter
|
||||
var/sheets_to_take = min(S.amount, Floor(space_left/matter_amount_per_sheet))
|
||||
var/sheets_to_take = min(S.amount, FLOOR(space_left/matter_amount_per_sheet, 1))
|
||||
if(sheets_to_take <= 0)
|
||||
to_chat(user, "<span class='warning'>\The [src] is too full.</span>")
|
||||
return
|
||||
|
||||
@@ -143,7 +143,7 @@
|
||||
|
||||
/obj/machinery/camera/attack_generic(mob/user as mob)
|
||||
if(isanimal(user))
|
||||
var/mob/living/simple_animal/S = user
|
||||
var/mob/living/simple_mob/S = user
|
||||
set_status(0)
|
||||
S.do_attack_animation(src)
|
||||
S.setClickCooldown(user.get_attack_speed())
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
occupant.adjustCloneLoss(-2 * heal_rate)
|
||||
|
||||
//Premature clones may have brain damage.
|
||||
occupant.adjustBrainLoss(-(ceil(0.5*heal_rate)))
|
||||
occupant.adjustBrainLoss(-(CEILING(0.5*heal_rate, 1)))
|
||||
|
||||
//So clones don't die of oxyloss in a running pod.
|
||||
if(occupant.reagents.get_reagent_amount("inaprovaline") < 30)
|
||||
|
||||
@@ -201,7 +201,7 @@ var/prison_shuttle_timeleft = 0
|
||||
for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area...
|
||||
bug.gib()
|
||||
|
||||
for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug...
|
||||
for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug...
|
||||
pest.gib()
|
||||
|
||||
start_location.move_contents_to(end_location)
|
||||
|
||||
@@ -81,7 +81,7 @@ var/specops_shuttle_timeleft = 0
|
||||
for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area...
|
||||
bug.gib()
|
||||
|
||||
for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug...
|
||||
for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug...
|
||||
pest.gib()
|
||||
|
||||
start_location.move_contents_to(end_location)
|
||||
|
||||
@@ -166,7 +166,7 @@ var/syndicate_elite_shuttle_timeleft = 0
|
||||
for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area...
|
||||
bug.gib()
|
||||
|
||||
for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug...
|
||||
for(var/mob/living/simple_mob/pest in end_location) // And for the other kind of bug...
|
||||
pest.gib()
|
||||
|
||||
start_location.move_contents_to(end_location)
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
for(var/obj/effect/decal/cleanable/blood/B in linkedholodeck)
|
||||
qdel(B)
|
||||
|
||||
for(var/mob/living/simple_animal/hostile/carp/C in linkedholodeck)
|
||||
for(var/mob/living/simple_mob/animal/space/carp/C in linkedholodeck)
|
||||
qdel(C)
|
||||
|
||||
holographic_items = A.copy_contents_to(linkedholodeck , 1)
|
||||
@@ -228,7 +228,7 @@
|
||||
T.temperature = 5000
|
||||
T.hotspot_expose(50000,50000,1)
|
||||
if(L.name=="Holocarp Spawn")
|
||||
new /mob/living/simple_animal/hostile/carp(L.loc)
|
||||
new /mob/living/simple_mob/animal/space/carp(L.loc)
|
||||
|
||||
|
||||
/datum/file/program/holodeck/proc/emergencyShutdown()
|
||||
|
||||
+11
-12
@@ -199,14 +199,13 @@
|
||||
return
|
||||
if(occupant)
|
||||
to_chat(user,"<span class='warning'>\The [src] is already occupied by [occupant].</span>")
|
||||
for(var/mob/living/simple_animal/slime/M in range(1,grab.affecting))
|
||||
if(M.victim == grab.affecting)
|
||||
to_chat(usr, "[grab.affecting.name] will not fit into the cryo because they have a slime latched onto their head.")
|
||||
return
|
||||
if(grab.affecting.has_buckled_mobs())
|
||||
to_chat(user, span("warning", "\The [grab.affecting] has other entities attached to it. Remove them first."))
|
||||
return
|
||||
var/mob/M = grab.affecting
|
||||
qdel(grab)
|
||||
put_mob(M)
|
||||
|
||||
|
||||
return
|
||||
|
||||
/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to put people into cryo without external assistance
|
||||
@@ -349,14 +348,14 @@
|
||||
set name = "Move Inside"
|
||||
set category = "Object"
|
||||
set src in oview(1)
|
||||
for(var/mob/living/simple_animal/slime/M in range(1,usr))
|
||||
if(M.victim == usr)
|
||||
to_chat(usr, "You're too busy getting your life sucked out of you.")
|
||||
if(isliving(usr))
|
||||
var/mob/living/L = usr
|
||||
if(L.has_buckled_mobs())
|
||||
to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first."))
|
||||
return
|
||||
if(usr.stat != 0)
|
||||
return
|
||||
put_mob(usr)
|
||||
return
|
||||
if(L.stat != CONSCIOUS)
|
||||
return
|
||||
put_mob(L)
|
||||
|
||||
/atom/proc/return_air_for_internal_lifeform(var/mob/living/lifeform)
|
||||
return return_air()
|
||||
|
||||
@@ -391,6 +391,13 @@
|
||||
var/obj/belly/B = belly
|
||||
for(var/mob/living/sub_L in B)
|
||||
despawn_occupant(sub_L)
|
||||
for(var/obj/item/W in B)
|
||||
W.forceMove(src)
|
||||
if(W.contents.len)
|
||||
for(var/obj/item/O in W.contents)
|
||||
if(istype(O,/obj/item/weapon/storage/internal))
|
||||
continue
|
||||
O.forceMove(src)
|
||||
if(ishuman(to_despawn))
|
||||
var/mob/living/carbon/human/H = to_despawn
|
||||
if(H.nif)
|
||||
@@ -567,9 +574,10 @@
|
||||
to_chat(usr, "<span class='notice'><B>\The [src] is in use.</B></span>")
|
||||
return
|
||||
|
||||
for(var/mob/living/simple_animal/slime/M in range(1,usr))
|
||||
if(M.victim == usr)
|
||||
to_chat(usr, "You're too busy getting your life sucked out of you.")
|
||||
if(isliving(usr))
|
||||
var/mob/living/L = usr
|
||||
if(L.has_buckled_mobs())
|
||||
to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first."))
|
||||
return
|
||||
|
||||
visible_message("[usr] [on_enter_visible_message] [src].", 3)
|
||||
|
||||
@@ -46,11 +46,12 @@
|
||||
var/bolt_up_sound = 'sound/machines/boltsup.ogg'
|
||||
var/bolt_down_sound = 'sound/machines/boltsdown.ogg'
|
||||
|
||||
/obj/machinery/door/airlock/attack_generic(var/mob/user, var/damage)
|
||||
/obj/machinery/door/airlock/attack_generic(var/mob/living/user, var/damage)
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
if(damage >= 10)
|
||||
if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD)
|
||||
if(src.locked || src.welded)
|
||||
visible_message("<span class='danger'>\The [user] begins breaking into \the [src] internals!</span>")
|
||||
user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything.
|
||||
if(do_after(user,10 SECONDS,src))
|
||||
src.locked = 0
|
||||
src.welded = 0
|
||||
@@ -58,6 +59,7 @@
|
||||
open(1)
|
||||
if(prob(25))
|
||||
src.shock(user, 100)
|
||||
user.set_AI_busy(FALSE)
|
||||
else if(src.density)
|
||||
visible_message("<span class='danger'>\The [user] forces \the [src] open!</span>")
|
||||
open(1)
|
||||
@@ -508,9 +510,6 @@ About the new airlock wires panel:
|
||||
return
|
||||
..(user)
|
||||
|
||||
/obj/machinery/door/airlock/bumpopen(mob/living/simple_animal/user as mob)
|
||||
..(user)
|
||||
|
||||
/obj/machinery/door/airlock/proc/isElectrified()
|
||||
if(src.electrified_until != 0)
|
||||
return 1
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
return
|
||||
|
||||
else if(istype(C, /obj/item/stack/material) && C.get_material_name() == "plasteel") // Repairing.
|
||||
var/amt = Ceiling((maxhealth - health)/150)
|
||||
var/amt = CEILING((maxhealth - health)/150, 1)
|
||||
if(!amt)
|
||||
to_chat(user, "<span class='notice'>\The [src] is already fully repaired.</span>")
|
||||
return
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
|
||||
/obj/machinery/door/attack_generic(var/mob/user, var/damage)
|
||||
if(isanimal(user))
|
||||
var/mob/living/simple_animal/S = user
|
||||
if(damage >= 10)
|
||||
var/mob/living/simple_mob/S = user
|
||||
if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD)
|
||||
visible_message("<span class='danger'>\The [user] smashes into the [src]!</span>")
|
||||
playsound(src, S.attack_sound, 75, 1)
|
||||
take_damage(damage)
|
||||
|
||||
@@ -216,22 +216,26 @@
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/machinery/door/firedoor/attack_generic(var/mob/user, var/damage)
|
||||
/obj/machinery/door/firedoor/attack_generic(var/mob/living/user, var/damage)
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
if(damage >= 10)
|
||||
if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD)
|
||||
var/time_to_force = (2 + (2 * blocked)) * 5
|
||||
if(src.density)
|
||||
visible_message("<span class='danger'>\The [user] starts forcing \the [src] open!</span>")
|
||||
user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything.
|
||||
if(do_after(user, time_to_force, src))
|
||||
visible_message("<span class='danger'>\The [user] forces \the [src] open!</span>")
|
||||
src.blocked = 0
|
||||
open(1)
|
||||
user.set_AI_busy(FALSE)
|
||||
else
|
||||
time_to_force = (time_to_force / 2)
|
||||
visible_message("<span class='danger'>\The [user] starts forcing \the [src] closed!</span>")
|
||||
user.set_AI_busy(TRUE) // If the mob doesn't have an AI attached, this won't do anything.
|
||||
if(do_after(user, time_to_force, src))
|
||||
visible_message("<span class='danger'>\The [user] forces \the [src] closed!</span>")
|
||||
close(1)
|
||||
user.set_AI_busy(FALSE)
|
||||
else
|
||||
visible_message("<span class='notice'>\The [user] strains fruitlessly to force \the [src] [density ? "open" : "closed"].</span>")
|
||||
return
|
||||
|
||||
@@ -179,29 +179,29 @@
|
||||
|
||||
/obj/machinery/partslathe/proc/canBuild(var/datum/category_item/partslathe/D)
|
||||
for(var/M in D.resources)
|
||||
if(materials[M] < Ceiling(D.resources[M] * mat_efficiency))
|
||||
if(materials[M] < CEILING((D.resources[M] * mat_efficiency), 1))
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/obj/machinery/partslathe/proc/getLackingMaterials(var/datum/category_item/partslathe/D)
|
||||
var/ret = ""
|
||||
for(var/M in D.resources)
|
||||
if(materials[M] < Ceiling(D.resources[M] * mat_efficiency))
|
||||
if(materials[M] < CEILING((D.resources[M] * mat_efficiency), 1))
|
||||
if(ret != "")
|
||||
ret += ", "
|
||||
ret += "[Ceiling(D.resources[M] * mat_efficiency) - materials[M]] [M]"
|
||||
ret += "[CEILING((D.resources[M] * mat_efficiency), 1) - materials[M]] [M]"
|
||||
return ret
|
||||
|
||||
/obj/machinery/partslathe/proc/build(var/datum/category_item/partslathe/D)
|
||||
for(var/M in D.resources)
|
||||
materials[M] = max(0, materials[M] - Ceiling(D.resources[M] * mat_efficiency))
|
||||
materials[M] = max(0, materials[M] - CEILING((D.resources[M] * mat_efficiency), 1))
|
||||
var/obj/new_item = D.build(loc);
|
||||
if(new_item)
|
||||
new_item.loc = loc
|
||||
if(mat_efficiency < 1) // No matter out of nowhere
|
||||
if(new_item.matter && new_item.matter.len > 0)
|
||||
for(var/i in new_item.matter)
|
||||
new_item.matter[i] = Ceiling(new_item.matter[i] * mat_efficiency)
|
||||
new_item.matter[i] = CEILING((new_item.matter[i] * mat_efficiency), 1)
|
||||
|
||||
// 0 amount = 0 means ejecting a full stack; -1 means eject everything
|
||||
/obj/machinery/partslathe/proc/eject_materials(var/material, var/amount)
|
||||
|
||||
@@ -406,16 +406,16 @@ var/list/turret_icons
|
||||
attacked = 0
|
||||
..()
|
||||
|
||||
/obj/machinery/porta_turret/attack_generic(mob/user as mob, var/damage)
|
||||
if(isanimal(user))
|
||||
var/mob/living/simple_animal/S = user
|
||||
if(damage >= 10)
|
||||
/obj/machinery/porta_turret/attack_generic(mob/living/L, damage)
|
||||
if(isanimal(L))
|
||||
var/mob/living/simple_mob/S = L
|
||||
if(damage >= STRUCTURE_MIN_DAMAGE_THRESHOLD)
|
||||
var/incoming_damage = round(damage - (damage / 5)) //Turrets are slightly armored, assumedly.
|
||||
visible_message("<span class='danger'>\The [S] [pick(S.attacktext)] \the [src]!</span>")
|
||||
take_damage(incoming_damage)
|
||||
S.do_attack_animation(src)
|
||||
return 1
|
||||
visible_message("<span class='notice'>\The [user] bonks \the [src]'s casing!</span>")
|
||||
visible_message("<span class='notice'>\The [L] bonks \the [src]'s casing!</span>")
|
||||
return ..()
|
||||
|
||||
/obj/machinery/porta_turret/emag_act(var/remaining_charges, var/mob/user)
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
|
||||
if("cellremove")
|
||||
if(panel_open && cell && !usr.get_active_hand())
|
||||
usr.visible_message("<span class='notice'>\The usr] removes \the [cell] from \the [src].</span>", "<span class='notice'>You remove \the [cell] from \the [src].</span>")
|
||||
usr.visible_message("<span class='notice'>\The [usr] removes \the [cell] from \the [src].</span>", "<span class='notice'>You remove \the [cell] from \the [src].</span>")
|
||||
cell.update_icon()
|
||||
usr.put_in_hands(cell)
|
||||
cell.add_fingerprint(usr)
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
for(var/obj/machinery/teleport/hub/H in range(1))
|
||||
var/amount = rand(2,5)
|
||||
for(var/i=0;i<amount;i++)
|
||||
new /mob/living/simple_animal/hostile/carp(get_turf(H))
|
||||
new /mob/living/simple_mob/animal/space/carp(get_turf(H))
|
||||
//
|
||||
else
|
||||
for(var/mob/O in hearers(src, null))
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
else
|
||||
state = 5
|
||||
update_icon()
|
||||
playsound(src, 'sound/items/washingmachine.ogg', 50, 1, 1)
|
||||
sleep(200)
|
||||
for(var/atom/A in washing)
|
||||
A.clean_blood()
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
set popup_menu = 0
|
||||
if(usr!=src.occupant)
|
||||
return
|
||||
playsound(src, 'sound/mecha/duranddefencemode.ogg', 50, 1)
|
||||
defence = !defence
|
||||
if(defence)
|
||||
deflect_chance = defence_deflect
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
wreckage = /obj/effect/decal/mecha_wreckage/gygax/dark
|
||||
max_equip = 4
|
||||
step_energy_drain = 5
|
||||
mech_faction = MECH_FACTION_SYNDI
|
||||
|
||||
max_hull_equip = 1
|
||||
max_weapon_equip = 2
|
||||
@@ -84,6 +85,7 @@
|
||||
step_energy_drain = step_energy_drain*overload_coeff
|
||||
src.occupant_message("<font color='red'>You enable leg actuators overload.</font>")
|
||||
src.log_message("Toggled leg actuators overload.")
|
||||
playsound(src, 'sound/mecha/mechanical_toggle.ogg', 50, 1)
|
||||
return
|
||||
|
||||
/obj/mecha/combat/gygax/dyndomove(direction)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
internal_damage_threshold = 25
|
||||
force = 45
|
||||
max_equip = 4
|
||||
mech_faction = MECH_FACTION_NT
|
||||
|
||||
max_hull_equip = 3
|
||||
max_weapon_equip = 3
|
||||
@@ -42,6 +43,7 @@
|
||||
force = 55
|
||||
max_equip = 5
|
||||
|
||||
|
||||
/obj/mecha/combat/marauder/mauler
|
||||
desc = "Heavy-duty, combat exosuit, developed off of the existing Marauder model."
|
||||
name = "Mauler"
|
||||
@@ -49,6 +51,7 @@
|
||||
initial_icon = "mauler"
|
||||
operation_req_access = list(access_syndicate)
|
||||
wreckage = /obj/effect/decal/mecha_wreckage/mauler
|
||||
mech_faction = MECH_FACTION_SYNDI
|
||||
|
||||
/obj/mecha/combat/marauder/New()
|
||||
..()
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
var/required_type = /obj/mecha //may be either a type or a list of allowed types
|
||||
var/equip_type = null //mechaequip2
|
||||
var/allow_duplicate = FALSE
|
||||
var/ready_sound = 'sound/mecha/mech_reload_default.ogg' //Sound to play once the fire delay passed.
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(target=1)
|
||||
sleep(equip_cooldown)
|
||||
set_ready_state(1)
|
||||
if(ready_sound) //Kind of like the kinetic accelerator.
|
||||
playsound(loc, ready_sound, 50, 1, -1)
|
||||
if(target && chassis)
|
||||
return 1
|
||||
return 0
|
||||
@@ -69,10 +72,22 @@
|
||||
src.update_chassis_page()
|
||||
chassis.occupant_message("<font color='red'>The [src] is destroyed!</font>")
|
||||
chassis.log_append_to_last("[src] is destroyed.",1)
|
||||
if(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon))
|
||||
chassis.occupant << sound('sound/mecha/weapdestr.ogg',volume=50)
|
||||
else
|
||||
chassis.occupant << sound('sound/mecha/critdestr.ogg',volume=50)
|
||||
if(istype(src, /obj/item/mecha_parts/mecha_equipment/weapon))//Gun
|
||||
switch(chassis.mech_faction)
|
||||
if(MECH_FACTION_NT)
|
||||
src.chassis.occupant << sound('sound/mecha/weapdestrnano.ogg',volume=70)
|
||||
if(MECH_FACTION_SYNDI)
|
||||
src.chassis.occupant << sound('sound/mecha/weapdestrsyndi.ogg',volume=60)
|
||||
else
|
||||
src.chassis.occupant << sound('sound/mecha/weapdestr.ogg',volume=50)
|
||||
else //Not a gun
|
||||
switch(chassis.mech_faction)
|
||||
if(MECH_FACTION_NT)
|
||||
src.chassis.occupant << sound('sound/mecha/critdestrnano.ogg',volume=70)
|
||||
if(MECH_FACTION_SYNDI)
|
||||
src.chassis.occupant << sound('sound/mecha/critdestrsyndi.ogg',volume=70)
|
||||
else
|
||||
src.chassis.occupant << sound('sound/mecha/critdestr.ogg',volume=50)
|
||||
spawn
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
@@ -40,10 +40,9 @@
|
||||
if(occupant)
|
||||
occupant_message("The sleeper is already occupied")
|
||||
return
|
||||
for(var/mob/living/simple_animal/slime/M in range(1,target))
|
||||
if(M.victim == target)
|
||||
occupant_message("[target] will not fit into the sleeper because they have a slime latched onto their head.")
|
||||
return
|
||||
if(target.has_buckled_mobs())
|
||||
occupant_message(span("warning", "\The [target] has other entities attached to it. Remove them first."))
|
||||
return
|
||||
occupant_message("You start putting [target] into [src].")
|
||||
chassis.visible_message("[chassis] starts putting [target] into the [src].")
|
||||
var/C = chassis.loc
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
desc = "This is an upgraded version of the drill that'll pierce the heavens! (Can be attached to: Combat and Engineering Exosuits)"
|
||||
icon_state = "mecha_diamond_drill"
|
||||
origin_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3)
|
||||
equip_cooldown = 20
|
||||
equip_cooldown = 10
|
||||
force = 15
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill/action(atom/target)
|
||||
@@ -1203,9 +1203,10 @@
|
||||
usr << "<span class='danger'>Kinda hard to climb in while handcuffed don't you think?</span>"
|
||||
return
|
||||
|
||||
for(var/mob/living/simple_animal/slime/M in range(1,usr))
|
||||
if(M.victim == usr)
|
||||
usr << "<span class='danger'>You're too busy getting your life sucked out of you.</span>"
|
||||
if(isliving(usr))
|
||||
var/mob/living/L = usr
|
||||
if(L.has_buckled_mobs())
|
||||
to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first."))
|
||||
return
|
||||
|
||||
//search for a valid passenger compartment
|
||||
@@ -1329,4 +1330,4 @@
|
||||
/obj/item/mecha_parts/mecha_equipment/tool/jetpack/do_after_cooldown()
|
||||
sleep(equip_cooldown)
|
||||
wait = 0
|
||||
return 1
|
||||
return 1
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
#define MELEE 1
|
||||
#define RANGED 2
|
||||
|
||||
|
||||
#define MECH_FACTION_NT "nano"
|
||||
#define MECH_FACTION_SYNDI "syndi"
|
||||
#define MECH_FACTION_NONE "none"
|
||||
|
||||
/obj/mecha
|
||||
name = "Mecha"
|
||||
desc = "Exosuit"
|
||||
@@ -41,6 +46,10 @@
|
||||
var/lights_power = 6
|
||||
var/force = 0
|
||||
|
||||
var/mech_faction = null
|
||||
var/firstactivation = 0 //It's simple. If it's 0, no one entered it yet. Otherwise someone entered it at least once.
|
||||
|
||||
|
||||
//inner atmos
|
||||
var/use_internal_tank = 0
|
||||
var/internal_tank_valve = ONE_ATMOSPHERE
|
||||
@@ -500,7 +509,7 @@
|
||||
internal_damage |= int_dam_flag
|
||||
pr_internal_damage.start()
|
||||
log_append_to_last("Internal damage of type [int_dam_flag].",1)
|
||||
occupant << sound('sound/machines/warning-buzzer.ogg',wait=0)
|
||||
occupant << sound('sound/mecha/internaldmgalarm.ogg',volume=50) //Better sounding.
|
||||
return
|
||||
|
||||
/obj/mecha/proc/clearInternalDamage(int_dam_flag)
|
||||
@@ -1008,6 +1017,7 @@
|
||||
if(network && !(internal_tank.return_air() in network.gases))
|
||||
network.gases += internal_tank.return_air()
|
||||
network.update = 1
|
||||
playsound(src, 'sound/mecha/gasconnected.ogg', 50, 1)
|
||||
log_message("Connected to gas port.")
|
||||
return 1
|
||||
|
||||
@@ -1021,6 +1031,7 @@
|
||||
|
||||
connected_port.connected_device = null
|
||||
connected_port = null
|
||||
playsound(src, 'sound/mecha/gasdisconnected.ogg', 50, 1)
|
||||
src.log_message("Disconnected from gas port.")
|
||||
return 1
|
||||
|
||||
@@ -1078,6 +1089,7 @@
|
||||
else set_light(light_range - lights_power)
|
||||
src.occupant_message("Toggled lights [lights?"on":"off"].")
|
||||
log_message("Toggled lights [lights?"on":"off"].")
|
||||
playsound(src, 'sound/mecha/heavylightswitch.ogg', 50, 1)
|
||||
return
|
||||
|
||||
|
||||
@@ -1142,10 +1154,12 @@
|
||||
to_chat(usr,"<span class='warning'>Access denied</span>")
|
||||
src.log_append_to_last("Permission denied.")
|
||||
return
|
||||
for(var/mob/living/simple_animal/slime/M in range(1,usr))
|
||||
if(M.victim == usr)
|
||||
to_chat(usr,"You're too busy getting your life sucked out of you.")
|
||||
if(isliving(usr))
|
||||
var/mob/living/L = usr
|
||||
if(L.has_buckled_mobs())
|
||||
to_chat(L, span("warning", "You have other entities attached to yourself. Remove them first."))
|
||||
return
|
||||
|
||||
// usr << "You start climbing into [src.name]"
|
||||
|
||||
visible_message("<span class='notice'>\The [usr] starts to climb into [src.name]</span>")
|
||||
@@ -1176,8 +1190,22 @@
|
||||
src.icon_state = src.reset_icon()
|
||||
set_dir(dir_in)
|
||||
playsound(src, 'sound/machines/windowdoor.ogg', 50, 1)
|
||||
if(!hasInternalDamage())
|
||||
src.occupant << sound('sound/mecha/nominal.ogg',volume=50)
|
||||
if(!hasInternalDamage()) //Otherwise it's not nominal!
|
||||
switch(mech_faction)
|
||||
if(MECH_FACTION_NT)//The good guys category
|
||||
if(firstactivation)//First time = long activation sound
|
||||
firstactivation = 1
|
||||
src.occupant << sound('sound/mecha/LongNanoActivation.ogg',volume=50)
|
||||
else
|
||||
src.occupant << sound('sound/mecha/nominalnano.ogg',volume=50)
|
||||
if(MECH_FACTION_SYNDI)//Bad guys
|
||||
if(firstactivation)
|
||||
firstactivation = 1
|
||||
src.occupant << sound('sound/mecha/LongSyndiActivation.ogg',volume=50)
|
||||
else
|
||||
src.occupant << sound('sound/mecha/nominalsyndi.ogg',volume=50)
|
||||
else//Everyone else gets the normal noise
|
||||
src.occupant << sound('sound/mecha/nominal.ogg',volume=50)
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
@@ -99,6 +99,14 @@
|
||||
name = "Dark Gygax wreckage"
|
||||
icon_state = "darkgygax-broken"
|
||||
|
||||
/obj/effect/decal/mecha_wreckage/gygax/adv
|
||||
name = "Advanced Dark Gygax wreckage"
|
||||
icon_state = "darkgygax_adv-broken"
|
||||
|
||||
/obj/effect/decal/mecha_wreckage/gygax/medgax
|
||||
name = "Medgax wreckage"
|
||||
icon_state = "medgax-broken"
|
||||
|
||||
/obj/effect/decal/mecha_wreckage/marauder
|
||||
name = "Marauder wreckage"
|
||||
icon_state = "marauder-broken"
|
||||
@@ -198,6 +206,9 @@
|
||||
parts -= part
|
||||
return
|
||||
|
||||
/obj/effect/decal/mecha_wreckage/odysseus/murdysseus
|
||||
icon_state = "murdysseus-broken"
|
||||
|
||||
/obj/effect/decal/mecha_wreckage/hoverpod
|
||||
name = "Hover pod wreckage"
|
||||
icon_state = "engineering_pod-broken"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user