Merge branch 'master' into 91-files-changed-fml
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
/// 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)
|
||||
@@ -387,6 +387,9 @@ SUBSYSTEM_DEF(ticker)
|
||||
for(var/mob/dead/new_player/N in GLOB.player_list)
|
||||
var/mob/living/carbon/human/player = N.new_character
|
||||
if(istype(player) && player.mind && player.mind.assigned_role)
|
||||
var/datum/job/J = SSjob.GetJob(player.mind.assigned_role)
|
||||
if(J)
|
||||
J.standard_assign_skills(player.mind)
|
||||
if(player.mind.assigned_role == "Captain")
|
||||
captainless=0
|
||||
if(player.mind.assigned_role != player.mind.special_role)
|
||||
|
||||
@@ -63,7 +63,11 @@
|
||||
var/force_escaped = FALSE // Set by Into The Sunset command of the shuttle manipulator
|
||||
var/list/learned_recipes //List of learned recipe TYPES.
|
||||
|
||||
/// Our skill holder.
|
||||
var/datum/skill_holder/skill_holder
|
||||
|
||||
/datum/mind/New(var/key)
|
||||
skill_holder = new
|
||||
src.key = key
|
||||
soulOwner = src
|
||||
martial_art = default_martial_art
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// yeah yeah verbs suck whatever I suck at this fix this someone please - kevinz000
|
||||
|
||||
/mob/verb/check_skills()
|
||||
set name = "Check Skills"
|
||||
set category = "IC"
|
||||
set desc = "Check your skills (if you have any..)"
|
||||
|
||||
if(!mind)
|
||||
to_chat(usr, "<span class='warning'>How do you check the skills of [(usr == src)? "yourself when you are" : "something"] without a mind?</span>")
|
||||
return
|
||||
if(!mind.skill_holder)
|
||||
to_chat(usr, "<span class='warning'>How do you check the skills of [(usr == src)? "yourself when you are" : "something"] without the capability for skills? (PROBABLY A BUG, PRESS F1.)</span>")
|
||||
return
|
||||
var/datum/browser/B = new(usr, "skilldisplay_[REF(src)]", "Skills of [src]")
|
||||
B.set_content(mind.skill_holder.html_readout())
|
||||
B.open()
|
||||
@@ -0,0 +1,95 @@
|
||||
GLOBAL_LIST_INIT(skill_datums, init_skill_datums())
|
||||
|
||||
/proc/init_skill_datums()
|
||||
. = list()
|
||||
for(var/path in subtypesof(/datum/skill))
|
||||
var/datum/skill/S = path
|
||||
if(initial(S.abstract_type) == path)
|
||||
continue
|
||||
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)
|
||||
// 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)
|
||||
// don't check, if we runtime let it happen.
|
||||
return S.is_value_greater(existing, new_value)
|
||||
|
||||
/**
|
||||
* Skill datums
|
||||
*/
|
||||
/datum/skill
|
||||
/// Our name
|
||||
var/name
|
||||
/// Our description
|
||||
var/desc
|
||||
/// Our progression type
|
||||
var/progression_type
|
||||
/// Abstract type
|
||||
var/abstract_type = /datum/skill
|
||||
|
||||
/**
|
||||
* Ensures what someone's setting as a value for this skill is valid.
|
||||
*/
|
||||
/datum/skill/proc/sanitize_value(new_value)
|
||||
return new_value
|
||||
|
||||
/**
|
||||
* Checks if a value is greater
|
||||
*/
|
||||
/datum/skill/proc/is_value_greater(existing, new_value)
|
||||
if(!existing)
|
||||
return TRUE
|
||||
return new_value > existing
|
||||
|
||||
/**
|
||||
* Standard value "render"
|
||||
*/
|
||||
/datum/skill/proc/standard_render_value(value)
|
||||
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.
|
||||
// Aka: So people don't forget to change checks if they change a skill's progression type.
|
||||
|
||||
/datum/skill/binary
|
||||
abstract_type = /datum/skill/binary
|
||||
progression_type = SKILL_PROGRESSION_BINARY
|
||||
|
||||
/datum/skill/binary/sanitize_value(new_value)
|
||||
return new_value? TRUE : FALSE
|
||||
|
||||
/datum/skill/binary/standard_render_value(value)
|
||||
return value? "Yes" : "No"
|
||||
|
||||
/datum/skill/numerical
|
||||
abstract_type = /datum/skill/numerical
|
||||
progression_type = SKILL_PROGRESSION_NUMERICAL
|
||||
/// Max value of this skill
|
||||
var/max_value = 100
|
||||
/// Min value of this skill
|
||||
var/min_value = 0
|
||||
/// Display as a percent in standard_render_value?
|
||||
var/display_as_percent = FALSE
|
||||
|
||||
/datum/skill/numerical/sanitize_value(new_value)
|
||||
return clamp(new_value, min_value, max_value)
|
||||
|
||||
/datum/skill/numerical/standard_render_value(value)
|
||||
return display_as_percent? "[round(value/max_value/100, 0.01)]%" : "[value] / [max_value]"
|
||||
|
||||
/datum/skill/enum
|
||||
abstract_type = /datum/skill/enum
|
||||
progression_type = SKILL_PROGRESSION_ENUM
|
||||
/// Valid values for the skill
|
||||
var/list/valid_values = list()
|
||||
|
||||
/datum/skill/enum/sanitize_value(new_value)
|
||||
if(new_value in valid_values)
|
||||
return new_value
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Skill holder datums
|
||||
*/
|
||||
/datum/skill_holder
|
||||
/// 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
|
||||
|
||||
/**
|
||||
* Grabs the value of a skill.
|
||||
*/
|
||||
/datum/skill_holder/proc/get_skill_value(skill)
|
||||
if(!ispath(skill))
|
||||
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 skills[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
|
||||
var/affinity = skill_affinities[skill]
|
||||
if(isnull(affinity))
|
||||
return 1
|
||||
return affinity
|
||||
|
||||
/**
|
||||
* 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)
|
||||
if(!isnull(value))
|
||||
skills[skill] = value
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Boosts a skill to a value if not aobve
|
||||
*/
|
||||
/datum/skill_holder/proc/boost_skill_value_to(skill, value)
|
||||
var/current = get_skill_value(skill)
|
||||
if(!is_skill_value_greater(skill, current, value))
|
||||
return FALSE
|
||||
set_skill_value(skill, value)
|
||||
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!")
|
||||
var/current = get_skill_value(skill)
|
||||
var/affinity = get_skill_affinity(skill)
|
||||
boost_skill_value_to(skill, current + (value * affinity))
|
||||
|
||||
/**
|
||||
* Generates a HTML readout of our skills.
|
||||
* Port to tgui-next when?
|
||||
*/
|
||||
/datum/skill_holder/proc/html_readout()
|
||||
var/list/out = list("<center><h1>Skills</h1></center><hr>")
|
||||
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 += "</table>"
|
||||
return out.Join("")
|
||||
@@ -0,0 +1,3 @@
|
||||
/datum/skill/numerical/surgery
|
||||
name = "Surgery"
|
||||
desc = "How proficient you are at doing surgery."
|
||||
@@ -77,6 +77,7 @@
|
||||
new /obj/item/clothing/mask/gas(src)
|
||||
new /obj/item/clothing/glasses/meson/engine(src)
|
||||
new /obj/item/storage/box/emptysandbags(src)
|
||||
new /obj/item/cartridge/engineering(src)
|
||||
|
||||
/obj/structure/closet/secure_closet/atmospherics
|
||||
name = "\proper atmospheric technician's locker"
|
||||
@@ -97,6 +98,7 @@
|
||||
new /obj/item/clothing/head/hardhat/atmos(src)
|
||||
new /obj/item/clothing/glasses/meson/engine/tray(src)
|
||||
new /obj/item/extinguisher/advanced(src)
|
||||
new /obj/item/cartridge/atmos(src)
|
||||
|
||||
/*
|
||||
* Empty lockers
|
||||
|
||||
@@ -66,6 +66,11 @@
|
||||
// How much threat this job is worth in dynamic. Is subtracted if the player's not an antag, added if they are.
|
||||
var/threat = 0
|
||||
|
||||
/// Starting skill levels.
|
||||
var/list/starting_skills
|
||||
/// Skill affinities to set
|
||||
var/list/skill_affinities
|
||||
|
||||
//Only override this proc
|
||||
//H is usually a human unless an /equip override transformed it
|
||||
/datum/job/proc/after_spawn(mob/living/H, mob/M, latejoin = FALSE)
|
||||
@@ -142,7 +147,6 @@
|
||||
return TRUE //Available in 0 days = available right now = player is old enough to play.
|
||||
return FALSE
|
||||
|
||||
|
||||
/datum/job/proc/available_in_days(client/C)
|
||||
if(!C)
|
||||
return 0
|
||||
@@ -166,6 +170,17 @@
|
||||
/datum/job/proc/radio_help_message(mob/M)
|
||||
to_chat(M, "<b>Prefix your message with :h to speak on your department's radio. To see other prefixes, look closely at your headset.</b>")
|
||||
|
||||
/datum/job/proc/standard_assign_skills(datum/mind/M)
|
||||
if(!starting_skills)
|
||||
return
|
||||
for(var/skill in starting_skills)
|
||||
M.skill_holder.boost_skill_value_to(skill, starting_skills[skill])
|
||||
// do wipe affinities though
|
||||
M.skill_holder.skill_affinities = list()
|
||||
for(var/skill in skill_affinities)
|
||||
M.skill_holder.skill_affinities[skill] = skill_affinities[skill]
|
||||
UNSETEMPTY(M.skill_holder.skill_affinities) //if we didn't set any.
|
||||
|
||||
/datum/outfit/job
|
||||
name = "Standard Gear"
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
display_order = JOB_DISPLAY_ORDER_CHEMIST
|
||||
threat = 1.5
|
||||
|
||||
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
|
||||
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
|
||||
|
||||
/datum/outfit/job/chemist
|
||||
name = "Chemist"
|
||||
jobtype = /datum/job/chemist
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
blacklisted_quirks = list(/datum/quirk/mute, /datum/quirk/brainproblems, /datum/quirk/insanity)
|
||||
threat = 2
|
||||
|
||||
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
|
||||
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
|
||||
|
||||
/datum/outfit/job/cmo
|
||||
name = "Chief Medical Officer"
|
||||
jobtype = /datum/job/cmo
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
display_order = JOB_DISPLAY_ORDER_GENETICIST
|
||||
threat = 1.5
|
||||
|
||||
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
|
||||
|
||||
/datum/outfit/job/geneticist
|
||||
name = "Geneticist"
|
||||
jobtype = /datum/job/geneticist
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
display_order = JOB_DISPLAY_ORDER_MEDICAL_DOCTOR
|
||||
threat = 0.5
|
||||
|
||||
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
|
||||
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
|
||||
|
||||
/datum/outfit/job/doctor
|
||||
name = "Medical Doctor"
|
||||
jobtype = /datum/job/doctor
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
|
||||
threat = 0.5
|
||||
|
||||
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
|
||||
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
|
||||
|
||||
/datum/outfit/job/paramedic
|
||||
name = "Paramedic"
|
||||
jobtype = /datum/job/paramedic
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
|
||||
threat = 1.5
|
||||
|
||||
starting_skills = list(/datum/skill/numerical/surgery = STARTING_SKILL_SURGERY_MEDICAL)
|
||||
skill_affinities = list(/datum/skill/numerical/surgery = STARTING_SKILL_AFFINITY_SURGERY_MEDICAL)
|
||||
|
||||
/datum/outfit/job/virologist
|
||||
name = "Virologist"
|
||||
jobtype = /datum/job/virologist
|
||||
|
||||
@@ -392,6 +392,8 @@
|
||||
|
||||
character.update_parallax_teleport()
|
||||
|
||||
job.standard_assign_skills(character.mind)
|
||||
|
||||
SSticker.minds += character.mind
|
||||
|
||||
var/mob/living/carbon/human/humanc
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
adjustCloneLoss(damage_amount, forced = forced)
|
||||
if(STAMINA)
|
||||
if(BP)
|
||||
if(damage > 0 ? BP.receive_damage(0, 0, damage_amount) : BP.heal_damage(0, 0, abs(damage_amount)))
|
||||
if(damage > 0 ? BP.receive_damage(0, 0, damage_amount) : BP.heal_damage(0, 0, abs(damage_amount), FALSE, FALSE))
|
||||
update_damage_overlays()
|
||||
else
|
||||
adjustStaminaLoss(damage_amount, forced = forced)
|
||||
|
||||
@@ -91,4 +91,9 @@
|
||||
if((C.dna.features["spines"] != "None" ) && (C.dna.features["tail_lizard"] == "None")) //tbh, it's kinda ugly for them not to have a tail yet have floating spines
|
||||
C.dna.features["tail_lizard"] = "Smooth"
|
||||
C.update_body()
|
||||
if(C.dna.features["legs"] != "digitigrade")
|
||||
C.dna.features["legs"] = "digitigrade"
|
||||
for(var/obj/item/bodypart/leggie in C.bodyparts)
|
||||
if(leggie.body_zone == BODY_ZONE_L_LEG || leggie.body_zone == BODY_ZONE_R_LEG)
|
||||
leggie.update_limb(FALSE, C)
|
||||
return ..()
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
path_image_color = "#993299"
|
||||
weather_immunities = list("lava","ash")
|
||||
|
||||
var/clean_time = 50 //How long do we take to clean?
|
||||
var/broom = FALSE //Do we have an speed buff from a broom?
|
||||
var/adv_mop = FALSE //Do we have a cleaning buff from a better mop?
|
||||
|
||||
|
||||
var/blood = 1
|
||||
var/trash = 0
|
||||
var/pests = 0
|
||||
@@ -75,6 +80,26 @@
|
||||
to_chat(user, "<span class='warning'>Please close the access panel before locking it.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>\The [src] doesn't seem to respect your authority.</span>")
|
||||
|
||||
if(istype(W, /obj/item/mop/advanced))
|
||||
if(bot_core.allowed(user) && open && adv_mop == TRUE)
|
||||
to_chat(user, "<span class='notice'>You replace \the [src] old mop with a new better one!</span>")
|
||||
adv_mop = TRUE
|
||||
clean_time = 20 //2.5 the speed!
|
||||
window_name = "Automatic Station Cleaner v2.1 BETA" //New!
|
||||
qdel(W)
|
||||
else
|
||||
to_chat(user, "<span class='notice'>\the [src] already has this mop!</span>")
|
||||
|
||||
if(istype(W, /obj/item/twohanded/broom))
|
||||
if(bot_core.allowed(user) && open && broom == TRUE)
|
||||
to_chat(user, "<span class='notice'>You add to \the [src] a broom speeding it up!</span>")
|
||||
broom = TRUE
|
||||
base_speed = 1 //2x faster!
|
||||
qdel(W)
|
||||
else
|
||||
to_chat(user, "<span class='notice'>\the [src] already has a broom!</span>")
|
||||
|
||||
else
|
||||
return ..()
|
||||
|
||||
@@ -221,7 +246,7 @@
|
||||
icon_state = "cleanbot-c"
|
||||
visible_message("<span class='notice'>[src] begins to clean up [A].</span>")
|
||||
mode = BOT_CLEANING
|
||||
spawn(50)
|
||||
spawn(clean_time)
|
||||
if(mode == BOT_CLEANING)
|
||||
if(A && isturf(A.loc))
|
||||
var/atom/movable/AM = A
|
||||
|
||||
@@ -315,6 +315,8 @@
|
||||
objs += O
|
||||
var/obj/O = safepick(objs)
|
||||
if(O)
|
||||
if(length(O.buckled_mobs))
|
||||
return pick(O.buckled_mobs)
|
||||
return O
|
||||
//Nothing else is here that we can hit, hit the turf if we haven't.
|
||||
if(!(T in permutated) && can_hit_target(T, permutated, T == original, TRUE))
|
||||
|
||||
@@ -58,7 +58,10 @@
|
||||
return FALSE
|
||||
if(tool)
|
||||
speed_mod = tool.toolspeed
|
||||
if(do_after(user, time * speed_mod, target = target))
|
||||
var/skill_mod = 1
|
||||
if(user?.mind?.skill_holder)
|
||||
skill_mod = SURGERY_SKILL_SPEEDUP_NUMERICAL_SCALE(user.mind.skill_holder.get_skill_value(/datum/skill/numerical/surgery))
|
||||
if(do_after(user, time * speed_mod * skill_mod, target = target))
|
||||
var/prob_chance = 100
|
||||
if(implement_type) //this means it isn't a require hand or any item step.
|
||||
prob_chance = implements[implement_type]
|
||||
@@ -66,6 +69,7 @@
|
||||
|
||||
if((prob(prob_chance) || (iscyborg(user) && !silicons_obey_prob)) && chem_check(target) && !try_to_fail)
|
||||
if(success(user, target, target_zone, tool, surgery))
|
||||
user?.mind?.skill_holder?.auto_gain_experience(/datum/skill/numerical/surgery, SKILL_GAIN_SURGERY_PER_STEP)
|
||||
advance = TRUE
|
||||
else
|
||||
if(failure(user, target, target_zone, tool, surgery))
|
||||
|
||||
Reference in New Issue
Block a user