Merge remote-tracking branch 'citadel/master' into 12742

This commit is contained in:
silicons
2020-07-21 11:25:26 -07:00
1726 changed files with 52105 additions and 121253 deletions
+3 -1
View File
@@ -108,6 +108,8 @@
#define TR_KEEPSE (1<<5) // changelings shouldn't edit the DNA's SE when turning into a monkey
#define TR_DEFAULTMSG (1<<6)
#define TR_KEEPORGANS (1<<8)
#define TR_KEEPREAGENTS (1<<10)
#define TR_KEEPSTUNS (1<<9)
#define CLONER_FRESH_CLONE "fresh"
@@ -184,4 +186,4 @@
#define G_MALE 1
#define G_FEMALE 2
#define G_PLURAL 3
#define G_NEUTER 4
#define G_NEUTER 4
+20 -1
View File
@@ -142,7 +142,26 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
/// The attack is from a parry counterattack.
#define ATTACKCHAIN_PARRY_COUNTERATTACK (1<<0)
// UnarmedAttack() flags
/// Attack is from a parry counterattack
#define UNARMED_ATTACK_PARRY (1<<0)
/// If the thing can reflect light (lasers/energy)
#define RICOCHET_SHINY (1<<0)
/// If the thing can reflect matter (bullets/bomb shrapnel)
#define RICOCHET_HARD (1<<1)
#define RICOCHET_HARD (1<<1)
#define KEEP_TOGETHER_ORIGINAL "keep_together_original"
//setter for KEEP_TOGETHER to allow for multiple sources to set and unset it
#define ADD_KEEP_TOGETHER(x, source)\
if ((x.appearance_flags & KEEP_TOGETHER) && !HAS_TRAIT(x, TRAIT_KEEP_TOGETHER)) ADD_TRAIT(x, TRAIT_KEEP_TOGETHER, KEEP_TOGETHER_ORIGINAL); \
ADD_TRAIT(x, TRAIT_KEEP_TOGETHER, source);\
x.appearance_flags |= KEEP_TOGETHER
#define REMOVE_KEEP_TOGETHER(x, source)\
REMOVE_TRAIT(x, TRAIT_KEEP_TOGETHER, source);\
if(HAS_TRAIT_FROM_ONLY(x, TRAIT_KEEP_TOGETHER, KEEP_TOGETHER_ORIGINAL))\
REMOVE_TRAIT(x, TRAIT_KEEP_TOGETHER, KEEP_TOGETHER_ORIGINAL);\
else if(!HAS_TRAIT(x, TRAIT_KEEP_TOGETHER))\
x.appearance_flags &= ~KEEP_TOGETHER
+1
View File
@@ -52,3 +52,4 @@
#define ORGAN_VITAL (1<<4) //Currently only the brain
#define ORGAN_NO_SPOIL (1<<5) //Do not spoil under any circumstances
#define ORGAN_NO_DISMEMBERMENT (1<<6) //Immune to disembowelment.
#define ORGAN_EDIBLE (1<<5) //is a snack? :D
+2
View File
@@ -74,6 +74,8 @@
#define ADMIN_PUNISHMENT_MAZING "Puzzle"
#define ADMIN_PUNISHMENT_PIE "Cream Pie"
#define ADMIN_PUNISHMENT_CUSTOM_PIE "Custom Cream Pie"
#define ADMIN_PUNISHMENT_PICKLE "Pickle-ify"
#define ADMIN_PUNISHMENT_FRY "Fry"
#define AHELP_ACTIVE 1
#define AHELP_CLOSED 2
+14 -12
View File
@@ -136,7 +136,7 @@
#define TANK_FRAGMENT_SCALE (6.*ONE_ATMOSPHERE) //+1 for each SCALE kPa aboe threshold
#define TANK_MAX_RELEASE_PRESSURE (ONE_ATMOSPHERE*3)
#define TANK_MIN_RELEASE_PRESSURE 0
#define TANK_DEFAULT_RELEASE_PRESSURE 16
#define TANK_DEFAULT_RELEASE_PRESSURE 17
//CANATMOSPASS
#define ATMOS_PASS_YES 1
@@ -270,16 +270,9 @@
T.pixel_x = (PipingLayer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_P_X;\
T.pixel_y = (PipingLayer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_P_Y;
#define THERMAL_ENERGY(gas) (gas.temperature * gas.heat_capacity())
#define QUANTIZE(variable) (round(variable,0.0000001))/*I feel the need to document what happens here. Basically this is used to catch most rounding errors, however it's previous value made it so that
once gases got hot enough, most procedures wouldnt occur due to the fact that the mole counts would get rounded away. Thus, we lowered it a few orders of magnititude */
//prefer this to gas_mixture/total_moles in performance critical areas
#define TOTAL_MOLES(cached_gases, out_var)\
out_var = 0;\
for(var/total_moles_id in cached_gases){\
out_var += cached_gases[total_moles_id];\
}
#ifdef TESTING
GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0))
@@ -288,6 +281,19 @@ GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0))
#define CALCULATE_ADJACENT_TURFS(T) SSadjacent_air.queue[T] = 1
#endif
#define EXTOOLS (world.system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so")
GLOBAL_VAR(atmos_extools_initialized) // this must be an uninitialized (null) one or init_monstermos will be called twice because reasons
#define ATMOS_EXTOOLS_CHECK if(!GLOB.atmos_extools_initialized){\
GLOB.atmos_extools_initialized=TRUE;\
if(fexists(EXTOOLS)){\
var/result = call(EXTOOLS,"init_monstermos")();\
if(result != "ok") {CRASH(result);}\
} else {\
CRASH("[EXTOOLS] does not exist!");\
}\
}
//Unomos - So for whatever reason, garbage collection actually drastically decreases the cost of atmos later in the round. Turning this into a define yields massively improved performance.
#define GAS_GARBAGE_COLLECT(GASGASGAS)\
var/list/CACHE_GAS = GASGASGAS;\
@@ -296,10 +302,6 @@ GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0))
CACHE_GAS -= id;\
}
#define ARCHIVE_TEMPERATURE(gas) gas.temperature_archived = gas.temperature
#define ARCHIVE(gas) gas.temperature_archived = gas.temperature; gas.gas_archive = gas.gases.Copy();
GLOBAL_LIST_INIT(pipe_paint_colors, list(
"amethyst" = rgb(130,43,255), //supplymain
"blue" = rgb(0,0,255),
+4
View File
@@ -0,0 +1,4 @@
// Reaction priorities, higher makes it checked first. Otherwise, it goes based on reaction temperature requirements.
#define CHEMICAL_REACTION_PRIORITY_DEFAULT 100
#define CHEMICAL_REACTION_PRIORITY_SMOKE 1000
+8 -1
View File
@@ -269,11 +269,12 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
//stamina cost defines.
#define STAM_COST_ATTACK_OBJ_MULT 1.2
#define STAM_COST_ATTACK_MOB_MULT 0.8
#define STAM_COST_ATTACK_MOB_MULT 1
#define STAM_COST_BATON_MOB_MULT 1
#define STAM_COST_NO_COMBAT_MULT 1.25
#define STAM_COST_W_CLASS_MULT 1.25
#define STAM_COST_THROW_MULT 2
#define STAM_COST_THROW_MOB 2.5 //multiplied by (mob size + 1)^2.
///Multiplier of the (STAMINA_NEAR_CRIT - user current stamina loss) : (STAMINA_NEAR_CRIT - STAMINA_SOFTCRIT) ratio used in damage penalties when stam soft-critted.
#define STAM_CRIT_ITEM_ATTACK_PENALTY 0.66
@@ -284,6 +285,12 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
/// Added delay when firing guns stam-softcritted. Summed with a hardset CLICK_CD_RANGE delay, similar to STAM_CRIT_DAMAGE_DELAY otherwise.
#define STAM_CRIT_GUN_DELAY 2.75
//stamina recovery defines. Blocked if combat mode is on.
#define STAM_RECOVERY_STAM_CRIT -7.5
#define STAM_RECOVERY_RESTING -6
#define STAM_RECOVERY_NORMAL -3
#define STAM_RECOVERY_LIMB 4 //limbs recover stamina separately from handle_status_effects(), and aren't blocked by combat mode.
/**
* should the current-attack-damage be lower than the item force multiplied by this value,
* a "inefficiently" prefix will be added to the message.
+9
View File
@@ -8,3 +8,12 @@
//flags
#define CONFIG_ENTRY_LOCKED 1 //can't edit
#define CONFIG_ENTRY_HIDDEN 2 //can't see value
// Policy config keys
// MAKE SURE THESE ARE UPPERCASE
/// Displayed to cloned patients
#define POLICYCONFIG_ON_CLONE "ON_CLONE"
/// Displayed to defibbed/revival surgery'd patients before the memory loss time threshold
#define POLICYCONFIG_ON_DEFIB_INTACT "ON_DEFIB_INTACT"
/// Displayed to defibbed/revival surgery'd patients after the memory loss time threshold
#define POLICYCONFIG_ON_DEFIB_LATE "ON_DEFIB_LATE"
+71
View File
@@ -0,0 +1,71 @@
//// COOLDOWN SYSTEMS
/*
* We have 2 cooldown systems: timer cooldowns (divided between stoppable and regular) and world.time cooldowns.
*
* When to use each?
*
* * Adding a commonly-checked cooldown, like on a subsystem to check for processing
* * * Use the world.time ones, as they are cheaper.
*
* * Adding a rarely-used one for special situations, such as giving an uncommon item a cooldown on a target.
* * * Timer cooldown, as adding a new variable on each mob to track the cooldown of said uncommon item is going too far.
*
* * Triggering events at the end of a cooldown.
* * * Timer cooldown, registering to its signal.
*
* * Being able to check how long left for the cooldown to end.
* * * Either world.time or stoppable timer cooldowns, depending on the other factors. Regular timer cooldowns do not support this.
*
* * Being able to stop the timer before it ends.
* * * Either world.time or stoppable timer cooldowns, depending on the other factors. Regular timer cooldowns do not support this.
*/
/*
* Cooldown system based on an datum-level associative lazylist using timers.
*/
//INDEXES
#define COOLDOWN_EMPLOYMENT_CABINET "employment cabinet"
//TIMER COOLDOWN MACROS
#define COMSIG_CD_STOP(cd_index) "cooldown_[cd_index]"
#define COMSIG_CD_RESET(cd_index) "cd_reset_[cd_index]"
#define TIMER_COOLDOWN_START(cd_source, cd_index, cd_time) LAZYSET(cd_source.cooldowns, cd_index, addtimer(CALLBACK(GLOBAL_PROC, /proc/end_cooldown, cd_source, cd_index), cd_time))
#define TIMER_COOLDOWN_CHECK(cd_source, cd_index) LAZYACCESS(cd_source.cooldowns, cd_index)
#define TIMER_COOLDOWN_END(cd_source, cd_index) LAZYREMOVE(cd_source.cooldowns, cd_index)
/*
* Stoppable timer cooldowns.
* Use indexes the same as the regular tiemr cooldowns.
* They make use of the TIMER_COOLDOWN_CHECK() and TIMER_COOLDOWN_END() macros the same, just not the TIMER_COOLDOWN_START() one.
* A bit more expensive than the regular timers, but can be reset before they end and the time left can be checked.
*/
#define S_TIMER_COOLDOWN_START(cd_source, cd_index, cd_time) LAZYSET(cd_source.cooldowns, cd_index, addtimer(CALLBACK(GLOBAL_PROC, /proc/end_cooldown, cd_source, cd_index), cd_time, TIMER_STOPPABLE))
#define S_TIMER_COOLDOWN_RESET(cd_source, cd_index) reset_cooldown(cd_source, cd_index)
#define S_TIMER_COOLDOWN_TIMELEFT(cd_source, cd_index) (timeleft(TIMER_COOLDOWN_CHECK(cd_source, cd_index)))
/*
* Cooldown system based on storing world.time on a variable, plus the cooldown time.
* Better performance over timer cooldowns, lower control. Same functionality.
*/
#define COOLDOWN_DECLARE(cd_index) var/##cd_index = 0
#define COOLDOWN_START(cd_source, cd_index, cd_time) (cd_source.cd_index = world.time + cd_time)
//Returns true if the cooldown has run its course, false otherwise
#define COOLDOWN_FINISHED(cd_source, cd_index) (cd_source.cd_index < world.time)
#define COOLDOWN_RESET(cd_source, cd_index) cd_source.cd_index = 0
#define COOLDOWN_TIMELEFT(cd_source, cd_index) (max(0, cd_source.cd_index - world.time))
+38 -8
View File
@@ -32,16 +32,21 @@
#define COMSIG_ELEMENT_DETACH "element_detach"
// /atom signals
//from base of atom/proc/Initialize(): sent any time a new atom is created
#define COMSIG_ATOM_CREATED "atom_created"
#define COMSIG_PARENT_ATTACKBY "atom_attackby" //from base of atom/attackby(): (/obj/item, /mob/living, params)
#define COMPONENT_NO_AFTERATTACK 1 //Return this in response if you don't want afterattack to be called
#define COMSIG_ATOM_HULK_ATTACK "hulk_attack" //from base of atom/attack_hulk(): (/mob/living/carbon/human)
#define COMSIG_ATOM_ATTACK_ANIMAL "attack_animal" //from base of atom/animal_attack(): (/mob/user)
#define COMSIG_PARENT_EXAMINE "atom_examine" //from base of atom/examine(): (/mob, list/examine_return_text)
#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name" //from base of atom/get_examine_name(): (/mob, list/overrides)
///from base of atom/get_examine_name(): (/mob, list/overrides)
#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name"
#define COMSIG_PARENT_EXAMINE_MORE "atom_examine_more" ///from base of atom/examine_more(): (/mob)
//Positions for overrides list
#define EXAMINE_POSITION_ARTICLE 1
#define EXAMINE_POSITION_BEFORE 2
#define EXAMINE_POSITION_ARTICLE (1<<0)
#define EXAMINE_POSITION_BEFORE (1<<1)
//End positions
#define COMPONENT_EXNAME_CHANGED 1
#define COMPONENT_EXNAME_CHANGED (1<<0)
#define COMSIG_ATOM_UPDATE_ICON "atom_update_icon" //from base of atom/update_icon(): ()
#define COMSIG_ATOM_NO_UPDATE_ICON_STATE 1
#define COMSIG_ATOM_NO_UPDATE_OVERLAYS 2
@@ -140,10 +145,10 @@
#define HEARING_SPEAKER 2
// #define HEARING_LANGUAGE 3
#define HEARING_RAW_MESSAGE 4
/* #define HEARING_RADIO_FREQ 5
#define HEARING_SPANS 6
// #define HEARING_RADIO_FREQ 5
// #define HEARING_SPANS 6
#define HEARING_MESSAGE_MODE 7
#define HEARING_SOURCE 8*/
// #define HEARING_SOURCE 8
#define COMSIG_MOVABLE_DISPOSING "movable_disposing" //called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source)
#define COMSIG_MOVABLE_TELEPORTED "movable_teleported" //from base of do_teleport(): (channel, turf/origin, turf/destination)
@@ -178,6 +183,10 @@
#define COMSIG_MOB_THROW "mob_throw" //from base of /mob/throw_item(): (atom/target)
#define COMSIG_MOB_KEY_CHANGE "mob_key_change" //from base of /mob/transfer_ckey(): (new_character, old_character)
#define COMSIG_MOB_PRE_PLAYER_CHANGE "mob_pre_player_change" //sent to the target mob from base of /mob/transfer_ckey() and /mind/transfer_to(): (our_character, their_character)
///from /mob/living/handle_eye_contact(): (mob/living/other_mob)
#define COMSIG_MOB_EYECONTACT "mob_eyecontact"
/// return this if you want to block printing this message to this person, if you want to print your own (does not affect the other person's message)
#define COMSIG_BLOCK_EYECONTACT (1<<0)
// #define COMPONENT_STOP_MIND_TRANSFER 1
#define COMSIG_MOB_UPDATE_SIGHT "mob_update_sight" //from base of /mob/update_sight(): ()
#define COMSIG_MOB_ON_NEW_MIND "mob_on_new_mind" //called when a new mind is assigned to a mob: ()
@@ -197,6 +206,8 @@
#define COMSIG_MOB_ANTAG_ON_GAIN "mob_antag_on_gain" //from base of /datum/antagonist/on_gain(): (antag_datum)
#define COMSIG_MOB_SPELL_CAN_CAST "mob_spell_can_cast" //from base of /obj/effect/proc_holder/spell/can_cast(): (spell)
#define COMSIG_MOB_SWAP_HANDS "mob_swap_hands" //from base of mob/swap_hand(): (obj/item)
#define COMPONENT_BLOCK_SWAP 1
// /client signals
#define COMSIG_MOB_CLIENT_LOGIN "mob_client_login" //sent when a mob/login() finishes: (client)
@@ -221,6 +232,11 @@
#define COMSIG_LIVING_RUN_BLOCK "living_do_run_block" //from base of mob/living/do_run_block(): (real_attack, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone)
#define COMSIG_LIVING_GET_BLOCKING_ITEMS "get_blocking_items" //from base of mob/living/get_blocking_items(): (list/items)
#define COMSIG_LIVING_ACTIVE_BLOCK_START "active_block_start" //from base of mob/living/keybind_start_active_blocking(): (obj/item/blocking_item, list/backup_items)
#define COMPONENT_PREVENT_BLOCK_START 1
#define COMSIG_LIVING_ACTIVE_PARRY_START "active_parry_start" //from base of mob/living/initiate_parry_sequence(): (parrying_method, datum/parrying_item_mob_or_art, list/backup_items)
#define COMPONENT_PREVENT_PARRY_START 1
//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS!
#define COMSIG_LIVING_STATUS_STUN "living_stun" //from base of mob/living/Stun() (amount, update, ignore)
#define COMSIG_LIVING_STATUS_KNOCKDOWN "living_knockdown" //from base of mob/living/Knockdown() (amount, update, ignore)
@@ -254,6 +270,9 @@
#define COMSIG_OBJ_DECONSTRUCT "obj_deconstruct" //from base of obj/deconstruct(): (disassembled)
#define COMSIG_OBJ_BREAK "obj_break" //from base of /obj/obj_break(): (damage_flag)
#define COMSIG_OBJ_SETANCHORED "obj_setanchored" //called in /obj/structure/setAnchored(): (value)
#define COMSIG_OBJ_ATTACK_GENERIC "obj_attack_generic" //from base of atom/animal_attack(): (/mob/user)
#define COMPONENT_STOP_GENERIC_ATTACK 1
// /machinery signals
#define COMSIG_MACHINE_EJECT_OCCUPANT "eject_occupant" //from base of obj/machinery/dropContents() (occupant)
@@ -279,7 +298,13 @@
#define COMSIG_ITEM_ATTACK_ZONE "item_attack_zone" //from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone)
#define COMSIG_ITEM_IMBUE_SOUL "item_imbue_soul" //return a truthy value to prevent ensouling, checked in /obj/effect/proc_holder/spell/targeted/lichdom/cast(): (mob/user)
#define COMSIG_ITEM_HIT_REACT "item_hit_react" //from base of obj/item/hit_reaction(): (list/args)
#define COMSIG_ITEM_WEARERCROSSED "wearer_crossed" //called on item when crossed by something (): (/atom/movable)
#define COMSIG_ITEM_WEARERCROSSED "wearer_crossed" //called on item when crossed by something (): (/atom/movable)
#define COMSIG_ITEM_SHARPEN_ACT "sharpen_act" //from base of item/sharpener/attackby(): (amount, max)
#define COMPONENT_BLOCK_SHARPEN_APPLIED 1
#define COMPONENT_BLOCK_SHARPEN_BLOCKED 2
#define COMPONENT_BLOCK_SHARPEN_ALREADY 4
#define COMPONENT_BLOCK_SHARPEN_MAXED 8
#define COMSIG_ITEM_MICROWAVE_ACT "microwave_act" //called on item when microwaved (): (obj/machinery/microwave/M)
#define COMSIG_ITEM_WORN_OVERLAYS "item_worn_overlays" //from base of obj/item/worn_overlays(): (isinhands, icon_file, used_state, style_flags, list/overlays)
// THE FOLLOWING TWO BLOCKS SHOULD RETURN BLOCK FLAGS AS DEFINED IN __DEFINES/combat.dm!
#define COMSIG_ITEM_CHECK_BLOCK "check_block" //from base of obj/item/check_block(): (mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
@@ -414,6 +439,11 @@
#define COMSIG_TRY_STORAGE_RETURN_INVENTORY "storage_return_inventory" //(list/list_to_inject_results_into, recursively_search_inside_storages = TRUE)
#define COMSIG_TRY_STORAGE_CAN_INSERT "storage_can_equip" //(obj/item/insertion_candidate, mob/user, silent) - returns bool
// /datum/component/two_handed signals
#define COMSIG_TWOHANDED_WIELD "twohanded_wield" //from base of datum/component/two_handed/proc/wield(mob/living/carbon/user): (/mob/user)
#define COMPONENT_TWOHANDED_BLOCK_WIELD 1
#define COMSIG_TWOHANDED_UNWIELD "twohanded_unwield" //from base of datum/component/two_handed/proc/unwield(mob/living/carbon/user): (/mob/user)
// /datum/action signals
#define COMSIG_ACTION_TRIGGER "action_trigger" //from base of datum/action/proc/Trigger(): (datum/action)
#define COMPONENT_ACTION_BLOCK_TRIGGER 1
+5 -1
View File
@@ -18,4 +18,8 @@
#define DRINK_VERYGOOD 3
#define DRINK_FANTASTIC 4
#define FOOD_AMAZING 5
#define RACE_DRINK 6
#define RACE_DRINK 6
#define FOOD_IN_CONTAINER (1<<0)
#define STOP_SERVING_BREAKFAST (15 MINUTES)
+9
View File
@@ -6,6 +6,7 @@
#define FOOTSTEP_GRASS "grass"
#define FOOTSTEP_WATER "water"
#define FOOTSTEP_LAVA "lava"
#define FOOTSTEP_MEAT "meat"
//barefoot sounds
#define FOOTSTEP_WOOD_BAREFOOT "woodbarefoot"
#define FOOTSTEP_WOOD_CLAW "woodclaw"
@@ -89,6 +90,8 @@ GLOBAL_LIST_INIT(footstep, list(
'sound/effects/footstep/lava1.ogg',
'sound/effects/footstep/lava2.ogg',
'sound/effects/footstep/lava3.ogg'), 100, 0),
FOOTSTEP_MEAT = list(list(
'sound/effects/meatslap.ogg'), 100, 0)
))
//bare footsteps lists
@@ -131,6 +134,8 @@ GLOBAL_LIST_INIT(barefootstep, list(
'sound/effects/footstep/lava1.ogg',
'sound/effects/footstep/lava2.ogg',
'sound/effects/footstep/lava3.ogg'), 100, 0),
FOOTSTEP_MEAT = list(list(
'sound/effects/meatslap.ogg'), 100, 0)
))
//claw footsteps lists
@@ -173,6 +178,8 @@ GLOBAL_LIST_INIT(clawfootstep, list(
'sound/effects/footstep/lava1.ogg',
'sound/effects/footstep/lava2.ogg',
'sound/effects/footstep/lava3.ogg'), 100, 0),
FOOTSTEP_MEAT = list(list(
'sound/effects/meatslap.ogg'), 100, 0)
))
//heavy footsteps list
@@ -189,4 +196,6 @@ GLOBAL_LIST_INIT(heavyfootstep, list(
'sound/effects/footstep/lava1.ogg',
'sound/effects/footstep/lava2.ogg',
'sound/effects/footstep/lava3.ogg'), 100, 0),
FOOTSTEP_MEAT = list(list(
'sound/effects/meatslap.ogg'), 100, 0)
))
+3 -1
View File
@@ -135,6 +135,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define iscat(A) (istype(A, /mob/living/simple_animal/pet/cat))
#define isdog(A) (istype(A, /mob/living/simple_animal/pet/dog))
#define iscorgi(A) (istype(A, /mob/living/simple_animal/pet/dog/corgi))
#define ishostile(A) (istype(A, /mob/living/simple_animal/hostile))
@@ -214,7 +216,7 @@ GLOBAL_LIST_INIT(pointed_types, typecacheof(list(
#define isgun(A) (istype(A, /obj/item/gun))
#define isfood(A) (istype(A, /obj/item/reagent_containers/food))
#define isfood(A) (istype(A, /obj/item/reagent_containers/food/snacks))
//Assemblies
#define isassembly(O) (istype(O, /obj/item/assembly))
+1
View File
@@ -38,6 +38,7 @@
#define LOG_ADMIN_PRIVATE (1 << 14)
#define LOG_ASAY (1 << 15)
#define LOG_VIRUS (1 << 16)
#define LOG_SHUTTLE (1 << 18)
//Individual logging panel pages
#define INDIVIDUAL_ATTACK_LOG (LOG_ATTACK)
+4
View File
@@ -115,3 +115,7 @@
//these flags are used to tell the DNA modifier if a plant gene cannot be extracted or modified.
#define PLANT_GENE_REMOVABLE (1<<0)
#define PLANT_GENE_EXTRACTABLE (1<<1)
#define CLONEPOD_GET_MIND 1
#define CLONEPOD_POLL_MIND 2
#define CLONEPOD_NO_MIND 3
+6 -2
View File
@@ -4,8 +4,12 @@
/// Hard materials, such as iron or metal
#define MAT_CATEGORY_RIGID "rigid material"
///Use this flag on TRUE if you want the basic recipes
#define MAT_CATEGORY_BASE_RECIPES "basic recipes"
/// Flag for atoms, this flag ensures it isn't re-colored by materials. Useful for snowflake icons such as default toolboxes.
#define MATERIAL_COLOR (1<<0)
#define MATERIAL_ADD_PREFIX (1<<1)
#define MATERIAL_EFFECTS (1<<2)
#define MATERIAL_AFFECT_STATISTICS (1<<3)
#define MATERIAL_AFFECT_STATISTICS (1<<2)
#define MATERIAL_SOURCE(mat) "[mat.name]_material"
+4 -1
View File
@@ -432,6 +432,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
//text files
#define BRAIN_DAMAGE_FILE "traumas.json"
#define ION_FILE "ion_laws.json"
#define REDPILL_FILE "redpill.json"
#define PIRATE_NAMES_FILE "pirates.json"
@@ -461,7 +462,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define EGG_LAYING_MESSAGES list("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.")
// list of all null rod weapons
#define HOLY_WEAPONS /obj/item/nullrod, /obj/item/twohanded/dualsaber/hypereutactic/chaplain, /obj/item/gun/energy/laser/redtag/hitscan/chaplain, /obj/item/multitool/chaplain, /obj/item/clothing/gloves/fingerless/pugilist/chaplain, /obj/item/melee/baseball_bat/chaplain
#define HOLY_WEAPONS /obj/item/nullrod, /obj/item/dualsaber/hypereutactic/chaplain, /obj/item/gun/energy/laser/redtag/hitscan/chaplain, /obj/item/multitool/chaplain, /obj/item/clothing/gloves/fingerless/pugilist/chaplain, /obj/item/melee/baseball_bat/chaplain
// Used by PDA and cartridge code to reduce repetitiveness of spritesheets
#define PDAIMG(what) {"<span class="pda16x16 [#what]"></span>"}
@@ -533,3 +534,5 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define LOOT_RESTRICTION_CKEY 2
#define LOOT_RESTRICTION_MIND_PILE 3 //limited to the current pile.
#define LOOT_RESTRICTION_CKEY_PILE 4 //Idem
#define WANTED_FILE "wanted_message.json"
+7
View File
@@ -98,6 +98,7 @@
#define BRAIN_TRAUMA_MILD /datum/brain_trauma/mild
#define BRAIN_TRAUMA_SEVERE /datum/brain_trauma/severe
#define BRAIN_TRAUMA_SPECIAL /datum/brain_trauma/special
#define BRAIN_TRAUMA_MAGIC /datum/brain_trauma/magic
#define TRAUMA_RESILIENCE_BASIC 1 //Curable with chems
#define TRAUMA_RESILIENCE_SURGERY 2 //Curable with brain surgery
@@ -309,3 +310,9 @@
#define FOV_90_DEGREES 90
#define FOV_180_DEGREES 180
#define FOV_270_DEGREES 270
/// How far away you can be to make eye contact with someone while examining
#define EYE_CONTACT_RANGE 5
/// If you examine the same atom twice in this timeframe, we call examine_more() instead of examine()
#define EXAMINE_MORE_TIME 1 SECONDS
+1 -1
View File
@@ -3,7 +3,7 @@
/// How much someone is slowed from fireman carrying a human
#define FIREMAN_CARRY_SLOWDOWN 0
/// How much someone is slowed by piggybacking a human
#define PIGGYBACK_CARRY_SLOWDOWN 1
#define PIGGYBACK_CARRY_SLOWDOWN 0
/// slowdown when in softcrit. Note that crawling slowdown will also apply at the same time!
#define SOFTCRIT_ADD_SLOWDOWN 2
/// slowdown when crawling
+2 -4
View File
@@ -6,7 +6,7 @@
#define RUSTG_JOB_ERROR "JOB PANICKED"
#define rustg_dmi_strip_metadata(fname) call(RUST_G, "dmi_strip_metadata")(fname)
#define rustg_dmi_create_png(fname,width,height,data) call(RUST_G, "dmi_create_png")(fname,width,height,data)
#define rustg_dmi_create_png(path, width, height, data) call(RUST_G, "dmi_create_png")(path, width, height, data)
#define rustg_git_revparse(rev) call(RUST_G, "rg_git_revparse")(rev)
#define rustg_git_commit_date(rev) call(RUST_G, "rg_git_commit_date")(rev)
@@ -14,14 +14,12 @@
#define rustg_log_write(fname, text, format) call(RUST_G, "log_write")(fname, text, format)
/proc/rustg_log_close_all() return call(RUST_G, "log_close_all")()
// RUST-G defines & procs for HTTP component
#define RUSTG_HTTP_METHOD_GET "get"
#define RUSTG_HTTP_METHOD_POST "post"
#define RUSTG_HTTP_METHOD_PUT "put"
#define RUSTG_HTTP_METHOD_DELETE "delete"
#define RUSTG_HTTP_METHOD_PATCH "patch"
#define RUSTG_HTTP_METHOD_HEAD "head"
#define RUSTG_HTTP_METHOD_POST "post"
#define rustg_http_request_blocking(method, url, body, headers) call(RUST_G, "http_request_blocking")(method, url, body, headers)
#define rustg_http_request_async(method, url, body, headers) call(RUST_G, "http_request_async")(method, url, body, headers)
#define rustg_http_check_request(req_id) call(RUST_G, "http_check_request")(req_id)
+14 -7
View File
@@ -8,12 +8,6 @@
/// Levels
#define SKILL_PROGRESSION_LEVEL 4
/// Max value of skill for numerical skills
#define SKILL_NUMERICAL_MAX 100
/// Min value of skill for numerical skills
#define SKILL_NUMERICAL_MIN 0
// Standard values for job starting skills
#define STARTING_SKILL_SURGERY_MEDICAL 35 //out of SKILL_NUMERICAL_MAX
@@ -26,6 +20,13 @@
#define DEF_SKILL_GAIN 1
#define SKILL_GAIN_SURGERY_PER_STEP 0.25
#define STD_USE_TOOL_MULT 1
#define EASY_USE_TOOL_MULT 0.75
#define TRIVIAL_USE_TOOL_MULT 0.5
#define BARE_USE_TOOL_MULT 0.25
//multiplier of the difference of max_value and min_value. Mostly for balance purposes between numerical and level-based skills.
#define STD_NUM_SKILL_ITEM_GAIN_MULTI 0.002
//An extra point for each few seconds of delay when using a tool. Before the multiplier.
#define SKILL_GAIN_DELAY_DIVISOR 3 SECONDS
@@ -100,4 +101,10 @@
///Ascending priority defines.
#define MODIFIER_SKILL_PRIORITY_LOW 100
#define MODIFIER_SKILL_PRIORITY_DEF 50
#define MODIFIER_SKILL_PRIORITY_MAX 1 //max priority, meant for job/antag modifiers so they don't null out other (de)buffs
#define MODIFIER_SKILL_PRIORITY_MAX 1 //max priority, meant for job/antag modifiers so they don't null out other (de)buffs
// UI Defines
///Categories of skills, these will be displayed alphabetically.
#define SKILL_UI_CAT_ENG "Engineering"
#define SKILL_UI_CAT_MED "Medical"
#define SKILL_UI_CAT_MISC "Misc"
+2 -3
View File
@@ -29,6 +29,5 @@
#endif
/world/proc/enable_debugger()
var/dll = world.GetConfig("env", "EXTOOLS_DLL")
if (dll)
call(dll, "debug_initialize")()
if (fexists(EXTOOLS))
call(EXTOOLS, "debug_initialize")()
+7 -6
View File
@@ -137,12 +137,13 @@
// SSair run section
#define SSAIR_PIPENETS 1
#define SSAIR_ATMOSMACHINERY 2
#define SSAIR_REACTQUEUE 3
#define SSAIR_EXCITEDGROUPS 4
#define SSAIR_HIGHPRESSURE 5
#define SSAIR_HOTSPOTS 6
#define SSAIR_SUPERCONDUCTIVITY 7
#define SSAIR_REBUILD_PIPENETS 8
#define SSAIR_EXCITEDGROUPS 3
#define SSAIR_HIGHPRESSURE 4
#define SSAIR_HOTSPOTS 5
#define SSAIR_SUPERCONDUCTIVITY 6
#define SSAIR_REBUILD_PIPENETS 7
#define SSAIR_EQUALIZE 8
#define SSAIR_ACTIVETURFS 9
#define COMPILE_OVERLAYS(A)\
if (TRUE) {\
+8
View File
@@ -61,6 +61,12 @@
} while (0)
#define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE)
#define HAS_TRAIT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (source in target.status_traits[trait]) : FALSE) : FALSE)
#define HAS_TRAIT_FROM_ONLY(target, trait, source) (\
target.status_traits ?\
(target.status_traits[trait] ?\
((source in target.status_traits[trait]) && (length(target.status_traits) == 1))\
: FALSE)\
: FALSE)
#define HAS_TRAIT_NOT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (length(target.status_traits[trait] - source) > 0) : FALSE) : FALSE)
//mob traits
@@ -226,6 +232,8 @@
#define VEHICLE_TRAIT "vehicle" // inherited from riding vehicles
#define INNATE_TRAIT "innate"
///Used for managing KEEP_TOGETHER in [appearance_flags]
#define TRAIT_KEEP_TOGETHER "keep-together"
// item traits
#define TRAIT_NODROP "nodrop"
+7
View File
@@ -76,6 +76,13 @@
#define VV_HK_ADDCOMPONENT "addcomponent"
#define VV_HK_MODIFY_TRAITS "modtraits"
// /datum/gas_mixture
#define VV_HK_SET_MOLES "set_moles"
#define VV_HK_EMPTY "empty"
#define VV_HK_SET_TEMPERATURE "set_temp"
#define VV_HK_PARSE_GASSTRING "parse_gasstring"
#define VV_HK_SET_VOLUME "set_volume"
// /atom
#define VV_HK_MODIFY_TRANSFORM "atom_transform"
#define VV_HK_ADD_REAGENT "addreagent"
+1 -1
View File
@@ -69,7 +69,7 @@
/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
var/total = input.len
if (!total)
return "[nothing_text]"
return nothing_text
else if (total == 1)
return "[input[1]]"
else if (total == 2)
+7
View File
@@ -118,6 +118,9 @@
//reusing the PDA option because I really don't think news comments are worth a config option
WRITE_LOG(GLOB.world_pda_log, "COMMENT: [text]")
/proc/log_paper(text)
WRITE_LOG(GLOB.world_paper_log, "PAPER: [text]")
/proc/log_telecomms(text)
if (CONFIG_GET(flag/log_telecomms))
WRITE_LOG(GLOB.world_telecomms_log, "TCOMMS: [text]")
@@ -131,6 +134,10 @@
if (CONFIG_GET(flag/log_vote))
WRITE_LOG(GLOB.world_game_log, "VOTE: [text]")
/proc/log_shuttle(text)
if (CONFIG_GET(flag/log_shuttle))
WRITE_LOG(GLOB.world_shuttle_log, "SHUTTLE: [text]")
/proc/log_craft(text)
if (CONFIG_GET(flag/log_craft))
WRITE_LOG(GLOB.world_crafting_log, "CRAFT: [text]")
+1
View File
@@ -1,4 +1,5 @@
#define pick_list(FILE, KEY) (pick(strings(FILE, KEY)))
#define pick_list_weighted(FILE, KEY) (pickweight(strings(FILE, KEY)))
#define pick_list_replacements(FILE, KEY) (strings_replacement(FILE, KEY))
#define json_load(FILE) (json_decode(file2text(FILE)))
+13
View File
@@ -120,3 +120,16 @@ GLOBAL_VAR_INIT(cmp_field, "name")
/proc/cmp_item_block_priority_asc(obj/item/A, obj/item/B)
return A.block_priority - B.block_priority
/proc/cmp_skill_categories(datum/skill/A, datum/skill/B)
if(A.ui_category == B.ui_category)
return sorttext(A.name, B.name)
return sorttext(A.ui_category, B.ui_category)
/proc/cmp_chemical_reactions_default(datum/chemical_reaction/A, datum/chemical_reaction/B)
if(A.priority != B.priority)
return B.priority - A.priority
else if(A.is_cold_recipe)
return A.required_temp - B.required_temp //return coldest
else
return B.required_temp - A.required_temp //return hottest
+2 -1
View File
@@ -5,8 +5,9 @@
#define GET_INITIALIZED_MUTATION(A) GLOB.all_mutations[A]
#define GET_GENE_STRING(A, B) (B.mutation_index[A])
#define GET_SEQUENCE(A) (GLOB.full_sequences[A])
#define GET_MUTATION_TYPE_FROM_ALIAS(A) (GLOB.alias_mutations[A])
#define GET_MUTATION_STABILIZER(A) ((A.stabilizer_coeff < 0) ? 1 : A.stabilizer_coeff)
#define GET_MUTATION_SYNCHRONIZER(A) ((A.synchronizer_coeff < 0) ? 1 : A.synchronizer_coeff)
#define GET_MUTATION_POWER(A) ((A.power_coeff < 0) ? 1 : A.power_coeff)
#define GET_MUTATION_ENERGY(A) ((A.energy_coeff < 0) ? 1 : A.energy_coeff)
#define GET_MUTATION_ENERGY(A) ((A.energy_coeff < 0) ? 1 : A.energy_coeff)
+1 -1
View File
@@ -3,7 +3,7 @@
for(var/file in args)
src << browse_rsc(file)
/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list("txt","log","htm", "html", "md"))
/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list("txt","log","htm", "html", "md", "json"))
var/path = root
for(var/i=0, i<max_iterations, i++)
+21
View File
@@ -53,6 +53,27 @@
for(var/I in adjacent_turfs)
. |= get_area(I)
/**
* Get a bounding box of a list of atoms.
*
* Arguments:
* - atoms - List of atoms. Can accept output of view() and range() procs.
*
* Returns: list(x1, y1, x2, y2)
*/
/proc/get_bbox_of_atoms(list/atoms)
var/list/list_x = list()
var/list/list_y = list()
for(var/_a in atoms)
var/atom/a = _a
list_x += a.x
list_y += a.y
return list(
min(list_x),
min(list_y),
max(list_x),
max(list_y))
// Like view but bypasses luminosity check
/proc/get_hear(range, atom/source)
-22
View File
@@ -90,7 +90,6 @@
init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes)
INVOKE_ASYNC(GLOBAL_PROC, /proc/init_ref_coin_values) //so the current procedure doesn't sleep because of UNTIL()
INVOKE_ASYNC(GLOBAL_PROC, /proc/setupGenetics)
//creates every subtype of prototype (excluding prototype) and adds it to list L.
//if no list/L is provided, one is created.
@@ -117,24 +116,3 @@
GLOB.coin_values[path] = C.value
qdel(C)
/proc/setupGenetics()
var/list/mutations = subtypesof(/datum/mutation/human)
shuffle_inplace(mutations)
for(var/A in subtypesof(/datum/generecipe))
var/datum/generecipe/GR = A
GLOB.mutation_recipes[initial(GR.required)] = initial(GR.result)
for(var/i in 1 to LAZYLEN(mutations))
var/path = mutations[i] //byond gets pissy when we do it in one line
var/datum/mutation/human/B = new path ()
B.alias = "Mutation #[i]"
GLOB.all_mutations[B.type] = B
GLOB.full_sequences[B.type] = generate_gene_sequence(B.blocks)
if(B.locked)
continue
if(B.quality == POSITIVE)
GLOB.good_mutations |= B
else if(B.quality == NEGATIVE)
GLOB.bad_mutations |= B
else if(B.quality == MINOR_NEGATIVE)
GLOB.not_good_mutations |= B
CHECK_TICK
+6 -4
View File
@@ -1136,9 +1136,10 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
if (!isicon(I))
if (isfile(thing)) //special snowflake
var/name = sanitize_filename("[generate_asset_name(thing)].png")
register_asset(name, thing)
if(!SSassets.cache[name])
register_asset(name, thing)
for (var/thing2 in targets)
send_asset(thing2, key, FALSE)
send_asset(thing2, key)
return "<img class='icon icon-misc' src=\"[url_encode(name)]\">"
var/atom/A = thing
if (isnull(dir))
@@ -1160,9 +1161,10 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
I = icon(I, icon_state, dir, frame, moving)
key = "[generate_asset_name(I)].png"
register_asset(key, I)
if(!SSassets.cache[key])
register_asset(key, I)
for (var/thing2 in targets)
send_asset(thing2, key, FALSE)
send_asset(thing2, key)
return "<img class='icon icon-[icon_state]' src=\"[url_encode(key)]\">"
+2
View File
@@ -375,6 +375,8 @@ GLOBAL_LIST_EMPTY(species_list)
override = TRUE
if(HAS_TRAIT(M, TRAIT_SIXTHSENSE))
override = TRUE
if(SSticker.current_state == GAME_STATE_FINISHED)
override = TRUE
if(isnewplayer(M) && !override)
continue
if(M.stat != DEAD && !override)
+2 -1
View File
@@ -174,7 +174,7 @@
to_chat(world, "<BR><BR><BR><span class='big bold'>The round has ended.</span>")
if(LAZYLEN(GLOB.round_end_notifiees))
send2irc("Notice", "[GLOB.round_end_notifiees.Join(", ")] the round has ended.")
world.TgsTargetedChatBroadcast("[GLOB.round_end_notifiees.Join(", ")] the round has ended.", FALSE)
for(var/I in round_end_events)
var/datum/callback/cb = I
@@ -233,6 +233,7 @@
for(var/antag_name in total_antagonists)
var/list/L = total_antagonists[antag_name]
log_game("[antag_name]s :[L.Join(", ")].")
set_observer_default_invisibility(0, "<span class='warning'>The round is over! You are now visible to the living.</span>")
CHECK_TICK
SSdbcore.SetRoundEnd()
+2 -1
View File
@@ -1437,7 +1437,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
/obj/item/reagent_containers/food/snacks/grown,
/obj/item/reagent_containers/food/snacks/grown/mushroom,
/obj/item/reagent_containers/food/snacks/grown/nettle, // base type
/obj/item/reagent_containers/food/snacks/deepfryholder,
/obj/item/reagent_containers/food/snacks/grown/shell,
/obj/item/reagent_containers/food/snacks/clothing,
/obj/item/reagent_containers/food/snacks/store/bread
@@ -1509,6 +1508,8 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
set waitfor = FALSE
return call(source, proctype)(arglist(arguments))
#define TURF_FROM_COORDS_LIST(List) (locate(List[1], List[2], List[3]))
/proc/num2sign(numeric)
if(numeric > 0)
return 1
+2 -1
View File
@@ -27,5 +27,6 @@ GLOBAL_LIST_EMPTY(full_sequences)
GLOBAL_LIST_EMPTY(bad_mutations)
GLOBAL_LIST_EMPTY(good_mutations)
GLOBAL_LIST_EMPTY(not_good_mutations)
GLOBAL_LIST_EMPTY(alias_mutations) //alias = type
GLOBAL_LIST_EMPTY(mutation_recipes)
GLOBAL_LIST_EMPTY(mutation_recipes)
+13 -7
View File
@@ -116,15 +116,16 @@ GLOBAL_LIST_INIT(ai_core_display_screens, list(
GLOBAL_LIST_INIT(security_depts_prefs, list(SEC_DEPT_RANDOM, SEC_DEPT_NONE, SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, SEC_DEPT_SCIENCE, SEC_DEPT_SUPPLY))
//Backpacks
#define GBACKPACK "Grey Backpack"
#define GSATCHEL "Grey Satchel"
#define GDUFFELBAG "Grey Duffel Bag"
#define LSATCHEL "Leather Satchel"
//Backpacks
#define DBACKPACK "Department Backpack"
#define DSATCHEL "Department Satchel"
#define DDUFFELBAG "Department Duffel Bag"
GLOBAL_LIST_INIT(backbaglist, list(DBACKPACK, DSATCHEL, DDUFFELBAG, GBACKPACK, GSATCHEL, GDUFFELBAG, LSATCHEL))
GLOBAL_LIST_INIT(backbaglist, list(DBACKPACK, DSATCHEL, DDUFFELBAG, //everything after this point is a non-department backpack
"Grey Backpack" = /obj/item/storage/backpack,
"Grey Satchel" = /obj/item/storage/backpack/satchel,
"Grey Duffel Bag" = /obj/item/storage/backpack/duffelbag,
"Leather Satchel" = /obj/item/storage/backpack/satchel/leather,
"Snail Shell" = /obj/item/storage/backpack/snail))
//Suit/Skirt
#define PREF_SUIT "Jumpsuit"
@@ -224,5 +225,10 @@ GLOBAL_LIST_INIT(admiral_messages, list("Do you know how expensive these station
GLOBAL_LIST_INIT(redacted_strings, list("\[REDACTED\]", "\[CLASSIFIED\]", "\[ARCHIVED\]", "\[EXPLETIVE DELETED\]", "\[EXPUNGED\]", "\[INFORMATION ABOVE YOUR SECURITY CLEARANCE\]", "\[MOVE ALONG CITIZEN\]", "\[NOTHING TO SEE HERE\]", "\[ACCESS DENIED\]"))
GLOBAL_LIST_INIT(wisdoms, world.file2list("strings/wisdoms.txt"))
GLOBAL_LIST_INIT(speech_verbs, list("default","says","gibbers", "states", "chitters", "declares", "bellows", "buzzes" ,"beeps", "chirps" ,"hisses" ,"poofs" ,"rattles", "mewls" ,"barks", "blorbles", "squeaks", "squawks", "flutters"))
GLOBAL_LIST_INIT(roundstart_tongues, list("default","human tongue" = /obj/item/organ/tongue, "lizard tongue" = /obj/item/organ/tongue/lizard, "skeleton tongue" = /obj/item/organ/tongue/bone, "fly tongue" = /obj/item/organ/tongue/fly, "ipc tongue" = /obj/item/organ/tongue/robot/ipc))
+1 -2
View File
@@ -38,8 +38,7 @@ GLOBAL_LIST_EMPTY(servant_spawns) //Servants of Ratvar spawn here
GLOBAL_LIST_EMPTY(city_of_cogs_spawns) //Anyone entering the City of Cogs spawns here
GLOBAL_LIST_EMPTY(ruin_landmarks)
//away missions
GLOBAL_LIST_EMPTY(awaydestinations) //a list of landmarks that the warpgate can take you to
//away missions
GLOBAL_LIST_EMPTY(vr_spawnpoints)
//used by jump-to-area etc. Updated by area/updateName()
+1
View File
@@ -23,6 +23,7 @@ GLOBAL_LIST_EMPTY(joined_player_list) //all clients that have joined the game a
GLOBAL_LIST_EMPTY(silicon_mobs) //all silicon mobs
GLOBAL_LIST_EMPTY(mob_living_list) //all instances of /mob/living and subtypes
GLOBAL_LIST_EMPTY(carbon_list) //all instances of /mob/living/carbon and subtypes, notably does not contain brains or simple animals
GLOBAL_LIST_EMPTY(human_list) //all instances of /mob/living/carbon/human and subtypes
GLOBAL_LIST_EMPTY(ai_list)
GLOBAL_LIST_EMPTY(pai_list)
GLOBAL_LIST_EMPTY(available_ai_shells)
+4 -2
View File
@@ -32,6 +32,8 @@ GLOBAL_VAR(world_asset_log)
GLOBAL_PROTECT(world_asset_log)
GLOBAL_VAR(world_map_error_log)
GLOBAL_PROTECT(world_map_error_log)
GLOBAL_VAR(world_paper_log)
GLOBAL_PROTECT(world_paper_log)
GLOBAL_VAR(subsystem_log)
GLOBAL_PROTECT(subsystem_log)
GLOBAL_VAR(reagent_log)
@@ -49,10 +51,10 @@ GLOBAL_LIST_EMPTY(lastsignalers) //keeps last 100 signals here in format: "[src]
GLOBAL_PROTECT(lastsignalers)
GLOBAL_LIST_EMPTY(lawchanges) //Stores who uploaded laws to which silicon-based lifeform, and what the law was
GLOBAL_PROTECT(lawchanges)
GLOBAL_VAR(tgui_log)
GLOBAL_PROTECT(tgui_log)
GLOBAL_VAR(world_shuttle_log)
GLOBAL_PROTECT(world_shuttle_log)
GLOBAL_LIST_EMPTY(combatlog)
GLOBAL_PROTECT(combatlog)
GLOBAL_LIST_EMPTY(IClog)
+2 -1
View File
@@ -94,8 +94,9 @@
The below is only really for safety, or you can alter the way
it functions and re-insert it above.
*/
/mob/living/silicon/ai/UnarmedAttack(atom/A)
/mob/living/silicon/ai/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
A.attack_ai(src)
/mob/living/silicon/ai/RangedAttack(atom/A)
A.attack_ai(src)
+1 -2
View File
@@ -269,10 +269,9 @@
proximity_flag is not currently passed to attack_hand, and is instead used
in human click code to allow glove touches only at melee range.
*/
/mob/proc/UnarmedAttack(atom/A, proximity_flag)
/mob/proc/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
if(ismob(A))
changeNext_move(CLICK_CD_MELEE)
return
/*
Ranged unarmed attack:
+2 -1
View File
@@ -175,8 +175,9 @@
clicks, you can do so here, but you will have to
change attack_robot() above to the proper function
*/
/mob/living/silicon/robot/UnarmedAttack(atom/A)
/mob/living/silicon/robot/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
A.attack_robot(src)
/mob/living/silicon/robot/RangedAttack(atom/A)
A.attack_robot(src)
+165
View File
@@ -0,0 +1,165 @@
/client
/**
* Assoc list with all the active maps - when a screen obj is added to
* a map, it's put in here as well.
*
* Format: list(<mapname> = list(/obj/screen))
*/
var/list/screen_maps = list()
/**
* A screen object, which acts as a container for turfs and other things
* you want to show on the map, which you usually attach to "vis_contents".
*/
/obj/screen
/**
* Map name assigned to this object.
* Automatically set by /client/proc/add_obj_to_map.
*/
var/assigned_map
/**
* Mark this object as garbage-collectible after you clean the map
* it was registered on.
*
* This could probably be changed to be a proc, for conditional removal.
* But for now, this works.
*/
var/del_on_map_removal = TRUE
/**
* A generic background object.
* It is also implicitly used to allocate a rectangle on the map, which will
* be used for auto-scaling the map.
*/
/obj/screen/background
name = "background"
icon = 'icons/mob/map_backgrounds.dmi'
icon_state = "clear"
layer = GAME_PLANE
plane = GAME_PLANE
/**
* Sets screen_loc of this screen object, in form of point coordinates,
* with optional pixel offset (px, py).
*
* If applicable, "assigned_map" has to be assigned before this proc call.
*/
/obj/screen/proc/set_position(x, y, px = 0, py = 0)
if(assigned_map)
screen_loc = "[assigned_map]:[x]:[px],[y]:[py]"
else
screen_loc = "[x]:[px],[y]:[py]"
/**
* Sets screen_loc to fill a rectangular area of the map.
*
* If applicable, "assigned_map" has to be assigned before this proc call.
*/
/obj/screen/proc/fill_rect(x1, y1, x2, y2)
if(assigned_map)
screen_loc = "[assigned_map]:[x1],[y1] to [x2],[y2]"
else
screen_loc = "[x1],[y1] to [x2],[y2]"
/**
* Registers screen obj with the client, which makes it visible on the
* assigned map, and becomes a part of the assigned map's lifecycle.
*/
/client/proc/register_map_obj(obj/screen/screen_obj)
if(!screen_obj.assigned_map)
CRASH("Can't register [screen_obj] without 'assigned_map' property.")
if(!screen_maps[screen_obj.assigned_map])
screen_maps[screen_obj.assigned_map] = list()
// NOTE: Possibly an expensive operation
var/list/screen_map = screen_maps[screen_obj.assigned_map]
if(!screen_map.Find(screen_obj))
screen_map += screen_obj
if(!screen.Find(screen_obj))
screen += screen_obj
/**
* Clears the map of registered screen objects.
*
* Not really needed most of the time, as the client's screen list gets reset
* on relog. any of the buttons are going to get caught by garbage collection
* anyway. they're effectively qdel'd.
*/
/client/proc/clear_map(map_name)
if(!map_name || !(map_name in screen_maps))
return FALSE
for(var/obj/screen/screen_obj in screen_maps[map_name])
screen_maps[map_name] -= screen_obj
if(screen_obj.del_on_map_removal)
qdel(screen_obj)
screen_maps -= map_name
/**
* Clears all the maps of registered screen objects.
*/
/client/proc/clear_all_maps()
for(var/map_name in screen_maps)
clear_map(map_name)
/**
* Creates a popup window with a basic map element in it, without any
* further initialization.
*
* Ratio is how many pixels by how many pixels (keep it simple).
*
* Returns a map name.
*/
/client/proc/create_popup(name, ratiox = 100, ratioy = 100)
winclone(src, "popupwindow", name)
var/list/winparams = list()
winparams["size"] = "[ratiox]x[ratioy]"
winparams["on-close"] = "handle-popup-close [name]"
winset(src, "[name]", list2params(winparams))
winshow(src, "[name]", 1)
var/list/params = list()
params["parent"] = "[name]"
params["type"] = "map"
params["size"] = "[ratiox]x[ratioy]"
params["anchor1"] = "0,0"
params["anchor2"] = "[ratiox],[ratioy]"
winset(src, "[name]_map", list2params(params))
return "[name]_map"
/**
* Create the popup, and get it ready for generic use by giving
* it a background.
*
* Width and height are multiplied by 64 by default.
*/
/client/proc/setup_popup(popup_name, width = 9, height = 9, \
tilesize = 2, bg_icon)
if(!popup_name)
return
clear_map("[popup_name]_map")
var/x_value = world.icon_size * tilesize * width
var/y_value = world.icon_size * tilesize * height
var/map_name = create_popup(popup_name, x_value, y_value)
var/obj/screen/background/background = new
background.assigned_map = map_name
background.fill_rect(1, 1, width, height)
if(bg_icon)
background.icon_state = bg_icon
register_map_obj(background)
return map_name
/**
* Closes a popup.
*/
/client/proc/close_popup(popup)
winshow(src, popup, 0)
handle_popup_close(popup)
/**
* When the popup closes in any way (player or proc call) it calls this.
*/
/client/verb/handle_popup_close(window_id as text)
set hidden = TRUE
clear_map("[window_id]_map")
+1 -1
View File
@@ -153,7 +153,7 @@
var/image/item_overlay = image(holding)
item_overlay.alpha = 92
if(!user.can_equip(holding, slot_id, TRUE))
if(!user.can_equip(holding, slot_id, TRUE, TRUE, TRUE))
item_overlay.color = "#FF0000"
else
item_overlay.color = "#00ff00"
+4 -3
View File
@@ -123,7 +123,7 @@
if(totitemdamage)
totitemdamage = user.mind.item_action_skills_mod(I, totitemdamage, I.skill_difficulty, SKILL_ATTACK_OBJ, bad_trait)
for(var/skill in I.used_skills)
if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_OBJ))
if(!(SKILL_TRAIN_ATTACK_OBJ in I.used_skills[skill]))
continue
user.mind.auto_gain_experience(skill, I.skill_gain)
@@ -192,9 +192,10 @@
if(.)
. = user.mind.item_action_skills_mod(I, ., I.skill_difficulty, SKILL_ATTACK_MOB, bad_trait)
for(var/skill in I.used_skills)
if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_MOB))
if(!(SKILL_TRAIN_ATTACK_MOB in I.used_skills[skill]))
continue
user.mind.auto_gain_experience(skill, I.skill_gain)
var/datum/skill/S = GLOB.skill_datums[skill]
user.mind.auto_gain_experience(skill, I.skill_gain*S.item_skill_gain_multi)
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
// Click parameters is the params string from byond Click() code, see that documentation.
+3 -12
View File
@@ -63,18 +63,9 @@
// And here are some good things for free:
// Now you can click through portals, wormholes, gateways, and teleporters while observing. -Sayu
/obj/machinery/gateway/centerstation/attack_ghost(mob/user)
if(awaygate)
user.forceMove(awaygate.loc)
else
to_chat(user, "[src] has no destination.")
return ..()
/obj/machinery/gateway/centeraway/attack_ghost(mob/user)
if(stationgate)
user.forceMove(stationgate.loc)
else
to_chat(user, "[src] has no destination.")
/obj/effect/gateway_portal_bumper/attack_ghost(mob/user)
if(gateway)
gateway.Transfer(user)
return ..()
/obj/machinery/teleport/hub/attack_ghost(mob/user)
+25 -36
View File
@@ -4,7 +4,7 @@
Otherwise pretty standard.
*/
/mob/living/carbon/human/UnarmedAttack(atom/A, proximity)
/mob/living/carbon/human/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
if(!has_active_hand()) //can't attack without a hand.
to_chat(src, "<span class='notice'>You look at your arm and sigh.</span>")
@@ -20,16 +20,16 @@
var/override = 0
for(var/datum/mutation/human/HM in dna.mutations)
override += HM.on_attack_hand(A, proximity)
override += HM.on_attack_hand(A, proximity, intent, flags)
if(override)
return
SEND_SIGNAL(src, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, A)
A.attack_hand(src)
A.attack_hand(src, intent, flags)
//Return TRUE to cancel other attack hand effects that respect it.
/atom/proc/attack_hand(mob/user)
/atom/proc/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = FALSE
if(!(interaction_flags_atom & INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND))
add_fingerprint(user)
@@ -104,11 +104,11 @@
/*
Animals & All Unspecified
*/
/mob/living/UnarmedAttack(atom/A)
A.attack_animal(src)
/mob/living/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
A.attack_animal(src, intent, flags)
/atom/proc/attack_animal(mob/user)
return
SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_ANIMAL, user)
/mob/living/RestrainedClickOn(atom/A)
return
@@ -116,8 +116,8 @@
/*
Monkeys
*/
/mob/living/carbon/monkey/UnarmedAttack(atom/A)
A.attack_paw(src)
/mob/living/carbon/monkey/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
A.attack_paw(src, intent, flags)
/atom/proc/attack_paw(mob/user)
if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_PAW, user) & COMPONENT_NO_ATTACK_HAND)
@@ -162,8 +162,8 @@
Aliens
Defaults to same as monkey in most places
*/
/mob/living/carbon/alien/UnarmedAttack(atom/A)
A.attack_alien(src)
/mob/living/carbon/alien/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
A.attack_alien(src, intent, flags)
/atom/proc/attack_alien(mob/living/carbon/alien/user)
attack_paw(user)
@@ -173,29 +173,29 @@
return
// Babby aliens
/mob/living/carbon/alien/larva/UnarmedAttack(atom/A)
A.attack_larva(src)
/mob/living/carbon/alien/larva/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
A.attack_larva(src, intent, flags)
/atom/proc/attack_larva(mob/user)
return
/*
Slimes
Nothing happening here
*/
/mob/living/simple_animal/slime/UnarmedAttack(atom/A)
A.attack_slime(src)
/mob/living/simple_animal/slime/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
A.attack_slime(src, intent, flags)
/atom/proc/attack_slime(mob/user)
return
/mob/living/simple_animal/slime/RestrainedClickOn(atom/A)
return
/*
Drones
*/
/mob/living/simple_animal/drone/UnarmedAttack(atom/A)
A.attack_drone(src)
/mob/living/simple_animal/drone/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
A.attack_drone(src, intent, flags)
/atom/proc/attack_drone(mob/living/simple_animal/drone/user)
attack_hand(user) //defaults to attack_hand. Override it when you don't want drones to do same stuff as humans.
@@ -203,55 +203,44 @@
/mob/living/simple_animal/slime/RestrainedClickOn(atom/A)
return
/*
True Devil
*/
/mob/living/carbon/true_devil/UnarmedAttack(atom/A, proximity)
/mob/living/carbon/true_devil/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
A.attack_hand(src)
/*
Brain
*/
/mob/living/brain/UnarmedAttack(atom/A)//Stops runtimes due to attack_animal being the default
/mob/living/brain/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
return
/*
pAI
*/
/mob/living/silicon/pai/UnarmedAttack(atom/A)//Stops runtimes due to attack_animal being the default
/mob/living/silicon/pai/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
return
/*
Simple animals
*/
/mob/living/simple_animal/UnarmedAttack(atom/A, proximity)
/mob/living/simple_animal/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
if(!dextrous)
return ..()
if(!ismob(A))
A.attack_hand(src)
A.attack_hand(src, intent, flags)
update_inv_hands()
/*
Hostile animals
*/
/mob/living/simple_animal/hostile/UnarmedAttack(atom/A)
/mob/living/simple_animal/hostile/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE)
target = A
if(dextrous && !ismob(A))
..()
else
AttackingTarget()
/*
New Players:
Have no reason to click on anything at all.
+1 -2
View File
@@ -102,9 +102,8 @@
//stops TK grabs being equipped anywhere but into hands
/obj/item/tk_grab/equipped(mob/user, slot)
if(slot == SLOT_HANDS)
return
return ..()
qdel(src)
return
/obj/item/tk_grab/examine(user)
if (focus)
+11 -3
View File
@@ -83,7 +83,7 @@
var/auto_trim = TRUE
/datum/config_entry/string/vv_edit_var(var_name, var_value)
return var_name != "auto_trim" && ..()
return var_name != NAMEOF(src, auto_trim) && ..()
/datum/config_entry/string/ValidateAndSet(str_val, during_load)
if(!VASProcCallGuard(str_val))
@@ -110,7 +110,7 @@
return FALSE
/datum/config_entry/number/vv_edit_var(var_name, var_value)
var/static/list/banned_edits = list("max_val", "min_val", "integer")
var/static/list/banned_edits = list(NAMEOF(src, max_val), NAMEOF(src, min_val), NAMEOF(src, integer))
return !(var_name in banned_edits) && ..()
/datum/config_entry/flag
@@ -209,12 +209,20 @@
new_value = new_list
continue_check_value = new_list.len
if(continue_check_value && continue_check_key && ValidateListEntry(new_key, new_value))
new_key = preprocess_key(new_key)
new_value = preprocess_value(new_value)
config_entry_value[new_key] = new_value
return TRUE
return FALSE
/datum/config_entry/keyed_list/vv_edit_var(var_name, var_value)
return var_name != "splitter" && ..()
return var_name != NAMEOF(src, splitter) && ..()
/datum/config_entry/keyed_list/proc/preprocess_key(key)
return key
/datum/config_entry/keyed_list/proc/preprocess_value(value)
return value
//snowflake for donator things being on one line smh
/datum/config_entry/multi_keyed_flag
@@ -14,6 +14,7 @@
var/list/modes // allowed modes
var/list/gamemode_cache
var/list/votable_modes // votable modes
// var/list/ic_filter_regex
var/list/storyteller_cache
var/list/mode_names
var/list/mode_reports
@@ -104,7 +105,9 @@
var/list/lines = world.file2list("[directory]/[filename]")
var/list/_entries = entries
var/list/postload_required = list()
var/linenumber = 0
for(var/L in lines)
linenumber++
L = trim(L)
if(!L)
continue
@@ -132,7 +135,7 @@
if(entry == "$include")
if(!value)
log_config("Warning: Invalid $include directive: [value]")
log_config("LINE [linenumber]: Invalid $include directive: [value]")
else
LoadEntries(value, stack)
++.
@@ -140,7 +143,7 @@
var/datum/config_entry/E = _entries[entry]
if(!E)
log_config("Unknown setting in configuration: '[entry]'")
log_config("LINE [linenumber]: Unknown setting: '[entry]'")
continue
if(lockthis)
@@ -150,7 +153,7 @@
var/datum/config_entry/new_ver = entries_by_type[E.deprecated_by]
var/new_value = E.DeprecationUpdate(value)
var/good_update = istext(new_value)
log_config("Entry [entry] is deprecated and will be removed soon. Migrate to [new_ver.name]![good_update ? " Suggested new value is: [new_value]" : ""]")
log_config("LINE [linenumber]: [entry] is deprecated and will be removed soon. Migrate to [new_ver.name]![good_update ? " Suggested new value is: [new_value]" : ""]")
if(!warned_deprecated_configs)
addtimer(CALLBACK(GLOBAL_PROC, /proc/message_admins, "This server is using deprecated configuration settings. Please check the logs and update accordingly."), 0)
warned_deprecated_configs = TRUE
@@ -162,10 +165,10 @@
var/validated = E.ValidateAndSet(value, TRUE)
if(!validated)
log_config("Failed to validate setting \"[value]\" for [entry]")
log_config("LINE [linenumber]: Failed to validate setting \"[value]\" for [entry]")
else
if(E.modified && !E.dupes_allowed)
log_config("Duplicate setting for [entry] ([value], [E.resident_file]) detected! Using latest.")
log_config("LINE [linenumber]: Duplicate setting for [entry] ([value], [E.resident_file]) detected! Using latest.")
if(E.postload_required)
postload_required[E] = TRUE
@@ -412,3 +415,21 @@
continue
runnable_modes[M] = probabilities[M.config_tag]
return runnable_modes
/*
/datum/controller/configuration/proc/LoadChatFilter()
var/list/in_character_filter = list()
if(!fexists("[directory]/in_character_filter.txt"))
return
log_config("Loading config file in_character_filter.txt...")
for(var/line in world.file2list("[directory]/in_character_filter.txt"))
if(!line)
continue
if(findtextEx(line,"#",1,2))
continue
in_character_filter += REGEX_QUOTE(line)
ic_filter_regex = in_character_filter.len ? regex("\\b([jointext(in_character_filter, "|")])\\b", "i") : null
syncChatRegexes()
*/
@@ -25,4 +25,23 @@
/datum/config_entry/string/medal_hub_address
/datum/config_entry/string/medal_hub_password
protection = CONFIG_ENTRY_HIDDEN
protection = CONFIG_ENTRY_HIDDEN
/datum/config_entry/keyed_list/cross_server_bunker_override
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_TEXT
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/keyed_list/cross_server_bunker_override/ValidateAndSet(str_val)
. = ..()
if(.)
var/list/newv = list()
for(var/I in config_entry_value)
newv[replacetext(I, "+", " ")] = config_entry_value[I]
config_entry_value = newv
/datum/config_entry/keyed_list/cross_server_bunker_override/ValidateListEntry(key_name, key_value)
return key_value != "byond:\\address:port" && ..()
/datum/config_entry/flag/allow_cross_server_bunker_override
protection = CONFIG_ENTRY_LOCKED
@@ -168,12 +168,14 @@
/datum/config_entry/flag/join_with_mutant_humans //players can pick mutant bodyparts for humans before joining the game
/datum/config_entry/flag/no_summon_guns //No
/datum/config_entry/flag/no_summon_guns //No
/datum/config_entry/flag/no_summon_magic //Fun
/datum/config_entry/flag/no_summon_events //Allowed
/datum/config_entry/flag/no_summon_traumas //!
/datum/config_entry/flag/no_intercept_report //Whether or not to send a communications intercept report roundstart. This may be overridden by gamemodes.
/datum/config_entry/number/arrivals_shuttle_dock_window //Time from when a player late joins on the arrivals shuttle to when the shuttle docks on the station
@@ -56,6 +56,8 @@
/datum/config_entry/flag/log_adminchat // log admin chat messages
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/flag/log_shuttle // log shuttle related actions, ie shuttle computers, shuttle manipulator, emergency console
/datum/config_entry/flag/log_pda // log pda messages
/datum/config_entry/flag/log_telecomms // log telecomms messages
@@ -0,0 +1,11 @@
/// Seconds for CMD on defib-with-memory-loss policy config to display instead of defib-intact config
/datum/config_entry/number/defib_cmd_time_limit
config_entry_value = 300
integer = TRUE
/datum/config_entry/keyed_list/policyconfig
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_TEXT
/datum/config_entry/keyed_list/policyconfig/preprocess_key(key)
return uppertext(..())
+3 -3
View File
@@ -210,10 +210,10 @@
/datum/controller/subsystem/vv_edit_var(var_name, var_value)
switch (var_name)
if ("can_fire")
if (NAMEOF(src, can_fire))
//this is so the subsystem doesn't rapid fire to make up missed ticks causing more lag
if (var_value)
next_fire = world.time + wait
if ("queued_priority") //editing this breaks things.
return 0
if (NAMEOF(src, queued_priority)) //editing this breaks things.
return FALSE
. = ..()
+87 -54
View File
@@ -6,7 +6,7 @@ SUBSYSTEM_DEF(air)
flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/cost_turf_reactions = 0
var/cost_turfs = 0
var/cost_groups = 0
var/cost_highpressure = 0
var/cost_hotspots = 0
@@ -14,10 +14,9 @@ SUBSYSTEM_DEF(air)
var/cost_pipenets = 0
var/cost_rebuilds = 0
var/cost_atmos_machinery = 0
var/cost_equalize = 0
var/list/excited_groups = list()
var/list/active_turfs = list()
var/list/turf_react_queue = list()
var/list/hotspots = list()
var/list/networks = list()
var/list/pipenets_needing_rebuilt = list()
@@ -38,20 +37,25 @@ SUBSYSTEM_DEF(air)
var/map_loading = TRUE
var/list/queued_for_activation
var/log_explosive_decompression = TRUE // If things get spammy, admemes can turn this off.
var/monstermos_turf_limit = 10
var/monstermos_hard_turf_limit = 2000
var/monstermos_enabled = TRUE
/datum/controller/subsystem/air/stat_entry(msg)
msg += "C:{"
msg += "RQ:[round(cost_turf_reactions,1)]|"
msg += "EQ:[round(cost_equalize,1)]|"
msg += "AT:[round(cost_turfs,1)]|"
msg += "EG:[round(cost_groups,1)]|"
msg += "HP:[round(cost_highpressure,1)]|"
msg += "HS:[round(cost_hotspots,1)]|"
msg += "SC:[round(cost_superconductivity,1)]|"
msg += "PN:[round(cost_pipenets,1)]|"
msg += "RB:[round(cost_rebuilds,1)]|"
msg += "AM:[round(cost_atmos_machinery,1)]"
msg += "} "
msg += "AT:[active_turfs.len]|"
msg += "RQ:[turf_react_queue.len]|"
msg += "EG:[excited_groups.len]|"
msg += "EG:[get_amt_excited_groups()]|"
msg += "HS:[hotspots.len]|"
msg += "PN:[networks.len]|"
msg += "HP:[high_pressure_delta.len]|"
@@ -59,8 +63,8 @@ SUBSYSTEM_DEF(air)
msg += "AT/MS:[round((cost ? active_turfs.len/cost : 0),0.1)]"
..(msg)
/datum/controller/subsystem/air/Initialize(timeofday)
extools_update_ssair()
map_loading = FALSE
setup_allturfs()
setup_atmos_machinery()
@@ -68,6 +72,7 @@ SUBSYSTEM_DEF(air)
gas_reactions = init_gas_reactions()
return ..()
/datum/controller/subsystem/air/proc/extools_update_ssair()
/datum/controller/subsystem/air/fire(resumed = 0)
var/timer = TICK_USAGE_REAL
@@ -101,12 +106,21 @@ SUBSYSTEM_DEF(air)
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_REACTQUEUE
currentpart = monstermos_enabled ? SSAIR_EQUALIZE : SSAIR_ACTIVETURFS
if(currentpart == SSAIR_REACTQUEUE)
if(currentpart == SSAIR_EQUALIZE)
timer = TICK_USAGE_REAL
process_react_queue(resumed)
cost_turf_reactions = MC_AVERAGE(cost_turf_reactions, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
process_turf_equalize(resumed)
cost_equalize = MC_AVERAGE(cost_equalize, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_ACTIVETURFS
if(currentpart == SSAIR_ACTIVETURFS)
timer = TICK_USAGE_REAL
process_active_turfs(resumed)
cost_turfs = MC_AVERAGE(cost_turfs, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
@@ -148,6 +162,8 @@ SUBSYSTEM_DEF(air)
resumed = 0
currentpart = SSAIR_REBUILD_PIPENETS
/datum/controller/subsystem/air/proc/process_pipenets(resumed = 0)
if (!resumed)
src.currentrun = networks.Copy()
@@ -182,19 +198,6 @@ SUBSYSTEM_DEF(air)
return
/datum/controller/subsystem/air/proc/process_react_queue(resumed = 0)
if(!resumed)
src.currentrun = turf_react_queue.Copy()
var/list/currentrun = src.currentrun
while(currentrun.len)
var/turf/open/T = currentrun[currentrun.len]
currentrun.len--
if(T)
T.process_cell_reaction()
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_super_conductivity(resumed = 0)
if (!resumed)
src.currentrun = active_super_conductivity.Copy()
@@ -229,10 +232,45 @@ SUBSYSTEM_DEF(air)
high_pressure_delta.len--
T.high_pressure_movements()
T.pressure_difference = 0
T.pressure_specific_target = null
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_turf_equalize(resumed = 0)
//cache for sanic speed
var/fire_count = times_fired
if (!resumed)
src.currentrun = active_turfs.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/turf/open/T = currentrun[currentrun.len]
currentrun.len--
if (T)
T.equalize_pressure_in_zone(fire_count)
//equalize_pressure_in_zone(T, fire_count)
if (MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_active_turfs(resumed = 0)
//cache for sanic speed
var/fire_count = times_fired
if (!resumed)
src.currentrun = active_turfs.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/turf/open/T = currentrun[currentrun.len]
currentrun.len--
if (T)
T.process_cell(fire_count)
if (MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_excited_groups(resumed = 0)
if(process_excited_groups_extools(resumed, (Master.current_ticklimit - TICK_USAGE) * 0.01 * world.tick_lag))
sleep()
/*
if (!resumed)
src.currentrun = excited_groups.Copy()
//cache for sanic speed (lists are references anyways)
@@ -248,29 +286,33 @@ SUBSYSTEM_DEF(air)
EG.dismantle()
if (MC_TICK_CHECK)
return
*/
/datum/controller/subsystem/air/proc/process_excited_groups_extools()
/datum/controller/subsystem/air/proc/get_amt_excited_groups()
/datum/controller/subsystem/air/proc/remove_from_active(turf/open/T)
active_turfs -= T
SSair_turfs.currentrun -= T
if(currentpart == SSAIR_ACTIVETURFS)
currentrun -= T
#ifdef VISUALIZE_ACTIVE_TURFS
T.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, "#00ff00")
#endif
if(istype(T))
T.excited = 0
if(T.excited_group)
T.excited_group.garbage_collect()
remove_from_react_queue(T)
T.set_excited(FALSE)
T.eg_garbage_collect()
/datum/controller/subsystem/air/proc/add_to_active(turf/open/T, blockchanges = 1)
if(istype(T) && T.air)
#ifdef VISUALIZE_ACTIVE_TURFS
T.add_atom_colour("#00ff00", TEMPORARY_COLOUR_PRIORITY)
#endif
T.excited = TRUE
active_turfs[T] = SSair_turfs.currentrun[T] = TRUE
if(blockchanges && T.excited_group)
T.excited_group.garbage_collect()
add_to_react_queue(T)
T.set_excited(TRUE)
active_turfs |= T
if(currentpart == SSAIR_ACTIVETURFS)
currentrun |= T
if(blockchanges)
T.eg_garbage_collect()
else if(T.flags_1 & INITIALIZED_1)
for(var/turf/S in T.atmos_adjacent_turfs)
add_to_active(S)
@@ -281,17 +323,6 @@ SUBSYSTEM_DEF(air)
else
T.requires_activation = TRUE
/datum/controller/subsystem/air/proc/add_to_react_queue(turf/open/T)
if(istype(T) && T.air)
turf_react_queue[T] = TRUE
if(currentpart == SSAIR_REACTQUEUE)
currentrun[T] = TRUE
/datum/controller/subsystem/air/proc/remove_from_react_queue(turf/open/T)
turf_react_queue -= T
if(currentpart == SSAIR_REACTQUEUE)
currentrun -= T
/datum/controller/subsystem/air/StartLoadingMap()
LAZYINITLIST(queued_for_activation)
map_loading = TRUE
@@ -339,11 +370,11 @@ SUBSYSTEM_DEF(air)
while (turfs_to_check.len)
var/ending_ats = active_turfs.len
for(var/thing in excited_groups)
/*for(var/thing in excited_groups)
var/datum/excited_group/EG = thing
EG.self_breakdown(space_is_all_consuming = 1)
EG.dismantle()
CHECK_TICK
//EG.self_breakdown(space_is_all_consuming = 1)
//EG.dismantle()
CHECK_TICK*/
var/msg = "HEY! LISTEN! [DisplayTimeText(world.timeofday - timer)] were wasted processing [starting_ats] turf(s) (connected to [ending_ats] other turfs) with atmos differences at round start."
to_chat(world, "<span class='boldannounce'>[msg]</span>")
@@ -351,6 +382,7 @@ SUBSYSTEM_DEF(air)
/turf/open/proc/resolve_active_graph()
. = list()
/*
var/datum/excited_group/EG = excited_group
if (blocks_air || !air)
return
@@ -371,7 +403,8 @@ SUBSYSTEM_DEF(air)
EG.add_turf(ET)
if (!ET.excited)
ET.excited = 1
. += ET
. += ET*/
/turf/open/space/resolve_active_graph()
return list()
@@ -389,9 +422,8 @@ SUBSYSTEM_DEF(air)
CHECK_TICK
/datum/controller/subsystem/air/proc/setup_template_machinery(list/atmos_machines)
if(!initialized)
return
if(!initialized) // yogs - fixes randomized bars
return // yogs
for(var/A in atmos_machines)
var/obj/machinery/atmospherics/AM = A
AM.atmosinit()
@@ -415,6 +447,7 @@ SUBSYSTEM_DEF(air)
#undef SSAIR_PIPENETS
#undef SSAIR_ATMOSMACHINERY
#undef SSAIR_ACTIVETURFS
#undef SSAIR_EXCITEDGROUPS
#undef SSAIR_HIGHPRESSURE
#undef SSAIR_HOTSPOTS
+2 -1
View File
@@ -1,5 +1,5 @@
//WHAT IF WE TAKE THE ACTIVE TURF PROCESSING AND PUSH IT SOMEWHERE ELSE!!!
/*
SUBSYSTEM_DEF(air_turfs)
name = "Atmospherics - Turfs"
init_order = INIT_ORDER_AIR_TURFS
@@ -24,3 +24,4 @@ SUBSYSTEM_DEF(air_turfs)
return
resumed = 0
return
*/
+24
View File
@@ -16,6 +16,7 @@ SUBSYSTEM_DEF(atoms)
/datum/controller/subsystem/atoms/Initialize(timeofday)
GLOB.fire_overlay.appearance_flags = RESET_COLOR
setupGenetics()
initialized = INITIALIZATION_INNEW_MAPLOAD
InitializeAtoms()
return ..()
@@ -106,6 +107,29 @@ SUBSYSTEM_DEF(atoms)
old_initialized = SSatoms.old_initialized
BadInitializeCalls = SSatoms.BadInitializeCalls
/datum/controller/subsystem/atoms/proc/setupGenetics()
var/list/mutations = subtypesof(/datum/mutation/human)
shuffle_inplace(mutations)
for(var/A in subtypesof(/datum/generecipe))
var/datum/generecipe/GR = A
GLOB.mutation_recipes[initial(GR.required)] = initial(GR.result)
for(var/i in 1 to LAZYLEN(mutations))
var/path = mutations[i] //byond gets pissy when we do it in one line
var/datum/mutation/human/B = new path ()
B.alias = "Mutation [i]"
GLOB.all_mutations[B.type] = B
GLOB.full_sequences[B.type] = generate_gene_sequence(B.blocks)
GLOB.alias_mutations[B.alias] = B.type
if(B.locked)
continue
if(B.quality == POSITIVE)
GLOB.good_mutations |= B
else if(B.quality == NEGATIVE)
GLOB.bad_mutations |= B
else if(B.quality == MINOR_NEGATIVE)
GLOB.not_good_mutations |= B
CHECK_TICK
/datum/controller/subsystem/atoms/proc/InitLog()
. = ""
for(var/path in BadInitializeCalls)
+2 -2
View File
@@ -60,9 +60,9 @@ SUBSYSTEM_DEF(blackbox)
/datum/controller/subsystem/blackbox/vv_edit_var(var_name, var_value)
switch(var_name)
if("feedback")
if(NAMEOF(src, feedback))
return FALSE
if("sealed")
if(NAMEOF(src, sealed))
if(var_value)
return Seal()
return FALSE
+5 -4
View File
@@ -4,6 +4,7 @@ SUBSYSTEM_DEF(chat)
wait = 1
priority = FIRE_PRIORITY_CHAT
init_order = INIT_ORDER_CHAT
var/list/payload = list()
@@ -17,7 +18,7 @@ SUBSYSTEM_DEF(chat)
return
/datum/controller/subsystem/chat/proc/queue(target, message, handle_whitespace = TRUE)
/datum/controller/subsystem/chat/proc/queue(target, message, handle_whitespace = TRUE, trailing_newline = TRUE, confidential = TRUE)
if(!target || !message)
return
@@ -35,8 +36,8 @@ SUBSYSTEM_DEF(chat)
if(handle_whitespace)
message = replacetext(message, "\n", "<br>")
message = replacetext(message, "\t", "[FOURSPACES][FOURSPACES]")
message += "<br>"
if (trailing_newline)
message += "<br>"
//url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
//Do the double-encoding here to save nanoseconds
@@ -47,7 +48,7 @@ SUBSYSTEM_DEF(chat)
var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
if(!C)
return
continue
//Send it to the old style output window.
SEND_TEXT(C, original_message)
+2 -1
View File
@@ -12,7 +12,7 @@ SUBSYSTEM_DEF(events)
var/frequency_upper = 6000 //10 minutes upper bound. Basically an event will happen every 3 to 10 minutes.
var/list/holidays //List of all holidays occuring today or null if no holidays
var/wizardmode = 0
var/wizardmode = FALSE
/datum/controller/subsystem/events/Initialize(time, zlevel)
for(var/type in typesof(/datum/round_event_control))
@@ -91,6 +91,7 @@ SUBSYSTEM_DEF(events)
if(. == EVENT_CANT_RUN)//we couldn't run this event for some reason, set its max_occurrences to 0
E.max_occurrences = 0
else if(. == EVENT_READY)
E.random = TRUE
E.runEvent(TRUE)
//allows a client to trigger an event
+27 -9
View File
@@ -64,19 +64,33 @@ SUBSYSTEM_DEF(input)
// Misc
macroset_classic_input["Tab"] = "\".winset \\\"mainwindow.macro=[SKIN_MACROSET_CLASSIC_HOTKEYS] map.focus=true input.background-color=[COLOR_INPUT_DISABLED]\\\"\""
macroset_classic_input["Escape"] = "\".winset \\\"input.text=\\\"\\\"\\\"\""
// FINALLY, WE CAN DO SOMETHING MORE NORMAL FOR THE SNOWFLAKE-BUT-LESS KEYSET.
// HAHA - SIKE. Because of BYOND weirdness (tl;dr not specifically binding this way results in potentially duplicate chatboxes when
// conflicts occur with something like say indicator vs say), we're going to snowflake this anyways
var/list/hard_binds = list(
"O" = "ooc",
"T" = "say",
"L" = "looc",
"M" = "me"
)
var/list/hard_bind_anti_collision = list()
var/list/anti_collision_modifiers = list("Ctrl", "Alt", "Shift", "Ctrl+Alt", "Ctrl+Shift", "Alt+Shift", "Ctrl+Alt+Shift")
for(var/key in hard_binds)
for(var/modifier in anti_collision_modifiers)
hard_bind_anti_collision["[modifier]+[key]"] = ".NONSENSICAL_VERB_THAT_DOES_NOTHING"
macroset_classic_hotkey = list(
"Any" = "\"KeyDown \[\[*\]\]\"",
"Any+UP" = "\"KeyUp \[\[*\]\]\"",
"Tab" = "\".winset \\\"mainwindow.macro=[SKIN_MACROSET_CLASSIC_INPUT] input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"",
"Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
"O" = "ooc",
"T" = "say",
"L" = "looc",
"M" = "me"
)
macroset_classic_hotkey |= hard_binds
macroset_classic_hotkey |= hard_bind_anti_collision
// And finally, the modern set.
macroset_hotkey = list(
@@ -85,11 +99,10 @@ SUBSYSTEM_DEF(input)
"Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=[COLOR_INPUT_DISABLED]:input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"",
"Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
"O" = "ooc",
"T" = "say",
"L" = "looc",
"M" = "me"
)
macroset_hotkey |= hard_binds
macroset_hotkey |= hard_bind_anti_collision
// Badmins just wanna have fun ♪
/datum/controller/subsystem/input/proc/refresh_client_macro_sets()
@@ -104,3 +117,8 @@ SUBSYSTEM_DEF(input)
for(var/i in 1 to clients.len)
var/client/C = clients[i]
C.keyLoop()
/// *sigh
/client/verb/NONSENSICAL_VERB_THAT_DOES_NOTHING()
set name = ".NONSENSICAL_VERB_THAT_DOES_NOTHING"
set hidden = TRUE
+15 -26
View File
@@ -334,6 +334,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
for (var/map in mapvotes)
if (!map)
mapvotes.Remove(map)
continue
if (!(map in global.config.maplist))
mapvotes.Remove(map)
continue
@@ -468,9 +469,9 @@ GLOBAL_LIST_EMPTY(the_station_areas)
else
return
possible_options += "Custom"
var/lvl_name
var/datum/space_level/level
possible_options = "Custom"
var/away_name
var/datum/space_level/away_level
var/answer = input("What kind ? ","Away/VR") as null|anything in possible_options
switch(answer)
@@ -480,34 +481,22 @@ GLOBAL_LIST_EMPTY(the_station_areas)
var/mapfile = input("Pick file:", "File") as null|file
if(!mapfile)
return
lvl_name = "[mapfile] custom"
to_chat(usr,"<span class='notice'>Loading [lvl_name]...</span>")
away_name = "[mapfile] custom"
to_chat(usr,"<span class='notice'>Loading [away_name]...</span>")
var/datum/map_template/template = new(mapfile, choice, ztraits)
level = template.load_new_z(ztraits)
away_level = template.load_new_z(ztraits)
else
lvl_name = answer
to_chat(usr,"<span class='notice'>Loading [lvl_name]...</span>")
var/datum/map_template/template = new(lvl_name, choice)
level = template.load_new_z(ztraits)
away_name = answer
to_chat(usr,"<span class='notice'>Loading [away_name]...</span>")
var/datum/map_template/template = new(away_name, choice)
away_level = template.load_new_z(ztraits)
message_admins("Admin [key_name_admin(usr)] has loaded [lvl_name] [choice].")
log_admin("Admin [key_name(usr)] has loaded [lvl_name] [choice].")
if(!level)
message_admins("Loading [lvl_name] failed!")
message_admins("Admin [key_name_admin(usr)] has loaded [away_name] away mission.")
log_admin("Admin [key_name(usr)] has loaded [away_name] away mission.")
if(!away_level)
message_admins("Loading [away_name] failed!")
return
if(choice == AWAY_MISSION_NAME && GLOB.the_gateway)
//Link any found away gate with station gate
var/obj/machinery/gateway/centeraway/new_gate
for(var/obj/machinery/gateway/centeraway/G in GLOB.machines)
if(G.z == level.z_value) //I'll have to refactor gateway shitcode before multi-away support.
new_gate = G
break
//Link station gate with away gate and remove wait time.
GLOB.the_gateway.awaygate = new_gate
GLOB.the_gateway.wait = world.time
/datum/controller/subsystem/mapping/proc/RequestBlockReservation(width, height, z, type = /datum/turf_reservation, turf_type_override, border_type_override)
UNTIL((!z || reservation_ready["[z]"]) && !clearing_reserved_turfs)
var/datum/turf_reservation/reserve = new type
+29 -1
View File
@@ -10,24 +10,52 @@ SUBSYSTEM_DEF(materials)
var/list/materials
///Dictionary of category || list of material refs
var/list/materials_by_category
///Dictionary of category || list of material types, mostly used by rnd machines like autolathes.
var/list/materialtypes_by_category
///A cache of all material combinations that have been used
var/list/list/material_combos
///List of stackcrafting recipes for materials using rigid materials
var/list/rigid_stack_recipes = list(
new /datum/stack_recipe("chair", /obj/structure/chair/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("toilet", /obj/structure/toilet/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("sink", /obj/structure/sink/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("Floor tile", /obj/item/stack/tile/material, 1, 4, 20, applies_mats = TRUE)
)
///Ran on initialize, populated the materials and materials_by_category dictionaries with their appropiate vars (See these variables for more info)
/datum/controller/subsystem/materials/proc/InitializeMaterials()
materials = list()
materials_by_category = list()
materialtypes_by_category = list()
material_combos = list()
for(var/type in subtypesof(/datum/material))
var/datum/material/ref = new type
materials[type] = ref
for(var/c in ref.categories)
materials_by_category[c] += list(ref)
materialtypes_by_category[c] += list(type)
/datum/controller/subsystem/materials/proc/GetMaterialRef(datum/material/fakemat)
if(!materials)
InitializeMaterials()
return materials[fakemat] || fakemat
return materials[fakemat] || fakemat
///Returns a list to be used as an object's custom_materials. Lists will be cached and re-used based on the parameters.
/datum/controller/subsystem/materials/proc/FindOrCreateMaterialCombo(list/materials_declaration, multiplier)
if(!material_combos)
InitializeMaterials()
var/list/combo_params = list()
for(var/x in materials_declaration)
var/datum/material/mat = x
var/path_name = ispath(mat) ? "[mat]" : "[mat.type]"
combo_params += "[path_name]=[materials_declaration[mat] * multiplier]"
sortTim(combo_params, /proc/cmp_text_asc) // We have to sort now in case the declaration was not in order
var/combo_index = combo_params.Join("-")
var/list/combo = material_combos[combo_index]
if(!combo)
combo = list()
for(var/mat in materials_declaration)
combo[GetMaterialRef(mat)] = materials_declaration[mat] * multiplier
material_combos[combo_index] = combo
return combo
+310 -39
View File
@@ -7,10 +7,9 @@ SUBSYSTEM_DEF(shuttle)
flags = SS_KEEP_TIMING|SS_NO_TICK_CHECK
runlevels = RUNLEVEL_SETUP | RUNLEVEL_GAME
var/obj/machinery/shuttle_manipulator/manipulator
var/list/mobile = list()
var/list/stationary = list()
var/list/beacons = list()
var/list/transit = list()
var/list/transit_requesters = list()
@@ -57,6 +56,15 @@ SUBSYSTEM_DEF(shuttle)
var/realtimeofstart = 0
var/datum/map_template/shuttle/selected
var/obj/docking_port/mobile/existing_shuttle
var/obj/docking_port/mobile/preview_shuttle
var/datum/map_template/shuttle/preview_template
var/datum/turf_reservation/preview_reservation
/datum/controller/subsystem/shuttle/Initialize(timeofday)
ordernum = rand(1, 9000)
@@ -76,13 +84,10 @@ SUBSYSTEM_DEF(shuttle)
WARNING("No /obj/docking_port/mobile/emergency/backup placed on the map!")
if(!supply)
WARNING("No /obj/docking_port/mobile/supply placed on the map!")
realtimeofstart = world.realtime
realtimeofstart = world.realtime
return ..()
/datum/controller/subsystem/shuttle/proc/initial_load()
if(!istype(manipulator))
CRASH("No shuttle manipulator found.")
for(var/s in stationary)
var/obj/docking_port/stationary/S = s
S.load_roundstart()
@@ -143,11 +148,13 @@ SUBSYSTEM_DEF(shuttle)
++alive
var/total = GLOB.joined_player_list.len
if(total <= 0)
return //no players no autoevac
if(alive / total <= threshold)
var/msg = "Automatically dispatching shuttle due to crew death."
var/msg = "Automatically dispatching emergency shuttle due to crew death."
message_admins(msg)
log_game("[msg] Alive: [alive], Roundstart: [total], Threshold: [threshold]")
log_shuttle("[msg] Alive: [alive], Roundstart: [total], Threshold: [threshold]")
emergencyNoRecall = TRUE
priority_announce("Catastrophic casualties detected: crisis shuttle protocols activated - jamming recall signals across all frequencies.")
if(emergency.timeLeft(1) > emergencyCallTime * 0.4)
@@ -172,6 +179,34 @@ SUBSYSTEM_DEF(shuttle)
return S
WARNING("couldn't find dock with id: [id]")
/datum/controller/subsystem/shuttle/proc/canEvac(mob/user)
var/srd = CONFIG_GET(number/shuttle_refuel_delay)
if(world.time - SSticker.round_start_time < srd)
to_chat(user, "<span class='alert'>The emergency shuttle is refueling. Please wait [DisplayTimeText(srd - (world.time - SSticker.round_start_time))] before trying again.</span>")
return FALSE
switch(emergency.mode)
if(SHUTTLE_RECALL)
to_chat(user, "<span class='alert'>The emergency shuttle may not be called while returning to CentCom.</span>")
return FALSE
if(SHUTTLE_CALL)
to_chat(user, "<span class='alert'>The emergency shuttle is already on its way.</span>")
return FALSE
if(SHUTTLE_DOCKED)
to_chat(user, "<span class='alert'>The emergency shuttle is already here.</span>")
return FALSE
if(SHUTTLE_IGNITING)
to_chat(user, "<span class='alert'>The emergency shuttle is firing its engines to leave.</span>")
return FALSE
if(SHUTTLE_ESCAPE)
to_chat(user, "<span class='alert'>The emergency shuttle is moving away to a safe distance.</span>")
return FALSE
if(SHUTTLE_STRANDED)
to_chat(user, "<span class='alert'>The emergency shuttle has been disabled by CentCom.</span>")
return FALSE
return TRUE
/datum/controller/subsystem/shuttle/proc/requestEvac(mob/user, call_reason)
if(!emergency)
WARNING("requestEvac(): There is no emergency shuttle, but the \
@@ -185,35 +220,14 @@ SUBSYSTEM_DEF(shuttle)
manually, and then calling register() on the mobile docking port. \
Good luck.")
emergency = backup_shuttle
var/srd = CONFIG_GET(number/shuttle_refuel_delay)
if(world.time - SSticker.round_start_time < srd)
to_chat(user, "The emergency shuttle is refueling. Please wait [DisplayTimeText(srd - (world.time - SSticker.round_start_time))] before trying again.")
return
switch(emergency.mode)
if(SHUTTLE_RECALL)
to_chat(user, "The emergency shuttle may not be called while returning to CentCom.")
return
if(SHUTTLE_CALL)
to_chat(user, "The emergency shuttle is already on its way.")
return
if(SHUTTLE_DOCKED)
to_chat(user, "The emergency shuttle is already here.")
return
if(SHUTTLE_IGNITING)
to_chat(user, "The emergency shuttle is firing its engines to leave.")
return
if(SHUTTLE_ESCAPE)
to_chat(user, "The emergency shuttle is moving away to a safe distance.")
return
if(SHUTTLE_STRANDED)
to_chat(user, "The emergency shuttle has been disabled by CentCom.")
return
if(!canEvac(user))
return
call_reason = trim(html_encode(call_reason))
if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH && GLOB.security_level > SEC_LEVEL_GREEN)
to_chat(user, "You must provide a reason.")
to_chat(user, "<span class='alert'>You must provide a reason.</span>")
return
var/area/signal_origin = get_area(user)
@@ -235,11 +249,11 @@ SUBSYSTEM_DEF(shuttle)
var/area/A = get_area(user)
log_game("[key_name(user)] has called the shuttle.")
deadchat_broadcast("<span class='deadsay'><span class='name'>[user.real_name]</span> has called the shuttle at <span class='name'>[A.name]</span>.</span>", user)
log_shuttle("[key_name(user)] has called the emergency shuttle.")
deadchat_broadcast(" has called the shuttle at <span class='name'>[A.name]</span>.", "<span class='name'>[user.real_name]</span>", user)
if(call_reason)
SSblackbox.record_feedback("text", "shuttle_reason", 1, "[call_reason]")
log_game("Shuttle call reason: [call_reason]")
log_shuttle("Shuttle call reason: [call_reason]")
message_admins("[ADMIN_LOOKUPFLW(user)] has called the shuttle. (<A HREF='?_src_=holder;[HrefToken()];trigger_centcom_recall=1'>TRIGGER CENTCOM RECALL</A>)")
/datum/controller/subsystem/shuttle/proc/centcom_recall(old_timer, admiral_message)
@@ -272,9 +286,9 @@ SUBSYSTEM_DEF(shuttle)
/datum/controller/subsystem/shuttle/proc/cancelEvac(mob/user)
if(canRecall())
emergency.cancel(get_area(user))
log_game("[key_name(user)] has recalled the shuttle.")
log_shuttle("[key_name(user)] has recalled the shuttle.")
message_admins("[ADMIN_LOOKUPFLW(user)] has recalled the shuttle.")
deadchat_broadcast("<span class='deadsay'><span class='name'>[user.real_name]</span> has recalled the shuttle from <span class='name'>[get_area_name(user, TRUE)]</span>.</span>", user)
deadchat_broadcast(" has recalled the shuttle from <span class='name'>[get_area_name(user, TRUE)]</span>.", "<span class='name'>[user.real_name]</span>", user)
return 1
/datum/controller/subsystem/shuttle/proc/canRecall()
@@ -294,7 +308,7 @@ SUBSYSTEM_DEF(shuttle)
else
if(emergency.timeLeft(1) < emergencyCallTime * 0.25)
return
return 1
return TRUE
/datum/controller/subsystem/shuttle/proc/autoEvac()
if (!SSticker.IsRoundInProgress())
@@ -322,7 +336,7 @@ SUBSYSTEM_DEF(shuttle)
if(callShuttle)
if(EMERGENCY_IDLE_OR_RECALLED)
emergency.request(null, set_coefficient = 2.5)
log_game("There is no means of calling the shuttle anymore. Shuttle automatically called.")
log_shuttle("There is no means of calling the emergency shuttle anymore. Shuttle automatically called.")
message_admins("All the communications consoles were destroyed and all AIs are inactive. Shuttle called.")
/datum/controller/subsystem/shuttle/proc/registerHostileEnvironment(datum/bad)
@@ -559,6 +573,14 @@ SUBSYSTEM_DEF(shuttle)
shuttle_purchased = SSshuttle.shuttle_purchased
lockdown = SSshuttle.lockdown
selected = SSshuttle.selected
existing_shuttle = SSshuttle.existing_shuttle
preview_shuttle = SSshuttle.preview_shuttle
preview_template = SSshuttle.preview_template
preview_reservation = SSshuttle.preview_reservation
/datum/controller/subsystem/shuttle/proc/is_in_shuttle_bounds(atom/A)
var/area/current = get_area(A)
@@ -641,3 +663,252 @@ SUBSYSTEM_DEF(shuttle)
message_admins("Round end vote passed. Shuttle has been auto-called.")
emergencyNoRecall = TRUE
endvote_passed = TRUE
/datum/controller/subsystem/shuttle/proc/action_load(datum/map_template/shuttle/loading_template, obj/docking_port/stationary/destination_port)
// Check for an existing preview
if(preview_shuttle && (loading_template != preview_template))
preview_shuttle.jumpToNullSpace()
preview_shuttle = null
preview_template = null
QDEL_NULL(preview_reservation)
if(!preview_shuttle)
if(load_template(loading_template))
preview_shuttle.linkup(loading_template, destination_port)
preview_template = loading_template
// get the existing shuttle information, if any
var/timer = 0
var/mode = SHUTTLE_IDLE
var/obj/docking_port/stationary/D
if(istype(destination_port))
D = destination_port
else if(existing_shuttle)
timer = existing_shuttle.timer
mode = existing_shuttle.mode
D = existing_shuttle.get_docked()
if(!D)
D = generate_transit_dock(preview_shuttle)
if(!D)
CRASH("No dock found for preview shuttle ([preview_template.name]), aborting.")
var/result = preview_shuttle.canDock(D)
// truthy value means that it cannot dock for some reason
// but we can ignore the someone else docked error because we'll
// be moving into their place shortly
if((result != SHUTTLE_CAN_DOCK) && (result != SHUTTLE_SOMEONE_ELSE_DOCKED))
WARNING("Template shuttle [preview_shuttle] cannot dock at [D] ([result]).")
return
if(existing_shuttle)
existing_shuttle.jumpToNullSpace()
var/list/force_memory = preview_shuttle.movement_force
preview_shuttle.movement_force = list("KNOCKDOWN" = 0, "THROW" = 0)
preview_shuttle.initiate_docking(D)
preview_shuttle.movement_force = force_memory
. = preview_shuttle
// Shuttle state involves a mode and a timer based on world.time, so
// plugging the existing shuttles old values in works fine.
preview_shuttle.timer = timer
preview_shuttle.mode = mode
preview_shuttle.register()
// TODO indicate to the user that success happened, rather than just
// blanking the modification tab
preview_shuttle = null
preview_template = null
existing_shuttle = null
selected = null
QDEL_NULL(preview_reservation)
/datum/controller/subsystem/shuttle/proc/load_template(datum/map_template/shuttle/S)
. = FALSE
// load shuttle template, centred at shuttle import landmark,
preview_reservation = SSmapping.RequestBlockReservation(S.width, S.height, SSmapping.transit.z_value, /datum/turf_reservation/transit)
if(!preview_reservation)
CRASH("failed to reserve an area for shuttle template loading")
var/turf/BL = TURF_FROM_COORDS_LIST(preview_reservation.bottom_left_coords)
S.load(BL, centered = FALSE, register = FALSE)
var/affected = S.get_affected_turfs(BL, centered=FALSE)
var/found = 0
// Search the turfs for docking ports
// - We need to find the mobile docking port because that is the heart of
// the shuttle.
// - We need to check that no additional ports have slipped in from the
// template, because that causes unintended behaviour.
for(var/T in affected)
for(var/obj/docking_port/P in T)
if(istype(P, /obj/docking_port/mobile))
found++
if(found > 1)
qdel(P, force=TRUE)
log_world("Map warning: Shuttle Template [S.mappath] has multiple mobile docking ports.")
else
preview_shuttle = P
if(istype(P, /obj/docking_port/stationary))
log_world("Map warning: Shuttle Template [S.mappath] has a stationary docking port.")
if(!found)
var/msg = "load_template(): Shuttle Template [S.mappath] has no mobile docking port. Aborting import."
for(var/T in affected)
var/turf/T0 = T
T0.empty()
message_admins(msg)
WARNING(msg)
return
//Everything fine
S.post_load(preview_shuttle)
return TRUE
/datum/controller/subsystem/shuttle/proc/unload_preview()
if(preview_shuttle)
preview_shuttle.jumpToNullSpace()
preview_shuttle = null
/datum/controller/subsystem/shuttle/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "ShuttleManipulator", name, 800, 600, master_ui, state)
ui.open()
/datum/controller/subsystem/shuttle/ui_data(mob/user)
var/list/data = list()
data["tabs"] = list("Status", "Templates", "Modification")
// Templates panel
data["templates"] = list()
var/list/templates = data["templates"]
data["templates_tabs"] = list()
data["selected"] = list()
for(var/shuttle_id in SSmapping.shuttle_templates)
var/datum/map_template/shuttle/S = SSmapping.shuttle_templates[shuttle_id]
if(!templates[S.port_id])
data["templates_tabs"] += S.port_id
templates[S.port_id] = list(
"port_id" = S.port_id,
"templates" = list())
var/list/L = list()
L["name"] = S.name
L["shuttle_id"] = S.shuttle_id
L["port_id"] = S.port_id
L["description"] = S.description
L["admin_notes"] = S.admin_notes
if(selected == S)
data["selected"] = L
templates[S.port_id]["templates"] += list(L)
data["templates_tabs"] = sortList(data["templates_tabs"])
data["existing_shuttle"] = null
// Status panel
data["shuttles"] = list()
for(var/i in mobile)
var/obj/docking_port/mobile/M = i
var/timeleft = M.timeLeft(1)
var/list/L = list()
L["name"] = M.name
L["id"] = M.id
L["timer"] = M.timer
L["timeleft"] = M.getTimerStr()
if (timeleft > 1 HOURS)
L["timeleft"] = "Infinity"
L["can_fast_travel"] = M.timer && timeleft >= 50
L["can_fly"] = TRUE
if(istype(M, /obj/docking_port/mobile/emergency))
L["can_fly"] = FALSE
else if(!M.destination)
L["can_fast_travel"] = FALSE
if (M.mode != SHUTTLE_IDLE)
L["mode"] = capitalize(M.mode)
L["status"] = M.getDbgStatusText()
if(M == existing_shuttle)
data["existing_shuttle"] = L
data["shuttles"] += list(L)
return data
/datum/controller/subsystem/shuttle/ui_act(action, params)
if(..())
return
var/mob/user = usr
// Preload some common parameters
var/shuttle_id = params["shuttle_id"]
var/datum/map_template/shuttle/S = SSmapping.shuttle_templates[shuttle_id]
switch(action)
if("select_template")
if(S)
existing_shuttle = getShuttle(S.port_id)
selected = S
. = TRUE
if("jump_to")
if(params["type"] == "mobile")
for(var/i in mobile)
var/obj/docking_port/mobile/M = i
if(M.id == params["id"])
user.forceMove(get_turf(M))
. = TRUE
break
if("fly")
for(var/i in mobile)
var/obj/docking_port/mobile/M = i
if(M.id == params["id"])
. = TRUE
M.admin_fly_shuttle(user)
break
if("fast_travel")
for(var/i in mobile)
var/obj/docking_port/mobile/M = i
if(M.id == params["id"] && M.timer && M.timeLeft(1) >= 50)
M.setTimer(50)
. = TRUE
message_admins("[key_name_admin(usr)] fast travelled [M]")
log_admin("[key_name(usr)] fast travelled [M]")
SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[M.name]")
break
if("preview")
if(S)
. = TRUE
unload_preview()
load_template(S)
if(preview_shuttle)
preview_template = S
user.forceMove(get_turf(preview_shuttle))
if("load")
if(existing_shuttle == backup_shuttle)
// TODO make the load button disabled
WARNING("The shuttle that the selected shuttle will replace \
is the backup shuttle. Backup shuttle is required to be \
intact for round sanity.")
else if(S)
. = TRUE
// If successful, returns the mobile docking port
var/obj/docking_port/mobile/mdp = action_load(S)
if(mdp)
user.forceMove(get_turf(mdp))
message_admins("[key_name_admin(usr)] loaded [mdp] with the shuttle manipulator.")
log_admin("[key_name(usr)] loaded [mdp] with the shuttle manipulator.</span>")
SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[mdp.name]")
+1 -1
View File
@@ -11,7 +11,7 @@ SUBSYSTEM_DEF(tgui)
var/basehtml // The HTML base used for all UIs.
/datum/controller/subsystem/tgui/PreInit()
basehtml = file2text('tgui-next/packages/tgui/public/tgui-main.html')
basehtml = file2text('tgui/packages/tgui/public/tgui.html')
/datum/controller/subsystem/tgui/Shutdown()
close_all_uis()
+2 -2
View File
@@ -47,7 +47,7 @@ SUBSYSTEM_DEF(title)
. = ..()
if(.)
switch(var_name)
if("icon")
if(NAMEOF(src, icon))
if(splash_turf)
splash_turf.icon = icon
@@ -66,4 +66,4 @@ SUBSYSTEM_DEF(title)
icon = SStitle.icon
splash_turf = SStitle.splash_turf
file_path = SStitle.file_path
previous_icon = SStitle.previous_icon
previous_icon = SStitle.previous_icon
+5 -5
View File
@@ -79,7 +79,7 @@ SUBSYSTEM_DEF(traumas)
/obj/item/pda/clown, /obj/item/grown/bananapeel)),
"greytide" = typecacheof(list(/obj/item/clothing/under/color/grey, /obj/item/melee/baton/cattleprod,
/obj/item/twohanded/spear, /obj/item/clothing/mask/gas)),
/obj/item/spear, /obj/item/clothing/mask/gas)),
"lizards" = typecacheof(list(/obj/item/toy/plush/lizardplushie, /obj/item/reagent_containers/food/snacks/kebab/tail,
/obj/item/organ/tail/lizard, /obj/item/reagent_containers/food/drinks/bottle/lizardwine)),
@@ -120,7 +120,7 @@ SUBSYSTEM_DEF(traumas)
/obj/item/card/id/captains_spare, /obj/item/card/id/centcom, /obj/machinery/door/airlock/command)),
"the supernatural" = typecacheof(list(/obj/structure/destructible/cult, /obj/item/tome,
/obj/item/melee/cultblade, /obj/item/twohanded/required/cult_bastard, /obj/item/restraints/legcuffs/bola/cult,
/obj/item/melee/cultblade, /obj/item/cult_bastard, /obj/item/restraints/legcuffs/bola/cult,
/obj/item/clothing/suit/cultrobes, /obj/item/clothing/suit/space/hardsuit/cult,
/obj/item/clothing/suit/hooded/cultrobes, /obj/item/clothing/head/hooded/cult_hoodie, /obj/effect/rune,
/obj/item/stack/sheet/runed_metal, /obj/machinery/door/airlock/cult, /obj/singularity/narsie,
@@ -151,7 +151,7 @@ SUBSYSTEM_DEF(traumas)
"anime" = typecacheof(list(/obj/item/clothing/under/costume/schoolgirl, /obj/item/katana, /obj/item/reagent_containers/food/snacks/sashimi, /obj/item/reagent_containers/food/snacks/chawanmushi,
/obj/item/reagent_containers/food/drinks/bottle/sake, /obj/item/throwing_star, /obj/item/clothing/head/kitty/genuine, /obj/item/clothing/suit/space/space_ninja,
/obj/item/clothing/mask/gas/space_ninja, /obj/item/clothing/shoes/space_ninja, /obj/item/clothing/gloves/space_ninja, /obj/item/twohanded/vibro_weapon,
/obj/item/clothing/mask/gas/space_ninja, /obj/item/clothing/shoes/space_ninja, /obj/item/clothing/gloves/space_ninja, /obj/item/vibro_weapon,
/obj/item/nullrod/scythe/vibro, /obj/item/energy_katana, /obj/item/toy/katana, /obj/item/nullrod/claymore/katana, /obj/structure/window/paperframe, /obj/structure/mineral_door/paperframe)),
"mimes" = typecacheof(list(/obj/item/pda/mime, /obj/item/clothing/under/rank/civilian/mime, /obj/item/clothing/mask/gas/mime,
@@ -168,7 +168,7 @@ SUBSYSTEM_DEF(traumas)
/obj/machinery/porta_turret/syndicate, /obj/structure/closet/syndicate, /obj/machinery/suit_storage_unit/syndicate, /obj/item/clothing/under/syndicate, /obj/item/folder/syndicate, /obj/item/documents/syndicate, /obj/item/clothing/glasses/phantomthief/syndicate, /obj/item/antag_spawner/nuke_ops, /obj/item/storage/box/syndicate,
/obj/structure/fluff/empty_sleeper/syndicate, /obj/item/implant/radio/syndicate, /obj/item/clothing/head/helmet/space/syndicate, /obj/machinery/nuclearbomb/syndicate, /obj/item/grenade/syndieminibomb, /obj/item/storage/backpack/duffelbag/syndie, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver,
/obj/item/gun/ballistic/automatic/shotgun/bulldog, /obj/item/gun/ballistic/automatic/c20r, /obj/item/gun/ballistic/automatic/m90, /obj/item/gun/ballistic/automatic/l6_saw, /obj/item/storage/belt/grenade/full, /obj/item/gun/ballistic/automatic/sniper_rifle/syndicate, /obj/item/gun/energy/kinetic_accelerator/crossbow,
/obj/item/melee/transforming/energy/sword/saber, /obj/item/twohanded/dualsaber, /obj/item/melee/powerfist, /obj/item/storage/box/syndie_kit, /obj/item/grenade/spawnergrenade/manhacks, /obj/item/grenade/chem_grenade/bioterrorfoam, /obj/item/reagent_containers/spray/chemsprayer/bioterror, /obj/item/ammo_box/magazine/m10mm,
/obj/item/melee/transforming/energy/sword/saber, /obj/item/dualsaber, /obj/item/melee/powerfist, /obj/item/storage/box/syndie_kit, /obj/item/grenade/spawnergrenade/manhacks, /obj/item/grenade/chem_grenade/bioterrorfoam, /obj/item/reagent_containers/spray/chemsprayer/bioterror, /obj/item/ammo_box/magazine/m10mm,
/obj/item/ammo_box/magazine/pistolm9mm, /obj/item/ammo_box/a357, /obj/item/ammo_box/magazine/m12g, /obj/item/ammo_box/magazine/mm195x129, /obj/item/antag_spawner/nuke_ops, /obj/mecha/combat/gygax/dark, /obj/mecha/combat/marauder/mauler, /obj/item/soap/syndie, /obj/item/gun/syringe/syndicate, /obj/item/cartridge/virus/syndicate,
/obj/item/cartridge/virus/frame, /obj/item/chameleon, /obj/item/storage/box/syndie_kit/cutouts, /obj/item/clothing/suit/space/hardsuit/syndi, /obj/item/card/emag, /obj/item/storage/toolbox/syndicate, /obj/item/storage/book/bible/syndicate, /obj/item/encryptionkey/binary, /obj/item/encryptionkey/syndicate, /obj/item/aiModule/syndicate,
/obj/item/clothing/shoes/magboots/syndie, /obj/item/powersink, /obj/item/sbeacondrop, /obj/item/sbeacondrop/bomb, /obj/item/syndicatedetonator, /obj/item/shield/energy, /obj/item/assault_pod, /obj/item/slimepotion/slime/sentience/nuclear, /obj/item/stack/telecrystal, /obj/item/jammer, /obj/item/codespeak_manual/unlimited,
@@ -176,7 +176,7 @@ SUBSYSTEM_DEF(traumas)
/obj/machinery/computer/pod/old/syndicate, /obj/machinery/vending/medical/syndicate_access, /obj/item/mmi/syndie, /obj/item/target/syndicate, /obj/machinery/vending/cigarette/syndicate, /obj/item/robot_module/syndicate, /obj/item/clothing/mask/gas/syndicate, /obj/machinery/power/singularity_beacon/syndicate, /obj/item/clothing/head/syndicatefake,
/obj/item/radio/headset/syndicate, /obj/item/gun/ballistic/automatic/pistol/antitank/syndicate, /obj/item/pda/syndicate, /obj/item/clothing/suit/armor/vest/capcarapace/syndicate, /obj/item/gun/ballistic/automatic/flechette, /obj/item/ammo_box/magazine/flechette, /obj/item/clothing/suit/toggle/lawyer/black/syndie, /obj/item/melee/transforming/energy/sword/cx/traitor,
/obj/structure/sign/poster/contraband/syndicate_pistol, /obj/structure/sign/poster/contraband/syndicate_recruitment, /obj/item/bedsheet/syndie, /obj/item/borg/upgrade/syndicate, /obj/item/tank/jetpack/oxygen/harness, /obj/item/firing_pin/implant/pindicate, /obj/item/reagent_containers/glass/bottle/traitor, /obj/item/storage/belt/military,
/obj/item/twohanded/shockpaddles/syndicate, /obj/item/clothing/mask/cigarette/syndicate, /obj/item/toy/plush/nukeplushie)),
/obj/item/shockpaddles/syndicate, /obj/item/clothing/mask/cigarette/syndicate, /obj/item/toy/plush/nukeplushie)),
"eye" = typecacheof(list(/obj/item/organ/eyes, /obj/item/reagent_containers/syringe))
)
+52 -1
View File
@@ -15,6 +15,8 @@ SUBSYSTEM_DEF(vote)
var/vote_system = PLURALITY_VOTING
var/question = null
var/list/choices = list()
/// List of choice = object for statclick objects for statpanel voting
var/list/choice_statclicks = list()
var/list/scores = list()
var/list/choice_descs = list() // optional descriptions
var/list/voted = list()
@@ -47,7 +49,33 @@ SUBSYSTEM_DEF(vote)
client_popup.open(0)
next_pop = world.time+VOTE_COOLDOWN
/**
* Renders a statpanel. Directly uses statpanel/stat calls since this is called from base of mob/Stat().
*/
/datum/controller/subsystem/vote/proc/render_statpanel(mob/M)
if(!mode) // check if vote is running
return
if(!statpanel("Status")) // don't bother if they're not focused on this panel
return
var/static/list/supported = list(PLURALITY_VOTING, APPROVAL_VOTING)
stat("Vote active!", "There is currently a vote running. Question: [question]")
if(!(vote_system in supported))
stat("<STATPANEL VOTING DISABLED>", "The current vote system is not supported by statpanel rendering. Please vote manually by opening the vote popup using the action button or chat link.")
return
stat("Time Left:", "[round(end_time - world.time)] seconds")
stat(null, null)
stat("Choices:", null)
stat(null, null)
for(var/i in 1 to choice_statclicks.len)
var/choice = choice_statclicks[i]
var/ivotedforthis = FALSE
switch(vote_system)
if(APPROVAL_VOTING)
ivotedforthis = voted[usr.ckey] && (i in voted[usr.ckey])
if(PLURALITY_VOTING)
ivotedforthis = voted[usr.ckey] == i
stat(ivotedforthis? "\[X\]" : "\[ \]", choice_statclicks[choice])
stat(null, null)
/datum/controller/subsystem/vote/proc/reset()
initiator = null
@@ -59,9 +87,26 @@ SUBSYSTEM_DEF(vote)
voted.Cut()
voting.Cut()
scores.Cut()
cleanup_statclicks()
display_votes = initial(display_votes) //CIT CHANGE - obfuscated votes
remove_action_buttons()
/datum/controller/subsystem/vote/proc/cleanup_statclicks()
for(var/choice in choice_statclicks)
qdel(choice_statclicks[choice])
choice_statclicks = list()
/obj/effect/statclick/vote
name = "ERROR"
var/choice
/obj/effect/statclick/vote/Click()
SSvote.submit_vote(choice)
/obj/effect/statclick/vote/New(loc, choice, name)
src.choice = choice
src.name = name
/datum/controller/subsystem/vote/proc/get_result()
//get the highest number of votes
var/greatest_votes = 0
@@ -536,6 +581,12 @@ SUBSYSTEM_DEF(vote)
vp = CONFIG_GET(number/vote_period)
to_chat(world, "\n<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 [DisplayTimeText(vp)] to vote.</font>")
end_time = started_time+vp
// generate statclick list
cleanup_statclicks()
for(var/i in 1 to choices.len)
var/choice = choices[i]
choice_statclicks[choice] = new /obj/effect/statclick/vote(null, i, choice)
//
for(var/c in GLOB.clients)
SEND_SOUND(c, sound('sound/misc/server-ready.ogg'))
var/client/C = c
+55
View File
@@ -5,6 +5,61 @@
/datum/brain_trauma/magic
resilience = TRAUMA_RESILIENCE_LOBOTOMY
/datum/brain_trauma/magic/lumiphobia
name = "Lumiphobia"
desc = "Patient has an inexplicable adverse reaction to light."
scan_desc = "light hypersensitivity"
gain_text = "<span class='warning'>You feel a craving for darkness.</span>"
lose_text = "<span class='notice'>Light no longer bothers you.</span>"
var/next_damage_warning = 0
/datum/brain_trauma/magic/lumiphobia/on_life()
..()
var/turf/T = owner.loc
if(istype(T))
var/light_amount = T.get_lumcount()
if(light_amount > SHADOW_SPECIES_LIGHT_THRESHOLD) //if there's enough light, start dying
if(world.time > next_damage_warning)
to_chat(owner, "<span class='warning'><b>The light burns you!</b></span>")
next_damage_warning = world.time + 100 //Avoid spamming
owner.take_overall_damage(0,3)
/datum/brain_trauma/magic/poltergeist
name = "Poltergeist"
desc = "Patient appears to be targeted by a violent invisible entity."
scan_desc = "paranormal activity"
gain_text = "<span class='warning'>You feel a hateful presence close to you.</span>"
lose_text = "<span class='notice'>You feel the hateful presence fade away.</span>"
/datum/brain_trauma/magic/poltergeist/on_life()
..()
if(prob(4))
var/most_violent = -1 //So it can pick up items with 0 throwforce if there's nothing else
var/obj/item/throwing
for(var/obj/item/I in view(5, get_turf(owner)))
if(I.anchored)
continue
if(I.throwforce > most_violent)
most_violent = I.throwforce
throwing = I
if(throwing)
throwing.throw_at(owner, 8, 2)
/datum/brain_trauma/magic/antimagic
name = "Athaumasia"
desc = "Patient is completely inert to magical forces."
scan_desc = "thaumic blank"
gain_text = "<span class='notice'>You realize that magic cannot be real.</span>"
lose_text = "<span class='notice'>You realize that magic might be real.</span>"
/datum/brain_trauma/magic/antimagic/on_gain()
ADD_TRAIT(owner, TRAIT_ANTIMAGIC, TRAUMA_TRAIT)
..()
/datum/brain_trauma/magic/antimagic/on_lose()
REMOVE_TRAIT(owner, TRAIT_ANTIMAGIC, TRAUMA_TRAIT)
..()
/datum/brain_trauma/magic/stalker
name = "Stalking Phantom"
desc = "Patient is stalked by a phantom only they can see."
+2 -1
View File
@@ -119,8 +119,9 @@
/datum/brain_trauma/severe/paralysis/spinesnapped
random_gain = FALSE
clonable = FALSE
paralysis_type = "legs"
resilience = TRAUMA_RESILIENCE_LOBOTOMY
resilience = TRAUMA_RESILIENCE_LOBOTOMY // It shouldn't fix severed spinal cords really, but there is no specific surgery for that yet.
/datum/brain_trauma/severe/narcolepsy
name = "Narcolepsy"
+1 -1
View File
@@ -104,7 +104,7 @@
QDEL_IN(src, 300)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/effect/hallucination/simple/bluespace_stream/attack_hand(mob/user)
/obj/effect/hallucination/simple/bluespace_stream/attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(user != seer || !linked_to)
return
var/slip_in_message = pick("slides sideways in an odd way, and disappears", "jumps into an unseen dimension",\
+463
View File
@@ -0,0 +1,463 @@
/datum/component/personal_crafting/Initialize()
if(ismob(parent))
RegisterSignal(parent, COMSIG_MOB_CLIENT_LOGIN, .proc/create_mob_button)
/datum/component/personal_crafting/proc/create_mob_button(mob/user, client/CL)
var/datum/hud/H = user.hud_used
var/obj/screen/craft/C = new()
C.icon = H.ui_style
H.static_inventory += C
CL.screen += C
RegisterSignal(C, COMSIG_CLICK, .proc/component_ui_interact)
/datum/component/personal_crafting
var/busy
var/viewing_category = 1 //typical powergamer starting on the Weapons tab
var/viewing_subcategory = 1
var/list/categories = list(
CAT_WEAPONRY = list(
CAT_WEAPON,
CAT_AMMO,
),
CAT_ROBOT = CAT_NONE,
CAT_MISC = list(
CAT_MISCELLANEOUS,
CAT_TOOL,
CAT_FURNITURE,
),
CAT_PRIMAL = CAT_NONE,
CAT_FOOD = list(
CAT_BREAD,
CAT_BURGER,
CAT_CAKE,
CAT_DONUT,
CAT_EGG,
CAT_ICE,
CAT_MEAT,
CAT_MEXICAN,
CAT_MISCFOOD,
CAT_PASTRY,
CAT_PIE,
CAT_PIZZA,
CAT_SEAFOOD,
CAT_SALAD,
CAT_SANDWICH,
CAT_SOUP,
CAT_SPAGHETTI,
),
CAT_DRINK = CAT_NONE,
CAT_CLOTHING = CAT_NONE,
)
var/cur_category = CAT_NONE
var/cur_subcategory = CAT_NONE
var/datum/action/innate/crafting/button
var/display_craftable_only = FALSE
var/display_compact = TRUE
/* This is what procs do:
get_environment - gets a list of things accessable for crafting by user
get_surroundings - takes a list of things and makes a list of key-types to values-amounts of said type in the list
check_contents - takes a recipe and a key-type list and checks if said recipe can be done with available stuff
check_tools - takes recipe, a key-type list, and a user and checks if there are enough tools to do the stuff, checks bugs one level deep
construct_item - takes a recipe and a user, call all the checking procs, calls do_after, checks all the things again, calls del_reqs, creates result, calls CheckParts of said result with argument being list returned by deel_reqs
del_reqs - takes recipe and a user, loops over the recipes reqs var and tries to find everything in the list make by get_environment and delete it/add to parts list, then returns the said list
*/
/**
* Check that the contents of the recipe meet the requirements.
*
* user: The /mob that initated the crafting.
* R: The /datum/crafting_recipe being attempted.
* contents: List of items to search for R's reqs.
*/
/datum/component/personal_crafting/proc/check_contents(atom/a, datum/crafting_recipe/R, list/contents)
var/list/item_instances = contents["instances"]
contents = contents["other"]
var/list/requirements_list = list()
// Process all requirements
for(var/requirement_path in R.reqs)
// Check we have the appropriate amount available in the contents list
var/needed_amount = R.reqs[requirement_path]
for(var/content_item_path in contents)
// Right path and not blacklisted
if(!ispath(content_item_path, requirement_path) || R.blacklist.Find(content_item_path))
continue
needed_amount -= contents[content_item_path]
if(needed_amount <= 0)
break
if(needed_amount > 0)
return FALSE
// Store the instances of what we will use for R.check_requirements() for requirement_path
var/list/instances_list = list()
for(var/instance_path in item_instances)
if(ispath(instance_path, requirement_path))
instances_list += item_instances[instance_path]
requirements_list[requirement_path] = instances_list
for(var/requirement_path in R.chem_catalysts)
if(contents[requirement_path] < R.chem_catalysts[requirement_path])
return FALSE
return R.check_requirements(a, requirements_list)
/datum/component/personal_crafting/proc/get_environment(atom/a, list/blacklist = null, radius_range = 1)
. = list()
if(!isturf(a.loc))
return
for(var/atom/movable/AM in range(radius_range, a))
if(AM.flags_1 & HOLOGRAM_1)
continue
. += AM
/datum/component/personal_crafting/proc/get_surroundings(atom/a)
. = list()
.["tool_behaviour"] = list()
.["other"] = list()
.["instances"] = list()
for(var/obj/item/I in get_environment(a))
if(I.flags_1 & HOLOGRAM_1)
continue
if(.["instances"][I.type])
.["instances"][I.type] += I
else
.["instances"][I.type] = list(I)
if(istype(I, /obj/item/stack))
var/obj/item/stack/S = I
.["other"][I.type] += S.amount
else if(I.tool_behaviour)
.["tool_behaviour"] += I.tool_behaviour
.["other"][I.type] += 1
else
if(istype(I, /obj/item/reagent_containers))
var/obj/item/reagent_containers/RC = I
if(RC.is_drainable())
for(var/datum/reagent/A in RC.reagents.reagent_list)
.["other"][A.type] += A.volume
.["other"][I.type] += 1
/datum/component/personal_crafting/proc/check_tools(atom/a, datum/crafting_recipe/R, list/contents)
if(!R.tools.len)
return TRUE
var/list/possible_tools = list()
var/list/present_qualities = list()
present_qualities |= contents["tool_behaviour"]
for(var/obj/item/I in a.contents)
if(istype(I, /obj/item/storage))
for(var/obj/item/SI in I.contents)
possible_tools += SI.type
if(SI.tool_behaviour)
present_qualities.Add(SI.tool_behaviour)
possible_tools += I.type
if(I.tool_behaviour)
present_qualities.Add(I.tool_behaviour)
possible_tools |= contents["other"]
main_loop:
for(var/A in R.tools)
if(A in present_qualities)
continue
else
for(var/I in possible_tools)
if(ispath(I, A))
continue main_loop
return FALSE
return TRUE
/datum/component/personal_crafting/proc/construct_item(atom/a, datum/crafting_recipe/R)
var/list/contents = get_surroundings(a)
var/send_feedback = 1
if(check_contents(a, R, contents))
if(check_tools(a, R, contents))
//If we're a mob we'll try a do_after; non mobs will instead instantly construct the item
if(ismob(a) && !do_after(a, R.time, target = a))
return "."
contents = get_surroundings(a)
if(!check_contents(a, R, contents))
return ", missing component."
if(!check_tools(a, R, contents))
return ", missing tool."
var/list/parts = del_reqs(R, a)
var/atom/movable/I = new R.result (get_turf(a.loc))
I.CheckParts(parts, R)
if(send_feedback)
SSblackbox.record_feedback("tally", "object_crafted", 1, I.type)
return I //Send the item back to whatever called this proc so it can handle whatever it wants to do with the new item
return ", missing tool."
return ", missing component."
/*Del reqs works like this:
Loop over reqs var of the recipe
Set var amt to the value current cycle req is pointing to, its amount of type we need to delete
Get var/surroundings list of things accessable to crafting by get_environment()
Check the type of the current cycle req
If its reagent then do a while loop, inside it try to locate() reagent containers, inside such containers try to locate needed reagent, if there isnt remove thing from surroundings
If there is enough reagent in the search result then delete the needed amount, create the same type of reagent with the same data var and put it into deletion list
If there isnt enough take all of that reagent from the container, put into deletion list, substract the amt var by the volume of reagent, remove the container from surroundings list and keep searching
While doing above stuff check deletion list if it already has such reagnet, if yes merge instead of adding second one
If its stack check if it has enough amount
If yes create new stack with the needed amount and put in into deletion list, substract taken amount from the stack
If no put all of the stack in the deletion list, substract its amount from amt and keep searching
While doing above stuff check deletion list if it already has such stack type, if yes try to merge them instead of adding new one
If its anything else just locate() in in the list in a while loop, each find --s the amt var and puts the found stuff in deletion loop
Then do a loop over parts var of the recipe
Do similar stuff to what we have done above, but now in deletion list, until the parts conditions are satisfied keep taking from the deletion list and putting it into parts list for return
After its done loop over deletion list and delete all the shit that wasnt taken by parts loop
del_reqs return the list of parts resulting object will receive as argument of CheckParts proc, on the atom level it will add them all to the contents, on all other levels it calls ..() and does whatever is needed afterwards but from contents list already
*/
/datum/component/personal_crafting/proc/del_reqs(datum/crafting_recipe/R, atom/a)
var/list/surroundings
var/list/Deletion = list()
. = list()
var/data
var/amt
main_loop:
for(var/A in R.reqs)
amt = R.reqs[A]
surroundings = get_environment(a, R.blacklist)
surroundings -= Deletion
if(ispath(A, /datum/reagent))
var/datum/reagent/RG = new A
var/datum/reagent/RGNT
while(amt > 0)
var/obj/item/reagent_containers/RC = locate() in surroundings
RG = RC.reagents.get_reagent(A)
if(RG)
if(!locate(RG.type) in Deletion)
Deletion += new RG.type()
if(RG.volume > amt)
RG.volume -= amt
data = RG.data
RC.reagents.conditional_update(RC)
RG = locate(RG.type) in Deletion
RG.volume = amt
RG.data += data
continue main_loop
else
surroundings -= RC
amt -= RG.volume
RC.reagents.reagent_list -= RG
RC.reagents.conditional_update(RC)
RGNT = locate(RG.type) in Deletion
RGNT.volume += RG.volume
RGNT.data += RG.data
qdel(RG)
RC.on_reagent_change()
else
surroundings -= RC
else if(ispath(A, /obj/item/stack))
var/obj/item/stack/S
var/obj/item/stack/SD
while(amt > 0)
S = locate(A) in surroundings
if(S.amount >= amt)
if(!locate(S.type) in Deletion)
SD = new S.type()
Deletion += SD
S.use(amt)
SD = locate(S.type) in Deletion
SD.amount += amt
continue main_loop
else
amt -= S.amount
if(!locate(S.type) in Deletion)
Deletion += S
else
data = S.amount
S = locate(S.type) in Deletion
S.add(data)
surroundings -= S
else
var/atom/movable/I
while(amt > 0)
I = locate(A) in surroundings
Deletion += I
surroundings -= I
amt--
var/list/partlist = list(R.parts.len)
for(var/M in R.parts)
partlist[M] = R.parts[M]
for(var/A in R.parts)
if(istype(A, /datum/reagent))
var/datum/reagent/RG = locate(A) in Deletion
if(RG.volume > partlist[A])
RG.volume = partlist[A]
. += RG
Deletion -= RG
continue
else if(istype(A, /obj/item/stack))
var/obj/item/stack/ST = locate(A) in Deletion
if(ST.amount > partlist[A])
ST.amount = partlist[A]
. += ST
Deletion -= ST
continue
else
while(partlist[A] > 0)
var/atom/movable/AM = locate(A) in Deletion
. += AM
Deletion -= AM
partlist[A] -= 1
while(Deletion.len)
var/DL = Deletion[Deletion.len]
Deletion.Cut(Deletion.len)
qdel(DL)
/datum/component/personal_crafting/proc/component_ui_interact(obj/screen/craft/image, location, control, params, user)
if(user == parent)
ui_interact(user)
//For the UI related things we're going to assume the user is a mob rather than typesetting it to an atom as the UI isn't generated if the parent is an atom
/datum/component/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_turf_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
cur_category = categories[1]
if(islist(categories[cur_category]))
var/list/subcats = categories[cur_category]
cur_subcategory = subcats[1]
else
cur_subcategory = CAT_NONE
ui = new(user, src, ui_key, "PersonalCrafting", "Crafting Menu", 700, 800, master_ui, state)
ui.open()
/datum/component/personal_crafting/ui_data(mob/user)
var/list/data = list()
data["busy"] = busy
data["category"] = cur_category
data["subcategory"] = cur_subcategory
data["display_craftable_only"] = display_craftable_only
data["display_compact"] = display_compact
var/list/surroundings = get_surroundings(user)
var/list/craftability = list()
for(var/rec in GLOB.crafting_recipes)
var/datum/crafting_recipe/R = rec
if(!R.always_availible && !(R.type in user?.mind?.learned_recipes)) //User doesn't actually know how to make this.
continue
if((R.category != cur_category) || (R.subcategory != cur_subcategory))
continue
craftability["[REF(R)]"] = check_contents(user, R, surroundings)
data["craftability"] = craftability
return data
/datum/component/personal_crafting/ui_static_data(mob/user)
var/list/data = list()
var/list/crafting_recipes = list()
for(var/rec in GLOB.crafting_recipes)
var/datum/crafting_recipe/R = rec
if(R.name == "") //This is one of the invalid parents that sneaks in
continue
if(!R.always_availible && !(R.type in user?.mind?.learned_recipes)) //User doesn't actually know how to make this.
continue
if(isnull(crafting_recipes[R.category]))
crafting_recipes[R.category] = list()
if(R.subcategory == CAT_NONE)
crafting_recipes[R.category] += list(build_recipe_data(R))
else
if(isnull(crafting_recipes[R.category][R.subcategory]))
crafting_recipes[R.category][R.subcategory] = list()
crafting_recipes[R.category]["has_subcats"] = TRUE
crafting_recipes[R.category][R.subcategory] += list(build_recipe_data(R))
data["crafting_recipes"] = crafting_recipes
return data
/datum/component/personal_crafting/ui_act(action, params)
if(..())
return
switch(action)
if("make")
var/mob/user = usr
var/datum/crafting_recipe/TR = locate(params["recipe"]) in GLOB.crafting_recipes
busy = TRUE
ui_interact(user)
var/atom/movable/result = construct_item(user, TR)
if(!istext(result)) //We made an item and didn't get a fail message
if(ismob(user) && isitem(result)) //In case the user is actually possessing a non mob like a machine
user.put_in_hands(result)
else
result.forceMove(user.drop_location())
to_chat(user, "<span class='notice'>[TR.name] constructed.</span>")
else
to_chat(user, "<span class='warning'>Construction failed[result]</span>")
busy = FALSE
if("toggle_recipes")
display_craftable_only = !display_craftable_only
. = TRUE
if("toggle_compact")
display_compact = !display_compact
. = TRUE
if("set_category")
if(!isnull(params["category"]))
cur_category = params["category"]
if(!isnull(params["subcategory"]))
if(params["subcategory"] == "0")
cur_subcategory = ""
else
cur_subcategory = params["subcategory"]
. = TRUE
/datum/component/personal_crafting/proc/build_recipe_data(datum/crafting_recipe/R)
var/list/data = list()
data["name"] = R.name
data["ref"] = "[REF(R)]"
var/req_text = ""
var/tool_text = ""
var/catalyst_text = ""
for(var/a in R.reqs)
//We just need the name, so cheat-typecast to /atom for speed (even tho Reagents are /datum they DO have a "name" var)
//Also these are typepaths so sadly we can't just do "[a]"
var/atom/A = a
req_text += " [R.reqs[A]] [initial(A.name)],"
req_text = replacetext(req_text,",","",-1)
data["req_text"] = req_text
for(var/a in R.chem_catalysts)
var/atom/A = a //cheat-typecast
catalyst_text += " [R.chem_catalysts[A]] [initial(A.name)],"
catalyst_text = replacetext(catalyst_text,",","",-1)
data["catalyst_text"] = catalyst_text
for(var/a in R.tools)
if(ispath(a, /obj/item))
var/obj/item/b = a
tool_text += " [initial(b.name)],"
else
tool_text += " [a],"
tool_text = replacetext(tool_text,",","",-1)
data["tool_text"] = tool_text
return data
//Mind helpers
/datum/mind/proc/teach_crafting_recipe(R)
if(!learned_recipes)
learned_recipes = list()
learned_recipes |= R
@@ -92,7 +92,7 @@
qdel(src)
/obj/item/glasswork/glasses
name = "Hand Made Glasses"
desc = "Hande made glasses that have not been polished at all making them useless. Selling them could still be worth a bit of credits."
name = "Handmade Glasses"
desc = "Handmade glasses that have not been polished at all making them useless. Selling them could still be worth a few credits."
icon = 'icons/obj/glass_ware.dmi'
icon_state = "frames_2"
+11 -51
View File
@@ -1,4 +1,4 @@
k// PARTS //
// PARTS //
/obj/item/weaponcrafting
icon = 'icons/obj/improvised.dmi'
@@ -8,61 +8,33 @@ k// PARTS //
custom_materials = list(/datum/material/wood = MINERAL_MATERIAL_AMOUNT * 6)
icon_state = "riflestock"
/obj/item/weaponcrafting/durathread_string
name = "durathread string"
desc = "A long piece of durathread with some resemblance to cable coil."
/obj/item/weaponcrafting/string
name = "wound thread"
desc = "A long piece of thread with some resemblance to cable coil."
icon_state = "durastring"
////////////////////////////////
// KAT IMPROVISED WEAPON PARTS//
// IMPROVISED WEAPON PARTS//
////////////////////////////////
/obj/item/weaponcrafting/improvised_parts
name = "Eerie bunch of coloured dots."
desc = "You feel the urge to report to Central that the parent type of guncrafting, which should never appear in this reality, has appeared. Whatever that means."
name = "Debug Improvised Gun Part"
desc = "A badly coded gun part. You should report coders if you see this."
icon = 'icons/obj/guns/gun_parts.dmi'
icon_state = "palette"
// BARRELS
/obj/item/weaponcrafting/improvised_parts/barrel_rifle
name = "rifle barrel"
desc = "A pipe with a diameter just the right size to fire 7.62 rounds out of."
icon_state = "barrel_rifle"
/obj/item/weaponcrafting/improvised_parts/barrel_shotgun
name = "shotgun barrel"
desc = "A twenty bore shotgun barrel."
icon_state = "barrel_shotgun"
/obj/item/weaponcrafting/improvised_parts/barrel_pistol
name = "pistol barrel"
desc = "A pipe with a small diameter and some holes finely cut into it. It fits .32 ACP bullets. Probably."
icon_state = "barrel_pistol"
w_class = WEIGHT_CLASS_SMALL
// RECEIVERS
/obj/item/weaponcrafting/improvised_parts/rifle_receiver
name = "bolt action receiver"
desc = "A crudely constructed receiver to create an improvised bolt-action breechloaded rifle. It's generic enough to modify to create other rifles, potentially."
name = "rifle receiver"
desc = "A crudely constructed receiver to create an improvised bolt-action breechloaded rifle." // removed some text implying that the item had more uses than it does
icon_state = "receiver_rifle"
w_class = WEIGHT_CLASS_SMALL
/obj/item/weaponcrafting/improvised_parts/pistol_receiver
name = "pistol receiver"
desc = "A receiver to connect house and connects all the parts to make an improvised pistol."
icon_state = "receiver_pistol"
w_class = WEIGHT_CLASS_SMALL
/obj/item/weaponcrafting/improvised_parts/laser_receiver
name = "energy emitter assembly"
desc = "A mixture of components haphazardly wired together to form an energy emitter."
icon_state = "laser_assembly"
/obj/item/weaponcrafting/improvised_parts/shotgun_receiver
name = "break-action assembly"
desc = "An improvised receiver to create a break-action breechloaded shotgun. Parts of this are still useful if you want to make another type of shotgun, however."
name = "shotgun reciever"
desc = "An improvised receiver to create a break-action breechloaded shotgun." // removed some text implying that the item had more uses than it does
icon_state = "receiver_shotgun"
w_class = WEIGHT_CLASS_SMALL
@@ -78,15 +50,3 @@ k// PARTS //
name = "wooden firearm body"
desc = "A crudely fashioned wooden body to help keep higher calibre improvised weapons from blowing themselves apart."
icon_state = "wooden_body"
/obj/item/weaponcrafting/improvised_parts/wooden_grip
name = "wooden pistol grip"
desc = "A nice wooden grip hollowed out for pistol magazines."
icon_state = "wooden_pistolgrip"
w_class = WEIGHT_CLASS_SMALL
/obj/item/weaponcrafting/improvised_parts/makeshift_lens
name = "makeshift focusing lens"
desc = "A properly made lens made with actual glassworking tools would perform much better, but this will have to do."
icon_state = "focusing_lens"
w_class = WEIGHT_CLASS_TINY
@@ -92,7 +92,7 @@
time = 150
subcategory = CAT_MISCELLANEOUS
category = CAT_MISC
always_availible = FALSE // Disabled til learned
always_availible = FALSE // Disabled until learned
/datum/crafting_recipe/bloodsucker/candelabrum
@@ -244,7 +244,7 @@
/datum/crafting_recipe/rcl
name = "Makeshift Rapid Cable Layer"
result = /obj/item/twohanded/rcl/ghetto
result = /obj/item/rcl/ghetto
time = 40
tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WRENCH)
reqs = list(/obj/item/stack/sheet/metal = 15)
@@ -50,7 +50,7 @@
/datum/crafting_recipe/bonespear
name = "Bone Spear"
result = /obj/item/twohanded/bonespear
result = /obj/item/spear/bonespear
time = 30
reqs = list(/obj/item/stack/sheet/bone = 4,
/obj/item/stack/sheet/sinew = 1)
@@ -58,7 +58,7 @@
/datum/crafting_recipe/boneaxe
name = "Bone Axe"
result = /obj/item/twohanded/fireaxe/boneaxe
result = /obj/item/fireaxe/boneaxe
time = 50
reqs = list(/obj/item/stack/sheet/bone = 6,
/obj/item/stack/sheet/sinew = 3)
@@ -74,20 +74,20 @@
/datum/crafting_recipe/headpike
name = "Spike Head (Glass Spear)"
time = 65
reqs = list(/obj/item/twohanded/spear = 1,
reqs = list(/obj/item/spear = 1,
/obj/item/bodypart/head = 1)
parts = list(/obj/item/bodypart/head = 1,
/obj/item/twohanded/spear = 1)
/obj/item/spear = 1)
result = /obj/structure/headpike
category = CAT_PRIMAL
/datum/crafting_recipe/headpikebone
name = "Spike Head (Bone Spear)"
time = 65
reqs = list(/obj/item/twohanded/bonespear = 1,
reqs = list(/obj/item/spear/bonespear = 1,
/obj/item/bodypart/head = 1)
parts = list(/obj/item/bodypart/head = 1,
/obj/item/twohanded/bonespear = 1)
/obj/item/spear/bonespear = 1)
result = /obj/structure/headpike/bone
category = CAT_PRIMAL
@@ -103,7 +103,7 @@
/datum/crafting_recipe/bone_bow
name = "Bone Bow"
result = /obj/item/gun/ballistic/bow/ashen
time = 200
time = 120 // 80+120 = 200
always_availible = FALSE
reqs = list(/obj/item/stack/sheet/bone = 8,
/obj/item/stack/sheet/sinew = 4)
@@ -112,7 +112,7 @@
/datum/crafting_recipe/bow_tablet
name = "Sandstone Bow Making Manual"
result = /obj/item/book/granter/crafting_recipe/bone_bow
time = 600 //Scribing
time = 200 //Scribing // don't care
always_availible = FALSE
reqs = list(/obj/item/stack/rods = 1,
/obj/item/stack/sheet/mineral/sandstone = 4)
@@ -42,7 +42,7 @@
/datum/crafting_recipe/spear
name = "Spear"
result = /obj/item/twohanded/spear
result = /obj/item/spear
reqs = list(/obj/item/restraints/handcuffs/cable = 1,
/obj/item/shard = 1,
/obj/item/stack/rods = 1)
@@ -110,7 +110,7 @@
/datum/crafting_recipe/chainsaw
name = "Chainsaw"
result = /obj/item/twohanded/required/chainsaw
result = /obj/item/chainsaw
reqs = list(/obj/item/circular_saw = 1,
/obj/item/stack/cable_coil = 3,
/obj/item/stack/sheet/plasteel = 5)
@@ -141,7 +141,7 @@
result = /obj/item/bombcore/chemical
reqs = list(
/obj/item/stock_parts/matter_bin = 1,
/obj/item/twohanded/required/gibtonite = 1,
/obj/item/gibtonite = 1,
/obj/item/grenade/chem_grenade = 2
)
parts = list(/obj/item/stock_parts/matter_bin = 1, /obj/item/grenade/chem_grenade = 2)
@@ -173,10 +173,10 @@
/datum/crafting_recipe/lance
name = "Explosive Lance (Grenade)"
result = /obj/item/twohanded/spear
reqs = list(/obj/item/twohanded/spear = 1,
result = /obj/item/spear
reqs = list(/obj/item/spear = 1,
/obj/item/grenade = 1)
parts = list(/obj/item/twohanded/spear = 1,
parts = list(/obj/item/spear = 1,
/obj/item/grenade = 1)
time = 15
category = CAT_WEAPONRY
@@ -192,8 +192,8 @@
result = /obj/item/gun/ballistic/bow/pipe
reqs = list(/obj/item/pipe = 5,
/obj/item/stack/sheet/plastic = 15,
/obj/item/weaponcrafting/durathread_string = 5)
time = 450
/obj/item/weaponcrafting/string = 5)
time = 150
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
@@ -248,10 +248,10 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
/datum/crafting_recipe/ishotgun
/datum/crafting_recipe/ishotgun // smaller and more versatile gun requires some better materials
name = "Improvised Shotgun"
result = /obj/item/gun/ballistic/revolver/doublebarrel/improvised
reqs = list(/obj/item/weaponcrafting/improvised_parts/barrel_shotgun = 1,
reqs = list(/obj/item/pipe = 2, // putting a large amount of meaningless timegates by forcing people to turn base resources into upgraded resources kinda sucks
/obj/item/weaponcrafting/improvised_parts/shotgun_receiver = 1,
/obj/item/weaponcrafting/improvised_parts/trigger_assembly = 1,
/obj/item/weaponcrafting/improvised_parts/wooden_body = 1,
@@ -262,10 +262,10 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
/datum/crafting_recipe/irifle
/datum/crafting_recipe/irifle // larger and less versatile gun, but a bit easier to make
name = "Improvised Rifle (7.62mm)"
result = /obj/item/gun/ballistic/shotgun/boltaction/improvised
reqs = list(/obj/item/weaponcrafting/improvised_parts/barrel_rifle = 1,
reqs = list(/obj/item/pipe = 2, // above
/obj/item/weaponcrafting/improvised_parts/rifle_receiver = 1,
/obj/item/weaponcrafting/improvised_parts/trigger_assembly = 1,
/obj/item/weaponcrafting/improvised_parts/wooden_body = 1,
@@ -276,49 +276,6 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
/datum/crafting_recipe/ipistol
name = "Improvised Pistol (.32)"
result = /obj/item/gun/ballistic/automatic/pistol/improvised/nomag
reqs = list(/obj/item/weaponcrafting/improvised_parts/barrel_pistol = 1,
/obj/item/weaponcrafting/improvised_parts/pistol_receiver = 1,
/obj/item/weaponcrafting/improvised_parts/trigger_assembly = 1,
/obj/item/weaponcrafting/improvised_parts/wooden_grip = 1,
/obj/item/stack/sheet/plastic = 15,
/obj/item/stack/sheet/plasteel = 1)
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER, TOOL_WIRECUTTER)
time = 100
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
/datum/crafting_recipe/ilaser
name = "Improvised Energy Gun"
result = /obj/item/gun/energy/e_gun/old/improvised
reqs = list(/obj/item/weaponcrafting/improvised_parts/laser_receiver = 1,
/obj/item/weaponcrafting/improvised_parts/trigger_assembly = 1,
/obj/item/weaponcrafting/improvised_parts/makeshift_lens = 1,
/obj/item/stock_parts/cell = 1,
/obj/item/stack/sheet/metal = 10,
/obj/item/stack/sheet/plasteel = 5,
/obj/item/stack/cable_coil = 10)
tools = list(TOOL_SCREWDRIVER)
time = 100
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
/datum/crafting_recipe/ilaser/upgraded
name = "Improvised Energy Gun Upgrade"
result = /obj/item/gun/energy/e_gun/old/improvised/upgraded
reqs = list(/obj/item/gun/energy/e_gun/old/improvised = 1,
/obj/item/glasswork/glass_base/lens = 1,
/obj/item/stock_parts/capacitor/quadratic = 2,
/obj/item/stock_parts/micro_laser/ultra = 1,
/obj/item/stock_parts/cell/bluespace = 1,
/obj/item/stack/cable_coil = 5)
tools = list(TOOL_SCREWDRIVER, TOOL_MULTITOOL)
time = 100
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
//////////////////
///AMMO CRAFTING//
//////////////////
@@ -326,9 +283,9 @@
/datum/crafting_recipe/arrow
name = "Arrow"
result = /obj/item/ammo_casing/caseless/arrow/wood
time = 30
time = 5 // these only do 15 damage
reqs = list(/obj/item/stack/sheet/mineral/wood = 1,
/obj/item/stack/sheet/durathread = 1,
/obj/item/stack/sheet/cloth = 1,
/obj/item/stack/rods = 1) // 1 metal sheet = 2 rods = 2 arrows
category = CAT_WEAPONRY
subcategory = CAT_AMMO
@@ -336,7 +293,7 @@
/datum/crafting_recipe/bone_arrow
name = "Bone Arrow"
result = /obj/item/ammo_casing/caseless/arrow/bone
time = 30
time = 5
always_availible = FALSE
reqs = list(/obj/item/stack/sheet/bone = 1,
/obj/item/stack/sheet/sinew = 1,
@@ -348,7 +305,7 @@
name = "Ashen Arrow"
result = /obj/item/ammo_casing/caseless/arrow/ash
tools = list(TOOL_WELDER)
time = 30
time = 10 // 1.5 seconds minimum per actually worthwhile arrow excluding interface lag
always_availible = FALSE
reqs = list(/obj/item/ammo_casing/caseless/arrow/wood = 1)
category = CAT_WEAPONRY
@@ -442,92 +399,28 @@
category = CAT_WEAPONRY
subcategory = CAT_AMMO
/datum/crafting_recipe/m32acp
name = ".32ACP Empty Magazine"
result = /obj/item/ammo_box/magazine/m32acp/empty
reqs = list(/obj/item/stack/sheet/metal = 3,
/obj/item/stack/sheet/plasteel = 1,
/obj/item/stack/packageWrap = 1)
tools = list(TOOL_WELDER,TOOL_SCREWDRIVER)
time = 5
category = CAT_WEAPONRY
subcategory = CAT_AMMO
////////////////////
// PARTS CRAFTING //
////////////////////
// BARRELS
/datum/crafting_recipe/rifle_barrel
name = "Improvised Rifle Barrel"
result = /obj/item/weaponcrafting/improvised_parts/barrel_rifle
reqs = list(/obj/item/pipe = 2)
tools = list(TOOL_WELDER,TOOL_SAW)
time = 150
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/shotgun_barrel
name = "Improvised Shotgun Barrel"
result = /obj/item/weaponcrafting/improvised_parts/barrel_shotgun
reqs = list(/obj/item/pipe = 2)
tools = list(TOOL_WELDER,TOOL_SAW)
time = 150
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/pistol_barrel
name = "Improvised Pistol Barrel"
result = /obj/item/weaponcrafting/improvised_parts/barrel_pistol
reqs = list(/obj/item/pipe = 1,
/obj/item/stack/sheet/plasteel = 1)
tools = list(TOOL_WELDER,TOOL_SAW)
time = 150
category = CAT_WEAPONRY
subcategory = CAT_PARTS
// RECEIVERS
/datum/crafting_recipe/rifle_receiver
name = "Improvised Rifle Receiver"
result = /obj/item/weaponcrafting/improvised_parts/rifle_receiver
reqs = list(/obj/item/stack/sheet/metal = 10,
/obj/item/stack/sheet/plasteel = 1)
reqs = list(/obj/item/stack/sheet/metal = 15) // you can carry multiple shotguns
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER)
time = 50
time = 25
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/shotgun_receiver
name = "Improvised Shotgun Receiver"
result = /obj/item/weaponcrafting/improvised_parts/shotgun_receiver
reqs = list(/obj/item/stack/sheet/metal = 10,
/obj/item/stack/sheet/plasteel = 1)
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER) // Dual wielding has been removed, plasteel is a soft timesink to obtain for most to make mass production harder.
time = 50
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/pistol_receiver
name = "Improvised Pistol Receiver"
result = /obj/item/weaponcrafting/improvised_parts/pistol_receiver
reqs = list(/obj/item/stack/sheet/metal = 5,
/obj/item/stack/sheet/plasteel = 1)
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER, TOOL_SAW)
time = 50
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/laser_receiver
name = "Energy Weapon Assembly"
result = /obj/item/weaponcrafting/improvised_parts/laser_receiver
reqs = list(/obj/item/stack/sheet/metal = 10,
/obj/item/stock_parts/capacitor = 2,
/obj/item/stock_parts/micro_laser = 1,
/obj/item/assembly/prox_sensor = 1)
tools = list(TOOL_SCREWDRIVER, TOOL_MULTITOOL, TOOL_WELDER) // Prox sensor and multitool for the circuit board, welder for extremely ghetto soldering.
time = 150
reqs = list(/obj/item/stack/sheet/metal = 15,
/obj/item/stack/sheet/plasteel = 1) // requires access or hacking since shotgun is better
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER)
time = 25
category = CAT_WEAPONRY
subcategory = CAT_PARTS
@@ -539,16 +432,6 @@
reqs = list(/obj/item/stack/sheet/metal = 3,
/obj/item/assembly/igniter = 1)
tools = list(TOOL_SCREWDRIVER, TOOL_WELDER)
time = 150
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/makeshift_lens
name = "Makeshift Lens"
result = /obj/item/weaponcrafting/improvised_parts/makeshift_lens
reqs = list(/obj/item/stack/sheet/metal = 1,
/obj/item/stack/sheet/glass = 2)
tools = list(TOOL_WELDER) // Glassmaking lets you make non-makeshift lenses.
time = 50
time = 25
category = CAT_WEAPONRY
subcategory = CAT_PARTS
+245
View File
@@ -0,0 +1,245 @@
/*!
This component makes it possible to make things edible. What this means is that you can take a bite or force someone to take a bite (in the case of items).
These items take a specific time to eat, and can do most of the things our original food items could.
Behavior that's still missing from this component that original food items had that should either be put into seperate components or somewhere else:
Components:
Drying component (jerky etc)
Customizable component (custom pizzas etc)
Processable component (Slicing and cooking behavior essentialy, making it go from item A to B when conditions are met.)
Dunkable component (Dunking things into reagent containers to absorb a specific amount of reagents)
Misc:
Something for cakes (You can store things inside)
*/
/datum/component/edible
///Amount of reagents taken per bite
var/bite_consumption = 2
///Amount of bites taken so far
var/bitecount = 0
///Flags for food
var/food_flags = NONE
///Bitfield of the types of this food
var/foodtypes = NONE
///Amount of seconds it takes to eat this food
var/eat_time = 30
///Defines how much it lowers someones satiety (Need to eat, essentialy)
var/junkiness = 0
///Message to send when eating
var/list/eatverbs
///Callback to be ran for when you take a bite of something
var/datum/callback/after_eat
///Last time we checked for food likes
var/last_check_time
/datum/component/edible/Initialize(list/initial_reagents, food_flags = NONE, foodtypes = NONE, volume = 50, eat_time = 30, list/tastes, list/eatverbs = list("bite","chew","nibble","gnaw","gobble","chomp"), bite_consumption = 2, datum/callback/after_eat)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine)
RegisterSignal(parent, COMSIG_ATOM_ATTACK_ANIMAL, .proc/UseByAnimal)
if(isitem(parent))
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/UseFromHand)
else if(isturf(parent))
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, .proc/TryToEatTurf)
src.bite_consumption = bite_consumption
src.food_flags = food_flags
src.foodtypes = foodtypes
src.eat_time = eat_time
src.eatverbs = eatverbs
src.junkiness = junkiness
src.after_eat = after_eat
var/atom/owner = parent
if(!owner.reagents) //we don't want to override what's in the item if it possibly contains reagents already
owner.create_reagents(volume, INJECTABLE)
if(initial_reagents)
for(var/rid in initial_reagents)
var/amount = initial_reagents[rid]
if(tastes && tastes.len && (rid == /datum/reagent/consumable/nutriment || rid == /datum/reagent/consumable/nutriment/vitamin))
owner.reagents.add_reagent(rid, amount, tastes.Copy())
else
owner.reagents.add_reagent(rid, amount)
/datum/component/edible/proc/examine(datum/source, mob/user, list/examine_list)
if(!(food_flags & FOOD_IN_CONTAINER))
switch (bitecount)
if (0)
return
if(1)
examine_list += "[parent] was bitten by someone!"
if(2,3)
examine_list += "[parent] was bitten [bitecount] times!"
else
examine_list += "[parent] was bitten multiple times!"
/datum/component/edible/proc/UseFromHand(obj/item/source, mob/living/M, mob/living/user)
return TryToEat(M, user)
/datum/component/edible/proc/TryToEatTurf(datum/source, mob/user)
return TryToEat(user, user)
///All the checks for the act of eating itself and
/datum/component/edible/proc/TryToEat(mob/living/eater, mob/living/feeder)
set waitfor = FALSE
var/atom/owner = parent
if(feeder.a_intent == INTENT_HARM)
return
if(!owner.reagents.total_volume)//Shouldn't be needed but it checks to see if it has anything left in it.
to_chat(feeder, "<span class='warning'>None of [owner] left, oh no!</span>")
if(isturf(parent))
var/turf/T = parent
T.ScrapeAway(1, CHANGETURF_INHERIT_AIR)
else
qdel(parent)
return
if(!CanConsume(eater, feeder))
return
var/fullness = eater.nutrition + 10 //The theoretical fullness of the person eating if they were to eat this
for(var/datum/reagent/consumable/C in eater.reagents.reagent_list) //we add the nutrition value of what we're currently digesting
fullness += C.nutriment_factor * C.volume / C.metabolization_rate
. = COMPONENT_ITEM_NO_ATTACK //Point of no return I suppose
if(eater == feeder)//If you're eating it yourself.
if(!do_mob(feeder, eater, eat_time)) //Gotta pass the minimal eat time
return
var/eatverb = pick(eatverbs)
if(junkiness && eater.satiety < -150 && eater.nutrition > NUTRITION_LEVEL_STARVING + 50 && !HAS_TRAIT(eater, TRAIT_VORACIOUS))
to_chat(eater, "<span class='warning'>You don't feel like eating any more junk food at the moment!</span>")
return
else if(fullness <= 50)
eater.visible_message("<span class='notice'>[eater] hungrily [eatverb]s \the [parent], gobbling it down!</span>", "<span class='notice'>You hungrily [eatverb] \the [parent], gobbling it down!</span>")
else if(fullness > 50 && fullness < 150)
eater.visible_message("<span class='notice'>[eater] hungrily [eatverb]s \the [parent].</span>", "<span class='notice'>You hungrily [eatverb] \the [parent].</span>")
else if(fullness > 150 && fullness < 500)
eater.visible_message("<span class='notice'>[eater] [eatverb]s \the [parent].</span>", "<span class='notice'>You [eatverb] \the [parent].</span>")
else if(fullness > 500 && fullness < 600)
eater.visible_message("<span class='notice'>[eater] unwillingly [eatverb]s a bit of \the [parent].</span>", "<span class='notice'>You unwillingly [eatverb] a bit of \the [parent].</span>")
else if(fullness > (600 * (1 + eater.overeatduration / 2000))) // The more you eat - the more you can eat
eater.visible_message("<span class='warning'>[eater] cannot force any more of \the [parent] to go down [eater.p_their()] throat!</span>", "<span class='warning'>You cannot force any more of \the [parent] to go down your throat!</span>")
return
else //If you're feeding it to someone else.
if(isbrain(eater))
to_chat(feeder, "<span class='warning'>[eater] doesn't seem to have a mouth!</span>")
return
if(fullness <= (600 * (1 + eater.overeatduration / 1000)))
eater.visible_message("<span class='danger'>[feeder] attempts to feed [eater] [parent].</span>", \
"<span class='userdanger'>[feeder] attempts to feed you [parent].</span>")
else
eater.visible_message("<span class='warning'>[feeder] cannot force any more of [parent] down [eater]'s throat!</span>", \
"<span class='warning'>[feeder] cannot force any more of [parent] down your throat!</span>")
return
if(!do_mob(feeder, eater)) //Wait 3 seconds before you can feed
return
log_combat(feeder, eater, "fed", owner.reagents.log_list())
eater.visible_message("<span class='danger'>[feeder] forces [eater] to eat [parent]!</span>", \
"<span class='userdanger'>[feeder] forces you to eat [parent]!</span>")
TakeBite(eater, feeder)
///This function lets the eater take a bite and transfers the reagents to the eater.
/datum/component/edible/proc/TakeBite(mob/living/eater, mob/living/feeder)
var/atom/owner = parent
if(!owner?.reagents)
return FALSE
if(eater.satiety > -200)
eater.satiety -= junkiness
playsound(eater.loc,'sound/items/eatfood.ogg', rand(10,50), TRUE)
if(owner.reagents.total_volume)
SEND_SIGNAL(parent, COMSIG_FOOD_EATEN, eater, feeder)
var/fraction = min(bite_consumption / owner.reagents.total_volume, 1)
owner.reagents.reaction(eater, INGEST, fraction)
owner.reagents.trans_to(eater, bite_consumption)
bitecount++
On_Consume(eater)
checkLiked(fraction, eater)
//Invoke our after eat callback if it is valid
if(after_eat)
after_eat.Invoke(eater, feeder)
return TRUE
///Checks whether or not the eater can actually consume the food
/datum/component/edible/proc/CanConsume(mob/living/eater, mob/living/feeder)
if(!iscarbon(eater))
return FALSE
var/mob/living/carbon/C = eater
var/covered = ""
if(C.is_mouth_covered(head_only = 1))
covered = "headgear"
else if(C.is_mouth_covered(mask_only = 1))
covered = "mask"
if(covered)
var/who = (isnull(feeder) || eater == feeder) ? "your" : "[eater.p_their()]"
to_chat(feeder, "<span class='warning'>You have to remove [who] [covered] first!</span>")
return FALSE
return TRUE
///Check foodtypes to see if we should send a moodlet
/datum/component/edible/proc/checkLiked(var/fraction, mob/M)
if(last_check_time + 50 > world.time)
return FALSE
if(!ishuman(M))
return FALSE
var/mob/living/carbon/human/H = M
if(HAS_TRAIT(H, TRAIT_AGEUSIA) && foodtypes & H.dna.species.toxic_food)
to_chat(H, "<span class='warning'>You don't feel so good...</span>")
H.adjust_disgust(25 + 30 * fraction)
else
if(foodtypes & H.dna.species.toxic_food)
to_chat(H,"<span class='warning'>What the hell was that thing?!</span>")
H.adjust_disgust(25 + 30 * fraction)
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "toxic_food", /datum/mood_event/disgusting_food)
else if(foodtypes & H.dna.species.disliked_food)
to_chat(H,"<span class='notice'>That didn't taste very good...</span>")
H.adjust_disgust(11 + 15 * fraction)
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "gross_food", /datum/mood_event/gross_food)
else if(foodtypes & H.dna.species.liked_food)
to_chat(H,"<span class='notice'>I love this taste!</span>")
H.adjust_disgust(-5 + -2.5 * fraction)
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "fav_food", /datum/mood_event/favorite_food)
if((foodtypes & BREAKFAST) && world.time - SSticker.round_start_time < STOP_SERVING_BREAKFAST)
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "breakfast", /datum/mood_event/breakfast)
last_check_time = world.time
///Delete the item when it is fully eaten
/datum/component/edible/proc/On_Consume(mob/living/eater)
var/atom/owner = parent
if(!eater)
return
if(!owner.reagents.total_volume)
if(isturf(parent))
var/turf/T = parent
T.ScrapeAway(1, CHANGETURF_INHERIT_AIR)
else
qdel(parent)
///Ability to feed food to puppers
/datum/component/edible/proc/UseByAnimal(datum/source, mob/user)
var/atom/owner = parent
if(!isdog(user))
return
var/mob/living/L = user
if(bitecount == 0 || prob(50))
L.emote("me", 1, "nibbles away at \the [parent]")
bitecount++
. = COMPONENT_ITEM_NO_ATTACK
L.taste(owner.reagents) // why should carbons get all the fun?
if(bitecount >= 5)
var/sattisfaction_text = pick("burps from enjoyment", "yaps for more", "woofs twice", "looks at the area where \the [parent] was")
if(sattisfaction_text)
L.emote("me", 1, "[sattisfaction_text]")
qdel(parent)
+107
View File
@@ -0,0 +1,107 @@
/*!
This component essentially encapsulates frying and utilizes the edible component
This means fried items can work like regular ones, and generally the code is far less messy
*/
/datum/component/fried
var/fry_power //how powerfully was this item fried
var/atom/owner //the atom it is owned by
var/stored_name //name of the owner when the component was first added
var/frying_examine_text = "the coders messed frying code up, report this!"
/datum/component/fried/Initialize(frying_power)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine)
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, .proc/restore) //basically, unfry people who are being cleaned (badmemes fried someone)
fry_power = frying_power
owner = parent
stored_name = owner.name
setup_fried_item()
//some stuff to do with the contents of fried junk
GLOBAL_VAR_INIT(frying_hardmode, TRUE)
GLOBAL_VAR_INIT(frying_bad_chem_add_volume, TRUE)
GLOBAL_LIST_INIT(frying_bad_chems, list(
/datum/reagent/toxin/bad_food = 1,
/datum/reagent/toxin = 1,
/datum/reagent/lithium = 1,
/datum/reagent/mercury = 1,
))
/datum/component/fried/proc/examine(datum/source, mob/user, list/examine_list)
examine_list += "[parent] has been [frying_examine_text]"
/datum/component/fried/proc/setup_fried_item() //sets the name, colour and examine text and edibility up
//first we do some checks depending on the type of item being fried
var/list/fried_tastes = list("crispy")
var/fried_foodtypes = FRIED
var/fried_junk = FALSE
if(!isfood(owner) && GLOB.frying_hardmode && GLOB.frying_bad_chems.len && !owner.reagents) //you fried some junk, it's not gonna taste great
fried_junk = TRUE
fried_foodtypes |= TOXIC // junk tastes toxic too
else
if(isfood(owner))
var/obj/item/reagent_containers/food/snacks/food_item = owner
fried_tastes += food_item.tastes
fried_foodtypes |= food_item.foodtype
var/fried_eat_time = 0
if(isturf(owner))
fried_eat_time = 30 //we want turfs to be eaten slowly
var/colour_priority = FIXED_COLOUR_PRIORITY
if(ismob(owner))
colour_priority = WASHABLE_COLOUR_PRIORITY //badmins fried someone and we want to let them wash the fry colour off
//lets heavily hint at how to undo their frying
to_chat(owner, "<span class='warning'>You've been coated in hot cooking oil! You should probably go wash it off at the showers.</span>")
else
owner.AddComponent(/datum/component/edible, foodtypes = fried_tastes, tastes = fried_tastes, eat_time = fried_eat_time) //we don't want mobs to get the edible component
switch(fry_power)
if(0 to 15)
owner.name = "lightly fried [owner.name]"
owner.add_atom_colour(rgb(166,103,54), colour_priority)
frying_examine_text = "lightly fried"
if(16 to 49)
owner.name = "fried [owner.name]"
owner.add_atom_colour(rgb(103,63,24), colour_priority)
frying_examine_text = "moderately fried"
if(50 to 59)
owner.name = "deep fried [owner.name]"
owner.add_atom_colour(rgb(63,23,4), colour_priority)
frying_examine_text = "deeply fried"
else
owner.name = "the physical manifestation of fried foods"
owner.add_atom_colour(rgb(33,19,9), colour_priority)
frying_examine_text = "incomprehensibly fried to a crisp"
//adding the edible component gives it reagents meaning we can now add the bad frying reagents if it's junk
if(fried_junk && owner.reagents) //check again just incase
var/R = rand(1, GLOB.frying_bad_chems.len)
var/bad_chem = GLOB.frying_bad_chems[R]
var/bad_chem_amount = max(4,GLOB.frying_bad_chems[bad_chem] * (fry_power/12.5)) //4u of bad chem reached when deeply fried
owner.reagents.add_reagent(bad_chem, bad_chem_amount)
/datum/component/fried/proc/restore_name() //restore somethings name
//we do string manipulation and not restoring their name to real_name because some things hide your real_name and we want to maintain that
if(copytext(owner.name,1,14) == "lightly fried ")
owner.name = copytext(owner.name,15)
else
if(copytext(owner.name,1,6) == "fried ")
owner.name = copytext(owner.name,7)
else
if(copytext(owner.name,1,11) == "deep fried ")
owner.name = copytext(owner.name, 12)
else
if(owner.name == "the physical manifestation of fried foods") //if the name is still this, their name hasn't changed, so we can safely restore their stored name
owner.name = stored_name
/datum/component/fried/proc/restore() //restore a fried mob to being not-fried
if(ismob(owner))
//restore the name, the colour should wash off itself, and then remove the component
restore_name()
RemoveComponent()
+153
View File
@@ -0,0 +1,153 @@
///Global GPS_list. All GPS components get saved in here for easy reference.
GLOBAL_LIST_EMPTY(GPS_list)
///GPS component. Atoms that have this show up on gps. Pretty simple stuff.
/datum/component/gps
var/gpstag = "COM0"
var/tracking = TRUE
var/emped = FALSE
/datum/component/gps/Initialize(_gpstag = "COM0")
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
gpstag = _gpstag
GLOB.GPS_list += src
/datum/component/gps/Destroy()
GLOB.GPS_list -= src
return ..()
///GPS component subtype. Only gps/item's can be used to open the UI.
/datum/component/gps/item
var/updating = TRUE //Automatic updating of GPS list. Can be set to manual by user.
var/global_mode = TRUE //If disabled, only GPS signals of the same Z level are shown
/datum/component/gps/item/Initialize(_gpstag = "COM0", emp_proof = FALSE)
. = ..()
if(. == COMPONENT_INCOMPATIBLE || !isitem(parent))
return COMPONENT_INCOMPATIBLE
var/atom/A = parent
A.add_overlay("working")
A.name = "[initial(A.name)] ([gpstag])"
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/interact)
if(!emp_proof)
RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp_act)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine)
RegisterSignal(parent, COMSIG_CLICK_ALT, .proc/on_AltClick)
///Called on COMSIG_ITEM_ATTACK_SELF
/datum/component/gps/item/proc/interact(datum/source, mob/user)
if(user)
ui_interact(user)
///Called on COMSIG_PARENT_EXAMINE
/datum/component/gps/item/proc/on_examine(datum/source, mob/user, list/examine_list)
examine_list += "<span class='notice'>Alt-click to switch it [tracking ? "off":"on"].</span>"
///Called on COMSIG_ATOM_EMP_ACT
/datum/component/gps/item/proc/on_emp_act(datum/source, severity)
emped = TRUE
var/atom/A = parent
A.cut_overlay("working")
A.add_overlay("emp")
addtimer(CALLBACK(src, .proc/reboot), 300, TIMER_UNIQUE|TIMER_OVERRIDE) //if a new EMP happens, remove the old timer so it doesn't reactivate early
SStgui.close_uis(src) //Close the UI control if it is open.
///Restarts the GPS after getting turned off by an EMP.
/datum/component/gps/item/proc/reboot()
emped = FALSE
var/atom/A = parent
A.cut_overlay("emp")
A.add_overlay("working")
///Calls toggletracking
/datum/component/gps/item/proc/on_AltClick(datum/source, mob/user)
toggletracking(user)
///Toggles the tracking for the gps
/datum/component/gps/item/proc/toggletracking(mob/user)
if(!user.canUseTopic(parent, BE_CLOSE))
return //user not valid to use gps
if(emped)
to_chat(user, "<span class='warning'>It's busted!</span>")
return
var/atom/A = parent
if(tracking)
A.cut_overlay("working")
to_chat(user, "<span class='notice'>[parent] is no longer tracking, or visible to other GPS devices.</span>")
tracking = FALSE
else
A.add_overlay("working")
to_chat(user, "<span class='notice'>[parent] is now tracking, and visible to other GPS devices.</span>")
tracking = TRUE
/datum/component/gps/item/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
if(emped)
to_chat(user, "<span class='hear'>[parent] fizzles weakly.</span>")
return
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
// Variable window height, depending on how many GPS units there are
// to show, clamped to relatively safe range.
var/gps_window_height = clamp(325 + GLOB.GPS_list.len * 14, 325, 700)
ui = new(user, src, ui_key, "Gps", "Global Positioning System", 470, gps_window_height, master_ui, state) //width, height
ui.open()
ui.set_autoupdate(state = updating)
/datum/component/gps/item/ui_data(mob/user)
var/list/data = list()
data["power"] = tracking
data["tag"] = gpstag
data["updating"] = updating
data["globalmode"] = global_mode
if(!tracking || emped) //Do not bother scanning if the GPS is off or EMPed
return data
var/turf/curr = get_turf(parent)
data["currentArea"] = "[get_area_name(curr, TRUE)]"
data["currentCoords"] = "[curr.x], [curr.y], [curr.z]"
var/list/signals = list()
data["signals"] = list()
for(var/gps in GLOB.GPS_list)
var/datum/component/gps/G = gps
if(G.emped || !G.tracking || G == src)
continue
var/turf/pos = get_turf(G.parent)
if(!pos || !global_mode && pos.z != curr.z)
continue
var/list/signal = list()
signal["entrytag"] = G.gpstag //Name or 'tag' of the GPS
signal["coords"] = "[pos.x], [pos.y], [pos.z]"
if(pos.z == curr.z) //Distance/Direction calculations for same z-level only
signal["dist"] = max(get_dist(curr, pos), 0) //Distance between the src and remote GPS turfs
signal["degrees"] = round(Get_Angle(curr, pos)) //0-360 degree directional bearing, for more precision.
signals += list(signal) //Add this signal to the list of signals
data["signals"] = signals
return data
/datum/component/gps/item/ui_act(action, params)
if(..())
return
switch(action)
if("rename")
var/atom/parentasatom = parent
var/a = stripped_input(usr, "Please enter desired tag.", parentasatom.name, gpstag, 20)
if (!a)
return
gpstag = a
. = TRUE
parentasatom.name = "global positioning system ([gpstag])"
if("power")
toggletracking(usr)
. = TRUE
if("updating")
updating = !updating
. = TRUE
if("globalmode")
global_mode = !global_mode
. = TRUE
+1 -1
View File
@@ -105,7 +105,7 @@
/// Proc specifically for inserting items, returns the amount of materials entered.
/datum/component/material_container/proc/insert_item(obj/item/I, var/multiplier = 1, stack_amt)
if(!I)
if(QDELETED(I))
return FALSE
multiplier = CEILING(multiplier, 0.01)
+3 -2
View File
@@ -69,8 +69,9 @@
out += "[out ? " and it " : "[master] "]seems to be glowing a bit."
if(RAD_AMOUNT_HIGH to INFINITY) //At this level the object can contaminate other objects
out += "[out ? " and it " : "[master] "]hurts to look at."
else
out += "."
if(!LAZYLEN(out))
return
out += "."
examine_list += out.Join()
/datum/component/radioactive/proc/rad_attack(datum/source, atom/movable/target, mob/living/user)
+10 -3
View File
@@ -8,6 +8,8 @@
var/list/faction = list("mining")
/datum/component/spawner/Initialize(_mob_types, _spawn_time, _faction, _spawn_text, _max_mobs)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
if(_spawn_time)
spawn_time=_spawn_time
if(_mob_types)
@@ -19,20 +21,25 @@
if(_max_mobs)
max_mobs=_max_mobs
RegisterSignal(parent, list(COMSIG_PARENT_QDELETING), .proc/stop_spawning)
RegisterSignal(parent, COMSIG_PARENT_QDELETING, .proc/stop_spawning)
RegisterSignal(parent, COMSIG_OBJ_ATTACK_GENERIC, .proc/on_attack_generic)
START_PROCESSING(SSprocessing, src)
/datum/component/spawner/process()
try_spawn_mob()
/datum/component/spawner/proc/stop_spawning(force, hint)
/datum/component/spawner/proc/stop_spawning(datum/source, force, hint)
STOP_PROCESSING(SSprocessing, src)
for(var/mob/living/simple_animal/L in spawned_mobs)
if(L.nest == src)
L.nest = null
spawned_mobs = null
// Stopping clientless simple mobs' from indiscriminately bashing their own spawners due DestroySurroundings() et similars.
/datum/component/spawner/proc/on_attack_generic(datum/source, mob/user, damage_amount, damage_type, damage_flag, sound_effect, armor_penetration)
if(!user.client && ((user.faction & faction) || (user in spawned_mobs)))
return COMPONENT_STOP_GENERIC_ATTACK
/datum/component/spawner/proc/try_spawn_mob()
var/atom/P = parent
if(spawned_mobs.len >= max_mobs)
@@ -34,3 +34,8 @@
qdel(A)
return
. = ..()
/datum/component/storage/concrete/bluespace/bag_of_holding/can_be_inserted(obj/item/I, stop_messages = FALSE, mob/M)
if(I.GetComponent(/datum/component/storage/concrete/bluespace/bag_of_holding))
return TRUE
return ..()
+311
View File
@@ -0,0 +1,311 @@
/**
* Two Handed Component
*
* When applied to an item it will make it two handed
*
*/
/datum/component/two_handed
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS // Only one of the component can exist on an item
var/wielded = FALSE /// Are we holding the two handed item properly
var/force_multiplier = 0 /// The multiplier applied to force when wielded, does not work with force_wielded, and force_unwielded
var/force_wielded = 0 /// The force of the item when weilded
var/force_unwielded = 0 /// The force of the item when unweilded
var/wieldsound = FALSE /// Play sound when wielded
var/unwieldsound = FALSE /// Play sound when unwielded
var/attacksound = FALSE /// Play sound on attack when wielded
var/require_twohands = FALSE /// Does it have to be held in both hands
var/icon_wielded = FALSE /// The icon that will be used when wielded
var/obj/item/offhand/offhand_item = null /// Reference to the offhand created for the item
var/sharpened_increase = 0 /// The amount of increase recived from sharpening the item
/**
* Two Handed component
*
* vars:
* * require_twohands (optional) Does the item need both hands to be carried
* * wieldsound (optional) The sound to play when wielded
* * unwieldsound (optional) The sound to play when unwielded
* * attacksound (optional) The sound to play when wielded and attacking
* * force_multiplier (optional) The force multiplier when wielded, do not use with force_wielded, and force_unwielded
* * force_wielded (optional) The force setting when the item is wielded, do not use with force_multiplier
* * force_unwielded (optional) The force setting when the item is unwielded, do not use with force_multiplier
* * icon_wielded (optional) The icon to be used when wielded
*/
/datum/component/two_handed/Initialize(require_twohands=FALSE, wieldsound=FALSE, unwieldsound=FALSE, attacksound=FALSE, \
force_multiplier=0, force_wielded=0, force_unwielded=0, icon_wielded=FALSE)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
src.require_twohands = require_twohands
src.wieldsound = wieldsound
src.unwieldsound = unwieldsound
src.attacksound = attacksound
src.force_multiplier = force_multiplier
src.force_wielded = force_wielded
src.force_unwielded = force_unwielded
src.icon_wielded = icon_wielded
// Inherit the new values passed to the component
/datum/component/two_handed/InheritComponent(datum/component/two_handed/new_comp, original, require_twohands, wieldsound, unwieldsound, \
force_multiplier, force_wielded, force_unwielded, icon_wielded)
if(!original)
return
if(require_twohands)
src.require_twohands = require_twohands
if(wieldsound)
src.wieldsound = wieldsound
if(unwieldsound)
src.unwieldsound = unwieldsound
if(attacksound)
src.attacksound = attacksound
if(force_multiplier)
src.force_multiplier = force_multiplier
if(force_wielded)
src.force_wielded = force_wielded
if(force_unwielded)
src.force_unwielded = force_unwielded
if(icon_wielded)
src.icon_wielded = icon_wielded
// register signals withthe parent item
/datum/component/two_handed/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/on_attack_self)
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/on_attack)
RegisterSignal(parent, COMSIG_ATOM_UPDATE_ICON, .proc/on_update_icon)
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/on_moved)
RegisterSignal(parent, COMSIG_ITEM_SHARPEN_ACT, .proc/on_sharpen)
// Remove all siginals registered to the parent item
/datum/component/two_handed/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED,
COMSIG_ITEM_DROPPED,
COMSIG_ITEM_ATTACK_SELF,
COMSIG_ITEM_ATTACK,
COMSIG_ATOM_UPDATE_ICON,
COMSIG_MOVABLE_MOVED,
COMSIG_ITEM_SHARPEN_ACT))
/// Triggered on equip of the item containing the component
/datum/component/two_handed/proc/on_equip(datum/source, mob/user, slot)
if(require_twohands && slot == SLOT_HANDS) // force equip the item
wield(user)
if(!user.is_holding(parent) && wielded && !require_twohands)
unwield(user)
/// Triggered on drop of item containing the component
/datum/component/two_handed/proc/on_drop(datum/source, mob/user)
if(require_twohands)
unwield(user, show_message=TRUE)
if(wielded)
unwield(user)
if(source == offhand_item && !QDELETED(src))
qdel(src)
/// Triggered on attack self of the item containing the component
/datum/component/two_handed/proc/on_attack_self(datum/source, mob/user)
if(wielded)
unwield(user)
else
wield(user)
/**
* Wield the two handed item in both hands
*
* vars:
* * user The mob/living/carbon that is wielding the item
*/
/datum/component/two_handed/proc/wield(mob/living/carbon/user)
if(wielded)
return
if(ismonkey(user))
to_chat(user, "<span class='warning'>It's too heavy for you to wield fully.</span>")
return
if(user.get_inactive_held_item())
if(require_twohands)
to_chat(user, "<span class='notice'>[parent] is too cumbersome to carry in one hand!</span>")
user.dropItemToGround(parent, force=TRUE)
else
to_chat(user, "<span class='warning'>You need your other hand to be empty!</span>")
return
if(user.get_num_arms() < 2)
if(require_twohands)
user.dropItemToGround(parent, force=TRUE)
to_chat(user, "<span class='warning'>You don't have enough intact hands.</span>")
return
// wield update status
if(SEND_SIGNAL(parent, COMSIG_TWOHANDED_WIELD, user) & COMPONENT_TWOHANDED_BLOCK_WIELD)
return // blocked wield from item
wielded = TRUE
RegisterSignal(user, COMSIG_MOB_SWAP_HANDS, .proc/on_swap_hands)
// update item stats and name
var/obj/item/parent_item = parent
if(force_multiplier)
parent_item.force *= force_multiplier
else if(force_wielded)
parent_item.force = force_wielded
if(sharpened_increase)
parent_item.force += sharpened_increase
parent_item.name = "[parent_item.name] (Wielded)"
parent_item.update_icon()
if(iscyborg(user))
to_chat(user, "<span class='notice'>You dedicate your module to [parent].</span>")
else
to_chat(user, "<span class='notice'>You grab [parent] with both hands.</span>")
// Play sound if one is set
if(wieldsound)
playsound(parent_item.loc, wieldsound, 50, TRUE)
// Let's reserve the other hand
offhand_item = new(user)
offhand_item.name = "[parent_item.name] - offhand"
offhand_item.desc = "Your second grip on [parent_item]."
offhand_item.wielded = TRUE
RegisterSignal(offhand_item, COMSIG_ITEM_DROPPED, .proc/on_drop)
user.put_in_inactive_hand(offhand_item)
/**
* Unwield the two handed item
*
* vars:
* * user The mob/living/carbon that is unwielding the item
* * show_message (option) show a message to chat on unwield
*/
/datum/component/two_handed/proc/unwield(mob/living/carbon/user, show_message=TRUE)
if(!wielded || !user)
return
// wield update status
wielded = FALSE
UnregisterSignal(user, COMSIG_MOB_SWAP_HANDS)
SEND_SIGNAL(parent, COMSIG_TWOHANDED_UNWIELD, user)
// update item stats
var/obj/item/parent_item = parent
if(sharpened_increase)
parent_item.force -= sharpened_increase
if(force_multiplier)
parent_item.force /= force_multiplier
else if(force_unwielded)
parent_item.force = force_unwielded
// update the items name to remove the wielded status
var/sf = findtext(parent_item.name, " (Wielded)", -10) // 10 == length(" (Wielded)")
if(sf)
parent_item.name = copytext(parent_item.name, 1, sf)
else
parent_item.name = "[initial(parent_item.name)]"
// Update icons
parent_item.update_icon()
if(user.get_item_by_slot(ITEM_SLOT_BACK) == parent)
user.update_inv_back()
else
user.update_inv_hands()
// if the item requires two handed drop the item on unwield
if(require_twohands)
user.dropItemToGround(parent, force=TRUE)
// Show message if requested
if(show_message)
if(iscyborg(user))
to_chat(user, "<span class='notice'>You free up your module.</span>")
else if(require_twohands)
to_chat(user, "<span class='notice'>You drop [parent].</span>")
else
to_chat(user, "<span class='notice'>You are now carrying [parent] with one hand.</span>")
// Play sound if set
if(unwieldsound)
playsound(parent_item.loc, unwieldsound, 50, TRUE)
// Remove the object in the offhand
if(offhand_item)
UnregisterSignal(offhand_item, COMSIG_ITEM_DROPPED)
qdel(offhand_item)
// Clear any old refrence to an item that should be gone now
offhand_item = null
/**
* on_attack triggers on attack with the parent item
*/
/datum/component/two_handed/proc/on_attack(obj/item/source, mob/living/target, mob/living/user)
if(wielded && attacksound)
var/obj/item/parent_item = parent
playsound(parent_item.loc, attacksound, 50, TRUE)
/**
* on_update_icon triggers on call to update parent items icon
*
* Updates the icon using icon_wielded if set
*/
/datum/component/two_handed/proc/on_update_icon(datum/source)
if(icon_wielded && wielded)
var/obj/item/parent_item = parent
if(parent_item)
parent_item.icon_state = icon_wielded
return COMSIG_ATOM_NO_UPDATE_ICON_STATE
/**
* on_moved Triggers on item moved
*/
/datum/component/two_handed/proc/on_moved(datum/source, mob/user, dir)
unwield(user)
/**
* on_swap_hands Triggers on swapping hands, blocks swap if the other hand is busy
*/
/datum/component/two_handed/proc/on_swap_hands(mob/user, obj/item/held_item)
if(!held_item)
return
if(held_item == parent)
return COMPONENT_BLOCK_SWAP
/**
* on_sharpen Triggers on usage of a sharpening stone on the item
*/
/datum/component/two_handed/proc/on_sharpen(obj/item/item, amount, max_amount)
if(!item)
return COMPONENT_BLOCK_SHARPEN_BLOCKED
if(sharpened_increase)
return COMPONENT_BLOCK_SHARPEN_ALREADY
var/wielded_val = 0
if(force_multiplier)
var/obj/item/parent_item = parent
if(wielded)
wielded_val = parent_item.force
else
wielded_val = parent_item.force * force_multiplier
else
wielded_val = force_wielded
if(wielded_val > max_amount)
return COMPONENT_BLOCK_SHARPEN_MAXED
sharpened_increase = min(amount, (max_amount - wielded_val))
return COMPONENT_BLOCK_SHARPEN_APPLIED
/**
* The offhand dummy item for two handed items
*
*/
/obj/item/offhand
name = "offhand"
icon_state = "offhand"
w_class = WEIGHT_CLASS_HUGE
item_flags = ABSTRACT
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/wielded = FALSE // Off Hand tracking of wielded status
/obj/item/offhand/Destroy()
wielded = FALSE
return ..()
/obj/item/offhand/equipped(mob/user, slot)
. = ..()
if(wielded && !user.is_holding(src) && !QDELETED(src))
qdel(src)
+20 -23
View File
@@ -24,16 +24,17 @@ GLOBAL_LIST_EMPTY(uplinks)
var/unlock_note
var/unlock_code
var/failsafe_code
var/datum/ui_state/checkstate
var/compact_mode = FALSE
var/debug = FALSE
var/saved_player_population = 0
var/list/filters = list()
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate, datum/traitor_class/traitor_class)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/OnAttackBy)
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/interact)
if(istype(parent, /obj/item/implant))
@@ -65,7 +66,6 @@ GLOBAL_LIST_EMPTY(uplinks)
active = _enabled
gamemode = _gamemode
telecrystals = starting_tc
checkstate = _checkstate
if(!lockable)
active = TRUE
locked = FALSE
@@ -136,13 +136,13 @@ GLOBAL_LIST_EMPTY(uplinks)
/datum/component/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
state = checkstate ? checkstate : state
active = TRUE
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "uplink", name, 620, 580, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
ui.set_style("syndicate")
ui = new(user, src, ui_key, "Uplink", name, 620, 580, master_ui, state)
// This UI is only ever opened by one person,
// and never is updated outside of user input.
ui.set_autoupdate(FALSE)
ui.open()
/datum/component/uplink/ui_host(mob/user)
@@ -157,8 +157,7 @@ GLOBAL_LIST_EMPTY(uplinks)
var/list/data = list()
data["telecrystals"] = telecrystals
data["lockable"] = lockable
data["compact_mode"] = compact_mode
data["compactMode"] = compact_mode
return data
/datum/component/uplink/ui_static_data(mob/user)
@@ -192,21 +191,16 @@ GLOBAL_LIST_EMPTY(uplinks)
/datum/component/uplink/ui_act(action, params)
if(!active)
return
switch(action)
if("buy")
var/item = params["item"]
var/item_name = params["name"]
var/list/buyable_items = list()
for(var/category in uplink_items)
buyable_items += uplink_items[category]
if(item in buyable_items)
var/datum/uplink_item/I = buyable_items[item]
//check to make sure people cannot buy items when the player pop is below the requirement
if(GLOB.joined_player_list.len >= I.player_minimum)
MakePurchase(usr, I)
. = TRUE
if(item_name in buyable_items)
var/datum/uplink_item/I = buyable_items[item_name]
MakePurchase(usr, I)
return TRUE
if("lock")
active = FALSE
locked = TRUE
@@ -215,9 +209,10 @@ GLOBAL_LIST_EMPTY(uplinks)
SStgui.close_uis(src)
if("select")
selected_cat = params["category"]
return TRUE
if("compact_toggle")
compact_mode = !compact_mode
return TRUE
return TRUE
/datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U)
if(!istype(U))
@@ -262,12 +257,12 @@ GLOBAL_LIST_EMPTY(uplinks)
var/obj/item/pda/master = parent
if(trim(lowertext(new_ring_text)) != trim(lowertext(unlock_code)))
if(trim(lowertext(new_ring_text)) == trim(lowertext(failsafe_code)))
failsafe()
failsafe(user)
return COMPONENT_STOP_RINGTONE_CHANGE
return
locked = FALSE
interact(null, user)
to_chat(user, "The PDA softly beeps.")
to_chat(user, "<span class='hear'>The PDA softly beeps.</span>")
user << browse(null, "window=pda")
master.mode = 0
return COMPONENT_STOP_RINGTONE_CHANGE
@@ -279,7 +274,7 @@ GLOBAL_LIST_EMPTY(uplinks)
var/frequency = arguments[1]
if(frequency != unlock_code)
if(frequency == failsafe_code)
failsafe()
failsafe(master.loc)
return
locked = FALSE
if(ismob(master.loc))
@@ -316,11 +311,13 @@ GLOBAL_LIST_EMPTY(uplinks)
else if(istype(parent,/obj/item/pen))
return rand(1, 360)
/datum/component/uplink/proc/failsafe()
/datum/component/uplink/proc/failsafe(mob/living/carbon/user)
if(!parent)
return
var/turf/T = get_turf(parent)
if(!T)
return
message_admins("[ADMIN_LOOKUPFLW(user)] has triggered an uplink failsafe explosion at [AREACOORD(T)] The owner of the uplink was [ADMIN_LOOKUPFLW(owner)].")
log_game("[key_name(user)] triggered an uplink failsafe explosion. The owner of the uplink was [key_name(owner)].")
explosion(T,1,2,3)
qdel(parent) //Alternatively could brick the uplink.
+1 -1
View File
@@ -121,7 +121,7 @@
if(-INFINITY to T0C)
add_wet(TURF_WET_ICE, max_time_left()) //Water freezes into ice!
if(T0C to T0C + 100)
decrease = ((T.air.temperature - T0C) / SSwet_floors.temperature_coeff) * (diff / SSwet_floors.time_ratio)
decrease = ((T.air.return_temperature() - T0C) / SSwet_floors.temperature_coeff) * (diff / SSwet_floors.time_ratio)
if(T0C + 100 to INFINITY)
decrease = INFINITY
decrease = max(0, decrease)
+4 -4
View File
@@ -81,7 +81,7 @@
if(N.new_character)
log_manifest(N.ckey,N.new_character.mind,N.new_character)
if(ishuman(N.new_character))
manifest_inject(N.new_character, N.client)
manifest_inject(N.new_character, N.client, N.client.prefs)
CHECK_TICK
/datum/datacore/proc/manifest_modify(name, assignment)
@@ -197,7 +197,7 @@
return dat
/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H, client/C)
/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H, client/C, datum/preferences/prefs)
set waitfor = FALSE
var/static/list/show_directions = list(SOUTH, WEST)
if(H.mind && (H.mind.assigned_role != H.mind.special_role))
@@ -260,7 +260,7 @@
M.fields["alg_d"] = "No allergies have been detected in this patient."
M.fields["cdi"] = "None"
M.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
M.fields["notes"] = H.get_trait_string(medical)
M.fields["notes"] = "Trait information as of shift start: [H.get_trait_string(medical)]<br>[prefs.medical_records]"
medical += M
//Security Record
@@ -270,7 +270,7 @@
S.fields["criminal"] = "None"
S.fields["mi_crim"] = list()
S.fields["ma_crim"] = list()
S.fields["notes"] = "No notes."
S.fields["notes"] = prefs.security_records || "No notes."
security += S
//Locked Record
+34
View File
@@ -39,6 +39,9 @@
/// A weak reference to another datum
var/datum/weakref/weak_reference
///Lazy associative list of currently active cooldowns.
var/list/cooldowns
#ifdef TESTING
var/running_find_references
var/last_find_references = 0
@@ -201,3 +204,34 @@
qdel(D)
else
return returned
/**
* Callback called by a timer to end an associative-list-indexed cooldown.
*
* Arguments:
* * source - datum storing the cooldown
* * index - string index storing the cooldown on the cooldowns associative list
*
* This sends a signal reporting the cooldown end.
*/
/proc/end_cooldown(datum/source, index)
if(QDELETED(source))
return
SEND_SIGNAL(source, COMSIG_CD_STOP(index))
TIMER_COOLDOWN_END(source, index)
/**
* Proc used by stoppable timers to end a cooldown before the time has ran out.
*
* Arguments:
* * source - datum storing the cooldown
* * index - string index storing the cooldown on the cooldowns associative list
*
* This sends a signal reporting the cooldown end, passing the time left as an argument.
*/
/proc/reset_cooldown(datum/source, index)
if(QDELETED(source))
return
SEND_SIGNAL(source, COMSIG_CD_RESET(index), S_TIMER_COOLDOWN_TIMELEFT(source, index))
TIMER_COOLDOWN_END(source, index)
@@ -404,7 +404,7 @@
if(M.loc)
environment = M.loc.return_air()
if(environment)
plasmamount = environment.gases[/datum/gas/plasma]
plasmamount = environment.get_moles(/datum/gas/plasma)
if(plasmamount && plasmamount > GLOB.meta_gas_visibility[/datum/gas/plasma]) //if there's enough plasma in the air to see
. += power * 0.5
if(M.reagents.has_reagent(/datum/reagent/toxin/plasma))
+5 -5
View File
@@ -20,11 +20,11 @@
/datum/disease/transformation/Copy()
var/datum/disease/transformation/D = ..()
D.stage1 = stage1.Copy()
D.stage2 = stage2.Copy()
D.stage3 = stage3.Copy()
D.stage4 = stage4.Copy()
D.stage5 = stage5.Copy()
D.stage1 = stage1?.Copy()
D.stage2 = stage2?.Copy()
D.stage3 = stage3?.Copy()
D.stage4 = stage4?.Copy()
D.stage5 = stage5?.Copy()
D.new_form = D.new_form
return D
+16 -3
View File
@@ -15,6 +15,7 @@
var/mob/living/holder
var/delete_species = TRUE //Set to FALSE when a body is scanned by a cloner to fix #38875
var/mutation_index[DNA_MUTATION_BLOCKS] //List of which mutations this carbon has and its assigned block
var/default_mutation_genes[DNA_MUTATION_BLOCKS] //List of the default genes from this mutation to allow DNA Scanner highlighting
var/stability = 100
var/scrambled = FALSE //Did we take something like mutagen? In that case we cant get our genes scanned to instantly cheese all the powers.
var/skin_tone_override //because custom skin tones are not found in the skin_tones global list.
@@ -58,6 +59,7 @@
H.give_genitals(TRUE)//This gives the body the genitals of this DNA. Used for any transformations based on DNA
if(transfer_SE)
destination.dna.mutation_index = mutation_index
destination.dna.default_mutation_genes = default_mutation_genes
destination.dna.update_body_size(old_size)
@@ -66,6 +68,7 @@
/datum/dna/proc/copy_dna(datum/dna/new_dna)
new_dna.unique_enzymes = unique_enzymes
new_dna.mutation_index = mutation_index
new_dna.default_mutation_genes = default_mutation_genes
new_dna.uni_identity = uni_identity
new_dna.blood_type = blood_type
new_dna.skin_tone_override = skin_tone_override
@@ -160,15 +163,18 @@
if(!LAZYLEN(mutations_temp))
return
mutation_index.Cut()
default_mutation_genes.Cut()
shuffle_inplace(mutations_temp)
if(ismonkey(holder))
mutations |= new RACEMUT(MUT_NORMAL)
mutation_index[RACEMUT] = GET_SEQUENCE(RACEMUT)
else
mutation_index[RACEMUT] = create_sequence(RACEMUT, FALSE)
default_mutation_genes[RACEMUT] = mutation_index[RACEMUT]
for(var/i in 2 to DNA_MUTATION_BLOCKS)
var/datum/mutation/human/M = mutations_temp[i]
mutation_index[M.type] = create_sequence(M.type, FALSE,M.difficulty)
mutation_index[M.type] = create_sequence(M.type, FALSE, M.difficulty)
default_mutation_genes[M.type] = mutation_index[M.type]
shuffle_inplace(mutation_index)
//Used to generate original gene sequences for every mutation
@@ -389,7 +395,7 @@
return dna
/mob/living/carbon/human/proc/hardset_dna(ui, list/mutation_index, newreal_name, newblood_type, datum/species/mrace, newfeatures)
/mob/living/carbon/human/proc/hardset_dna(ui, list/mutation_index, newreal_name, newblood_type, datum/species/mrace, newfeatures, list/default_mutation_genes)
if(newreal_name)
real_name = newreal_name
@@ -414,6 +420,10 @@
if(LAZYLEN(mutation_index))
dna.mutation_index = mutation_index.Copy()
if(LAZYLEN(default_mutation_genes))
dna.default_mutation_genes = default_mutation_genes.Copy()
else
dna.default_mutation_genes = mutation_index.Copy()
domutcheck()
SEND_SIGNAL(src, COMSIG_HUMAN_HARDSET_DNA, ui, mutation_index, newreal_name, newblood_type, mrace, newfeatures)
@@ -505,8 +515,11 @@
. = TRUE
if(on)
mutation_index[HM.type] = GET_SEQUENCE(HM.type)
default_mutation_genes[HM.type] = mutation_index[HM.type]
else if(GET_SEQUENCE(HM.type) == mutation_index[HM.type])
mutation_index[HM.type] = create_sequence(HM.type, FALSE, HM.difficulty)
default_mutation_genes[HM.type] = mutation_index[HM.type]
/datum/dna/proc/activate_mutation(mutation) //note that this returns a boolean and not a new mob
if(!mutation)
@@ -678,7 +691,7 @@
holder.update_transform()
var/danger = CONFIG_GET(number/threshold_body_size_slowdown)
if(features["body_size"] < danger)
var/slowdown = 1 + round(danger/features["body_size"], 0.1) * CONFIG_GET(number/body_size_slowdown_multiplier)
var/slowdown = (1 - round(features["body_size"] / danger, 0.1)) * CONFIG_GET(number/body_size_slowdown_multiplier)
holder.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/small_stride, TRUE, slowdown)
else if(old_size < danger)
holder.remove_movespeed_modifier(/datum/movespeed_modifier/small_stride)
+5 -2
View File
@@ -8,8 +8,11 @@
if(. == ELEMENT_INCOMPATIBLE || !isatom(target) || isarea(target))
return ELEMENT_INCOMPATIBLE
beauty = beautyamount
RegisterSignal(target, COMSIG_ENTER_AREA, .proc/enter_area)
RegisterSignal(target, COMSIG_EXIT_AREA, .proc/exit_area)
if(ismovable(target))
RegisterSignal(target, COMSIG_ENTER_AREA, .proc/enter_area)
RegisterSignal(target, COMSIG_EXIT_AREA, .proc/exit_area)
var/area/A = get_area(target)
if(A)
enter_area(null, A)
+2 -1
View File
@@ -82,9 +82,10 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
return
if(href_list["show_flavor"])
var/atom/target = locate(href_list["show_flavor"])
var/mob/living/L = target
var/text = texts_by_atom[target]
if(text)
usr << browse("<HTML><HEAD><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><TITLE>[target.name]</TITLE></HEAD><BODY><TT>[replacetext(texts_by_atom[target], "\n", "<BR>")]</TT></BODY></HTML>", "window=[target.name];size=500x200")
usr << browse("<HTML><HEAD><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><TITLE>[isliving(target) ? L.get_visible_name() : target.name]</TITLE></HEAD><BODY><TT>[replacetext(texts_by_atom[target], "\n", "<BR>")]</TT></BODY></HTML>", "window=[isliving(target) ? L.get_visible_name() : target.name];size=500x200")
onclose(usr, "[target.name]")
return TRUE

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