This commit is contained in:
Ghommie
2020-05-11 00:34:22 +02:00
258 changed files with 2849 additions and 1895 deletions
File diff suppressed because it is too large Load Diff
+10 -7
View File
@@ -25,35 +25,38 @@
//SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
//subsystem does not initialize.
#define SS_NO_INIT 1
#define SS_NO_INIT (1<<0)
//subsystem does not fire.
// (like can_fire = 0, but keeps it from getting added to the processing subsystems list)
// (Requires a MC restart to change)
#define SS_NO_FIRE 2
#define SS_NO_FIRE (1<<1)
//subsystem only runs on spare cpu (after all non-background subsystems have ran that tick)
// SS_BACKGROUND has its own priority bracket
#define SS_BACKGROUND 4
#define SS_BACKGROUND (1<<2)
//subsystem does not tick check, and should not run unless there is enough time (or its running behind (unless background))
#define SS_NO_TICK_CHECK 8
#define SS_NO_TICK_CHECK (1<<3)
//Treat wait as a tick count, not DS, run every wait ticks.
// (also forces it to run first in the tick, above even SS_NO_TICK_CHECK subsystems)
// (implies all runlevels because of how it works)
// (overrides SS_BACKGROUND)
// This is designed for basically anything that works as a mini-mc (like SStimer)
#define SS_TICKER 16
#define SS_TICKER (1<<4)
//keep the subsystem's timing on point by firing early if it fired late last fire because of lag
// ie: if a 20ds subsystem fires say 5 ds late due to lag or what not, its next fire would be in 15ds, not 20ds.
#define SS_KEEP_TIMING 32
#define SS_KEEP_TIMING (1<<5)
//Calculate its next fire after its fired.
// (IE: if a 5ds wait SS takes 2ds to run, its next fire should be 5ds away, not 3ds like it normally would be)
// This flag overrides SS_KEEP_TIMING
#define SS_POST_FIRE_TIMING 64
#define SS_POST_FIRE_TIMING (1<<6)
/// Show in stat() by default even if SS_NO_FIRE
#define SS_ALWAYS_SHOW_STAT (1<<7)
//SUBSYSTEM STATES
#define SS_IDLE 0 //aint doing shit.
+1
View File
@@ -43,6 +43,7 @@
#define COLOR_RED_GRAY "#B4696A"
#define COLOR_PALE_BLUE_GRAY "#98C5DF"
#define COLOR_PALE_GREEN_GRAY "#B7D993"
#define COLOR_PALE_ORANGE "#FFC066"
#define COLOR_PALE_RED_GRAY "#D59998"
#define COLOR_PALE_PURPLE_GRAY "#CBB1CA"
#define COLOR_PURPLE_GRAY "#AE8CA8"
+26 -3
View File
@@ -35,9 +35,9 @@
/// Default combat flags for those affected by ((stamina combat))
#define COMBAT_FLAGS_DEFAULT NONE
/// Default combat flags for everyone else (so literally everyone but humans)
#define COMBAT_FLAGS_STAMSYSTEM_EXEMPT (COMBAT_FLAG_SPRINT_ACTIVE | COMBAT_FLAG_COMBAT_ACTIVE | COMBAT_FLAG_SPRINT_TOGGLED | COMBAT_FLAG_COMBAT_TOGGLED)
#define COMBAT_FLAGS_STAMSYSTEM_EXEMPT (COMBAT_FLAG_SPRINT_ACTIVE | COMBAT_FLAG_COMBAT_ACTIVE | COMBAT_FLAG_SPRINT_TOGGLED | COMBAT_FLAG_COMBAT_TOGGLED | COMBAT_FLAG_SPRINT_FORCED | COMBAT_FLAG_COMBAT_FORCED)
/// Default combat flags for those only affected by sprint (so just silicons)
#define COMBAT_FLAGS_STAMEXEMPT_YESSPRINT (COMBAT_FLAG_COMBAT_ACTIVE | COMBAT_FLAG_COMBAT_TOGGLED)
#define COMBAT_FLAGS_STAMEXEMPT_YESSPRINT (COMBAT_FLAG_COMBAT_ACTIVE | COMBAT_FLAG_COMBAT_TOGGLED | COMBAT_FLAG_COMBAT_FORCED)
/// The user wants combat mode on
#define COMBAT_FLAG_COMBAT_TOGGLED (1<<0)
@@ -57,6 +57,10 @@
#define COMBAT_FLAG_INTENTIONALLY_RESTING (1<<7)
/// Currently stamcritted but not as violently
#define COMBAT_FLAG_SOFT_STAMCRIT (1<<8)
/// Force combat mode on at all times, overrides everything including combat disable traits.
#define COMBAT_FLAG_COMBAT_FORCED (1<<9)
/// Force sprint mode on at all times, overrides everything including sprint disable traits.
#define COMBAT_FLAG_SPRINT_FORCED (1<<10)
// Helpers for getting someone's stamcrit state. Cast to living.
#define NOT_STAMCRIT 0
@@ -248,13 +252,32 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
#define TOTAL_MASS_MEDIEVAL_WEAPON 3.6 //very, very generic average sword/warpick/etc. weight in pounds.
#define TOTAL_MASS_TOY_SWORD 1.5
//stamina cost defines.
#define STAM_COST_ATTACK_OBJ_MULT 1.2
#define STAM_COST_ATTACK_MOB_MULT 0.8
#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
//bullet_act() return values
#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting.
#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting.
#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
/// Bitflags for check_block() and handle_block(). Meant to be combined. You can be hit and still reflect, for example, if you do not use BLOCK_SUCCESS.
/// Check whether or not we can block, without "triggering" a block. Basically run checks without effects like depleting shields.
/// Wrapper for do_run_block(). The arguments on that means the same as for this.
#define mob_check_block(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)\
do_run_block(FALSE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list)
/// Runs a block "sequence", effectively checking and then doing effects if necessary.
/// Wrapper for do_run_block(). The arguments on that means the same as for this.
#define mob_run_block(object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)\
do_run_block(TRUE, object, damage, attack_text, attack_type, armour_penetration, attacker, check_zone(def_zone), return_list)
/// Bitflags for check_block() and run_block(). Meant to be combined. You can be hit and still reflect, for example, if you do not use BLOCK_SUCCESS.
/// Attack was not blocked
#define BLOCK_NONE NONE
/// Attack was blocked, do not do damage. THIS FLAG MUST BE THERE FOR DAMAGE/EFFECT PREVENTION!
+22 -1
View File
@@ -183,7 +183,28 @@
// #define SPEECH_FORCED 7
#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" //called from base of /obj/effect/proc_holder/spell/can_cast(): (spell)
#define COMSIG_MOB_SPELL_CAN_CAST "mob_spell_can_cast" //from base of /obj/effect/proc_holder/spell/can_cast(): (spell)
#define COMSIG_MOB_ACTION_SKILL_MOD "mob_action_skill_mod" //from base of /datum/skill_holder/action_skills_mod() : (proc args list, list/mod_values)
#define ACTION_SKILL_MOD_SKILL 1 //redundancy.
#define ACTION_SKILL_MOD_VALUE 2
#define ACTION_SKILL_MOD_THRESHOLD 3
#define ACTION_SKILL_MOD_IS_MULTI 4
//other mod values, kept separate from the args
#define MOD_VALUES_SKILL_MOD 1
#define COMSIG_MOB_ITEM_ACTION_SKILLS_MOD "mob_item_action_skills_mod" //from base of /datum/skill_holder/item_action_skills_mod() : (proc args list, mod_values)
#define ITEM_SKILLS_MOD_ITEM 1 //redundancy
#define ITEM_SKILLS_MOD_VALUE 2
#define ITEM_SKILLS_MOD_FLAGS 3
#define ITEM_SKILLS_MOD_BAD_FLAGS 4
#define ITEM_SKILLS_MOD_IS_MULTI 5
//other mod values, kept separate from the args
#define MOD_VALUES_ITEM_SKILLS_SUM 1
#define MOD_VALUES_ITEM_SKILLS_DIV 2
#define MOD_VALUES_ITEM_SKILLS_CHECKED 3
#define COMSIG_MOB_SKILL_GET_AFFINITY "mob_skill_get_affinity" //from base of /datum/skill_holder/get_skill_affinity(): (skill_path, list/return_value)
// /mob/living signals
#define COMSIG_LIVING_REGENERATE_LIMBS "living_regenerate_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs)
+21 -2
View File
@@ -3,5 +3,24 @@
#define LANGUAGE_HIDE_ICON_IF_UNDERSTOOD 4
#define LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD 8
#define LANGUAGE_KNOWN "language_known"
#define LANGUAGE_SHADOWED "language_shadowed"
// LANGUAGE SOURCE DEFINES
#define LANGUAGE_ALL "all" // For use in full removal only.
#define LANGUAGE_ATOM "atom"
#define LANGUAGE_MIND "mind"
#define LANGUAGE_ABSORB "absorb"
#define LANGUAGE_APHASIA "aphasia"
#define LANGUAGE_BLOODSUCKER "bloodsucker"
#define LANGUAGE_CLOCKIE "clockie"
#define LANGUAGE_CULTIST "cultist"
#define LANGUAGE_CURATOR "curator"
#define LANGUAGE_DEVIL "devil"
#define LANGUAGE_GLAND "gland"
#define LANGUAGE_HAT "hat"
#define LANGUAGE_HIGH "high"
#define LANGUAGE_MALF "malf"
#define LANGUAGE_MASTER "master"
#define LANGUAGE_SOFTWARE "software"
#define LANGUAGE_STONER "stoner"
#define LANGUAGE_VASSAL "vassal"
#define LANGUAGE_VOICECHANGE "voicechange"
+1
View File
@@ -138,6 +138,7 @@
#define MOOD_LEVEL_SAD4 -25
//Sanity levels for humans
#define SANITY_AMAZING 150
#define SANITY_GREAT 125
#define SANITY_NEUTRAL 100
#define SANITY_DISTURBED 75
+77
View File
@@ -0,0 +1,77 @@
/// true/false
#define SKILL_PROGRESSION_BINARY 1
/// numerical
#define SKILL_PROGRESSION_NUMERICAL 2
/// Enum
#define SKILL_PROGRESSION_ENUM 3
/// 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
// Standard values for job starting skill affinities
#define STARTING_SKILL_AFFINITY_SURGERY_MEDICAL 1.2
#define STARTING_SKILL_AFFINITY_WIRING_ENGI_ROBO 1.2
// Standard values for skill gain (this is multiplied by affinity)
#define DEF_SKILL_GAIN 1
#define SKILL_GAIN_SURGERY_PER_STEP 0.25
#define SKILL_AFFINITY_MOOD_BONUS 1.25
///Items skill_flags and other defines
#define SKILL_USE_TOOL (1<<0)
#define SKILL_TRAINING_TOOL (1<<1)
#define SKILL_ATTACK_MOB (1<<2)
#define SKILL_TRAIN_ATTACK_MOB (1<<3)
#define SKILL_ATTACK_OBJ (1<<4)
#define SKILL_TRAIN_ATTACK_OBJ (1<<5)
#define SKILL_STAMINA_COST (1<<6) //Influences the stamina cost from weapon usage.
#define SKILL_THROW_STAM_COST (1<<7)
#define SKILL_COMBAT_MODE (1<<8) //The user must have combat mode on.
#define SKILL_USE_MOOD (1<<9) //Is the skill negatively affected by bad mood.
#define SKILL_TRAIN_MOOD (1<<10) //Is this skill training affected by good mood.
///competency_threshold index defines
#define THRESHOLD_COMPETENT 1
#define THRESHOLD_EXPERT 2
#define THRESHOLD_MASTER 3
/// Level/Experience skills defines.
#define STD_XP_LVL_UP 100
#define STD_XP_LVL_MULTI 2
#define STD_MAX_LVL 4
#define RPG_MAX_LVL 100
#define DORF_XP_LVL_UP 400
#define DORF_XP_LVL_MULTI 100
#define DORF_MAX_LVL 20 // Dabbling, novice, adequate, [...], legendary +3, legendary +4, legendary +5
//level up methods defines
#define STANDARD_LEVEL_UP "standard_level_up"
#define DWARFY_LEVEL_UP "dwarfy_level_up"
//job skill level defines
#define JOB_SKILL_UNTRAINED 0
#define JOB_SKILL_BASIC 1
#define JOB_SKILL_TRAINED 2
#define JOB_SKILL_EXPERT 3
#define JOB_SKILL_MASTER 4
//other skill level defines, not an exhaustive catalogue, only contains be most relevant ones.
#define DORF_SKILL_COMPETENT 3
#define DORF_SKILL_EXPERT 8
#define DORF_SKILL_MASTER 12
+8
View File
@@ -0,0 +1,8 @@
//How experience levels are calculated.
#define XP_LEVEL(std, multi, lvl) (std*((multi**lvl)/(multi-1))-std/(multi-1)) //don't use 1 as multi, you'll get division by zero errors
#define DORF_XP_LEVEL(std, extra, lvl) (std*lvl+extra*(lvl*(lvl/2+0.5)))
//More experience value getter macros
#define GET_STANDARD_LVL(lvl) XP_LEVEL(STD_XP_LVL_UP, STD_XP_LVL_MULTI, lvl)
#define GET_DORF_LVL(lvl) DORF_XP_LEVEL(DORF_XP_LVL_UP, DORF_XP_LVL_MULTI, lvl)
-28
View File
@@ -1,28 +0,0 @@
/// true/false
#define SKILL_PROGRESSION_BINARY 1
/// numerical
#define SKILL_PROGRESSION_NUMERICAL 2
/// Enum
#define SKILL_PROGRESSION_ENUM 3
/// 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
// Standard values for job starting skill affinities
#define STARTING_SKILL_AFFINITY_SURGERY_MEDICAL 1.2
// Standard values for skill gain (this is multiplied by affinity)
#define SKILL_GAIN_SURGERY_PER_STEP 0.25
// Misc
/// 40% speedup at 100 skill
#define SURGERY_SKILL_SPEEDUP_NUMERICAL_SCALE(number) clamp(number / 250, 1, 2)
+10 -1
View File
@@ -255,6 +255,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
current_user = M.client
//Blank
menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing",layer = ABOVE_HUD_LAYER)
menu_holder.plane = ABOVE_HUD_PLANE
menu_holder.appearance_flags |= KEEP_APART
menu_holder.vis_contents += elements + close_button
current_user.images += menu_holder
@@ -285,13 +286,16 @@ GLOBAL_LIST_EMPTY(radial_menus)
Choices should be a list where list keys are movables or text used for element names and return value
and list values are movables/icons/images used for element icons
*/
/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE)
/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE, no_repeat_close = FALSE)
if(!user || !anchor || !length(choices))
return
if(!uniqueid)
uniqueid = "defmenu_[REF(user)]_[REF(anchor)]"
if(GLOB.radial_menus[uniqueid])
if(!no_repeat_close)
var/datum/radial_menu/menu = GLOB.radial_menus[uniqueid]
menu.finished = TRUE
return
var/datum/radial_menu/menu = new
@@ -308,4 +312,9 @@ GLOBAL_LIST_EMPTY(radial_menus)
var/answer = menu.selected_choice
qdel(menu)
GLOB.radial_menus -= uniqueid
if(require_near && !in_range(anchor, user))
return
if(istype(custom_check))
if(!custom_check.Invoke())
return
return answer
+56 -20
View File
@@ -91,7 +91,7 @@
log_combat(user, M, "attacked", src.name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])")
add_fingerprint(user)
user.adjustStaminaLossBuffered(getweight()*0.8)//CIT CHANGE - makes attacking things cause stamina loss
user.adjustStaminaLossBuffered(getweight(user, STAM_COST_ATTACK_MOB_MULT))//CIT CHANGE - makes attacking things cause stamina loss
//the equivalent of the standard version of attack() but for object targets.
/obj/item/proc/attack_obj(obj/O, mob/living/user)
@@ -102,7 +102,7 @@
if(IS_STAMCRIT(user)) // CIT CHANGE - makes it impossible to attack in stamina softcrit
to_chat(user, "<span class='warning'>You're too exhausted.</span>") // CIT CHANGE - ditto
return // CIT CHANGE - ditto
user.adjustStaminaLossBuffered(getweight()*1.2)//CIT CHANGE - makes attacking things cause stamina loss
user.adjustStaminaLossBuffered(getweight(user, STAM_COST_ATTACK_OBJ_MULT))//CIT CHANGE - makes attacking things cause stamina loss
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(O)
O.attacked_by(src, user)
@@ -111,26 +111,32 @@
return
/obj/attacked_by(obj/item/I, mob/living/user)
if(I.force)
var/totitemdamage = I.force
var/bad_flag = NONE
if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && iscarbon(user))
totitemdamage *= 0.5
bad_flag |= SKILL_COMBAT_MODE //blacklist combat skills.
if(I.used_skills && user.mind)
if(totitemdamage)
totitemdamage = user.mind.skill_holder.item_action_skills_mod(I, totitemdamage, I.skill_difficulty, SKILL_ATTACK_OBJ, bad_flag)
for(var/skill in I.used_skills)
if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_OBJ))
continue
user.mind.skill_holder.auto_gain_experience(skill, I.skill_gain)
if(totitemdamage)
visible_message("<span class='danger'>[user] has hit [src] with [I]!</span>", null, null, COMBAT_MESSAGE_RANGE)
//only witnesses close by and the victim see a hit message.
log_combat(user, src, "attacked", I)
take_damage(I.force, I.damtype, "melee", 1)
take_damage(totitemdamage, I.damtype, "melee", 1)
/mob/living/attacked_by(obj/item/I, mob/living/user)
//CIT CHANGES START HERE - combatmode and resting checks
var/totitemdamage = I.force
if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
totitemdamage *= 0.5
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
totitemdamage *= 0.5
//CIT CHANGES END HERE
if((user != src) && run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user) & BLOCK_SUCCESS)
var/totitemdamage = pre_attacked_by(I, user)
if((user != src) && mob_run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, null, null) & BLOCK_SUCCESS)
return FALSE
send_item_attack_message(I, user)
I.do_stagger_action(src, user)
I.do_stagger_action(src, user, totitemdamage)
if(I.force)
apply_damage(totitemdamage, I.damtype) //CIT CHANGE - replaces I.force with totitemdamage
apply_damage(totitemdamage, I.damtype)
if(I.damtype == BRUTE)
if(prob(33))
I.add_mob_blood(src)
@@ -146,6 +152,28 @@
else
return ..()
/mob/living/proc/pre_attacked_by(obj/item/I, mob/living/user)
. = I.force
var/bad_flag = NONE
if(!(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE) && iscarbon(user))
. *= 0.5
bad_flag |= SKILL_COMBAT_MODE //blacklist combat skills.
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
. *= 0.5
if(!user.mind || !I.used_skills)
return
if(.)
. = user.mind.skill_holder.item_action_skills_mod(I, ., I.skill_difficulty, SKILL_ATTACK_MOB, bad_flag)
for(var/skill in I.used_skills)
if(!(I.used_skills[skill] & SKILL_TRAIN_ATTACK_MOB))
continue
user.mind.skill_holder.auto_gain_experience(skill, I.skill_gain)
/mob/living/carbon/pre_attacked_by(obj/item/I, mob/living/user)
. = ..()
if(!(combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
. *= 1.5
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
// Click parameters is the params string from byond Click() code, see that documentation.
/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
@@ -179,22 +207,30 @@
return 1
/// How much stamina this takes to swing this is not for realism purposes hecc off.
/obj/item/proc/getweight()
return total_mass || w_class * 1.25
/obj/item/proc/getweight(mob/living/user, multiplier = 1, flags = SKILL_STAMINA_COST)
. = (total_mass || w_class * STAM_COST_W_CLASS_MULT) * multiplier
if(!user)
return
var/bad_flag = NONE
if(iscarbon(user) && !(user.combat_flags & COMBAT_FLAG_COMBAT_ACTIVE))
. *= STAM_COST_NO_COMBAT_MULT
bad_flag |= SKILL_COMBAT_MODE
if(used_skills && user.mind)
. = user.mind.skill_holder.item_action_skills_mod(src, ., skill_difficulty, flags, bad_flag, FALSE)
/// How long this staggers for. 0 and negatives supported.
/obj/item/proc/melee_stagger_duration()
/obj/item/proc/melee_stagger_duration(force_override)
if(!isnull(stagger_force))
return stagger_force
/// totally not an untested, arbitrary equation.
return clamp((1.5 + (w_class/7.5)) * (force / 2), 0, 10 SECONDS)
return clamp((1.5 + (w_class/7.5)) * ((force_override || force) / 2), 0, 10 SECONDS)
/obj/item/proc/do_stagger_action(mob/living/target, mob/living/user)
/obj/item/proc/do_stagger_action(mob/living/target, mob/living/user, force_override)
if(!CHECK_BITFIELD(target.status_flags, CANSTAGGER))
return FALSE
if(target.combat_flags & COMBAT_FLAG_SPRINT_ACTIVE)
target.do_staggered_animation()
var/duration = melee_stagger_duration()
var/duration = melee_stagger_duration(force_override)
if(!duration) //0
return FALSE
else if(duration > 0)
+9 -3
View File
@@ -3,9 +3,7 @@
name = "Initializing..."
var/target
INITIALIZE_IMMEDIATE(/obj/effect/statclick)
/obj/effect/statclick/Initialize(mapload, text, target) //Don't port this to Initialize it's too critical
/obj/effect/statclick/New(loc, text, target) //Don't port this to Initialize it's too critical
. = ..()
name = text
src.target = target
@@ -33,6 +31,14 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick)
usr.client.debug_variables(target)
message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].")
/obj/effect/statclick/misc_subsystems/Click()
if(!usr.client.holder)
return
var/subsystem = input(usr, "Debug which subsystem?", "Debug nonprocessing subsystem") as null|anything in (Master.subsystems - Master.statworthy_subsystems)
if(!subsystem)
return
usr.client.debug_variables(subsystem)
message_admins("Admin [key_name_admin(usr)] is debugging the [subsystem] subsystem.")
// Debug verbs.
/client/proc/restart_controller(controller in list("Master", "Failsafe"))
@@ -35,6 +35,13 @@
/datum/config_entry/keyed_list/midround_antag/ValidateListEntry(key_name, key_value)
return key_name in config.modes
/datum/config_entry/keyed_list/force_antag_count
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_FLAG
/datum/config_entry/keyed_list/force_antag_count/ValidateListEntry(key_name, key_value)
return key_name in config.modes
/datum/config_entry/keyed_list/policy
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_TEXT
+15 -1
View File
@@ -28,6 +28,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
// List of subsystems to process().
var/list/subsystems
/// List of subsystems to include in the MC stat panel.
var/list/statworthy_subsystems
// Vars for keeping track of tick drift.
var/init_timeofday
@@ -65,6 +67,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
var/static/current_ticklimit = TICK_LIMIT_RUNNING
/// Statclick for misc subsystems
var/obj/effect/statclick/misc_subsystems/misc_statclick
/datum/controller/master/New()
if(!config)
config = new
@@ -87,6 +92,11 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
_subsystems += new I
Master = src
// We want to see all subsystems during init.
statworthy_subsystems = subsystems.Copy()
misc_statclick = new(null, "Debug")
if(!GLOB)
new /datum/controller/global_vars
@@ -257,10 +267,14 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/list/tickersubsystems = list()
var/list/runlevel_sorted_subsystems = list(list()) //ensure we always have at least one runlevel
var/timer = world.time
statworthy_subsystems = list()
for (var/thing in subsystems)
var/datum/controller/subsystem/SS = thing
if (SS.flags & SS_NO_FIRE)
if(SS.flags & SS_ALWAYS_SHOW_STAT)
statworthy_subsystems += SS
continue
statworthy_subsystems += SS
SS.queued_time = 0
SS.queue_next = null
SS.queue_prev = null
@@ -603,7 +617,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)")
stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration]) (TickLimit: [round(Master.current_ticklimit, 0.1)])"))
stat("Misc Subsystems", misc_statclick)
/datum/controller/master/StartLoadingMap()
//disallow more than one map to load at once, multithreading it will just cause race conditions
@@ -91,7 +91,6 @@
trauma = _trauma
owner = trauma.owner
copy_known_languages_from(owner, TRUE)
setup_friend()
+4 -10
View File
@@ -26,21 +26,15 @@
scan_desc = "extensive damage to the brain's language center"
gain_text = "<span class='warning'>You have trouble forming words in your head...</span>"
lose_text = "<span class='notice'>You suddenly remember how languages work.</span>"
var/datum/language_holder/prev_language
var/datum/language_holder/mob_language
/datum/brain_trauma/severe/aphasia/on_gain()
mob_language = owner.get_language_holder()
prev_language = mob_language.copy()
mob_language.remove_all_languages()
mob_language.grant_language(/datum/language/aphasia)
owner.add_blocked_language(subtypesof(/datum/language/) - /datum/language/aphasia, LANGUAGE_APHASIA)
owner.grant_language(/datum/language/aphasia, TRUE, TRUE, LANGUAGE_APHASIA)
..()
/datum/brain_trauma/severe/aphasia/on_lose()
mob_language.remove_language(/datum/language/aphasia)
mob_language.copy_known_languages_from(prev_language) //this will also preserve languages learned during the trauma
QDEL_NULL(prev_language)
mob_language = null
owner.remove_blocked_language(subtypesof(/datum/language/), LANGUAGE_APHASIA)
owner.remove_language(/datum/language/aphasia, TRUE, TRUE, LANGUAGE_APHASIA)
..()
/datum/brain_trauma/severe/blindness
@@ -117,7 +117,7 @@
/datum/crafting_recipe/upgraded_gauze
name = "Improved Gauze"
result = /obj/item/stack/medical/gauze/adv
result = /obj/item/stack/medical/gauze/adv/one
time = 1
reqs = list(/obj/item/stack/medical/gauze = 1,
/datum/reagent/space_cleaner/sterilizine = 10)
@@ -126,7 +126,7 @@
/datum/crafting_recipe/bruise_pack
name = "Bruise Pack"
result = /obj/item/stack/medical/bruise_pack
result = /obj/item/stack/medical/bruise_pack/one
time = 1
reqs = list(/obj/item/stack/medical/gauze = 1,
/datum/reagent/medicine/styptic_powder = 10)
@@ -134,8 +134,8 @@
subcategory = CAT_TOOL
/datum/crafting_recipe/burn_pack
name = "Brun Ointment"
result = /obj/item/stack/medical/ointment
name = "Burn Ointment"
result = /obj/item/stack/medical/ointment/one
time = 1
reqs = list(/obj/item/stack/medical/gauze = 1,
/datum/reagent/medicine/silver_sulfadiazine = 10)
+52 -4
View File
@@ -1,5 +1,7 @@
#define SLIGHT_INSANITY_PEN 1
#define MINOR_INSANITY_PEN 5
#define MAJOR_INSANITY_PEN 10
#define MOOD_INSANITY_MALUS 0.0054 // per point of sanity below SANITY_DISTURBED, a 40% debuff to skills at rock bottom depression.
/datum/component/mood
var/mood //Real happiness
@@ -22,7 +24,6 @@
RegisterSignal(parent, COMSIG_CLEAR_MOOD_EVENT, .proc/clear_event)
RegisterSignal(parent, COMSIG_MODIFY_SANITY, .proc/modify_sanity)
RegisterSignal(parent, COMSIG_LIVING_REVIVE, .proc/on_revive)
RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, .proc/modify_hud)
var/mob/living/owner = parent
if(owner.hud_used)
@@ -150,7 +151,7 @@
if(8)
setSanity(sanity+0.25, maximum=SANITY_GREAT)
if(9)
setSanity(sanity+0.4, maximum=SANITY_GREAT)
setSanity(sanity+0.4, maximum=SANITY_AMAZING)
HandleNutrition(owner)
@@ -166,6 +167,7 @@
else if(sanity > maximum && amount > sanity - 0.5)
amount = sanity - 0.5
var/old_sanity = sanity
// Disturbed stops you from getting any more sane
if(HAS_TRAIT(master, TRAIT_UNSTABLE))
sanity = min(amount,sanity)
@@ -182,7 +184,7 @@
master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/crazy)
sanity_level = 5
if(SANITY_UNSTABLE to SANITY_DISTURBED)
setInsanityEffect(0)
setInsanityEffect(SLIGHT_INSANITY_PEN)
master.add_movespeed_modifier(/datum/movespeed_modifier/sanity/disturbed)
sanity_level = 4
if(SANITY_DISTURBED to SANITY_NEUTRAL)
@@ -197,6 +199,12 @@
setInsanityEffect(0)
master.remove_movespeed_modifier(MOVESPEED_ID_SANITY)
sanity_level = 1
if(old_sanity > 1 && sanity == 1)
RegisterSignal(master, COMSIG_MOB_SKILL_GET_AFFINITY, .proc/on_get_skill_affinity)
else if(old_sanity == 1 && sanity > 1)
UnregisterSignal(master, COMSIG_MOB_SKILL_GET_AFFINITY)
//update_mood_icon()
/datum/component/mood/proc/setInsanityEffect(newval)//More code so that the previous proc works
@@ -204,9 +212,14 @@
return
//var/mob/living/master = parent
//master.crit_threshold = (master.crit_threshold - insanity_effect) + newval
if(!insanity_effect && newval)
RegisterSignal(parent, COMSIG_MOB_ACTION_SKILL_MOD, .proc/on_mob_action_skill_mod)
RegisterSignal(parent, COMSIG_MOB_ITEM_ACTION_SKILLS_MOD, .proc/on_item_action_skills_mod)
else if(insanity_effect && !newval)
UnregisterSignal(parent, list(COMSIG_MOB_ACTION_SKILL_MOD, COMSIG_MOB_ITEM_ACTION_SKILLS_MOD))
insanity_effect = newval
/datum/component/mood/proc/modify_sanity(datum/source, amount, minimum = -INFINITY, maximum = INFINITY)
/datum/component/mood/proc/modify_sanity(datum/source, amount, minimum = SANITY_INSANE, maximum = SANITY_AMAZING)
setSanity(sanity + amount, minimum, maximum)
/datum/component/mood/proc/add_event(datum/source, category, type, param) //Category will override any events in the same category, should be unique unless the event is based on the same thing like hunger.
@@ -288,5 +301,40 @@
remove_temp_moods()
setSanity(initial(sanity))
/datum/component/mood/proc/on_mob_action_skill_mod(mob/source, list/skill_args, list/mod_values)
var/datum/skill/S = GLOB.skill_datums[skill_args[ACTION_SKILL_MOD_SKILL]]
if(!(S.skill_flags & SKILL_USE_MOOD))
return
var/debuff = 1 - (SANITY_DISTURBED - sanity) * MOOD_INSANITY_MALUS
mod_values[MOD_VALUES_SKILL_MOD] *= skill_args[ACTION_SKILL_MOD_IS_MULTI] ? debuff : 1/debuff
/datum/component/mood/proc/on_item_action_skills_mod(mob/source, list/skill_args, list/mod_values)
if(skill_args[ITEM_SKILLS_MOD_BAD_FLAGS] & SKILL_USE_MOOD)
return
var/divisor = mod_values[MOD_VALUES_ITEM_SKILLS_DIV]
if(!divisor)
return
var/obj/item/I = skill_args[ITEM_SKILLS_MOD_ITEM]
var/list/L = mod_values[MOD_VALUES_ITEM_SKILLS_CHECKED]
var/skills_len = length(L)
var/affected_skills = skills_len
for(var/k in L)
var/datum/skill/S = k
var/our_flags = I.used_skills[S.type]|S.skill_flags
if(!(our_flags & SKILL_USE_MOOD))
affected_skills--
if(!affected_skills)
return
var/debuff = 1 - (SANITY_DISTURBED - sanity) * MOOD_INSANITY_MALUS * (affected_skills/skills_len)
mod_values[MOD_VALUES_ITEM_SKILLS_SUM] *= skill_args[ITEM_SKILLS_MOD_IS_MULTI] ? debuff : 1/debuff
/datum/component/mood/proc/on_get_skill_affinity(mob/source, skill_path, list/return_value)
var/datum/skill/S = GLOB.skill_datums[skill_path]
if(!S || !(S.skill_flags & SKILL_TRAIN_MOOD))
return
return_value[1] *= SKILL_AFFINITY_MOOD_BONUS
#undef SLIGHT_INSANITY_PEN
#undef MINOR_INSANITY_PEN
#undef MAJOR_INSANITY_PEN
#undef MOOD_INSANITY_MALUS
+1 -1
View File
@@ -208,13 +208,13 @@
RegisterSignal(parent, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_host_unarmed_melee)
/datum/component/riding/human/vehicle_mob_unbuckle(datum/source, mob/living/M, force = FALSE)
. = ..()
var/mob/living/carbon/human/H = parent
if(!length(H.buckled_mobs))
H.remove_movespeed_modifier(/datum/movespeed_modifier/human_carry)
if(!fireman_carrying)
M.Daze(25)
REMOVE_TRAIT(M, TRAIT_MOBILITY_NOUSE, src)
return ..()
/datum/component/riding/human/vehicle_mob_buckle(datum/source, mob/living/M, force = FALSE)
. = ..()
@@ -58,7 +58,7 @@
var/mob/living/carbon/C = M
if(prob(10))
if(trauma_heal_severe)
C.cure_trauma_type(resilience = TRAUMA_RESILIENCE_LOBOTOMY)
C.cure_trauma_type(resilience = TRAUMA_RESILIENCE_SURGERY)
else
C.cure_trauma_type(resilience = TRAUMA_RESILIENCE_BASIC)
@@ -30,7 +30,6 @@ Bonus
symptom_delay_max = 120
var/scramble_language = FALSE
var/datum/language/current_language
var/datum/language_holder/original_language
threshold_desc = list(
"Transmission 14" = "The host's language center of the brain is damaged, leading to complete inability to speak or understand any language.",
"Stage Speed 7" = "Changes voice more often.",
@@ -48,9 +47,6 @@ Bonus
symptom_delay_max = 85
if(A.properties["transmittable"] >= 14) //random language
scramble_language = TRUE
var/mob/living/M = A.affected_mob
var/datum/language_holder/mob_language = M.get_language_holder()
original_language = mob_language.copy()
/datum/symptom/voice_change/Activate(datum/disease/advance/A)
if(!..())
@@ -64,12 +60,10 @@ Bonus
if(ishuman(M))
var/mob/living/carbon/human/H = M
H.SetSpecialVoice(H.dna.species.random_name(H.gender))
if(scramble_language)
H.remove_language(current_language)
if(scramble_language && !current_language) // Last part prevents rerolling language with small amounts of cure.
current_language = pick(subtypesof(/datum/language) - /datum/language/common)
H.grant_language(current_language)
var/datum/language_holder/mob_language = H.get_language_holder()
mob_language.only_speaks_language = current_language
H.add_blocked_language(subtypesof(/datum/language) - current_language, LANGUAGE_VOICECHANGE)
H.grant_language(current_language, TRUE, TRUE, LANGUAGE_VOICECHANGE)
/datum/symptom/voice_change/End(datum/disease/advance/A)
..()
@@ -77,7 +71,5 @@ Bonus
var/mob/living/carbon/human/H = A.affected_mob
H.UnsetSpecialVoice()
if(scramble_language)
var/mob/living/M = A.affected_mob
M.copy_known_languages_from(original_language, TRUE)
current_language = null
QDEL_NULL(original_language)
A.affected_mob.remove_blocked_language(subtypesof(/datum/language), LANGUAGE_VOICECHANGE)
A.affected_mob.remove_all_languages(LANGUAGE_VOICECHANGE) // In case someone managed to get more than one anyway.
+5
View File
@@ -355,6 +355,11 @@
var/datum/species/old_species = dna.species
dna.species = new_race
dna.species.on_species_gain(src, old_species, pref_load)
if(ishuman(src))
qdel(language_holder)
var/species_holder = initial(mrace.species_language_holder)
language_holder = new species_holder(src)
update_atom_languages()
/mob/living/carbon/human/set_species(datum/species/mrace, icon_update = TRUE, pref_load = FALSE)
..()
+3 -1
View File
@@ -76,7 +76,9 @@ GLOBAL_LIST_EMPTY(explosions)
//I would make this not ex_act the thing that triggered the explosion,
//but everything that explodes gives us their loc or a get_turf()
//and somethings expect us to ex_act them so they can qdel()
stoplag() //tldr, let the calling proc call qdel(src) before we explode
//stoplag() //tldr, let the calling proc call qdel(src) before we explode
// no - use sleep. stoplag() results in quirky things like explosions taking too long to process and hanging mid-air for no reason.
sleep(0)
EX_PREPROCESS_EXIT_CHECK
+4 -6
View File
@@ -67,7 +67,7 @@
var/datum/skill_holder/skill_holder
/datum/mind/New(var/key)
skill_holder = new
skill_holder = new(src)
src.key = key
soulOwner = src
martial_art = default_martial_art
@@ -80,12 +80,12 @@
if(antag_datum.delete_on_mind_deletion)
qdel(i)
antag_datums = null
QDEL_NULL(skill_holder)
return ..()
/datum/mind/proc/get_language_holder()
if(!language_holder)
var/datum/language_holder/L = current.get_language_holder(shadow=FALSE)
language_holder = L.copy(src)
language_holder = new (src)
return language_holder
@@ -100,9 +100,6 @@
if(iscarbon(current))
var/mob/living/carbon/C = current
C.disable_intentional_combat_mode(TRUE)
if(!language_holder)
var/datum/language_holder/mob_holder = new_character.get_language_holder(shadow = FALSE)
language_holder = mob_holder.copy(src)
if(key)
if(new_character.key != key) //if we're transferring into a body with a key associated which is not ours
@@ -130,6 +127,7 @@
transfer_martial_arts(new_character)
if(active || force_key_move)
new_character.key = key //now transfer the key to link the client to our new body
current.update_atom_languages()
//CIT CHANGE - makes arousal update when transfering bodies
if(isliving(new_character)) //New humans and such are by default enabled arousal. Let's always use the new mind's prefs.
+4 -4
View File
@@ -277,10 +277,10 @@
/datum/mutation/human/stoner/on_acquiring(mob/living/carbon/human/owner)
..()
owner.grant_language(/datum/language/beachbum)
owner.remove_language(/datum/language/common)
owner.grant_language(/datum/language/beachbum, TRUE, TRUE, LANGUAGE_STONER)
owner.add_blocked_language(subtypesof(/datum/language) - /datum/language/beachbum, LANGUAGE_STONER)
/datum/mutation/human/stoner/on_losing(mob/living/carbon/human/owner)
..()
owner.grant_language(/datum/language/common)
owner.remove_language(/datum/language/beachbum)
owner.remove_language(/datum/language/beachbum, TRUE, TRUE, LANGUAGE_STONER)
owner.remove_blocked_language(subtypesof(/datum/language) - /datum/language/beachbum, LANGUAGE_STONER)
+122 -9
View File
@@ -1,4 +1,4 @@
GLOBAL_LIST_INIT(skill_datums, init_skill_datums())
GLOBAL_LIST_INIT_TYPED(skill_datums, /datum/skill, init_skill_datums())
/proc/init_skill_datums()
. = list()
@@ -9,16 +9,13 @@ GLOBAL_LIST_INIT(skill_datums, init_skill_datums())
S = new path
.[S.type] = S
/proc/get_skill_datum(path)
return GLOB.skill_datums[path]
/proc/sanitize_skill_value(path, value)
var/datum/skill/S = get_skill_datum(path)
var/datum/skill/S = GLOB.skill_datums[path]
// don't check, if we runtime let it happen.
return S.sanitize_value(value)
/proc/is_skill_value_greater(path, existing, new_value)
var/datum/skill/S = get_skill_datum(path)
var/datum/skill/S = GLOB.skill_datums[path]
// don't check, if we runtime let it happen.
return S.is_value_greater(existing, new_value)
@@ -30,10 +27,18 @@ GLOBAL_LIST_INIT(skill_datums, init_skill_datums())
var/name
/// Our description
var/desc
/// Color of the name as shown in the html readout
var/name_color = "#F0F0F0" // White on dark surface.
/// Our progression type
var/progression_type
/// Abstract type
var/abstract_type = /datum/skill
/// skill threshold used in generic skill modifiers calculations.
var/list/competency_thresholds = list(0, 0, 0)
/// Multiplier of the difference of the holder skill value and the selected threshold.
var/list/competency_mults = list(0, 0, 0)
/// In which way this skil can affect or be affected through actions.
var/skill_flags = SKILL_USE_MOOD|SKILL_TRAIN_MOOD
/**
* Ensures what someone's setting as a value for this skill is valid.
@@ -41,6 +46,13 @@ GLOBAL_LIST_INIT(skill_datums, init_skill_datums())
/datum/skill/proc/sanitize_value(new_value)
return new_value
/**
* Sets the new value of this skill in the holder skills list.
* As well as possible feedback messages or secondary effects on value change, that's on you.
*/
/datum/skill/proc/set_skill_value(datum/skill_holder/H, value, mob/owner)
H.skills[type] = value
/**
* Checks if a value is greater
*/
@@ -52,7 +64,7 @@ GLOBAL_LIST_INIT(skill_datums, init_skill_datums())
/**
* Standard value "render"
*/
/datum/skill/proc/standard_render_value(value)
/datum/skill/proc/standard_render_value(value, level)
return value
// Just saying, the choice to use different sub-parent-types is to force coders to resolve issues as I won't be implementing custom procs to grab skill levels in a certain context.
@@ -61,11 +73,13 @@ GLOBAL_LIST_INIT(skill_datums, init_skill_datums())
/datum/skill/binary
abstract_type = /datum/skill/binary
progression_type = SKILL_PROGRESSION_BINARY
competency_thresholds = list(FALSE, TRUE, TRUE)
competency_mults = list(0.5, 0.5, 0.5)
/datum/skill/binary/sanitize_value(new_value)
return new_value? TRUE : FALSE
/datum/skill/binary/standard_render_value(value)
/datum/skill/binary/standard_render_value(value, level)
return value? "Yes" : "No"
/datum/skill/numerical
@@ -81,7 +95,7 @@ GLOBAL_LIST_INIT(skill_datums, init_skill_datums())
/datum/skill/numerical/sanitize_value(new_value)
return clamp(new_value, min_value, max_value)
/datum/skill/numerical/standard_render_value(value)
/datum/skill/numerical/standard_render_value(value, level)
return display_as_percent? "[round(value/max_value/100, 0.01)]%" : "[value] / [max_value]"
/datum/skill/enum
@@ -93,3 +107,102 @@ GLOBAL_LIST_INIT(skill_datums, init_skill_datums())
/datum/skill/enum/sanitize_value(new_value)
if(new_value in valid_values)
return new_value
/**
* Classing r p g styled skills, tiered by lvl, and current/nextlvl experience.
*/
/datum/skill/level
abstract_type = /datum/skill/level
progression_type = SKILL_PROGRESSION_LEVEL
var/standard_xp_lvl_up = STD_XP_LVL_UP //the standard required to level up. def: 100
var/xp_lvl_multiplier = STD_XP_LVL_MULTI //standard required level up exp multiplier. def: 2 (100, 200, 400, 800 etc.)
var/max_levels = STD_MAX_LVL
var/level_up_method = STANDARD_LEVEL_UP //how levels are calculated.
var/list/levels = list() //level thresholds, if associative, these will be preceded by tiers such as "novice" or "trained"
var/associative = FALSE //See above.
var/unskilled_tier = "Unskilled" //Only relevant for associative experience levels
//Builds the levels list.
/datum/skill/level/New()
. = ..()
var/max_assoc = ""
var/max_assoc_start = 1
for(var/lvl in 1 to max_levels)
var/value
switch(level_up_method)
if(STANDARD_LEVEL_UP)
value = XP_LEVEL(standard_xp_lvl_up, xp_lvl_multiplier, lvl)
if(DWARFY_LEVEL_UP)
value = DORF_XP_LEVEL(standard_xp_lvl_up, xp_lvl_multiplier, lvl)
value = round(value, 1)
if(!associative)
levels += value
continue
if(max_assoc)
levels["[max_assoc] +[max_assoc_start++]"] = value
continue
var/key = LAZYACCESS(levels, lvl)
if(!key)
if(lvl == 1) //You dun goof it.
stack_trace("Skill datum [src] was set to have an associative levels list despite the latter having no key value.")
associative = FALSE
levels += value
continue
max_assoc = levels[lvl-1]
levels["[max_assoc] +[max_assoc_start++]"] = value
levels[key] = value
/datum/skill/level/sanitize_value(new_value)
return max(new_value, 0)
/datum/skill/level/set_skill_value(datum/skill_holder/H, value, datum/mind/M, silent = FALSE)
H.skills[type] = value
var/new_level
for(var/k in levels)
if(value < (associative ? levels[k] : k))
break
new_level++
var/old_level = LAZYACCESS(H.skill_levels, type)
LAZYSET(H.skill_levels, type, new_level)
. = new_level - old_level
if(silent || !(M?.current))
return
if(. > 0)
to_chat(M.current, "<span class='nicegreen'>I feel like I've become more proficient at [name]!</span>")
else if(. < 0)
to_chat(M.current, "<span class='warning'>I feel like I've become worse at [name]!</span>")
/datum/skill/level/standard_render_value(value, level)
var/current_lvl = associative ? (!level ? unskilled_tier : levels[level]) : level
var/current_lvl_xp_sum = 0
if(level)
current_lvl_xp_sum = associative ? levels[levels[level]] : levels[level]
var/next_index = max(max_levels, level+1)
var/next_lvl_xp = associative ? levels[levels[next_index]] : levels[next_index]
if(next_lvl_xp > current_lvl_xp_sum)
next_lvl_xp -= current_lvl_xp_sum
return "[associative ? current_lvl : "Lvl. [current_lvl]"] ([value - current_lvl_xp_sum]/[next_lvl_xp])[level == max_levels ? " \[MAX!\]" : ""]"
/datum/skill/level/job
levels = list("Basic", "Trained", "Experienced", "Master")
competency_thresholds = list(JOB_SKILL_TRAINED, JOB_SKILL_EXPERT, JOB_SKILL_MASTER)
competency_mults = list(0.15, 0.1, 0.1)
associative = TRUE
//quite the reference, no?
/datum/skill/level/dwarfy
abstract_type = /datum/skill/level/dwarfy
standard_xp_lvl_up = DORF_XP_LVL_UP
xp_lvl_multiplier = DORF_XP_LVL_MULTI
max_levels = DORF_MAX_LVL
level_up_method = DWARFY_LEVEL_UP
levels = list("Novice", "Adequate", "Competent", "Skilled",
"Proficient", "Talented", "Adept", "Expert",
"Professional", "Accomplished", "Great", "Master",
"High Master", "Grand Master", "Legendary")
competency_thresholds = list(DORF_SKILL_COMPETENT, DORF_SKILL_EXPERT, DORF_SKILL_MASTER)
competency_mults = list(0.15, 0.1, 0.08)
associative = TRUE
unskilled_tier = "Dabbling"
+114 -21
View File
@@ -2,10 +2,18 @@
* Skill holder datums
*/
/datum/skill_holder
var/datum/mind/owner
/// Our list of skills and values. Lazylist. Associative. Keys are datum typepaths to the skill.
var/list/skills
/// Same as [skills] but affinities, which are multiplied to increase amount when gaining skills.
var/list/skill_affinities
/// Let's say we want to get a specific skill "level" without looping through a proc everytime.
/// Only supported by skills with tiers or levels.
var/list/skill_levels
/datum/skill_holder/New(datum/mind/M)
. = ..()
owner = M
/**
* Grabs the value of a skill.
@@ -15,54 +23,139 @@
CRASH("Invalid get_skill_value call. Use typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
if(!skills)
return null
return LAZYACCESS(skills, skill)
return skills[skill]
/**
* Grabs the level of a skill. Only supported by skills with tiers or levels.
*/
/datum/skill_holder/proc/get_skill_level(skill)
if(!ispath(skill, /datum/skill))
CRASH("Invalid get_skill_value call. Use skill typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
if(!skill_levels)
return 0
return skill_levels[skill]
/**
* Grabs our affinity for a skill. !!This is a multiplier!!
*/
/datum/skill_holder/proc/get_skill_affinity(skill)
if(!ispath(skill))
CRASH("Invalid get_skill_affinity call. Use typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
if(!skills)
return 1
. = 1
if(!ispath(skill, /datum/skill))
CRASH("Invalid get_skill_affinity call. Use skill typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
var/affinity = LAZYACCESS(skill_affinities, skill)
if(isnull(affinity))
return 1
return affinity
if(!isnull(affinity))
. = affinity
var/list/wrapped = list(.)
SEND_SIGNAL(owner.current, COMSIG_MOB_SKILL_GET_AFFINITY, skill, wrapped)
. = wrapped[1]
/**
* Sets the value of a skill.
*/
/datum/skill_holder/proc/set_skill_value(skill, value)
if(!ispath(skill))
CRASH("Invalid set_skill_value call. Use typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
LAZYINITLIST(skills)
value = sanitize_skill_value(skill, value)
/datum/skill_holder/proc/set_skill_value(skill, value, silent = FALSE)
if(!ispath(skill, /datum/skill))
CRASH("Invalid set_skill_value call. Use skill typepaths.") //until a time when we somehow need text ids for dynamic skills, I'm enforcing this.
var/datum/skill/S = GLOB.skill_datums[skill]
value = S.sanitize_value(value)
if(!isnull(value))
LAZYSET(skills, skill, value)
LAZYINITLIST(skills)
S.set_skill_value(src, value, owner, silent)
return TRUE
return FALSE
/**
* Boosts a skill to a value if not aobve
*/
/datum/skill_holder/proc/boost_skill_value_to(skill, value)
/datum/skill_holder/proc/boost_skill_value_to(skill, value, silent = FALSE)
var/current = get_skill_value(skill)
if(!is_skill_value_greater(skill, current, value))
return FALSE
set_skill_value(skill, value)
set_skill_value(skill, value, silent)
return TRUE
/**
* Automatic skill increase, multiplied by skill affinity if existing.
* Only works if skill is numerical.
*/
/datum/skill_holder/proc/auto_gain_experience(skill, value)
if(!ispath(skill, /datum/skill/numerical))
CRASH("You cannot auto increment a non numerical skill!")
/datum/skill_holder/proc/auto_gain_experience(skill, value, maximum, silent = FALSE)
if(!ispath(skill, /datum/skill))
CRASH("Invalid set_skill_value call. Use skill typepaths.")
var/datum/skill/S = GLOB.skill_datums[skill]
if(S.progression_type != SKILL_PROGRESSION_NUMERICAL && S.progression_type != SKILL_PROGRESSION_LEVEL)
CRASH("You cannot auto increment a non numerical(experience skill!")
var/current = get_skill_value(skill)
var/affinity = get_skill_affinity(skill)
boost_skill_value_to(skill, current + (value * affinity))
var/target_value = current + (value * affinity)
if(maximum)
target_value = max(target_value, maximum)
if(target_value == maximum) //no more experience to gain, early return.
return
boost_skill_value_to(skill, target_value, silent)
/**
* Generic value modifier proc that uses one skill.
* Args:
* * value : the value to modify, may be a delay, damage, probability.
* * threshold : The difficulty of the action, in short. Refer to __DEFINES/skills/defines.dm for the defines.
* * modifier_is_multiplier : wheter the modifier is a multiplier or a divisor.
*/
/datum/skill_holder/proc/action_skills_mod(skill, value, threshold, modifier_is_multiplier = TRUE)
var/mod
var/datum/skill/S = GLOB.skill_datums[skill]
if(!S)
return
switch(S.progression_type)
if(SKILL_PROGRESSION_LEVEL)
mod = LAZYACCESS(skill_levels, S.type)
else
mod = LAZYACCESS(skills, S.type)
mod = (1+(mod-S.competency_thresholds[threshold])*S.competency_mults[threshold])
var/list/comsig_values = list(mod)
SEND_SIGNAL(owner.current, COMSIG_MOB_ACTION_SKILL_MOD, args, comsig_values)
mod = comsig_values[MOD_VALUES_SKILL_MOD]
. = modifier_is_multiplier ? value*mod : value/mod
/**
* Generic value modifier proc that uses several skills, intended for items.
* Args:
* * item/I : the item used in this action. its used_skills list variable contains the skills exercised with it.
* * value : the value to modify, may be a delay, damage, probability.
* * flags : the required flags that each skill (either in I.used_skills or the skill datum skill_flags) must have to influence
* * the value.
* * bad_flags : the opposite of the above, skills that must not be present to impact the value.
* * modifier_is_multiplier : wheter the modifier is a multiplier or a divisor.
*/
/datum/skill_holder/proc/item_action_skills_mod(obj/item/I, value, flags = NONE, bad_flags = NONE, modifier_is_multiplier = TRUE)
. = value
var/sum = 0
var/divisor = 0
var/list/checked_skills
for(var/k in I.used_skills)
var/datum/skill/S = GLOB.skill_datums[k]
if(!S)
continue
var/our_flags = (I.used_skills[k]|S.skill_flags)
if((flags && !(our_flags & flags)) || (bad_flags && our_flags & bad_flags))
continue
var/mod
switch(S.progression_type)
if(SKILL_PROGRESSION_LEVEL)
mod = LAZYACCESS(skill_levels, S.type)
else
mod = LAZYACCESS(skills, S.type)
sum += 1+(mod - S.competency_thresholds[I.skill_difficulty])*S.competency_mults[I.skill_difficulty]
LAZYADD(checked_skills, S)
var/list/comsig_values = list(sum, divisor, checked_skills)
SEND_SIGNAL(owner.current, COMSIG_MOB_ITEM_ACTION_SKILLS_MOD, args, comsig_values)
sum = comsig_values[MOD_VALUES_ITEM_SKILLS_SUM]
divisor = comsig_values[MOD_VALUES_ITEM_SKILLS_DIV]
if(divisor)
. = modifier_is_multiplier ? value*(sum/divisor) : value/(sum/divisor)
/**
* Generates a HTML readout of our skills.
@@ -73,6 +166,6 @@
out += "<table style=\"width:100%\"><tr><th><b>Skill</b><th><b>Value</b></tr>"
for(var/path in skills)
var/datum/skill/S = GLOB.skill_datums[path]
out += "<tr><td>[S.name]</td><td>[S.standard_render_value(skills[path])]</td></tr>"
out += "<tr><td><font color='[S.name_color]'>[S.name]</font></td><td>[S.standard_render_value(skills[path], LAZYACCESS(skill_levels, path) || 0)]</td></tr>"
out += "</table>"
return out.Join("")
+6
View File
@@ -0,0 +1,6 @@
/datum/skill/level/job/wiring
name = "Wiring"
desc = "How proficient and knowledged you are at wiring beyond laying cables on the floor."
name_color = COLOR_PALE_ORANGE
competency_thresholds = list(JOB_SKILL_BASIC, JOB_SKILL_EXPERT, JOB_SKILL_MASTER)
skill_flags = SKILL_USE_MOOD|SKILL_TRAIN_MOOD|SKILL_USE_TOOL|SKILL_TRAINING_TOOL
+2
View File
@@ -1,3 +1,5 @@
/datum/skill/numerical/surgery
name = "Surgery"
desc = "How proficient you are at doing surgery."
name_color = COLOR_PALE_BLUE_GRAY
competency_mults = list(0.025, 0.025, 0.025) // 60% surgery speed up at max value of 100.
+35 -6
View File
@@ -33,6 +33,9 @@
var/list/assemblies = list() // List of attached assemblies.
var/randomize = 0 // If every instance of these wires should be random.
// Prevents wires from showing up in station blueprints
var/req_knowledge = INFINITY //wiring skill level on which the functions are revealed.
var/req_skill = JOB_SKILL_BASIC //used in user's cutting/pulsing/mending speed calculations.
var/list/current_users //list of untrained people currently interacting with this set of wires.
/datum/wires/New(atom/holder)
..()
@@ -130,8 +133,22 @@
cut_wires += wire
on_cut(wire, mend = FALSE)
/datum/wires/proc/cut_color(color)
/datum/wires/proc/cut_color(color, mob/living/user)
LAZYINITLIST(current_users)
if(current_users[user])
return FALSE
if(req_skill && user?.mind)
var/level_diff = req_skill - user.mind.skill_holder.get_skill_level(/datum/skill/level/job/wiring)
if(level_diff > 0)
LAZYSET(current_users, user, TRUE)
to_chat(user, "<span class='notice'>You begin cutting [holder]'s [color] wire...</span>")
if(!do_after(user, 1.5 SECONDS * level_diff, target = holder) || !interactable(user))
LAZYREMOVE(current_users, user)
return FALSE
LAZYREMOVE(current_users, user)
to_chat(user, "<span class='notice'>You cut [holder]'s [color] wire.</span>")
cut(get_wire(color))
return TRUE
/datum/wires/proc/cut_random()
cut(wires[rand(1, wires.len)])
@@ -146,7 +163,21 @@
on_pulse(wire, user)
/datum/wires/proc/pulse_color(color, mob/living/user)
LAZYINITLIST(current_users)
if(current_users[user])
return FALSE
if(req_skill && user?.mind)
var/level_diff = req_skill - user.mind.skill_holder.get_skill_level(/datum/skill/level/job/wiring)
if(level_diff > 0)
LAZYSET(current_users, user, TRUE)
to_chat(user, "<span class='notice'>You begin pulsing [holder]'s [color] wire...</span>")
if(!do_after(user, 1.5 SECONDS * level_diff, target = holder) || !interactable(user))
LAZYREMOVE(current_users, user)
return FALSE
LAZYREMOVE(current_users, user)
to_chat(user, "<span class='notice'>You pulse [holder]'s [color] wire.</span>")
pulse(get_wire(color), user)
return TRUE
/datum/wires/proc/pulse_assembly(obj/item/assembly/S)
for(var/color in assemblies)
@@ -224,7 +255,7 @@
var/reveal_wires = FALSE
// Admin ghost can see a purpose of each wire.
if(IsAdminGhost(user))
if(IsAdminGhost(user) || user.mind.skill_holder.get_skill_level(/datum/skill/level/job/wiring) >= req_knowledge)
reveal_wires = TRUE
// Same for anyone with an abductor multitool.
@@ -259,18 +290,16 @@
if("cut")
I = L.is_holding_tool_quality(TOOL_WIRECUTTER)
if(I || IsAdminGhost(usr))
if(I && holder)
if(cut_color(target_wire, L) && I && holder)
I.play_tool_sound(holder, 20)
cut_color(target_wire)
. = TRUE
else
to_chat(L, "<span class='warning'>You need wirecutters!</span>")
if("pulse")
I = L.is_holding_tool_quality(TOOL_MULTITOOL)
if(I || IsAdminGhost(usr))
if(I && holder)
if(pulse_color(target_wire, L) && I && holder)
I.play_tool_sound(holder, 20)
pulse_color(target_wire, L)
. = TRUE
else
to_chat(L, "<span class='warning'>You need a multitool!</span>")
+1
View File
@@ -1,6 +1,7 @@
/datum/wires/airalarm
holder_type = /obj/machinery/airalarm
proper_name = "Air Alarm"
req_knowledge = JOB_SKILL_MASTER
/datum/wires/airalarm/New(atom/holder)
wires = list(
+4 -2
View File
@@ -1,6 +1,7 @@
/datum/wires/airlock
holder_type = /obj/machinery/door/airlock
proper_name = "Generic Airlock"
req_skill = JOB_SKILL_UNTRAINED //Training wheel, as per request.
var/wiretype
/datum/wires/airlock/secure
@@ -52,10 +53,11 @@
/datum/wires/airlock/interactable(mob/user)
var/obj/machinery/door/airlock/A = holder
if(!A.panel_open)
return FALSE
if(!A.hasSiliconAccessInArea(user) && A.isElectrified() && A.shock(user, 100))
return FALSE
if(A.panel_open)
return TRUE
return TRUE
/datum/wires/airlock/get_status()
var/obj/machinery/door/airlock/A = holder
+1
View File
@@ -1,6 +1,7 @@
/datum/wires/apc
holder_type = /obj/machinery/power/apc
proper_name = "APC"
req_knowledge = JOB_SKILL_MASTER
/datum/wires/apc/New(atom/holder)
wires = list(
+1
View File
@@ -1,6 +1,7 @@
/datum/wires/autolathe
holder_type = /obj/machinery/autolathe
proper_name = "Autolathe"
req_knowledge = JOB_SKILL_EXPERT
/datum/wires/autolathe/New(atom/holder)
wires = list(
+2
View File
@@ -1,6 +1,8 @@
/datum/wires/emitter
holder_type = /obj/machinery/power/emitter
req_knowledge = JOB_SKILL_TRAINED
req_skill = JOB_SKILL_UNTRAINED
/datum/wires/emitter/New(atom/holder)
wires = list(WIRE_ZAP,WIRE_HACK)
+2
View File
@@ -15,6 +15,7 @@
/datum/wires/explosive/c4
holder_type = /obj/item/grenade/plastic/c4
randomize = TRUE //Same behaviour since no wire actually disarms it
req_skill = JOB_SKILL_UNTRAINED
/datum/wires/explosive/c4/interactable(mob/user)
var/obj/item/grenade/plastic/c4/P = holder
@@ -29,6 +30,7 @@
/datum/wires/explosive/pizza
holder_type = /obj/item/pizzabox
randomize = TRUE
req_skill = JOB_SKILL_MASTER
/datum/wires/explosive/pizza/New(atom/holder)
wires = list(
+2
View File
@@ -1,6 +1,8 @@
/datum/wires/microwave
holder_type = /obj/machinery/microwave
proper_name = "Microwave"
req_knowledge = JOB_SKILL_TRAINED
req_skill = JOB_SKILL_UNTRAINED
/datum/wires/microwave/New(atom/holder)
wires = list(
+1
View File
@@ -1,6 +1,7 @@
/datum/wires/mulebot
holder_type = /mob/living/simple_animal/bot/mulebot
randomize = TRUE
req_knowledge = JOB_SKILL_MASTER
/datum/wires/mulebot/New(atom/holder)
wires = list(
@@ -1,6 +1,8 @@
/datum/wires/particle_accelerator/control_box
holder_type = /obj/machinery/particle_accelerator/control_box
proper_name = "Particle Accelerator"
req_knowledge = JOB_SKILL_EXPERT
req_skill = JOB_SKILL_TRAINED
/datum/wires/particle_accelerator/control_box/New(atom/holder)
wires = list(
+1
View File
@@ -1,6 +1,7 @@
/datum/wires/rnd
holder_type = /obj/machinery/rnd
randomize = TRUE
req_knowledge = JOB_SKILL_EXPERT
/datum/wires/rnd/New(atom/holder)
wires = list(
+2
View File
@@ -1,6 +1,8 @@
/datum/wires/radio
holder_type = /obj/item/radio
proper_name = "Radio"
req_knowledge = JOB_SKILL_TRAINED
req_skill = JOB_SKILL_UNTRAINED
/datum/wires/radio/New(atom/holder)
wires = list(
+2
View File
@@ -1,6 +1,8 @@
/datum/wires/robot
holder_type = /mob/living/silicon/robot
randomize = TRUE
req_knowledge = JOB_SKILL_MASTER
req_skill = JOB_SKILL_TRAINED
/datum/wires/robot/New(atom/holder)
wires = list(
+2
View File
@@ -1,6 +1,8 @@
/datum/wires/suit_storage_unit
holder_type = /obj/machinery/suit_storage_unit
proper_name = "Suit Storage Unit"
req_knowledge = JOB_SKILL_TRAINED
req_skill = JOB_SKILL_UNTRAINED
/datum/wires/suit_storage_unit/New(atom/holder)
wires = list(
+2
View File
@@ -1,6 +1,8 @@
/datum/wires/syndicatebomb
holder_type = /obj/machinery/syndicatebomb
randomize = TRUE
req_skill = JOB_SKILL_EXPERT //good luck, wannabe hero.
/datum/wires/syndicatebomb/New(atom/holder)
wires = list(
+1
View File
@@ -2,6 +2,7 @@
/datum/wires/tesla_coil
randomize = 1 //Only one wire don't need blueprints
holder_type = /obj/machinery/power/tesla_coil
req_knowledge = JOB_SKILL_TRAINED
/datum/wires/tesla_coil/New(atom/holder)
wires = list(WIRE_ZAP)
+1
View File
@@ -1,6 +1,7 @@
/datum/wires/vending
holder_type = /obj/machinery/vending
proper_name = "Vending Unit"
req_knowledge = JOB_SKILL_EXPERT
/datum/wires/vending/New(atom/holder)
wires = list(
+79 -73
View File
@@ -11,7 +11,7 @@
var/throw_range = 7
var/mob/pulledby = null
var/initial_language_holder = /datum/language_holder
var/datum/language_holder/language_holder
var/datum/language_holder/language_holder // Mindless mobs and objects need language too, some times. Mind holder takes prescedence.
var/verb_say = "says"
var/verb_ask = "asks"
var/verb_exclaim = "exclaims"
@@ -502,88 +502,94 @@
animate(src, pixel_y = initial(pixel_y), time = 10)
setMovetype(movement_type & ~FLOATING)
/* Language procs */
/atom/movable/proc/get_language_holder(shadow=TRUE)
if(language_holder)
return language_holder
else
/* Language procs
* Unless you are doing something very specific, these are the ones you want to use.
*/
/// Gets or creates the relevant language holder. For mindless atoms, gets the local one. For atom with mind, gets the mind one.
/atom/movable/proc/get_language_holder(get_minds = TRUE)
if(!language_holder)
language_holder = new initial_language_holder(src)
return language_holder
return language_holder
/atom/movable/proc/grant_language(datum/language/dt, body = FALSE)
var/datum/language_holder/H = get_language_holder(!body)
H.grant_language(dt, body)
/// Grants the supplied language and sets omnitongue true.
/atom/movable/proc/grant_language(language, understood = TRUE, spoken = TRUE, source = LANGUAGE_ATOM)
var/datum/language_holder/LH = get_language_holder()
return LH.grant_language(language, understood, spoken, source)
/atom/movable/proc/grant_all_languages(omnitongue=FALSE)
var/datum/language_holder/H = get_language_holder()
H.grant_all_languages(omnitongue)
/// Grants every language.
/atom/movable/proc/grant_all_languages(understood = TRUE, spoken = TRUE, grant_omnitongue = TRUE, source = LANGUAGE_MIND)
var/datum/language_holder/LH = get_language_holder()
return LH.grant_all_languages(understood, spoken, grant_omnitongue, source)
/// Removes a single language.
/atom/movable/proc/remove_language(language, understood = TRUE, spoken = TRUE, source = LANGUAGE_ALL)
var/datum/language_holder/LH = get_language_holder()
return LH.remove_language(language, understood, spoken, source)
/// Removes every language and sets omnitongue false.
/atom/movable/proc/remove_all_languages(source = LANGUAGE_ALL, remove_omnitongue = FALSE)
var/datum/language_holder/LH = get_language_holder()
return LH.remove_all_languages(source, remove_omnitongue)
/// Adds a language to the blocked language list. Use this over remove_language in cases where you will give languages back later.
/atom/movable/proc/add_blocked_language(language, source = LANGUAGE_ATOM)
var/datum/language_holder/LH = get_language_holder()
return LH.add_blocked_language(language, source)
/// Removes a language from the blocked language list.
/atom/movable/proc/remove_blocked_language(language, source = LANGUAGE_ATOM)
var/datum/language_holder/LH = get_language_holder()
return LH.remove_blocked_language(language, source)
/// Checks if atom has the language. If spoken is true, only checks if atom can speak the language.
/atom/movable/proc/has_language(language, spoken = FALSE)
var/datum/language_holder/LH = get_language_holder()
return LH.has_language(language, spoken)
/// Checks if atom can speak the language.
/atom/movable/proc/can_speak_language(language)
var/datum/language_holder/LH = get_language_holder()
return LH.can_speak_language(language)
/// Returns the result of tongue specific limitations on spoken languages.
/atom/movable/proc/could_speak_language(language)
return TRUE
/// Returns selected language, if it can be spoken, or finds, sets and returns a new selected language if possible.
/atom/movable/proc/get_selected_language()
var/datum/language_holder/LH = get_language_holder()
return LH.get_selected_language()
/// Gets a random understood language, useful for hallucinations and such.
/atom/movable/proc/get_random_understood_language()
var/datum/language_holder/H = get_language_holder()
. = H.get_random_understood_language()
var/datum/language_holder/LH = get_language_holder()
return LH.get_random_understood_language()
/atom/movable/proc/remove_language(datum/language/dt, body = FALSE)
var/datum/language_holder/H = get_language_holder(!body)
H.remove_language(dt, body)
/// Gets a random spoken language, useful for forced speech and such.
/atom/movable/proc/get_random_spoken_language()
var/datum/language_holder/LH = get_language_holder()
return LH.get_random_spoken_language()
/atom/movable/proc/remove_all_languages()
var/datum/language_holder/H = get_language_holder()
H.remove_all_languages()
/// Copies all languages into the supplied atom/language holder. Source should be overridden when you
/// do not want the language overwritten by later atom updates or want to avoid blocked languages.
/atom/movable/proc/copy_languages(from_holder, source_override)
if(isatom(from_holder))
var/atom/movable/thing = from_holder
from_holder = thing.get_language_holder()
var/datum/language_holder/LH = get_language_holder()
return LH.copy_languages(from_holder, source_override)
/atom/movable/proc/has_language(datum/language/dt)
var/datum/language_holder/H = get_language_holder()
. = H.has_language(dt)
/atom/movable/proc/copy_known_languages_from(thing, replace=FALSE)
var/datum/language_holder/H = get_language_holder()
. = H.copy_known_languages_from(thing, replace)
// Whether an AM can speak in a language or not, independent of whether
// it KNOWS the language
/atom/movable/proc/could_speak_in_language(datum/language/dt)
. = TRUE
/atom/movable/proc/can_speak_in_language(datum/language/dt)
var/datum/language_holder/H = get_language_holder()
if(!H.has_language(dt))
return FALSE
else if(H.omnitongue)
return TRUE
else if(could_speak_in_language(dt) && (!H.only_speaks_language || H.only_speaks_language == dt))
return TRUE
else
return FALSE
/atom/movable/proc/get_default_language()
// if no language is specified, and we want to say() something, which
// language do we use?
var/datum/language_holder/H = get_language_holder()
if(H.selected_default_language)
if(can_speak_in_language(H.selected_default_language))
return H.selected_default_language
else
H.selected_default_language = null
var/datum/language/chosen_langtype
var/highest_priority
for(var/lt in H.languages)
var/datum/language/langtype = lt
if(!can_speak_in_language(langtype))
continue
var/pri = initial(langtype.default_priority)
if(!highest_priority || (pri > highest_priority))
chosen_langtype = langtype
highest_priority = pri
H.selected_default_language = .
. = chosen_langtype
/// Empties out the atom specific languages and updates them according to the current atoms language holder.
/// As a side effect, it also creates missing language holders in the process.
/atom/movable/proc/update_atom_languages()
var/datum/language_holder/LH = get_language_holder()
return LH.update_atom_languages(src)
/* End language procs */
/atom/movable/proc/ConveyorMove(movedir)
set waitfor = FALSE
if(!anchored && has_gravity())
+1 -1
View File
@@ -417,7 +417,7 @@
if(player.assigned_role == job)
candidates -= player
if(candidates.len < recommended_enemies)
if(candidates.len < recommended_enemies && CONFIG_GET(keyed_list/force_antag_count)[config_tag])
for(var/mob/dead/new_player/player in players)
if(player.client && player.ready == PLAYER_READY_TO_PLAY)
if(!(role in player.client.prefs.be_special)) // We don't have enough people who want to be antagonist, make a separate list of people who don't want to be one
+4 -1
View File
@@ -90,7 +90,10 @@
sabotage_type = "cloner"
/datum/sabotage_objective/cloner/check_conditions()
return !(locate(/obj/machinery/clonepod) in GLOB.machines)
for(var/obj/machinery/clonepod/cloner in GLOB.machines)
if(is_station_level(cloner.z))
return FALSE
return TRUE
/datum/sabotage_objective/ai_law
name = "Upload a hacked law to the AI."
+5 -7
View File
@@ -17,7 +17,6 @@
var/list/L = list()
var/list/LL = list()
var/hacked = FALSE
var/hackable = TRUE
var/disabled = 0
var/shocked = FALSE
var/hack_wire
@@ -32,7 +31,7 @@
var/selected_category
var/screen = 1
var/datum/techweb/stored_research = /datum/techweb/specialized/autounlocking/autolathe
var/datum/techweb/specialized/autounlocking/stored_research = /datum/techweb/specialized/autounlocking/autolathe
var/list/categories = list(
"Tools",
"Electronics",
@@ -425,11 +424,11 @@
/obj/machinery/autolathe/proc/adjust_hacked(state)
hacked = state
if(!hackable && hacked)
return
for(var/id in SSresearch.techweb_designs)
var/datum/design/D = SSresearch.techweb_design_by_id(id)
if((D.build_type & AUTOLATHE) && ("hacked" in D.category))
if(D.build_type & stored_research.design_autounlock_skip_types)
continue
if((D.build_type & stored_research.design_autounlock_buildtypes) && ("hacked" in D.category))
if(hacked)
stored_research.add_design(D)
else
@@ -441,8 +440,7 @@
/obj/machinery/autolathe/secure
name = "secured autolathe"
desc = "An autolathe reprogrammed with security protocols to prevent hacking."
hackable = FALSE
desc = "It produces items using metal and glass. This model was reprogrammed without some of the more hazardous designs."
circuit = /obj/item/circuitboard/machine/autolathe/secure
stored_research = /datum/techweb/specialized/autounlocking/autolathe/public
base_print_speed = 20
@@ -48,8 +48,7 @@
if(2)
// State 2
if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/C = W
if(C.use(2))
if(W.use_tool(src, user, 0, 2))
to_chat(user, "<span class='notice'>You add wires to the assembly.</span>")
state = 3
else
@@ -83,7 +83,6 @@
if(istype(P, /obj/item/stack/cable_coil))
if(!P.tool_start_check(user, amount=5))
return
to_chat(user, "<span class='notice'>You start to add cables to the frame...</span>")
if(P.use_tool(src, user, 20, volume=50, amount=5))
to_chat(user, "<span class='notice'>You add cables to the frame.</span>")
+1 -2
View File
@@ -400,12 +400,11 @@
"<span class='notice'>You begin adding wires to [src]...</span>")
playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, 1)
if(do_after(user, 60, target = src))
if(constructionStep != CONSTRUCTION_GUTTED || B.get_amount() < 5 || !B)
if(constructionStep != CONSTRUCTION_GUTTED || !B.use_tool(src, user, 0, 5))
return
user.visible_message("<span class='notice'>[user] adds wires to [src].</span>", \
"<span class='notice'>You wire [src].</span>")
playsound(get_turf(src), 'sound/items/deconstruct.ogg', 50, 1)
B.use(5)
constructionStep = CONSTRUCTION_WIRES_EXPOSED
update_icon()
return
+1 -3
View File
@@ -205,11 +205,9 @@
return
if(1)
if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/coil = W
if(coil.get_amount() < 5)
if(!W.use_tool(src, user, 0, 5))
to_chat(user, "<span class='warning'>You need more cable for this!</span>")
else
coil.use(5)
buildstage = 2
to_chat(user, "<span class='notice'>You wire \the [src].</span>")
update_icon()
+3 -4
View File
@@ -396,7 +396,6 @@ GLOBAL_LIST_EMPTY(network_holopads)
Hologram.add_atom_colour("#77abff", FIXED_COLOUR_PRIORITY)
Hologram.Impersonation = user
Hologram.copy_known_languages_from(user,replace = TRUE)
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
Hologram.setAnchored(TRUE)//So space wind cannot drag it.
@@ -555,9 +554,8 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
Hologram.alpha = 170
Hologram.add_atom_colour("#77abff", FIXED_COLOUR_PRIORITY)
Hologram.dir = SOUTH //for now
Hologram.grant_all_languages(omnitongue=TRUE)
var/datum/language_holder/holder = Hologram.get_language_holder()
holder.selected_default_language = record.language
holder.selected_language = record.language
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
Hologram.setAnchored(TRUE)//So space wind cannot drag it.
@@ -649,7 +647,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
return
if(HOLORECORD_LANGUAGE)
var/datum/language_holder/holder = replay_holo.get_language_holder()
holder.selected_default_language = entry[2]
holder.selected_language = entry[2]
if(HOLORECORD_PRESET)
var/preset_type = entry[2]
var/datum/preset_holoimage/H = new preset_type
@@ -672,6 +670,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
updateDialog()
/obj/effect/overlay/holo_pad_hologram
initial_language_holder = /datum/language_holder/universal
var/mob/living/Impersonation
var/datum/holocall/HC
-1
View File
@@ -114,7 +114,6 @@
add_fingerprint(user)
if(charging)
charging.update_icon()
charging.forceMove(drop_location())
user.put_in_hands(charging)
/obj/machinery/recharger/attack_tk(mob/user)
+1 -4
View File
@@ -160,10 +160,7 @@
to_chat(user, "<span class='warning'>You need one length of cable to repair [src]!</span>")
return
to_chat(user, "<span class='notice'>You begin to replace the wires...</span>")
if(do_after(user, 30, target = src))
if(coil.get_amount() < 1)
return
coil.use(1)
if(W.use_tool(src, user, 30, 1))
obj_integrity = max_integrity
stat &= ~BROKEN
to_chat(user, "<span class='notice'>You repair \the [src].</span>")
@@ -100,7 +100,7 @@
obj/source, // the originating radio
frequency, // the frequency the signal is taking place on
atom/movable/virtualspeaker/speaker, // representation of the method's speaker
datum/language/language, // the langauge of the message
datum/language/language, // the language of the message
message, // the text content of the message
spans // the list of spans applied to the message
)
@@ -3,6 +3,46 @@
////////////////////////////////
/datum/component/construction/mecha
var/base_icon
var/looky_helpy = TRUE
/datum/component/construction/mecha/examine(mob/user)
. = ..()
if(looky_helpy)
switch(steps[index]["key"])
if(TOOL_WRENCH)
. += "<span class='notice'>The mech could be <b>wrenched</b> into place.</span>"
if(TOOL_SCREWDRIVER)
. += "<span class='notice'>The mech could be <b>screwed</b> into place.</span>"
if(TOOL_WIRECUTTER)
. += "<span class='notice'>The mech wires could be <b>trimmed</b> into place.</span>"
if(/obj/item/stack/cable_coil)
. += "<span class='notice'>The mech could use some <b>wiring</b>.</span>"
if(/obj/item/circuitboard)
. += "<span class='notice'>The mech could use a type of<b>circuitboard</b>.</span>"
if(/obj/item/stock_parts/scanning_module)
. += "<span class='notice'>The mech could use a <b>scanning stock part</b>.</span>"
if(/obj/item/stock_parts/capacitor)
. += "<span class='notice'>The mech could use a <b>power based stock part</b>.</span>"
if(/obj/item/stock_parts/cell)
. += "<span class='notice'>The mech could use a <b>power source</b>.</span>"
if(/obj/item/stack/sheet/metal)
. += "<span class='notice'>The mech could use some <b>sheets of metal</b>.</span>"
if(/obj/item/stack/sheet/plasteel)
. += "<span class='notice'>The mech could use some <b>sheets of strong steel</b>.</span>"
if(/obj/item/bikehorn)
. += "<span class='notice'>HONK IT!.</span>"
if(/obj/item/clothing/mask/gas/clown_hat)
. += "<span class='notice'>GIVE IT CLOWN MAKEUP HONK!.</span>"
if(/obj/item/clothing/shoes/clown_shoes)
. += "<span class='notice'>GIVE IT GOOFY SHOES HONK HONK!.</span>"
if(/obj/item/mecha_parts/part)
. += "<span class='notice'>The mech could use a mech <b>part</b>.</span>"
if(/obj/item/stack/ore/bluespace_crystal)
. += "<span class='notice'>The mech could use a <b>crystal</b> of sorts.</span>"
if(/obj/item/assembly/signaler/anomaly)
. += "<span class='notice'>The mech could use a <b>anomaly</b> of sorts.</span>"
else
return
/datum/component/construction/mecha/spawn_result()
if(!result)
+1 -2
View File
@@ -220,8 +220,7 @@
return
else if(istype(W, /obj/item/stack/cable_coil))
if(state == 3 && (internal_damage & MECHA_INT_SHORT_CIRCUIT))
var/obj/item/stack/cable_coil/CC = W
if(CC.use(2))
if(W.use_tool(src, user, 0, 2))
clearInternalDamage(MECHA_INT_SHORT_CIRCUIT)
to_chat(user, "<span class='notice'>You replace the fused wires.</span>")
else
+16 -1
View File
@@ -132,6 +132,12 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/list/grind_results //A reagent list containing the reagents this item produces when ground up in a grinder - this can be an empty list to allow for reagent transferring only
var/list/juice_results //A reagent list containing blah blah... but when JUICED in a grinder!
///Skills vars
//list of skill PATHS exercised when using this item. An associated bitfield can be set to indicate additional ways the skill is used by this specific item.
var/list/datum/skill/used_skills
var/skill_difficulty = THRESHOLD_COMPETENT //how difficult it's to use this item in general.
var/skill_gain = DEF_SKILL_GAIN //base skill value gain from using this item.
/obj/item/Initialize()
if (attack_verb)
@@ -783,7 +789,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
// Called when a mob tries to use the item as a tool.
// Handles most checks.
/obj/item/proc/use_tool(atom/target, mob/living/user, delay, amount=0, volume=0, datum/callback/extra_checks)
/obj/item/proc/use_tool(atom/target, mob/living/user, delay, amount=0, volume=0, datum/callback/extra_checks, skill_gain_mult = 1, max_level = INFINITY)
// No delay means there is no start message, and no reason to call tool_start_check before use_tool.
// Run the start check here so we wouldn't have to call it manually.
if(!delay && !tool_start_check(user, amount))
@@ -795,6 +801,9 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
play_tool_sound(target, volume)
if(delay)
if(user.mind && used_skills)
delay = user.mind.skill_holder.item_action_skills_mod(src, delay, skill_difficulty, SKILL_USE_TOOL, NONE, FALSE)
// Create a callback with checks that would be called every tick by do_after.
var/datum/callback/tool_check = CALLBACK(src, .proc/tool_check_callback, user, amount, extra_checks)
@@ -819,6 +828,12 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
if(delay >= MIN_TOOL_SOUND_DELAY)
play_tool_sound(target, volume)
if(user.mind && used_skills && skill_gain_mult)
for(var/skill in used_skills)
if(!(used_skills[skill] & SKILL_TRAINING_TOOL))
continue
user.mind.skill_holder.auto_gain_experience(skill, skill_gain*skill_gain_mult, GET_STANDARD_LVL(max_level))
return TRUE
// Called before use_tool if there is a delay, or by use_tool if there isn't.
+74 -24
View File
@@ -6,17 +6,42 @@
icon_state = "cutout_basic"
w_class = WEIGHT_CLASS_BULKY
resistance_flags = FLAMMABLE
// Possible restyles for the cutout;
// add an entry in change_appearance() if you add to here
var/list/possible_appearances = list("Assistant", "Clown", "Mime",
"Traitor", "Nuke Op", "Cultist", "Brass Cultist", "Clockwork Cultist",
"Revolutionary", "Wizard", "Shadowling", "Xenomorph", "Xenomorph Maid", "Swarmer",
"Ash Walker", "Deathsquad Officer", "Ian", "Slaughter Demon",
"Laughter Demon", "Private Security Officer", "Securitron", "Gondola", "Monkey")
var/pushed_over = FALSE //If the cutout is pushed over and has to be righted
var/deceptive = FALSE //If the cutout actually appears as what it portray and not a discolored version
/// Possible restyles for the cutout, add an entry in change_appearance() if you add to here
var/static/list/possible_appearances
/// If the cutout is pushed over and has to be righted
var/pushed_over = FALSE
/// If the cutout actually appears as what it portray and not a discolored version
var/deceptive = FALSE
var/lastattacker = null
/obj/item/cardboard_cutout/Initialize()
. = ..()
if(possible_appearances)
return
possible_appearances = sortList(list(
"Assistant" = image(icon = src.icon, icon_state = "cutout_greytide"),
"Clown" = image(icon = src.icon, icon_state = "cutout_clown"),
"Mime" = image(icon = src.icon, icon_state = "cutout_mime"),
"Traitor" = image(icon = src.icon, icon_state = "cutout_traitor"),
"Nuke Op" = image(icon = src.icon, icon_state = "cutout_fluke"),
"Cultist" = image(icon = src.icon, icon_state = "cutout_cultist"),
"Brass Cultist" = image(icon = src.icon, icon_state = "cutout_servant"),
"Clockwork Cultist" = image(icon = src.icon, icon_state = "cutout_new_servant"),
"Revolutionary" = image(icon = src.icon, icon_state = "cutout_viva"),
"Wizard" = image(icon = src.icon, icon_state = "cutout_wizard"),
"Shadowling" = image(icon = src.icon, icon_state = "cutout_shadowling"),
"Xenomorph" = image(icon = src.icon, icon_state = "cutout_fukken_xeno"),
"Xenomorph Maid" = image(icon = src.icon, icon_state = "cutout_lusty"),
"Swarmer" = image(icon = src.icon, icon_state = "cutout_swarmer"),
"Ash Walker" = image(icon = src.icon, icon_state = "cutout_free_antag"),
"Deathsquad Officer" = image(icon = src.icon, icon_state = "cutout_deathsquad"),
"Ian" = image(icon = src.icon, icon_state = "cutout_ian"),
"Slaughter Demon" = image(icon = 'icons/mob/mob.dmi', icon_state = "daemon"),
"Laughter Demon" = image(icon = 'icons/mob/mob.dmi', icon_state = "bowmon"),
"Private Security Officer" = image(icon = src.icon, icon_state = "cutout_ntsec"),
"Securitron" = image(icon = src.icon, icon_state = "cutout_law"),
"Gondola" = image(icon = src.icon, icon_state = "cutout_gondola"),
"Monkey" = image(icon = src.icon, icon_state = "cutout_monky"),
))
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/cardboard_cutout/attack_hand(mob/living/user)
@@ -76,22 +101,21 @@
push_over()
return BULLET_ACT_HIT
/**
* change_appearance: Changes a skin of the cardboard cutout based on a user's choice
*
* Arguments:
* * crayon The crayon used to change and recolor the cardboard cutout
* * user The mob choosing a skin of the cardboard cutout
*/
/obj/item/cardboard_cutout/proc/change_appearance(obj/item/toy/crayon/crayon, mob/living/user)
if(!crayon || !user)
return
if(pushed_over)
to_chat(user, "<span class='warning'>Right [src] first!</span>")
return
if(crayon.check_empty(user))
return
if(crayon.is_capped)
to_chat(user, "<span class='warning'>Take the cap off first!</span>")
return
var/new_appearance = input(user, "Choose a new appearance for [src].", "26th Century Deception") as null|anything in possible_appearances
if(!new_appearance || !crayon || !user.canUseTopic(src))
var/new_appearance = show_radial_menu(user, src, possible_appearances, custom_check = CALLBACK(src, .proc/check_menu, user, crayon), radius = 36, require_near = TRUE)
if(!new_appearance)
return
if(!do_after(user, 10, FALSE, src, TRUE))
return
return FALSE
if(!check_menu(user, crayon))
return FALSE
user.visible_message("<span class='notice'>[user] gives [src] a new look.</span>", "<span class='notice'>Voila! You give [src] a new look.</span>")
crayon.use_charges(1)
crayon.check_empty(user)
@@ -196,7 +220,33 @@
name = "monkey ([rand(1, 999)])"
desc = "A cardboard cutout of a monkey."
icon_state = "cutout_monky"
return 1
else
return FALSE
return TRUE
/**
* check_menu: Checks if we are allowed to interact with a radial menu
*
* Arguments:
* * user The mob interacting with a menu
* * crayon The crayon used to interact with a menu
*/
/obj/item/cardboard_cutout/proc/check_menu(mob/living/user, obj/item/toy/crayon/crayon)
if(!istype(user))
return FALSE
if(user.incapacitated())
return FALSE
if(pushed_over)
to_chat(user, "<span class='warning'>Right [src] first!</span>")
return FALSE
if(!crayon || !user.is_holding(crayon))
return FALSE
if(crayon.check_empty(user))
return FALSE
if(crayon.is_capped)
to_chat(user, "<span class='warning'>Take the cap off first!</span>")
return FALSE
return TRUE
/obj/item/cardboard_cutout/setDir(newdir)
dir = SOUTH
@@ -199,7 +199,7 @@
if(!spans)
spans = list(M.speech_span)
if(!language)
language = M.get_default_language()
language = M.get_selected_language()
INVOKE_ASYNC(src, .proc/talk_into_impl, M, message, channel, spans.Copy(), language)
return ITALICS | REDUCE_RANGE
@@ -97,8 +97,7 @@
to_chat(user, "<span class='notice'>You add [A] to the [initial(name)] assembly.</span>")
else if(stage == EMPTY && istype(I, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/C = I
if (C.use(1))
if (I.use_tool(src, user, 0, 1, max_level = JOB_SKILL_BASIC))
det_time = 50 // In case the cable_coil was removed and readded.
stage_change(WIRED)
to_chat(user, "<span class='notice'>You rig the [initial(name)] assembly.</span>")
+11 -12
View File
@@ -118,7 +118,7 @@
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
custom_materials = list(/datum/material/iron=150, /datum/material/glass=75)
breakouttime = 300 //Deciseconds = 30s
cuffsound = 'sound/weapons/cablecuff.ogg'
cuffsound = 'sound/weapons/cablecuff.ogg'
/obj/item/restraints/handcuffs/cable/attack_self(mob/user)
to_chat(user, "<span class='notice'>You start unwinding the cable restraints back into coil</span>")
@@ -130,7 +130,7 @@
user.put_in_hands(coil)
coil.color = color
to_chat(user, "<span class='notice'>You unwind the cable restraints back into coil</span>")
/obj/item/restraints/handcuffs/cable/red
color = "#ff0000"
@@ -225,7 +225,6 @@
/obj/item/restraints/handcuffs/fake/kinky
name = "kinky handcuffs"
desc = "Fake handcuffs meant for erotic roleplay."
icon = 'modular_citadel/icons/obj/items_and_weapons.dmi'
icon_state = "handcuffgag"
item_state = "kinkycuff"
@@ -252,7 +251,7 @@
throw_range = 1
icon_state = "beartrap"
desc = "A trap used to catch bears and other legged creatures."
var/armed = 0
var/armed = FALSE
var/trap_damage = 20
/obj/item/restraints/legcuffs/beartrap/Initialize()
@@ -275,14 +274,14 @@
if(armed && isturf(src.loc))
if(isliving(AM))
var/mob/living/L = AM
var/snap = 0
var/snap = FALSE
var/def_zone = BODY_ZONE_CHEST
if(iscarbon(L))
var/mob/living/carbon/C = L
snap = 1
if(!C.lying)
def_zone = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
if(!C.legcuffed && C.get_num_legs(FALSE) >= 2) //beartrap can't cuff your leg if there's already a beartrap or legcuffs, or you don't have two legs.
snap = TRUE
C.legcuffed = src
forceMove(C)
C.update_equipment_speed_mods()
@@ -291,21 +290,21 @@
else if(isanimal(L))
var/mob/living/simple_animal/SA = L
if(SA.mob_size > MOB_SIZE_TINY)
snap = 1
if(L.movement_type & FLYING)
snap = 0
snap = TRUE
if(L.movement_type & (FLYING | FLOATING))
snap = FALSE
if(snap)
armed = 0
armed = FALSE
icon_state = "[initial(icon_state)][armed]"
playsound(src.loc, 'sound/effects/snap.ogg', 50, 1)
L.visible_message("<span class='danger'>[L] triggers \the [src].</span>", \
"<span class='userdanger'>You trigger \the [src]!</span>")
L.apply_damage(trap_damage,BRUTE, def_zone)
L.apply_damage(trap_damage, BRUTE, def_zone)
..()
/obj/item/restraints/legcuffs/beartrap/energy
name = "energy snare"
armed = 1
armed = TRUE
icon_state = "e_snare"
trap_damage = 0
item_flags = DROPDEL
+3 -1
View File
@@ -519,7 +519,9 @@
S.name = name
S.ckey = C.ckey
S.status_flags |= GODMODE
S.language_holder = user.language_holder.copy(S)
S.copy_languages(user, LANGUAGE_MASTER) //Make sure the sword can understand and communicate with the user.
S.update_atom_languages()
grant_all_languages(FALSE, FALSE, TRUE) //Grants omnitongue
S.AddElement(/datum/element/ghost_role_eligibility,penalize_on_ghost = TRUE)
START_PROCESSING(SSprocessing,src)
var/input = stripped_input(S,"What are you named?", ,"", MAX_NAME_LEN)
+2 -2
View File
@@ -304,7 +304,7 @@
return
else
if(cooldown_check < world.time)
if(target.run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user) & BLOCK_SUCCESS)
if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS)
playsound(target, 'sound/weapons/genhit.ogg', 50, 1)
return
if(ishuman(target))
@@ -325,7 +325,7 @@
else
target.LAssailant = WEAKREF(user)
cooldown_check = world.time + cooldown
user.adjustStaminaLossBuffered(getweight())//CIT CHANGE - makes swinging batons cost stamina
user.adjustStaminaLossBuffered(getweight(user, STAM_COST_BATON_MOB_MULT))
else
var/wait_desc = get_wait_description()
if(wait_desc)
+1 -1
View File
@@ -11,7 +11,7 @@
var/charge_cost = 30
/obj/item/borg/stun/attack(mob/living/M, mob/living/user)
if(M.run_block(src, 0, "[M]'s [name]", ATTACK_TYPE_MELEE, 0, user, ran_zone(user.zone_selected)) & BLOCK_SUCCESS)
if(M.mob_run_block(src, 0, "[M]'s [name]", ATTACK_TYPE_MELEE, 0, user, ran_zone(user.zone_selected), null) & BLOCK_SUCCESS)
playsound(M, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
if(iscyborg(user))
@@ -59,6 +59,9 @@
self_delay = 20
grind_results = list(/datum/reagent/medicine/styptic_powder = 10)
/obj/item/stack/medical/bruise_pack/one
amount = 1
/obj/item/stack/medical/bruise_pack/heal(mob/living/M, mob/user)
if(M.stat == DEAD)
to_chat(user, "<span class='notice'> [M] is dead. You can not help [M.p_them()]!</span>")
@@ -134,6 +137,9 @@
singular_name = "sterilized medical gauze"
self_delay = 5
/obj/item/stack/medical/gauze/adv/one
amount = 1
/obj/item/stack/medical/gauze/cyborg
custom_materials = null
is_cyborg = 1
@@ -151,6 +157,9 @@
self_delay = 20
grind_results = list(/datum/reagent/medicine/silver_sulfadiazine = 10)
/obj/item/stack/medical/ointment/one
amount = 1
/obj/item/stack/medical/ointment/heal(mob/living/M, mob/user)
if(M.stat == DEAD)
to_chat(user, "<span class='notice'> [M] is dead. You can not help [M.p_them()]!</span>")
@@ -69,7 +69,7 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \
if (get_amount() < 1 || CC.get_amount() < 5)
to_chat(user, "<span class='warning>You need five lengths of coil and one sheet of glass to make wired glass!</span>")
return
CC.use(5)
CC.use_tool(src, user, 0, 5, max_level = JOB_SKILL_BASIC)
use(1)
to_chat(user, "<span class='notice'>You attach wire to the [name].</span>")
var/obj/item/stack/light_w/new_tile = new(user.loc)
+51 -37
View File
@@ -850,12 +850,6 @@
#define NODESIGN "None"
#define NANOTRASEN "NanotrasenStandard"
#define SYNDI "SyndiSnacks"
#define HEART "Heart"
#define SMILEY "SmileyFace"
/obj/item/storage/box/papersack
name = "paper sack"
desc = "A sack neatly crafted out of paper."
@@ -863,7 +857,18 @@
item_state = "paperbag_None"
resistance_flags = FLAMMABLE
foldable = null
var/design = NODESIGN
/// A list of all available papersack reskins
var/list/papersack_designs = list()
/obj/item/storage/box/papersack/Initialize(mapload)
. = ..()
papersack_designs = sortList(list(
"None" = image(icon = src.icon, icon_state = "paperbag_None"),
"NanotrasenStandard" = image(icon = src.icon, icon_state = "paperbag_NanotrasenStandard"),
"SyndiSnacks" = image(icon = src.icon, icon_state = "paperbag_SyndiSnacks"),
"Heart" = image(icon = src.icon, icon_state = "paperbag_Heart"),
"SmileyFace" = image(icon = src.icon, icon_state = "paperbag_SmileyFace")
))
/obj/item/storage/box/papersack/update_icon_state()
if(contents.len == 0)
@@ -871,55 +876,64 @@
else
icon_state = "[item_state]_closed"
/obj/item/storage/box/papersack/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/pen))
//if a pen is used on the sack, dialogue to change its design appears
if(contents.len)
to_chat(user, "<span class='warning'>You can't modify [src] with items still inside!</span>")
return
var/list/designs = list(NODESIGN, NANOTRASEN, SYNDI, HEART, SMILEY, "Cancel")
var/switchDesign = input("Select a Design:", "Paper Sack Design", designs[1]) in designs
if(get_dist(usr, src) > 1)
to_chat(usr, "<span class='warning'>You have moved too far away!</span>")
return
var/choice = designs.Find(switchDesign)
if(design == designs[choice] || designs[choice] == "Cancel")
return 0
to_chat(usr, "<span class='notice'>You make some modifications to [src] using your pen.</span>")
design = designs[choice]
icon_state = "paperbag_[design]"
item_state = "paperbag_[design]"
switch(designs[choice])
if(NODESIGN)
var/choice = show_radial_menu(user, src , papersack_designs, custom_check = CALLBACK(src, .proc/check_menu, user, W), radius = 36, require_near = TRUE)
if(!choice)
return FALSE
if(icon_state == "paperbag_[choice]")
return FALSE
switch(choice)
if("None")
desc = "A sack neatly crafted out of paper."
if(NANOTRASEN)
if("NanotrasenStandard")
desc = "A standard Nanotrasen paper lunch sack for loyal employees on the go."
if(SYNDI)
if("SyndiSnacks")
desc = "The design on this paper sack is a remnant of the notorious 'SyndieSnacks' program."
if(HEART)
if("Heart")
desc = "A paper sack with a heart etched onto the side."
if(SMILEY)
if("SmileyFace")
desc = "A paper sack with a crude smile etched onto the side."
return 0
else
return FALSE
to_chat(user, "<span class='notice'>You make some modifications to [src] using your pen.</span>")
icon_state = "paperbag_[choice]"
item_state = "paperbag_[choice]"
return FALSE
else if(W.get_sharpness())
if(!contents.len)
if(item_state == "paperbag_None")
user.show_message("<span class='notice'>You cut eyeholes into [src].</span>", MSG_VISUAL)
new /obj/item/clothing/head/papersack(user.loc)
qdel(src)
return 0
return FALSE
else if(item_state == "paperbag_SmileyFace")
user.show_message("<span class='notice'>You cut eyeholes into [src] and modify the design.</span>", MSG_VISUAL)
new /obj/item/clothing/head/papersack/smiley(user.loc)
qdel(src)
return 0
return FALSE
return ..()
#undef NODESIGN
#undef NANOTRASEN
#undef SYNDI
#undef HEART
#undef SMILEY
/**
* check_menu: Checks if we are allowed to interact with a radial menu
*
* Arguments:
* * user The mob interacting with a menu
* * P The pen used to interact with a menu
*/
/obj/item/storage/box/papersack/proc/check_menu(mob/user, obj/item/pen/P)
if(!istype(user))
return FALSE
if(user.incapacitated())
return FALSE
if(contents.len)
to_chat(user, "<span class='warning'>You can't modify [src] with items still inside!</span>")
return FALSE
if(!P || !user.is_holding(P))
to_chat(user, "<span class='warning'>You need a pen to modify [src]!</span>")
return FALSE
return TRUE
/obj/item/storage/box/ingredients //This box is for the randomly chosen version the chef spawns with, it shouldn't actually exist.
name = "ingredients box"
+2 -5
View File
@@ -163,14 +163,14 @@
if(status)
if(baton_stun(M, user, disarming))
user.do_attack_animation(M)
user.adjustStaminaLossBuffered(getweight()) //CIT CHANGE - makes stunbatonning others cost stamina
user.adjustStaminaLossBuffered(getweight(user, STAM_COST_BATON_MOB_MULT))
else if(user.a_intent != INTENT_HARM) //they'll try to bash in the last proc.
M.visible_message("<span class='warning'>[user] has prodded [M] with [src]. Luckily it was off.</span>", \
"<span class='warning'>[user] has prodded you with [src]. Luckily it was off</span>")
return disarming || (user.a_intent != INTENT_HARM)
/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user, disarming = FALSE)
if(L.run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user) & BLOCK_SUCCESS) //No message; check_shields() handles that
if(L.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS) //No message; check_shields() handles that
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
var/stunpwr = stamforce
@@ -232,11 +232,8 @@
/obj/item/melee/baton/stunsword
name = "stunsword"
desc = "not actually sharp, this sword is functionally identical to a stunbaton"
icon = 'modular_citadel/icons/obj/stunsword.dmi'
icon_state = "stunsword"
item_state = "sword"
lefthand_file = 'modular_citadel/icons/mob/inhands/stunsword_left.dmi'
righthand_file = 'modular_citadel/icons/mob/inhands/stunsword_right.dmi'
/obj/item/melee/baton/stunsword/get_belt_overlay()
if(istype(loc, /obj/item/storage/belt/sabre))
+59 -57
View File
@@ -241,11 +241,13 @@
playsound(user, activation_sound, transform_volume, 1)
w_class = WEIGHT_CLASS_BULKY
AddElement(/datum/element/sword_point)
total_mass = total_mass_on
else
to_chat(user, "<span class='notice'>[deactivation_message]</span>")
playsound(user, deactivation_sound, transform_volume, 1)
w_class = WEIGHT_CLASS_SMALL
RemoveElement(/datum/element/sword_point)
total_mass = initial(total_mass)
update_icon()
add_fingerprint(user)
@@ -287,9 +289,6 @@
else
return ..()
/obj/item/toy/sword/getweight()
return (active ? total_mass_on : total_mass) || w_class *1.25
/obj/item/toy/sword/cx
name = "\improper DX Non-Euplastic LightSword"
desc = "A deluxe toy replica of an energy sword. Realistic visuals and sounds! Ages 8 and up."
@@ -903,79 +902,57 @@
name = "hand of cards"
desc = "A number of cards not in a deck, customarily held in ones hand."
icon = 'icons/obj/toy.dmi'
icon_state = "nanotrasen_hand2"
icon_state = "none"
w_class = WEIGHT_CLASS_TINY
var/list/currenthand = list()
var/choice = null
/obj/item/toy/cards/cardhand/attack_self(mob/user)
user.set_machine(src)
var/list/handradial = list()
interact(user)
/obj/item/toy/cards/cardhand/ui_interact(mob/user)
. = ..()
var/dat = "You have:<BR>"
for(var/t in currenthand)
dat += "<A href='?src=[REF(src)];pick=[t]'>A [t].</A><BR>"
dat += "Which card will you remove next?"
var/datum/browser/popup = new(user, "cardhand", "Hand of Cards", 400, 240)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.set_content(dat)
popup.open()
handradial[t] = image(icon = src.icon, icon_state = "sc_[t]_[deckstyle]")
/obj/item/toy/cards/cardhand/Topic(href, href_list)
if(..())
return
if(usr.stat || !ishuman(usr))
return
var/mob/living/carbon/human/cardUser = usr
var/O = src
if(href_list["pick"])
if (cardUser.is_holding(src))
var/choice = href_list["pick"]
var/obj/item/toy/cards/singlecard/C = new/obj/item/toy/cards/singlecard(cardUser.loc)
src.currenthand -= choice
C.parentdeck = src.parentdeck
C.cardname = choice
C.apply_card_vars(C,O)
C.pickup(cardUser)
cardUser.put_in_hands(C)
cardUser.visible_message("<span class='notice'>[cardUser] draws a card from [cardUser.p_their()] hand.</span>", "<span class='notice'>You take the [C.cardname] from your hand.</span>")
interact(cardUser)
if(src.currenthand.len < 3)
src.icon_state = "[deckstyle]_hand2"
else if(src.currenthand.len < 4)
src.icon_state = "[deckstyle]_hand3"
else if(src.currenthand.len < 5)
src.icon_state = "[deckstyle]_hand4"
if(src.currenthand.len == 1)
var/obj/item/toy/cards/singlecard/N = new/obj/item/toy/cards/singlecard(src.loc)
N.parentdeck = src.parentdeck
N.cardname = src.currenthand[1]
N.apply_card_vars(N,O)
qdel(src)
N.pickup(cardUser)
cardUser.put_in_hands(N)
to_chat(cardUser, "<span class='notice'>You also take [currenthand[1]] and hold it.</span>")
cardUser << browse(null, "window=cardhand")
if(!(cardUser.mobility_flags & MOBILITY_USE))
return
var/O = src
var/choice = show_radial_menu(usr,src, handradial, custom_check = CALLBACK(src, .proc/check_menu, user), radius = 36, require_near = TRUE)
if(!choice)
return FALSE
var/obj/item/toy/cards/singlecard/C = new/obj/item/toy/cards/singlecard(cardUser.loc)
currenthand -= choice
handradial -= choice
C.parentdeck = parentdeck
C.cardname = choice
C.apply_card_vars(C,O)
C.pickup(cardUser)
cardUser.put_in_hands(C)
cardUser.visible_message("<span class='notice'>[cardUser] draws a card from [cardUser.p_their()] hand.</span>", "<span class='notice'>You take the [C.cardname] from your hand.</span>")
interact(cardUser)
update_sprite()
if(length(currenthand) == 1)
var/obj/item/toy/cards/singlecard/N = new/obj/item/toy/cards/singlecard(loc)
N.parentdeck = parentdeck
N.cardname = currenthand[1]
N.apply_card_vars(N,O)
qdel(src)
N.pickup(cardUser)
cardUser.put_in_hands(N)
to_chat(cardUser, "<span class='notice'>You also take [currenthand[1]] and hold it.</span>")
/obj/item/toy/cards/cardhand/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params)
if(istype(C))
if(C.parentdeck == src.parentdeck)
src.currenthand += C.cardname
user.visible_message("[user] adds a card to [user.p_their()] hand.", "<span class='notice'>You add the [C.cardname] to your hand.</span>")
user.visible_message("<span class='notice'>[user] adds a card to [user.p_their()] hand.</span>", "<span class='notice'>You add the [C.cardname] to your hand.</span>")
qdel(C)
interact(user)
if(currenthand.len > 4)
src.icon_state = "[deckstyle]_hand5"
else if(currenthand.len > 3)
src.icon_state = "[deckstyle]_hand4"
else if(currenthand.len > 2)
src.icon_state = "[deckstyle]_hand3"
update_sprite(src)
else
to_chat(user, "<span class='warning'>You can't mix cards from other decks!</span>")
else
@@ -984,7 +961,7 @@
/obj/item/toy/cards/cardhand/apply_card_vars(obj/item/toy/cards/newobj,obj/item/toy/cards/sourceobj)
..()
newobj.deckstyle = sourceobj.deckstyle
newobj.icon_state = "[deckstyle]_hand2" // Another dumb hack, without this the hand is invisible (or has the default deckstyle) until another card is added.
update_sprite()
newobj.card_hitsound = sourceobj.card_hitsound
newobj.card_force = sourceobj.card_force
newobj.card_throwforce = sourceobj.card_throwforce
@@ -993,6 +970,31 @@
newobj.card_attack_verb = sourceobj.card_attack_verb
newobj.resistance_flags = sourceobj.resistance_flags
/**
* check_menu: Checks if we are allowed to interact with a radial menu
*
* Arguments:
* * user The mob interacting with a menu
*/
/obj/item/toy/cards/cardhand/proc/check_menu(mob/living/user)
if(!istype(user))
return FALSE
if(user.incapacitated())
return FALSE
return TRUE
/**
* This proc updates the sprite for when you create a hand of cards
*/
/obj/item/toy/cards/cardhand/proc/update_sprite()
cut_overlays()
var/overlay_cards = currenthand.len
var/k = overlay_cards == 2 ? 1 : overlay_cards - 2
for(var/i = k; i <= overlay_cards; i++)
var/card_overlay = image(icon=src.icon,icon_state="sc_[currenthand[i]]_[deckstyle]",pixel_x=(1-i+k)*3,pixel_y=(1-i+k)*3)
add_overlay(card_overlay)
/obj/item/toy/cards/singlecard
name = "card"
desc = "a card"
+1 -1
View File
@@ -1182,7 +1182,7 @@
if(iscyborg(target))
..()
return
if(target.run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user) & BLOCK_SUCCESS) //No message; run_block() handles that
if(target.mob_run_block(src, 0, "[user]'s [name]", ATTACK_TYPE_MELEE, 0, user, null, null) & BLOCK_SUCCESS) //No message; run_block() handles that
playsound(target, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
if(user.a_intent != INTENT_HARM)
+1 -1
View File
@@ -131,7 +131,7 @@
if(C.get_amount() >= 5)
playsound(loc, 'sound/items/deconstruct.ogg', 50, 1)
to_chat(user, "<span class='notice'>You start to add cables to the frame...</span>")
if(do_after(user, 20, target = src) && state == SCREWED_CORE && C.use(5))
if(do_after(user, 20, target = src) && state == SCREWED_CORE && C.use_tool(src, user, 0, 5))
to_chat(user, "<span class='notice'>You add cables to the frame.</span>")
state = CABLED_CORE
update_icon()
+1 -2
View File
@@ -84,7 +84,6 @@
panel_open = FALSE
else if(istype(I, /obj/item/stack/cable_coil) && panel_open)
var/obj/item/stack/cable_coil/C = I
if(obj_flags & EMAGGED) //Emagged, not broken by EMP
to_chat(user, "<span class='warning'>Sign has been damaged beyond repair!</span>")
return
@@ -92,7 +91,7 @@
to_chat(user, "<span class='warning'>This sign is functioning properly!</span>")
return
if(C.use(2))
if(I.use_tool(src, user, 0, 2))
to_chat(user, "<span class='notice'>You replace the burnt wiring.</span>")
broken = FALSE
else
@@ -32,6 +32,7 @@
new_spawn.undershirt = "Nude" //changing underwear/shirt/socks doesn't seem to function correctly right now because of some bug elsewhere?
new_spawn.socks = "Nude"
new_spawn.update_body(TRUE)
new_spawn.language_holder.selected_language = /datum/language/sylvan
//Ash walker eggs: Spawns in ash walker dens in lavaland. Ghosts become unbreathing lizards that worship the Necropolis and are advised to retrieve corpses to create more ash walkers.
@@ -63,10 +64,6 @@
else
to_chat(new_spawn, "<span class='userdanger'>You have been born outside of your natural home! Whether you decide to return home, or make due with your new home is your own decision.</span>")
new_spawn.grant_language(/datum/language/draconic)
var/datum/language_holder/holder = new_spawn.get_language_holder()
holder.selected_default_language = /datum/language/draconic
//Ash walkers on birth understand how to make bone bows, bone arrows and ashen arrows
new_spawn.mind.teach_crafting_recipe(/datum/crafting_recipe/bone_arrow)
+50 -39
View File
@@ -32,7 +32,6 @@
/obj/structure/janitorialcart/proc/put_in_cart(obj/item/I, mob/user)
if(!user.transferItemToLoc(I, src))
return
updateUsrDialog()
to_chat(user, "<span class='notice'>You put [I] into [src].</span>")
return
@@ -96,70 +95,82 @@
. = ..()
if(.)
return
user.set_machine(src)
var/dat
var/list/items = list()
if(mybag)
dat += "<a href='?src=[REF(src)];garbage=1'>[mybag.name]</a><br>"
items += list("Trash bag" = image(icon = mybag.icon, icon_state = mybag.icon_state))
if(mymop)
dat += "<a href='?src=[REF(src)];mop=1'>[mymop.name]</a><br>"
items += list("Mop" = image(icon = mymop.icon, icon_state = mymop.icon_state))
if(mybroom)
dat += "<a href='?src=[REF(src)];broom=1'>[mybroom.name]</a><br>"
items += list("Broom" = image(icon = mybroom.icon, icon_state = mybroom.icon_state))
if(myspray)
dat += "<a href='?src=[REF(src)];spray=1'>[myspray.name]</a><br>"
items += list("Spray bottle" = image(icon = myspray.icon, icon_state = myspray.icon_state))
if(myreplacer)
dat += "<a href='?src=[REF(src)];replacer=1'>[myreplacer.name]</a><br>"
if(signs)
dat += "<a href='?src=[REF(src)];sign=1'>[signs] sign\s</a><br>"
var/datum/browser/popup = new(user, "janicart", name, 240, 160)
popup.set_content(dat)
popup.open()
items += list("Light replacer" = image(icon = myreplacer.icon, icon_state = myreplacer.icon_state))
var/obj/item/caution/sign = locate() in src
if(sign)
items += list("Sign" = image(icon = sign.icon, icon_state = sign.icon_state))
/obj/structure/janitorialcart/Topic(href, href_list)
if(!in_range(src, usr))
if(!length(items))
return
if(!isliving(usr))
items = sortList(items)
var/pick = show_radial_menu(user, src, items, custom_check = CALLBACK(src, .proc/check_menu, user), radius = 38, require_near = TRUE)
if(!pick)
return
var/mob/living/user = usr
if(href_list["garbage"])
if(mybag)
switch(pick)
if("Trash bag")
if(!mybag)
return
user.put_in_hands(mybag)
to_chat(user, "<span class='notice'>You take [mybag] from [src].</span>")
mybag = null
if(href_list["mop"])
if(mymop)
if("Mop")
if(!mymop)
return
user.put_in_hands(mymop)
to_chat(user, "<span class='notice'>You take [mymop] from [src].</span>")
mymop = null
if(href_list["broom"])
if(mybroom)
if("Broom")
if(!mybroom)
return
user.put_in_hands(mybroom)
to_chat(user, "<span class='notice'>You take [mybroom] from [src].</span>")
mybroom = null
if(href_list["spray"])
if(myspray)
if("Spray bottle")
if(!myspray)
return
user.put_in_hands(myspray)
to_chat(user, "<span class='notice'>You take [myspray] from [src].</span>")
myspray = null
if(href_list["replacer"])
if(myreplacer)
if("Light replacer")
if(!myreplacer)
return
user.put_in_hands(myreplacer)
to_chat(user, "<span class='notice'>You take [myreplacer] from [src].</span>")
myreplacer = null
if(href_list["sign"])
if(signs)
var/obj/item/caution/Sign = locate() in src
if(Sign)
user.put_in_hands(Sign)
to_chat(user, "<span class='notice'>You take \a [Sign] from [src].</span>")
signs--
else
WARNING("Signs ([signs]) didn't match contents")
signs = 0
if("Sign")
if(signs <= 0)
return
user.put_in_hands(sign)
to_chat(user, "<span class='notice'>You take \a [sign] from [src].</span>")
signs--
else
return
update_icon()
updateUsrDialog()
/**
* check_menu: Checks if we are allowed to interact with a radial menu
*
* Arguments:
* * user The mob interacting with a menu
*/
/obj/structure/janitorialcart/proc/check_menu(mob/living/user)
if(!istype(user))
return FALSE
if(user.incapacitated())
return FALSE
return TRUE
/obj/structure/janitorialcart/update_overlays()
. = ..()
+11 -1
View File
@@ -91,8 +91,13 @@
if (!is_ghost && !in_range(src, user))
return
var/list/tool_list = list(
"Up" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH),
"Down" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH)
)
if (up && down)
var/result = alert("Go up or down [src]?", "Ladder", "Up", "Down", "Cancel")
var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, .proc/check_menu, user), require_near = TRUE, tooltips = TRUE)
if (!is_ghost && !in_range(src, user))
return // nice try
switch(result)
@@ -112,6 +117,11 @@
if(!is_ghost)
add_fingerprint(user)
/obj/structure/ladder/proc/check_menu(mob/user)
if(user.incapacitated() || !user.Adjacent(src))
return FALSE
return TRUE
/obj/structure/ladder/attack_hand(mob/user)
. = ..()
if(.)
@@ -169,8 +169,7 @@
if(do_after(user, 40, target = src))
if(!src || !anchored || src.state != "01")
return
var/obj/item/stack/cable_coil/CC = W
if(!CC.use(1))
if(!W.use_tool(src, user, 0, 1))
to_chat(user, "<span class='warning'>You need more cable to do this!</span>")
return
to_chat(user, "<span class='notice'>You wire the windoor.</span>")
+1 -1
View File
@@ -25,7 +25,7 @@ GLOBAL_LIST_INIT(freqtospan, list(
return
spans |= speech_span
if(!language)
language = get_default_language()
language = get_selected_language()
send_speech(message, 7, src, , spans, message_language=language)
/atom/movable/proc/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
@@ -492,7 +492,7 @@
user.do_attack_animation(L)
if(L.run_block(src, 0, "[user]'s [src]", ATTACK_TYPE_MELEE, 0, user, check_zone(user.zone_selected)) & BLOCK_SUCCESS)
if(L.mob_run_block(src, 0, "[user]'s [src]", ATTACK_TYPE_MELEE, 0, user, check_zone(user.zone_selected), null) & BLOCK_SUCCESS)
playsound(L, 'sound/weapons/genhit.ogg', 50, TRUE)
return FALSE
@@ -10,7 +10,12 @@
/obj/item/organ/heart/gland/slime/Insert(mob/living/carbon/M, special = 0, drop_if_replaced = TRUE)
..()
owner.faction |= "slime"
owner.grant_language(/datum/language/slime)
owner.grant_language(/datum/language/slime, TRUE, TRUE, LANGUAGE_GLAND)
/obj/item/organ/heart/gland/slime/Remove(mob/living/carbon/M, special = 0)
..()
owner.faction -= "slime"
owner.remove_language(/datum/language/slime, TRUE, TRUE, LANGUAGE_GLAND)
/obj/item/organ/heart/gland/slime/activate()
to_chat(owner, "<span class='warning'>You feel nauseated!</span>")
@@ -10,9 +10,9 @@
/obj/item/organ/heart/gland/trauma/activate()
to_chat(owner, "<span class='warning'>You feel a spike of pain in your head.</span>")
if(prob(33))
owner.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_LOBOTOMY))
owner.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_SURGERY))
else
if(prob(20))
owner.gain_trauma_type(BRAIN_TRAUMA_SEVERE, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_LOBOTOMY))
owner.gain_trauma_type(BRAIN_TRAUMA_SEVERE, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_SURGERY))
else
owner.gain_trauma_type(BRAIN_TRAUMA_MILD, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_LOBOTOMY))
owner.gain_trauma_type(BRAIN_TRAUMA_MILD, rand(TRAUMA_RESILIENCE_BASIC, TRAUMA_RESILIENCE_SURGERY))
@@ -20,7 +20,7 @@
var/poweron_feed = FALSE // Am I feeding?
var/poweron_masquerade = FALSE
// STATS
var/bloodsucker_level
var/bloodsucker_level
var/bloodsucker_level_unspent = 1
var/regen_rate = 0.3 // How fast do I regenerate?
var/additional_regen // How much additional blood regen we gain from bonuses such as high blood.
@@ -209,7 +209,7 @@
// Physiology
CheckVampOrgans() // Heart, Eyes
// Language
owner.current.grant_language(/datum/language/vampiric)
owner.current.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
owner.hasSoul = FALSE // If false, renders the character unable to sell their soul.
owner.isholy = FALSE // is this person a chaplain or admin role allowed to use bibles
// Disabilities
@@ -246,7 +246,7 @@
// Update Health
owner.current.setMaxHealth(100)
// Language
owner.current.remove_language(/datum/language/vampiric)
owner.current.remove_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
// Soul
if (owner.soulOwner == owner) // Return soul, if *I* own it.
owner.hasSoul = TRUE
@@ -649,10 +649,10 @@
return TRUE
// Check 3) If I am a BLOODSUCKER, then are they my Vassal?
if (mob_B && atom_V && (atom_V in mob_B.vassals))
return TRUE
return TRUE
// Check 4) If we are both VASSAL, then do we have the same master?
if (atom_V && mob_V && atom_V.master == mob_V.master)
return TRUE
return TRUE
return FALSE
@@ -710,7 +710,7 @@
invisibility = INVISIBILITY_ABSTRACT
/obj/screen/bloodsucker/proc/update_counter(value, valuecolor)
invisibility = 0
invisibility = 0
/obj/screen/bloodsucker/blood_counter
icon = 'icons/mob/actions/bloodsucker.dmi'
@@ -758,7 +758,7 @@
/obj/screen/bloodsucker/sunlight_counter/update_counter(value, valuecolor)
..()
maptext = "<div align='center' valign='bottom' style='position:relative; top:0px; left:6px'><font color='[valuecolor]'>[value]</font></div>"
maptext = "<div align='center' valign='bottom' style='position:relative; top:0px; left:6px'><font color='[valuecolor]'>[value]</font></div>"
/datum/antagonist/bloodsucker/proc/count_vassals(datum/mind/master)
var/datum/antagonist/bloodsucker/B = master.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
@@ -48,7 +48,7 @@
objectives += vassal_objective
objectives_given += vassal_objective
give_thrall_eyes()
owner.current.grant_language(/datum/language/vampiric)
owner.current.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_VASSAL)
// Add Antag HUD
update_vassal_icons_added(owner.current, "vassal")
. = ..()
@@ -81,7 +81,7 @@
qdel(O)
objectives_given = list()
remove_thrall_eyes()
owner.current.remove_language(/datum/language/vampiric)
owner.current.remove_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_VASSAL)
// Clear Antag HUD
update_vassal_icons_removed(owner.current)
@@ -78,6 +78,7 @@
create_initial_profile()
if(give_objectives)
forge_objectives()
owner.current.grant_all_languages(FALSE, FALSE, TRUE) //Grants omnitongue. We are able to transform our body after all.
remove_clownmut()
. = ..()
@@ -61,10 +61,10 @@
if(user.nutrition < NUTRITION_LEVEL_WELL_FED)
user.nutrition = min((user.nutrition + target.nutrition), NUTRITION_LEVEL_WELL_FED)
if(target.mind)//if the victim has got a mind
// Absorb a lizard, speak Draconic.
user.copy_known_languages_from(target)
// Absorb a lizard, speak Draconic.
user.copy_languages(target, LANGUAGE_ABSORB)
if(target.mind && user.mind)//if the victim and user have minds
target.mind.show_memory(user, 0) //I can read your mind, kekeke. Output all their notes.
//Some of target's recent speech, so the changeling can attempt to imitate them better.
@@ -13,5 +13,5 @@
//Recover from stuns.
/obj/effect/proc_holder/changeling/adrenaline/sting_action(mob/living/user)
user.do_adrenaline(0, FALSE, 70, 0, TRUE, list(/datum/reagent/medicine/epinephrine = 3, /datum/reagent/drug/methamphetamine/changeling = 10, /datum/reagent/medicine/mannitol = 10, /datum/reagent/medicine/regen_jelly = 10, /datum/reagent/medicine/changelingadrenaline = 5), "<span class='notice'>Energy rushes through us.</span>", 0, 0.75, 0)
return TRUE
user.do_adrenaline(0, FALSE, 70, 0, TRUE, list(/datum/reagent/medicine/epinephrine = 3, /datum/reagent/drug/methamphetamine/changeling = 10, /datum/reagent/medicine/changelingadrenaline = 5), "<span class='notice'>Energy rushes through us.</span>", 0, 0.75, 0)
return TRUE
@@ -27,12 +27,12 @@
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
if(E)
if(!active)
E.sight_flags |= SEE_MOBS | SEE_OBJS | SEE_TURFS //Add sight flags to the user's eyes
ADD_TRAIT(user, TRAIT_THERMAL_VISION, CHANGELING_TRAIT)
E.flash_protect = -1 //Adjust the user's eyes' flash protection
to_chat(user, "We adjust our eyes to sense prey through walls.")
active = TRUE //Defined in code/modules/spells/spell.dm
else
E.sight_flags ^= SEE_MOBS | SEE_OBJS | SEE_TURFS //Remove sight flags from the user's eyes
REMOVE_TRAIT(user, TRAIT_THERMAL_VISION, CHANGELING_TRAIT)
E.flash_protect = 2 //Adjust the user's eyes' flash protection
to_chat(user, "We adjust our eyes to protect them from bright lights.")
active = FALSE
@@ -47,10 +47,8 @@
/obj/effect/proc_holder/changeling/augmented_eyesight/on_refund(mob/user) //Get rid of X-ray vision and flash protection when the user refunds this ability
action.Remove(user)
REMOVE_TRAIT(user, TRAIT_THERMAL_VISION, CHANGELING_TRAIT)
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
if(E)
if (active)
E.sight_flags ^= SEE_MOBS | SEE_OBJS | SEE_TURFS
else
E.flash_protect = 0
user.update_sight()
E.flash_protect = initial(E.flash_protect)
user.update_sight()
@@ -2,7 +2,7 @@
name = "Biodegrade"
desc = "Dissolves restraints or other objects preventing free movement."
helptext = "This is obvious to nearby people, and can destroy standard restraints and closets. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
chemical_cost = 30 //High cost to prevent spam
chemical_cost = 15 //High cost to prevent spam
loudness = 1
dna_cost = 2
req_human = 1
@@ -3,7 +3,6 @@
desc = "Our skin pigmentation rapidly changes to suit our current environment."
helptext = "Allows us to become invisible after a few seconds of standing still. Can be toggled on and off."
dna_cost = 2
chemical_cost = 25
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_camouflage"
@@ -23,4 +22,4 @@
action.Remove(user)
if(user.has_dna())
var/mob/living/carbon/C = user
C.dna.remove_mutation(CHAMELEON)
C.dna.remove_mutation(CHAMELEON)
@@ -1,8 +1,9 @@
/obj/effect/proc_holder/changeling/fleshmend
name = "Fleshmend"
desc = "Our flesh rapidly regenerates, healing our burns, bruises, and shortness of breath. Functions while unconscious."
desc = "Our flesh rapidly regenerates, healing our burns, bruises, and shortness of breath. Functions while unconscious. This ability is loud, and might cause our blood to react violently to heat."
helptext = "If we are on fire, the healing effect will not function. Does not regrow limbs or restore lost blood."
chemical_cost = 20
loudness = 2
dna_cost = 2
req_stat = UNCONSCIOUS
action_icon = 'icons/mob/actions/actions_changeling.dmi'
@@ -137,7 +137,7 @@
name = "Arm Blade"
desc = "We reform one of our arms into a deadly blade."
helptext = "We may retract our armblade in the same manner as we form it. Cannot be used while in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 20
chemical_cost = 10
dna_cost = 2
loudness = 2
req_human = 1
@@ -410,7 +410,7 @@
desc = "We reform one of our arms into a hard shield."
helptext = "Organic tissue cannot resist damage forever; the shield will break after it is hit too much. The more genomes we absorb, the stronger it is. Cannot be used while in lesser form. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
chemical_cost = 20
dna_cost = 1
dna_cost = 2
loudness = 1
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
@@ -522,12 +522,12 @@
/obj/effect/proc_holder/changeling/suit/armor
name = "Chitinous Armor"
desc = "We turn our skin into tough chitin to protect us from damage."
helptext = "Upkeep of the armor requires a low expenditure of chemicals. The armor is strong against brute force, but does not provide much protection from lasers. Cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
helptext = "Upkeep of the armor requires a constant expenditure of chemicals, resulting in a reduced chemical generation. The armor is strong against brute force, but does not provide much protection from lasers. Cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 20
dna_cost = 1
loudness = 2
req_human = 1
recharge_slowdown = 0.25
recharge_slowdown = 0.5
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_armor"
action_background_icon_state = "bg_ling"
@@ -543,7 +543,7 @@
icon_state = "lingarmor"
item_flags = DROPDEL
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 20, "bomb" = 10, "bio" = 4, "rad" = 0, "fire" = 90, "acid" = 90)
armor = list("melee" = 70, "bullet" = 60, "laser" = 30, "energy" = 40, "bomb" = 10, "bio" = 4, "rad" = 0, "fire" = 50, "acid" = 90)
flags_inv = HIDEJUMPSUIT
cold_protection = 0
heat_protection = 0
@@ -559,7 +559,7 @@
desc = "A tough, hard covering of black chitin with transparent chitin in front."
icon_state = "lingarmorhelmet"
item_flags = DROPDEL
armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 20, "bomb" = 10, "bio" = 4, "rad" = 0, "fire" = 90, "acid" = 90)
armor = list("melee" = 70, "bullet" = 60, "laser" = 30, "energy" = 40, "bomb" = 10, "bio" = 4, "rad" = 0, "fire" = 50, "acid" = 90)
flags_inv = HIDEEARS|HIDEHAIR|HIDEEYES|HIDEFACIALHAIR|HIDEFACE
/obj/item/clothing/head/helmet/changeling/Initialize()
@@ -3,7 +3,7 @@
desc = "Our lungs and vocal cords shift, allowing us to briefly emit a noise that deafens and confuses the weak-minded."
helptext = "Emits a high-frequency sound that confuses and deafens humans, blows out nearby lights and overloads cyborg sensors. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
chemical_cost = 20
dna_cost = 1
dna_cost = 2
loudness = 1
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
@@ -37,7 +37,7 @@
desc = "We shift our vocal cords to release a high-frequency sound that overloads nearby electronics."
helptext = "Emits a high-frequency sound that overloads nearby electronics. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
chemical_cost = 20
dna_cost = 1
dna_cost = 2
loudness = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_dissonant"
@@ -5,7 +5,6 @@
name = "Strained Muscles"
desc = "We evolve the ability to reduce the acid buildup in our muscles, allowing us to move much faster."
helptext = "The strain will make us tired, and we will rapidly become fatigued. Standard weight restrictions, like hardsuits, still apply. Cannot be used in lesser form."
chemical_cost = 15
dna_cost = 1
req_human = 1
var/stacks = 0 //Increments every 5 seconds; damage increases over time
@@ -15,16 +14,13 @@
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/strained_muscles/sting_action(mob/living/carbon/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
active = !active
if(active)
to_chat(user, "<span class='notice'>Our muscles tense and strengthen.</span>")
changeling.chem_recharge_slowdown += 0.5
else
user.remove_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
to_chat(user, "<span class='notice'>Our muscles relax.</span>")
changeling.chem_recharge_slowdown -= 0.5
if(stacks >= 20)
if(stacks >= 10)
to_chat(user, "<span class='danger'>We collapse in exhaustion.</span>")
user.DefaultCombatKnockdown(60)
user.emote("gasp")
@@ -34,7 +30,6 @@
return TRUE
/obj/effect/proc_holder/changeling/strained_muscles/proc/muscle_loop(mob/living/carbon/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
while(active)
user.add_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
if(user.stat != CONSCIOUS || user.staminaloss >= 90)
@@ -42,18 +37,17 @@
to_chat(user, "<span class='notice'>Our muscles relax without the energy to strengthen them.</span>")
user.DefaultCombatKnockdown(40)
user.remove_movespeed_modifier(/datum/movespeed_modifier/strained_muscles)
changeling.chem_recharge_slowdown -= 0.5
break
stacks++
//user.take_bodypart_damage(stacks * 0.03, 0)
user.adjustStaminaLoss(stacks*1.3) //At first the changeling may regenerate stamina fast enough to nullify fatigue, but it will stack
user.adjustStaminaLoss(stacks*1.5) //At first the changeling may regenerate stamina fast enough to nullify fatigue, but it will stack
if(stacks == 10) //Warning message that the stacks are getting too high
if(stacks == 5) //Warning message that the stacks are getting too high
to_chat(user, "<span class='warning'>Our legs are really starting to hurt...</span>")
sleep(40)
while(!active && stacks) //Damage stacks decrease fairly rapidly while not in sanic mode
while(!active && stacks) //Damage stacks decrease slowly while not in sanic mode
stacks--
sleep(20)
sleep(100)
@@ -31,6 +31,7 @@
laws = new /datum/ai_laws/ratvar()
braintype = picked_name
GLOB.all_clockwork_objects += src
brainmob.add_blocked_language(subtypesof(/datum/language) - /datum/language/ratvar, LANGUAGE_CLOCKIE)
/obj/item/mmi/posibrain/soul_vessel/Destroy()
GLOB.all_clockwork_objects -= src
@@ -91,7 +91,7 @@
current = mob_override
GLOB.all_clockwork_mobs += current
current.faction |= "ratvar"
current.grant_language(/datum/language/ratvar)
current.grant_language(/datum/language/ratvar, TRUE, TRUE, LANGUAGE_CLOCKIE)
current.update_action_buttons_icon() //because a few clockcult things are action buttons and we may be wearing/holding them for whatever reason, we need to update buttons
if(issilicon(current))
var/mob/living/silicon/S = current
@@ -102,6 +102,7 @@
R.module.rebuild_modules()
else if(isAI(S))
var/mob/living/silicon/ai/A = S
A.add_blocked_language(subtypesof(/datum/language) - /datum/language/ratvar, LANGUAGE_CLOCKIE)
A.can_be_carded = FALSE
A.requires_power = POWER_REQ_CLOCKCULT
var/list/AI_frame = list(mutable_appearance('icons/mob/clockwork_mobs.dmi', "aiframe")) //make the AI's cool frame
@@ -142,7 +143,7 @@
current = mob_override
GLOB.all_clockwork_mobs -= current
current.faction -= "ratvar"
current.remove_language(/datum/language/ratvar)
current.remove_language(/datum/language/ratvar, TRUE, TRUE, LANGUAGE_CLOCKIE)
current.clear_alert("clockinfo")
for(var/datum/action/innate/clockwork_armaments/C in owner.current.actions) //Removes any bound clockwork armor
qdel(C)
@@ -152,6 +153,7 @@
var/mob/living/silicon/S = current
if(isAI(S))
var/mob/living/silicon/ai/A = S
A.remove_blocked_language(subtypesof(/datum/language) - /datum/language/ratvar, LANGUAGE_CLOCKIE)
A.can_be_carded = initial(A.can_be_carded)
A.requires_power = initial(A.requires_power)
A.cut_overlays()
+2 -2
View File
@@ -116,7 +116,7 @@
if(mob_override)
current = mob_override
current.faction |= "cult"
current.grant_language(/datum/language/narsie)
current.grant_language(/datum/language/narsie, TRUE, TRUE, LANGUAGE_CULTIST)
if(!cult_team?.cult_master)
vote.Grant(current)
communion.Grant(current)
@@ -134,7 +134,7 @@
if(mob_override)
current = mob_override
current.faction -= "cult"
current.remove_language(/datum/language/narsie)
current.remove_language(/datum/language/narsie, TRUE, TRUE, LANGUAGE_CULTIST)
vote.Remove(current)
communion.Remove(current)
magic.Remove(current)

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