Merge remote-tracking branch 'upstream/master' into snaxi2
This commit is contained in:
@@ -298,6 +298,8 @@ GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0))
|
||||
|
||||
#define ARCHIVE_TEMPERATURE(gas) gas.temperature_archived = gas.temperature
|
||||
|
||||
#define ARCHIVE(gas) gas.temperature_archived = gas.temperature; gas.gas_archive = gas.gases.Copy();
|
||||
|
||||
GLOBAL_LIST_INIT(pipe_paint_colors, list(
|
||||
"amethyst" = rgb(130,43,255), //supplymain
|
||||
"blue" = rgb(0,0,255),
|
||||
|
||||
@@ -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
|
||||
@@ -263,7 +267,17 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
|
||||
#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!
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -131,7 +131,7 @@
|
||||
|
||||
/mob/living/attacked_by(obj/item/I, mob/living/user)
|
||||
var/totitemdamage = pre_attacked_by(I, user)
|
||||
if((user != src) && run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user) & BLOCK_SUCCESS)
|
||||
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, totitemdamage)
|
||||
@@ -163,7 +163,7 @@
|
||||
if(!user.mind || !I.used_skills)
|
||||
return
|
||||
if(.)
|
||||
. = user.mind.skill_holder.item_action_skills_mod(., I.skill_difficulty, SKILL_ATTACK_MOB, bad_flag)
|
||||
. = 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
|
||||
@@ -216,7 +216,7 @@
|
||||
. *= STAM_COST_NO_COMBAT_MULT
|
||||
bad_flag |= SKILL_COMBAT_MODE
|
||||
if(used_skills && user.mind)
|
||||
. = user.mind.skill_holder.item_action_skills_mod(., skill_difficulty, flags, bad_flag, FALSE)
|
||||
. = 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(force_override)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -91,7 +91,6 @@
|
||||
|
||||
trauma = _trauma
|
||||
owner = trauma.owner
|
||||
copy_known_languages_from(owner, TRUE)
|
||||
|
||||
setup_friend()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#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.
|
||||
@@ -183,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)
|
||||
@@ -211,12 +212,12 @@
|
||||
return
|
||||
//var/mob/living/master = parent
|
||||
//master.crit_threshold = (master.crit_threshold - insanity_effect) + newval
|
||||
insanity_effect = newval
|
||||
if(insanity_effect)
|
||||
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
|
||||
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 = SANITY_INSANE, maximum = SANITY_AMAZING)
|
||||
setSanity(sanity + amount, minimum, maximum)
|
||||
@@ -333,6 +334,7 @@
|
||||
return
|
||||
return_value[1] *= SKILL_AFFINITY_MOOD_BONUS
|
||||
|
||||
#undef SLIGHT_INSANITY_PEN
|
||||
#undef MINOR_INSANITY_PEN
|
||||
#undef MAJOR_INSANITY_PEN
|
||||
#undef MOOD_INSANITY_MALUS
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
..()
|
||||
|
||||
@@ -5,12 +5,16 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
|
||||
id_arg_index = 3
|
||||
var/flavor_name = "Flavor Text"
|
||||
var/list/texts_by_atom = list()
|
||||
var/addendum = "This can also be used for OOC notes and preferences!"
|
||||
var/addendum = ""
|
||||
var/always_show = FALSE
|
||||
var/max_len = MAX_FLAVOR_LEN
|
||||
var/can_edit = TRUE
|
||||
/// For preference/DNA saving/loading. Null to prevent. Prefs are only loaded from obviously if it exists in preferences.features.
|
||||
var/save_key
|
||||
/// Do not attempt to render a preview on examine. If this is on, it will display as \[flavor_name\]
|
||||
var/examine_no_preview = FALSE
|
||||
|
||||
/datum/element/flavor_text/Attach(datum/target, text = "", _name = "Flavor Text", _addendum, _max_len = MAX_FLAVOR_LEN, _always_show = FALSE, _edit = TRUE)
|
||||
/datum/element/flavor_text/Attach(datum/target, text = "", _name = "Flavor Text", _addendum, _max_len = MAX_FLAVOR_LEN, _always_show = FALSE, _edit = TRUE, _save_key, _examine_no_preview = FALSE)
|
||||
. = ..()
|
||||
|
||||
if(. == ELEMENT_INCOMPATIBLE || !isatom(target)) //no reason why this shouldn't work on atoms too.
|
||||
@@ -25,6 +29,8 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
|
||||
addendum = _addendum
|
||||
always_show = _always_show
|
||||
can_edit = _edit
|
||||
save_key = _save_key
|
||||
examine_no_preview = _examine_no_preview
|
||||
|
||||
RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/show_flavor)
|
||||
|
||||
@@ -33,9 +39,12 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
|
||||
LAZYOR(GLOB.mobs_with_editable_flavor_text[M], src)
|
||||
M.verbs |= /mob/proc/manage_flavor_tests
|
||||
|
||||
if(save_key && ishuman(target))
|
||||
RegisterSignal(target, COMSIG_HUMAN_PREFS_COPIED_TO, .proc/update_prefs_flavor_text)
|
||||
|
||||
/datum/element/flavor_text/Detach(atom/A)
|
||||
. = ..()
|
||||
UnregisterSignal(A, COMSIG_PARENT_EXAMINE)
|
||||
UnregisterSignal(A, list(COMSIG_PARENT_EXAMINE, COMSIG_HUMAN_PREFS_COPIED_TO))
|
||||
texts_by_atom -= A
|
||||
if(can_edit && ismob(A))
|
||||
var/mob/M = A
|
||||
@@ -58,6 +67,9 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
|
||||
var/text = texts_by_atom[target]
|
||||
if(!text)
|
||||
return
|
||||
if(examine_no_preview)
|
||||
examine_list += "<span class='notice'><a href='?src=[REF(src)];show_flavor=[REF(target)]'>\[[flavor_name]\]</a></span>"
|
||||
return
|
||||
var/msg = replacetext(text, "\n", " ")
|
||||
if(length_char(msg) <= 40)
|
||||
examine_list += "<span class='notice'>[msg]</span>"
|
||||
@@ -112,13 +124,17 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/element/flavor_text/proc/update_prefs_flavor_text(mob/living/carbon/human/H, datum/preferences/P, icon_updates = TRUE, roundstart_checks = TRUE)
|
||||
if(P.features.Find(save_key))
|
||||
texts_by_atom[H] = P.features[save_key]
|
||||
|
||||
//subtypes with additional hooks for DNA and preferences.
|
||||
/datum/element/flavor_text/carbon
|
||||
//list of antagonists etcetera that should have nothing to do with people's snowflakes.
|
||||
var/static/list/i_dont_even_know_who_you_are = typecacheof(list(/datum/antagonist/abductor, /datum/antagonist/ert,
|
||||
/datum/antagonist/nukeop, /datum/antagonist/wizard))
|
||||
|
||||
/datum/element/flavor_text/carbon/Attach(datum/target, text = "", _name = "Flavor Text", _addendum, _max_len = MAX_FLAVOR_LEN, _always_show = FALSE, _edit = TRUE)
|
||||
/datum/element/flavor_text/carbon/Attach(datum/target, text = "", _name = "Flavor Text", _addendum, _max_len = MAX_FLAVOR_LEN, _always_show = FALSE, _edit = TRUE, _save_key = "flavor_text", _examine_no_preview = FALSE)
|
||||
if(!iscarbon(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
. = ..()
|
||||
@@ -127,7 +143,6 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
|
||||
RegisterSignal(target, COMSIG_CARBON_IDENTITY_TRANSFERRED_TO, .proc/update_dna_flavor_text)
|
||||
RegisterSignal(target, COMSIG_MOB_ANTAG_ON_GAIN, .proc/on_antag_gain)
|
||||
if(ishuman(target))
|
||||
RegisterSignal(target, COMSIG_HUMAN_PREFS_COPIED_TO, .proc/update_prefs_flavor_text)
|
||||
RegisterSignal(target, COMSIG_HUMAN_HARDSET_DNA, .proc/update_dna_flavor_text)
|
||||
RegisterSignal(target, COMSIG_HUMAN_ON_RANDOMIZE, .proc/unset_flavor)
|
||||
|
||||
@@ -136,15 +151,12 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
|
||||
UnregisterSignal(C, list(COMSIG_CARBON_IDENTITY_TRANSFERRED_TO, COMSIG_MOB_ANTAG_ON_GAIN, COMSIG_HUMAN_PREFS_COPIED_TO, COMSIG_HUMAN_HARDSET_DNA, COMSIG_HUMAN_ON_RANDOMIZE))
|
||||
|
||||
/datum/element/flavor_text/carbon/proc/update_dna_flavor_text(mob/living/carbon/C)
|
||||
texts_by_atom[C] = C.dna.features["flavor_text"]
|
||||
|
||||
/datum/element/flavor_text/carbon/proc/update_prefs_flavor_text(mob/living/carbon/human/H, datum/preferences/P, icon_updates = TRUE, roundstart_checks = TRUE)
|
||||
texts_by_atom[H] = P.features["flavor_text"]
|
||||
texts_by_atom[C] = C.dna.features[save_key]
|
||||
|
||||
/datum/element/flavor_text/carbon/set_flavor(mob/living/carbon/user)
|
||||
. = ..()
|
||||
if(. && user.dna)
|
||||
user.dna.features["flavor_text"] = texts_by_atom[user]
|
||||
user.dna.features[save_key] = texts_by_atom[user]
|
||||
|
||||
/datum/element/flavor_text/carbon/proc/unset_flavor(mob/living/carbon/user)
|
||||
texts_by_atom[user] = ""
|
||||
@@ -153,4 +165,4 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
|
||||
if(is_type_in_typecache(antag, i_dont_even_know_who_you_are))
|
||||
texts_by_atom[user] = ""
|
||||
if(user.dna)
|
||||
user.dna.features["flavor_text"] = ""
|
||||
user.dna.features[save_key] = ""
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
|
||||
|
||||
/datum/material/uranium/on_applied(atom/source, amount, material_flags)
|
||||
. = ..()
|
||||
source.AddComponent(/datum/component/radioactive, amount / 20, source, 0) //half-life of 0 because we keep on going.
|
||||
source.AddComponent(/datum/component/radioactive, amount / 60, source, 0) //half-life of 0 because we keep on going.
|
||||
|
||||
/datum/material/uranium/on_removed(atom/source, material_flags)
|
||||
. = ..()
|
||||
|
||||
+2
-5
@@ -85,8 +85,7 @@
|
||||
|
||||
/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
|
||||
|
||||
@@ -101,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
|
||||
@@ -131,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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
+79
-73
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
if(log)
|
||||
message_admins("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] ")
|
||||
log_game("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] ")
|
||||
log_game("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] ")
|
||||
|
||||
if(heavy_range >= 1)
|
||||
new /obj/effect/temp_visual/emp/pulse(epicenter)
|
||||
@@ -29,4 +29,4 @@
|
||||
T.emp_act(EMP_LIGHT)
|
||||
else if(distance <= light_range)
|
||||
T.emp_act(EMP_LIGHT)
|
||||
return 1
|
||||
return 1
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
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))
|
||||
|
||||
@@ -902,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
|
||||
@@ -983,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
|
||||
@@ -992,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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -176,3 +176,4 @@ GLOBAL_LIST_INIT(ore_probability, list(/obj/item/stack/ore/uranium = 50,
|
||||
new /obj/item/book/granter/spell/sacredflame(loc)
|
||||
if(28)
|
||||
new /mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/doom(loc)
|
||||
qdel(src)
|
||||
|
||||
@@ -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()
|
||||
. = ..()
|
||||
|
||||
@@ -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(.)
|
||||
|
||||
@@ -152,8 +152,8 @@
|
||||
pod_moving = 0
|
||||
if(!QDELETED(pod))
|
||||
var/datum/gas_mixture/floor_mixture = loc.return_air()
|
||||
ARCHIVE_TEMPERATURE(floor_mixture)
|
||||
ARCHIVE_TEMPERATURE(pod.air_contents)
|
||||
ARCHIVE(floor_mixture)
|
||||
ARCHIVE(pod.air_contents)
|
||||
pod.air_contents.share(floor_mixture, 1) //mix the pod's gas mixture with the tile it's on
|
||||
air_update_turf()
|
||||
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
@@ -203,8 +203,7 @@
|
||||
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/wolf = 50, /obj/structure/spawner/ice_moon = 3, \
|
||||
/mob/living/simple_animal/hostile/asteroid/polarbear = 30, /obj/structure/spawner/ice_moon/polarbear = 3, \
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/snow = 50, /mob/living/simple_animal/hostile/asteroid/goldgrub = 10)
|
||||
|
||||
flora_spawn_list = list(/obj/structure/flora/tree/pine = 2, /obj/structure/flora/grass/both = 12)
|
||||
flora_spawn_list = list(/obj/structure/flora/tree/pine = 2, /obj/structure/flora/grass/both = 12, /obj/structure/flora/rock/icy = 6, /obj/structure/flora/rock/pile/icy = 6)
|
||||
data_having_type = /turf/open/floor/plating/asteroid/airless/cave/snow/has_data
|
||||
turf_type = /turf/open/floor/plating/asteroid/snow/icemoon
|
||||
choose_turf_type = list(/turf/open/floor/plating/asteroid/snow/icemoon = 19, /turf/open/floor/plating/ice/icemoon = 1)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -527,7 +527,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
|
||||
|
||||
/datum/antagonist/devil/apply_innate_effects(mob/living/mob_override)
|
||||
give_appropriate_spells()
|
||||
owner.current.grant_all_languages(TRUE)
|
||||
owner.current.grant_all_languages(TRUE, TRUE, TRUE, LANGUAGE_DEVIL)
|
||||
update_hud()
|
||||
.=..()
|
||||
|
||||
@@ -536,6 +536,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
|
||||
var/obj/effect/proc_holder/spell/S = X
|
||||
if(is_type_in_typecache(S, devil_spells))
|
||||
owner.RemoveSpell(S)
|
||||
owner.current.remove_all_languages(LANGUAGE_DEVIL)
|
||||
.=..()
|
||||
|
||||
/datum/antagonist/devil/proc/printdevilinfo()
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
/mob/living/carbon/true_devil/Initialize()
|
||||
create_bodyparts() //initialize bodyparts
|
||||
create_internal_organs()
|
||||
grant_all_languages(omnitongue=TRUE)
|
||||
grant_all_languages()
|
||||
..()
|
||||
|
||||
/mob/living/carbon/true_devil/create_internal_organs()
|
||||
|
||||
@@ -65,5 +65,5 @@
|
||||
/datum/traitor_class/ai/finalize_traitor(datum/antagonist/traitor/T)
|
||||
T.add_law_zero()
|
||||
T.owner.current.playsound_local(get_turf(T.owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE)
|
||||
T.owner.current.grant_language(/datum/language/codespeak)
|
||||
T.owner.current.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MALF)
|
||||
return FALSE
|
||||
|
||||
@@ -233,7 +233,7 @@
|
||||
newstruct.cancel_camera()
|
||||
|
||||
|
||||
/obj/item/soulstone/proc/init_shade(mob/living/carbon/human/T, mob/U, vic = 0)
|
||||
/obj/item/soulstone/proc/init_shade(mob/living/carbon/human/T, mob/user, vic = 0)
|
||||
new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton
|
||||
T.stop_sound_channel(CHANNEL_HEARTBEAT)
|
||||
T.invisibility = INVISIBILITY_ABSTRACT
|
||||
@@ -245,20 +245,23 @@
|
||||
S.name = "Shade of [T.real_name]"
|
||||
S.real_name = "Shade of [T.real_name]"
|
||||
T.transfer_ckey(S)
|
||||
S.language_holder = U.language_holder.copy(S)
|
||||
if(U)
|
||||
S.faction |= "[REF(U)]" //Add the master as a faction, allowing inter-mob cooperation
|
||||
if(U && iscultist(U))
|
||||
S.copy_languages(T, LANGUAGE_MIND)//Copies the old mobs languages into the new mob holder.
|
||||
S.copy_languages(user, LANGUAGE_MASTER)
|
||||
S.update_atom_languages()
|
||||
grant_all_languages(FALSE, FALSE, TRUE) //Grants omnitongue
|
||||
if(user)
|
||||
S.faction |= "[REF(user)]" //Add the master as a faction, allowing inter-mob cooperation
|
||||
if(user && iscultist(user))
|
||||
SSticker.mode.add_cultist(S.mind, 0)
|
||||
S.cancel_camera()
|
||||
name = "soulstone: Shade of [T.real_name]"
|
||||
icon_state = "soulstone2"
|
||||
if(U && (iswizard(U) || usability))
|
||||
to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help [U.p_them()] succeed in [U.p_their()] goals at all costs.")
|
||||
else if(U && iscultist(U))
|
||||
if(user && (iswizard(user) || usability))
|
||||
to_chat(S, "Your soul has been captured! You are now bound to [user.real_name]'s will. Help [user.p_them()] succeed in [user.p_their()] goals at all costs.")
|
||||
else if(user && iscultist(user))
|
||||
to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.")
|
||||
if(vic && U)
|
||||
to_chat(U, "<span class='info'><b>Capture successful!</b>:</span> [T.real_name]'s soul has been ripped from [T.p_their()] body and stored within the soul stone.")
|
||||
if(vic && user)
|
||||
to_chat(user, "<span class='info'><b>Capture successful!</b>:</span> [T.real_name]'s soul has been ripped from [T.p_their()] body and stored within the soul stone.")
|
||||
|
||||
|
||||
/obj/item/soulstone/proc/getCultGhost(mob/living/carbon/human/T, mob/U)
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
/mob/living
|
||||
var/mb_cd_length = 5 SECONDS //5 second cooldown for masturbating because fuck spam.
|
||||
var/mb_cd_timer = 0 //The timer itself
|
||||
|
||||
/mob/living/carbon/human
|
||||
var/saved_underwear = ""//saves their underwear so it can be toggled later
|
||||
var/saved_undershirt = ""
|
||||
var/saved_socks = ""
|
||||
var/hidden_underwear = FALSE
|
||||
var/hidden_undershirt = FALSE
|
||||
var/hidden_socks = FALSE
|
||||
|
||||
//Mob procs
|
||||
/mob/living/carbon/human/proc/underwear_toggle()
|
||||
set name = "Toggle undergarments"
|
||||
set category = "IC"
|
||||
|
||||
var/confirm = input(src, "Select what part of your form to alter", "Undergarment Toggling") as null|anything in list("Top", "Bottom", "Socks", "All")
|
||||
if(!confirm)
|
||||
return
|
||||
if(confirm == "Top")
|
||||
hidden_undershirt = !hidden_undershirt
|
||||
|
||||
if(confirm == "Bottom")
|
||||
hidden_underwear = !hidden_underwear
|
||||
|
||||
if(confirm == "Socks")
|
||||
hidden_socks = !hidden_socks
|
||||
|
||||
if(confirm == "All")
|
||||
var/on_off = (hidden_undershirt || hidden_underwear || hidden_socks) ? FALSE : TRUE
|
||||
hidden_undershirt = on_off
|
||||
hidden_underwear = on_off
|
||||
hidden_socks = on_off
|
||||
|
||||
update_body(TRUE)
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/adjust_arousal(strength,aphro = FALSE,maso = FALSE) // returns all genitals that were adjust
|
||||
var/list/obj/item/organ/genital/genit_list = list()
|
||||
if(!client?.prefs.arousable || (aphro && (client?.prefs.cit_toggles & NO_APHRO)) || (maso && !HAS_TRAIT(src, TRAIT_MASO)))
|
||||
return // no adjusting made here
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(G.genital_flags & GENITAL_CAN_AROUSE && !G.aroused_state && prob(strength*G.sensitivity))
|
||||
G.set_aroused_state(strength > 0)
|
||||
G.update_appearance()
|
||||
if(G.aroused_state)
|
||||
genit_list += G
|
||||
return genit_list
|
||||
|
||||
/obj/item/organ/genital/proc/climaxable(mob/living/carbon/human/H, silent = FALSE) //returns the fluid source (ergo reagents holder) if found.
|
||||
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
|
||||
. = reagents
|
||||
else
|
||||
if(linked_organ)
|
||||
. = linked_organ.reagents
|
||||
if(!. && !silent)
|
||||
to_chat(H, "<span class='warning'>Your [name] is unable to produce it's own fluids, it's missing the organs for it.</span>")
|
||||
|
||||
/mob/living/carbon/human/proc/do_climax(datum/reagents/R, atom/target, obj/item/organ/genital/G, spill = TRUE)
|
||||
if(!G)
|
||||
return
|
||||
if(!target || !R)
|
||||
return
|
||||
var/turfing = isturf(target)
|
||||
G.generate_fluid(R)
|
||||
if(spill && R.total_volume >= 5)
|
||||
R.reaction(turfing ? target : target.loc, TOUCH, 1, 0)
|
||||
if(!turfing)
|
||||
R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1))
|
||||
G.time_since_last_orgasm = 0
|
||||
R.clear_reagents()
|
||||
|
||||
/mob/living/carbon/human/proc/mob_climax_outside(obj/item/organ/genital/G, mb_time = 30) //This is used for forced orgasms and other hands-free climaxes
|
||||
var/datum/reagents/fluid_source = G.climaxable(src, TRUE)
|
||||
if(!fluid_source)
|
||||
to_chat(src,"<span class='userdanger'>Your [G.name] cannot cum.</span>")
|
||||
return
|
||||
if(mb_time) //as long as it's not instant, give a warning
|
||||
to_chat(src,"<span class='userlove'>You feel yourself about to orgasm.</span>")
|
||||
if(!do_after(src, mb_time, target = src) || !G.climaxable(src, TRUE))
|
||||
return
|
||||
to_chat(src,"<span class='userlove'>You climax[isturf(loc) ? " onto [loc]" : ""] with your [G.name].</span>")
|
||||
do_climax(fluid_source, loc, G)
|
||||
|
||||
/mob/living/carbon/human/proc/mob_climax_partner(obj/item/organ/genital/G, mob/living/L, spillage = TRUE, mb_time = 30) //Used for climaxing with any living thing
|
||||
var/datum/reagents/fluid_source = G.climaxable(src)
|
||||
if(!fluid_source)
|
||||
return
|
||||
if(mb_time) //Skip warning if this is an instant climax.
|
||||
to_chat(src,"<span class='userlove'>You're about to climax with [L]!</span>")
|
||||
to_chat(L,"<span class='userlove'>[src] is about to climax with you!</span>")
|
||||
if(!do_after(src, mb_time, target = src) || !in_range(src, L) || !G.climaxable(src, TRUE))
|
||||
return
|
||||
if(spillage)
|
||||
to_chat(src,"<span class='userlove'>You orgasm with [L], spilling out of them, using your [G.name].</span>")
|
||||
to_chat(L,"<span class='userlove'>[src] climaxes with you, overflowing and spilling, using [p_their()] [G.name]!</span>")
|
||||
else //knots and other non-spilling orgasms
|
||||
to_chat(src,"<span class='userlove'>You climax with [L], your [G.name] spilling nothing.</span>")
|
||||
to_chat(L,"<span class='userlove'>[src] climaxes with you, [p_their()] [G.name] spilling nothing!</span>")
|
||||
SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "orgasm", /datum/mood_event/orgasm)
|
||||
do_climax(fluid_source, spillage ? loc : L, G, spillage)
|
||||
|
||||
/mob/living/carbon/human/proc/mob_fill_container(obj/item/organ/genital/G, obj/item/reagent_containers/container, mb_time = 30) //For beaker-filling, beware the bartender
|
||||
var/datum/reagents/fluid_source = G.climaxable(src)
|
||||
if(!fluid_source)
|
||||
return
|
||||
if(mb_time)
|
||||
to_chat(src,"<span class='userlove'>You start to [G.masturbation_verb] your [G.name] over [container].</span>")
|
||||
if(!do_after(src, mb_time, target = src) || !in_range(src, container) || !G.climaxable(src, TRUE))
|
||||
return
|
||||
to_chat(src,"<span class='userlove'>You used your [G.name] to fill [container].</span>")
|
||||
do_climax(fluid_source, container, G, FALSE)
|
||||
|
||||
/mob/living/carbon/human/proc/pick_climax_genitals(silent = FALSE)
|
||||
var/list/genitals_list
|
||||
var/list/worn_stuff = get_equipped_items()
|
||||
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH) && G.is_exposed(worn_stuff)) //filter out what you can't masturbate with
|
||||
LAZYADD(genitals_list, G)
|
||||
if(LAZYLEN(genitals_list))
|
||||
var/obj/item/organ/genital/ret_organ = input(src, "with what?", "Climax", null) as null|obj in genitals_list
|
||||
return ret_organ
|
||||
else if(!silent)
|
||||
to_chat(src, "<span class='warning'>You cannot climax without available genitals.</span>")
|
||||
|
||||
/mob/living/carbon/human/proc/pick_partner(silent = FALSE)
|
||||
var/list/partners = list()
|
||||
if(pulling)
|
||||
partners += pulling
|
||||
if(pulledby)
|
||||
partners += pulledby
|
||||
//Now we got both of them, let's check if they're proper
|
||||
for(var/mob/living/L in partners)
|
||||
if(!L.client || !L.mind) // can't consent, not a partner
|
||||
partners -= L
|
||||
if(iscarbon(L))
|
||||
var/mob/living/carbon/C = L
|
||||
if(!C.exposed_genitals.len && !C.is_groin_exposed() && !C.is_chest_exposed()) //Nothing through_clothing, no proper partner.
|
||||
partners -= C
|
||||
//NOW the list should only contain correct partners
|
||||
if(!partners.len)
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You cannot do this alone.</span>")
|
||||
return //No one left.
|
||||
var/mob/living/target = input(src, "With whom?", "Sexual partner", null) as null|anything in partners //pick one, default to null
|
||||
if(target && in_range(src, target))
|
||||
to_chat(src,"<span class='notice'>Waiting for consent...</span>")
|
||||
var/consenting = input(target, "Do you want [src] to climax with you?","Climax mechanics","No") in list("Yes","No")
|
||||
if(consenting == "Yes")
|
||||
return target
|
||||
else
|
||||
message_admins("[src] tried to climax with [target], but [target] did not consent.")
|
||||
log_consent("[src] tried to climax with [target], but [target] did not consent.")
|
||||
|
||||
/mob/living/carbon/human/proc/pick_climax_container(silent = FALSE)
|
||||
var/list/containers_list = list()
|
||||
|
||||
for(var/obj/item/reagent_containers/C in held_items)
|
||||
if(C.is_open_container() || istype(C, /obj/item/reagent_containers/food/snacks))
|
||||
containers_list += C
|
||||
for(var/obj/item/reagent_containers/C in range(1, src))
|
||||
if((C.is_open_container() || istype(C, /obj/item/reagent_containers/food/snacks)) && CanReach(C))
|
||||
containers_list += C
|
||||
|
||||
if(containers_list.len)
|
||||
var/obj/item/reagent_containers/SC = input(src, "Into or onto what?(Cancel for nowhere)", null) as null|obj in containers_list
|
||||
if(SC && CanReach(SC))
|
||||
return SC
|
||||
else if(!silent)
|
||||
to_chat(src, "<span class='warning'>You cannot do this without an appropriate container.</span>")
|
||||
|
||||
/mob/living/carbon/human/proc/available_rosie_palms(silent = FALSE, list/whitelist_typepaths = list(/obj/item/dildo))
|
||||
if(restrained(TRUE)) //TRUE ignores grabs
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You can't do that while restrained!</span>")
|
||||
return FALSE
|
||||
if(!get_num_arms() || !get_empty_held_indexes())
|
||||
if(whitelist_typepaths)
|
||||
if(!islist(whitelist_typepaths))
|
||||
whitelist_typepaths = list(whitelist_typepaths)
|
||||
for(var/path in whitelist_typepaths)
|
||||
if(is_holding_item_of_type(path))
|
||||
return TRUE
|
||||
if(!silent)
|
||||
to_chat(src, "<span class='warning'>You need at least one free arm.</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
//Here's the main proc itself
|
||||
/mob/living/carbon/human/proc/mob_climax(forced_climax=FALSE) //Forced is instead of the other proc, makes you cum if you have the tools for it, ignoring restraints
|
||||
if(mb_cd_timer > world.time)
|
||||
if(!forced_climax) //Don't spam the message to the victim if forced to come too fast
|
||||
to_chat(src, "<span class='warning'>You need to wait [DisplayTimeText((mb_cd_timer - world.time), TRUE)] before you can do that again!</span>")
|
||||
return
|
||||
|
||||
if(!client?.prefs.arousable || !has_dna())
|
||||
return
|
||||
if(stat == DEAD)
|
||||
if(!forced_climax)
|
||||
to_chat(src, "<span class='warning'>You can't do that while dead!</span>")
|
||||
return
|
||||
if(forced_climax) //Something forced us to cum, this is not a masturbation thing and does not progress to the other checks
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(!CHECK_BITFIELD(G.genital_flags, CAN_CLIMAX_WITH)) //Skip things like wombs and testicles
|
||||
continue
|
||||
var/mob/living/partner
|
||||
var/check_target
|
||||
var/list/worn_stuff = get_equipped_items()
|
||||
|
||||
if(G.is_exposed(worn_stuff))
|
||||
if(pulling) //Are we pulling someone? Priority target, we can't be making option menus for this, has to be quick
|
||||
if(isliving(pulling)) //Don't fuck objects
|
||||
check_target = pulling
|
||||
if(pulledby && !check_target) //prioritise pulled over pulledby
|
||||
if(isliving(pulledby))
|
||||
check_target = pulledby
|
||||
//Now we should have a partner, or else we have to come alone
|
||||
if(check_target)
|
||||
if(iscarbon(check_target)) //carbons can have clothes
|
||||
var/mob/living/carbon/C = check_target
|
||||
if(C.exposed_genitals.len || C.is_groin_exposed() || C.is_chest_exposed()) //Are they naked enough?
|
||||
partner = C
|
||||
else //A cat is fine too
|
||||
partner = check_target
|
||||
if(partner) //Did they pass the clothing checks?
|
||||
mob_climax_partner(G, partner, mb_time = 0) //Instant climax due to forced
|
||||
continue //You've climaxed once with this organ, continue on
|
||||
//not exposed OR if no partner was found while exposed, climax alone
|
||||
mob_climax_outside(G, mb_time = 0) //removed climax timer for sudden, forced orgasms
|
||||
//Now all genitals that could climax, have.
|
||||
//Since this was a forced climax, we do not need to continue with the other stuff
|
||||
mb_cd_timer = world.time + mb_cd_length
|
||||
return
|
||||
//If we get here, then this is not a forced climax and we gotta check a few things.
|
||||
|
||||
if(stat == UNCONSCIOUS) //No sleep-masturbation, you're unconscious.
|
||||
to_chat(src, "<span class='warning'>You must be conscious to do that!</span>")
|
||||
return
|
||||
|
||||
//Ok, now we check what they want to do.
|
||||
var/choice = input(src, "Select sexual activity", "Sexual activity:") as null|anything in list("Climax alone","Climax with partner", "Fill container")
|
||||
if(!choice)
|
||||
return
|
||||
|
||||
switch(choice)
|
||||
if("Climax alone")
|
||||
if(!available_rosie_palms())
|
||||
return
|
||||
var/obj/item/organ/genital/picked_organ = pick_climax_genitals()
|
||||
if(picked_organ && available_rosie_palms(TRUE))
|
||||
mob_climax_outside(picked_organ)
|
||||
if("Climax with partner")
|
||||
//We need no hands, we can be restrained and so on, so let's pick an organ
|
||||
var/obj/item/organ/genital/picked_organ = pick_climax_genitals()
|
||||
if(picked_organ)
|
||||
var/mob/living/partner = pick_partner() //Get someone
|
||||
if(partner)
|
||||
var/spillage = input(src, "Would your fluids spill outside?", "Choose overflowing option", "Yes") as null|anything in list("Yes", "No")
|
||||
if(spillage && in_range(src, partner))
|
||||
mob_climax_partner(picked_organ, partner, spillage == "Yes" ? TRUE : FALSE)
|
||||
if("Fill container")
|
||||
//We'll need hands and no restraints.
|
||||
if(!available_rosie_palms(FALSE, /obj/item/reagent_containers))
|
||||
return
|
||||
//We got hands, let's pick an organ
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = pick_climax_genitals() //Gotta be climaxable, not just masturbation, to fill with fluids.
|
||||
if(picked_organ)
|
||||
//Good, got an organ, time to pick a container
|
||||
var/obj/item/reagent_containers/fluid_container = pick_climax_container()
|
||||
if(fluid_container && available_rosie_palms(TRUE, /obj/item/reagent_containers))
|
||||
mob_fill_container(picked_organ, fluid_container)
|
||||
|
||||
mb_cd_timer = world.time + mb_cd_length
|
||||
|
||||
/mob/living/carbon/human/verb/climax_verb()
|
||||
set category = "IC"
|
||||
set name = "Climax"
|
||||
set desc = "Lets you choose a couple ways to ejaculate."
|
||||
mob_climax()
|
||||
@@ -0,0 +1,374 @@
|
||||
/obj/item/organ/genital
|
||||
color = "#fcccb3"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
organ_flags = ORGAN_NO_DISMEMBERMENT
|
||||
var/shape
|
||||
var/sensitivity = 1 // wow if this were ever used that'd be cool but it's not but i'm keeping it for my unshit code
|
||||
var/genital_flags //see citadel_defines.dm
|
||||
var/masturbation_verb = "masturbate"
|
||||
var/orgasm_verb = "cumming" //present continous
|
||||
var/arousal_verb = "You feel aroused"
|
||||
var/unarousal_verb = "You no longer feel aroused"
|
||||
var/fluid_transfer_factor = 0 //How much would a partner get in them if they climax using this?
|
||||
var/size = 2 //can vary between num or text, just used in icon_state strings
|
||||
var/datum/reagent/fluid_id = null
|
||||
var/fluid_max_volume = 50
|
||||
var/fluid_efficiency = 1
|
||||
var/fluid_rate = CUM_RATE
|
||||
var/fluid_mult = 1
|
||||
var/time_since_last_orgasm = 500
|
||||
var/aroused_state = FALSE //Boolean used in icon_state strings
|
||||
var/obj/item/organ/genital/linked_organ
|
||||
var/linked_organ_slot //used for linking an apparatus' organ to its other half on update_link().
|
||||
var/layer_index = GENITAL_LAYER_INDEX //Order should be very important. FIRST vagina, THEN testicles, THEN penis, as this affects the order they are rendered in.
|
||||
|
||||
/obj/item/organ/genital/Initialize(mapload, do_update = TRUE)
|
||||
. = ..()
|
||||
if(fluid_id)
|
||||
create_reagents(fluid_max_volume, NONE, NO_REAGENTS_VALUE)
|
||||
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
|
||||
reagents.add_reagent(fluid_id, fluid_max_volume)
|
||||
if(do_update)
|
||||
update()
|
||||
|
||||
/obj/item/organ/genital/proc/set_aroused_state(new_state)
|
||||
if(!(genital_flags & GENITAL_CAN_AROUSE))
|
||||
return FALSE
|
||||
if(!((HAS_TRAIT(owner,TRAIT_PERMABONER) && !new_state) || HAS_TRAIT(owner,TRAIT_NEVERBONER) && new_state))
|
||||
aroused_state = new_state
|
||||
return aroused_state
|
||||
|
||||
/obj/item/organ/genital/proc/update()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
update_size()
|
||||
update_appearance()
|
||||
if(genital_flags & UPDATE_OWNER_APPEARANCE && owner && ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.update_genitals()
|
||||
if(linked_organ_slot || (linked_organ && !owner))
|
||||
update_link()
|
||||
|
||||
//exposure and through-clothing code
|
||||
/mob/living/carbon
|
||||
var/list/exposed_genitals = list() //Keeping track of them so we don't have to iterate through every genitalia and see if exposed
|
||||
|
||||
/obj/item/organ/genital/proc/is_exposed()
|
||||
if(!owner || genital_flags & (GENITAL_INTERNAL|GENITAL_HIDDEN))
|
||||
return FALSE
|
||||
if(genital_flags & GENITAL_UNDIES_HIDDEN && ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
if(!(NO_UNDERWEAR in H.dna.species.species_traits))
|
||||
var/datum/sprite_accessory/underwear/top/T = H.hidden_undershirt ? null : GLOB.undershirt_list[H.undershirt]
|
||||
var/datum/sprite_accessory/underwear/bottom/B = H.hidden_underwear ? null : GLOB.underwear_list[H.underwear]
|
||||
if(zone == BODY_ZONE_CHEST ? (T?.covers_chest || B?.covers_chest) : (T?.covers_groin || B?.covers_groin))
|
||||
return FALSE
|
||||
if(genital_flags & GENITAL_THROUGH_CLOTHES)
|
||||
return TRUE
|
||||
|
||||
switch(zone) //update as more genitals are added
|
||||
if(BODY_ZONE_CHEST)
|
||||
return owner.is_chest_exposed()
|
||||
if(BODY_ZONE_PRECISE_GROIN)
|
||||
return owner.is_groin_exposed()
|
||||
|
||||
/obj/item/organ/genital/proc/toggle_visibility(visibility, update = TRUE)
|
||||
genital_flags &= ~(GENITAL_THROUGH_CLOTHES|GENITAL_HIDDEN|GENITAL_UNDIES_HIDDEN)
|
||||
if(owner)
|
||||
owner.exposed_genitals -= src
|
||||
switch(visibility)
|
||||
if(GEN_VISIBLE_ALWAYS)
|
||||
genital_flags |= GENITAL_THROUGH_CLOTHES
|
||||
if(owner)
|
||||
owner.exposed_genitals += src
|
||||
if(GEN_VISIBLE_NO_UNDIES)
|
||||
genital_flags |= GENITAL_UNDIES_HIDDEN
|
||||
if(GEN_VISIBLE_NEVER)
|
||||
genital_flags |= GENITAL_HIDDEN
|
||||
|
||||
if(update && owner && ishuman(owner)) //recast to use update genitals proc
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.update_genitals()
|
||||
|
||||
/mob/living/carbon/verb/toggle_genitals()
|
||||
set category = "IC"
|
||||
set name = "Expose/Hide genitals"
|
||||
set desc = "Allows you to toggle which genitals should show through clothes or not."
|
||||
|
||||
if(stat != CONSCIOUS)
|
||||
to_chat(usr, "<span class='warning'>You can toggle genitals visibility right now...</span>")
|
||||
return
|
||||
|
||||
var/list/genital_list = list()
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(!CHECK_BITFIELD(G.genital_flags, GENITAL_INTERNAL))
|
||||
genital_list += G
|
||||
if(!genital_list.len) //There is nothing to expose
|
||||
return
|
||||
//Full list of exposable genitals created
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = input(src, "Choose which genitalia to expose/hide", "Expose/Hide genitals") as null|anything in genital_list
|
||||
if(picked_organ && (picked_organ in internal_organs))
|
||||
var/picked_visibility = input(src, "Choose visibility setting", "Expose/Hide genitals") as null|anything in GLOB.genitals_visibility_toggles
|
||||
if(picked_visibility && picked_organ && (picked_organ in internal_organs))
|
||||
picked_organ.toggle_visibility(picked_visibility)
|
||||
return
|
||||
|
||||
/mob/living/carbon/verb/toggle_arousal_state()
|
||||
set category = "IC"
|
||||
set name = "Toggle genital arousal"
|
||||
set desc = "Allows you to toggle which genitals are showing signs of arousal."
|
||||
var/list/genital_list = list()
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(G.genital_flags & GENITAL_CAN_AROUSE)
|
||||
genital_list += G
|
||||
if(!genital_list.len) //There's nothing that can show arousal
|
||||
return
|
||||
var/obj/item/organ/genital/picked_organ
|
||||
picked_organ = input(src, "Choose which genitalia to toggle arousal on", "Set genital arousal", null) in genital_list
|
||||
if(picked_organ)
|
||||
var/original_state = picked_organ.aroused_state
|
||||
picked_organ.set_aroused_state(!picked_organ.aroused_state)
|
||||
if(original_state != picked_organ.aroused_state)
|
||||
to_chat(src,"<span class='userlove'>[picked_organ.aroused_state ? picked_organ.arousal_verb : picked_organ.unarousal_verb].</span>")
|
||||
else
|
||||
to_chat(src,"<span class='userlove'>You can't make that genital [picked_organ.aroused_state ? "unaroused" : "aroused"]!</span>")
|
||||
picked_organ.update_appearance()
|
||||
return
|
||||
|
||||
|
||||
/obj/item/organ/genital/proc/modify_size(modifier, min = -INFINITY, max = INFINITY)
|
||||
fluid_max_volume += modifier*2.5
|
||||
fluid_rate += modifier/10
|
||||
if(reagents)
|
||||
reagents.maximum_volume = fluid_max_volume
|
||||
return
|
||||
|
||||
/obj/item/organ/genital/proc/update_size()
|
||||
return
|
||||
|
||||
/obj/item/organ/genital/proc/update_appearance()
|
||||
if(!owner || owner.stat == DEAD)
|
||||
aroused_state = FALSE
|
||||
|
||||
/obj/item/organ/genital/on_life()
|
||||
. = ..()
|
||||
if(!reagents || !.)
|
||||
return
|
||||
reagents.maximum_volume = fluid_max_volume
|
||||
if(fluid_id && CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION))
|
||||
time_since_last_orgasm++
|
||||
|
||||
/obj/item/organ/genital/proc/generate_fluid(datum/reagents/R)
|
||||
var/amount = clamp(fluid_rate * time_since_last_orgasm * fluid_mult,0,fluid_max_volume)
|
||||
R.clear_reagents()
|
||||
R.add_reagent(fluid_id,amount)
|
||||
return TRUE
|
||||
|
||||
/obj/item/organ/genital/proc/update_link()
|
||||
if(owner)
|
||||
if(linked_organ)
|
||||
return FALSE
|
||||
linked_organ = owner.getorganslot(linked_organ_slot)
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = src
|
||||
linked_organ.upon_link()
|
||||
upon_link()
|
||||
return TRUE
|
||||
if(linked_organ)
|
||||
linked_organ.linked_organ = null
|
||||
linked_organ = null
|
||||
return FALSE
|
||||
|
||||
//post organ duo making arrangements.
|
||||
/obj/item/organ/genital/proc/upon_link()
|
||||
return
|
||||
|
||||
/obj/item/organ/genital/Insert(mob/living/carbon/M, special = FALSE, drop_if_replaced = TRUE)
|
||||
. = ..()
|
||||
if(.)
|
||||
update()
|
||||
RegisterSignal(owner, COMSIG_MOB_DEATH, .proc/update_appearance)
|
||||
if(genital_flags & GENITAL_THROUGH_CLOTHES)
|
||||
owner.exposed_genitals += src
|
||||
|
||||
/obj/item/organ/genital/Remove(special = FALSE)
|
||||
. = ..()
|
||||
var/mob/living/carbon/C = .
|
||||
update()
|
||||
if(!QDELETED(C))
|
||||
if(genital_flags & UPDATE_OWNER_APPEARANCE && ishuman(C))
|
||||
var/mob/living/carbon/human/H = .
|
||||
H.update_genitals()
|
||||
C.exposed_genitals -= src
|
||||
UnregisterSignal(C, COMSIG_MOB_DEATH)
|
||||
|
||||
//proc to give a player their genitals and stuff when they log in
|
||||
/mob/living/carbon/human/proc/give_genitals(clean = FALSE)//clean will remove all pre-existing genitals. proc will then give them any genitals that are enabled in their DNA
|
||||
if(clean)
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
qdel(G)
|
||||
if (NOGENITALS in dna.species.species_traits)
|
||||
return
|
||||
if(dna.features["has_vag"])
|
||||
give_genital(/obj/item/organ/genital/vagina)
|
||||
if(dna.features["has_womb"])
|
||||
give_genital(/obj/item/organ/genital/womb)
|
||||
if(dna.features["has_balls"])
|
||||
give_genital(/obj/item/organ/genital/testicles)
|
||||
if(dna.features["has_breasts"])
|
||||
give_genital(/obj/item/organ/genital/breasts)
|
||||
if(dna.features["has_cock"])
|
||||
give_genital(/obj/item/organ/genital/penis)
|
||||
|
||||
/mob/living/carbon/human/proc/give_genital(obj/item/organ/genital/G)
|
||||
if(!dna || (NOGENITALS in dna.species.species_traits) || getorganslot(initial(G.slot)))
|
||||
return FALSE
|
||||
G = new G(null, FALSE)
|
||||
G.get_features(src)
|
||||
G.Insert(src)
|
||||
return G
|
||||
|
||||
/obj/item/organ/genital/proc/get_features(mob/living/carbon/human/H)
|
||||
return
|
||||
|
||||
|
||||
//procs to handle sprite overlays being applied to humans
|
||||
|
||||
/mob/living/carbon/human/equip_to_slot(obj/item/I, slot)
|
||||
. = ..()
|
||||
if(!. && I && slot && !(slot in GLOB.no_genitals_update_slots)) //the item was successfully equipped, and the chosen slot wasn't merely storage, hands or cuffs.
|
||||
update_genitals()
|
||||
|
||||
/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE)
|
||||
var/no_update = FALSE
|
||||
if(!I || I == l_store || I == r_store || I == s_store || I == handcuffed || I == legcuffed || get_held_index_of_item(I)) //stops storages, cuffs and held items from triggering it.
|
||||
no_update = TRUE
|
||||
. = ..()
|
||||
if(!. || no_update)
|
||||
return
|
||||
update_genitals()
|
||||
|
||||
/mob/living/carbon/human/proc/update_genitals()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
var/static/list/relevant_layers = list("[GENITALS_BEHIND_LAYER]" = "BEHIND", "[GENITALS_FRONT_LAYER]" = "FRONT")
|
||||
var/static/list/layers_num
|
||||
if(!layers_num)
|
||||
for(var/L in relevant_layers)
|
||||
LAZYSET(layers_num, L, text2num(L))
|
||||
for(var/L in relevant_layers) //Less hardcode
|
||||
remove_overlay(layers_num[L])
|
||||
remove_overlay(GENITALS_EXPOSED_LAYER)
|
||||
if(!LAZYLEN(internal_organs) || ((NOGENITALS in dna.species.species_traits) && !genital_override) || HAS_TRAIT(src, TRAIT_HUSK))
|
||||
return
|
||||
|
||||
//start scanning for genitals
|
||||
|
||||
var/list/gen_index[GENITAL_LAYER_INDEX_LENGTH]
|
||||
var/list/genitals_to_add
|
||||
var/list/fully_exposed
|
||||
for(var/obj/item/organ/genital/G in internal_organs)
|
||||
if(G.is_exposed()) //Checks appropriate clothing slot and if it's through_clothes
|
||||
LAZYADD(gen_index[G.layer_index], G)
|
||||
for(var/L in gen_index)
|
||||
if(L) //skip nulls
|
||||
LAZYADD(genitals_to_add, L)
|
||||
if(!genitals_to_add)
|
||||
return
|
||||
//Now we added all genitals that aren't internal and should be rendered
|
||||
//start applying overlays
|
||||
for(var/layer in relevant_layers)
|
||||
var/list/standing = list()
|
||||
var/layertext = relevant_layers[layer]
|
||||
for(var/A in genitals_to_add)
|
||||
var/obj/item/organ/genital/G = A
|
||||
var/datum/sprite_accessory/S
|
||||
var/size = G.size
|
||||
switch(G.type)
|
||||
if(/obj/item/organ/genital/penis)
|
||||
S = GLOB.cock_shapes_list[G.shape]
|
||||
if(/obj/item/organ/genital/testicles)
|
||||
S = GLOB.balls_shapes_list[G.shape]
|
||||
if(/obj/item/organ/genital/vagina)
|
||||
S = GLOB.vagina_shapes_list[G.shape]
|
||||
if(/obj/item/organ/genital/breasts)
|
||||
S = GLOB.breasts_shapes_list[G.shape]
|
||||
|
||||
if(!S || S.icon_state == "none")
|
||||
continue
|
||||
var/aroused_state = G.aroused_state && S.alt_aroused
|
||||
var/accessory_icon = S.icon
|
||||
var/do_center = S.center
|
||||
var/dim_x = S.dimension_x
|
||||
var/dim_y = S.dimension_y
|
||||
if(G.genital_flags & GENITAL_CAN_TAUR && S.taur_icon && (!S.feat_taur || dna.features[S.feat_taur]) && dna.species.mutant_bodyparts["taur"])
|
||||
var/datum/sprite_accessory/taur/T = GLOB.taur_list[dna.features["taur"]]
|
||||
if(T?.taur_mode & S.accepted_taurs)
|
||||
accessory_icon = S.taur_icon
|
||||
do_center = TRUE
|
||||
dim_x = S.taur_dimension_x
|
||||
dim_y = S.taur_dimension_y
|
||||
|
||||
var/mutable_appearance/genital_overlay = mutable_appearance(accessory_icon, layer = -layer)
|
||||
if(do_center)
|
||||
genital_overlay = center_image(genital_overlay, dim_x, dim_y)
|
||||
|
||||
if(dna.species.use_skintones && dna.features["genitals_use_skintone"])
|
||||
genital_overlay.color = SKINTONE2HEX(skin_tone)
|
||||
else
|
||||
switch(S.color_src)
|
||||
if("cock_color")
|
||||
genital_overlay.color = "#[dna.features["cock_color"]]"
|
||||
if("balls_color")
|
||||
genital_overlay.color = "#[dna.features["balls_color"]]"
|
||||
if("breasts_color")
|
||||
genital_overlay.color = "#[dna.features["breasts_color"]]"
|
||||
if("vag_color")
|
||||
genital_overlay.color = "#[dna.features["vag_color"]]"
|
||||
|
||||
genital_overlay.icon_state = "[G.slot]_[S.icon_state]_[size][(dna.species.use_skintones && !dna.skin_tone_override) ? "_s" : ""]_[aroused_state]_[layertext]"
|
||||
|
||||
if(layers_num[layer] == GENITALS_FRONT_LAYER && G.genital_flags & GENITAL_THROUGH_CLOTHES)
|
||||
genital_overlay.layer = -GENITALS_EXPOSED_LAYER
|
||||
LAZYADD(fully_exposed, genital_overlay)
|
||||
else
|
||||
genital_overlay.layer = -layers_num[layer]
|
||||
standing += genital_overlay
|
||||
|
||||
if(LAZYLEN(standing))
|
||||
overlays_standing[layers_num[layer]] = standing
|
||||
|
||||
if(LAZYLEN(fully_exposed))
|
||||
overlays_standing[GENITALS_EXPOSED_LAYER] = fully_exposed
|
||||
apply_overlay(GENITALS_EXPOSED_LAYER)
|
||||
|
||||
for(var/L in relevant_layers)
|
||||
apply_overlay(layers_num[L])
|
||||
|
||||
|
||||
//Checks to see if organs are new on the mob, and changes their colours so that they don't get crazy colours.
|
||||
/mob/living/carbon/human/proc/emergent_genital_call()
|
||||
if(!client.prefs.arousable)
|
||||
return FALSE
|
||||
|
||||
var/organCheck = locate(/obj/item/organ/genital) in internal_organs
|
||||
var/breastCheck = getorganslot(ORGAN_SLOT_BREASTS)
|
||||
var/willyCheck = getorganslot(ORGAN_SLOT_PENIS)
|
||||
|
||||
if(organCheck == FALSE)
|
||||
if(ishuman(src) && dna.species.use_skintones)
|
||||
dna.features["genitals_use_skintone"] = TRUE
|
||||
if(src.dna.species.fixed_mut_color)
|
||||
dna.features["cock_color"] = "[dna.species.fixed_mut_color]"
|
||||
dna.features["breasts_color"] = "[dna.species.fixed_mut_color]"
|
||||
return
|
||||
//So people who haven't set stuff up don't get rainbow surprises.
|
||||
dna.features["cock_color"] = "[dna.features["mcolor"]]"
|
||||
dna.features["breasts_color"] = "[dna.features["mcolor"]]"
|
||||
else //If there's a new organ, make it the same colour.
|
||||
if(breastCheck == FALSE)
|
||||
dna.features["breasts_color"] = dna.features["cock_color"]
|
||||
else if (willyCheck == FALSE)
|
||||
dna.features["cock_color"] = dna.features["breasts_color"]
|
||||
return TRUE
|
||||
@@ -0,0 +1,121 @@
|
||||
/datum/sprite_accessory
|
||||
var/alt_aroused = FALSE //CIT CODE if this is TRUE, then the genitals will use an alternate icon_state when aroused.
|
||||
var/taur_icon //leave null if the genital doesn't have a taur counterpart.
|
||||
var/accepted_taurs = STYLE_HOOF_TAURIC|STYLE_PAW_TAURIC //Types that match with the accessory.
|
||||
var/feat_taur //the text string of the dna feature to check for those who want to opt out.
|
||||
var/taur_dimension_y = 32
|
||||
var/taur_dimension_x = 32
|
||||
|
||||
|
||||
//DICKS,COCKS,PENISES,WHATEVER YOU WANT TO CALL THEM
|
||||
/datum/sprite_accessory/penis
|
||||
icon = 'icons/obj/genitals/penis_onmob.dmi'
|
||||
name = "penis" //the preview name of the accessory
|
||||
color_src = "cock_color"
|
||||
alt_aroused = TRUE
|
||||
feat_taur = "cock_taur"
|
||||
|
||||
/datum/sprite_accessory/penis/human
|
||||
icon_state = "human"
|
||||
name = "Human"
|
||||
|
||||
/datum/sprite_accessory/penis/knotted
|
||||
icon_state = "knotted"
|
||||
name = "Knotted"
|
||||
taur_icon = 'icons/obj/genitals/taur_penis_onmob.dmi'
|
||||
taur_dimension_x = 64
|
||||
|
||||
/datum/sprite_accessory/penis/flared
|
||||
icon_state = "flared"
|
||||
name = "Flared"
|
||||
taur_icon = 'icons/obj/genitals/taur_penis_onmob.dmi'
|
||||
taur_dimension_x = 64
|
||||
|
||||
/datum/sprite_accessory/penis/barbknot
|
||||
icon_state = "barbknot"
|
||||
name = "Barbed, Knotted"
|
||||
|
||||
/datum/sprite_accessory/penis/tapered
|
||||
icon_state = "tapered"
|
||||
name = "Tapered"
|
||||
taur_icon = 'icons/obj/genitals/taur_penis_onmob.dmi'
|
||||
taur_dimension_x = 64
|
||||
|
||||
/datum/sprite_accessory/penis/tentacle
|
||||
icon_state = "tentacle"
|
||||
name = "Tentacled"
|
||||
|
||||
/datum/sprite_accessory/penis/hemi
|
||||
icon_state = "hemi"
|
||||
name = "Hemi"
|
||||
|
||||
/datum/sprite_accessory/penis/hemiknot
|
||||
icon_state = "hemiknot"
|
||||
name = "Knotted Hemi"
|
||||
|
||||
//Testicles
|
||||
/datum/sprite_accessory/testicles
|
||||
icon = 'icons/obj/genitals/testicles_onmob.dmi'
|
||||
icon_state = "testicle"
|
||||
name = "testicle" //the preview name of the accessory
|
||||
color_src = "balls_color"
|
||||
|
||||
/datum/sprite_accessory/testicles/single
|
||||
icon_state = "single"
|
||||
name = "Single" //Single as "single pair", for clarity.
|
||||
|
||||
//Vaginas
|
||||
/datum/sprite_accessory/vagina
|
||||
icon = 'icons/obj/genitals/vagina_onmob.dmi'
|
||||
name = "vagina"
|
||||
color_src = "vag_color"
|
||||
alt_aroused = TRUE
|
||||
|
||||
/datum/sprite_accessory/vagina/human
|
||||
icon_state = "human"
|
||||
name = "Human"
|
||||
|
||||
/datum/sprite_accessory/vagina/tentacles
|
||||
icon_state = "tentacle"
|
||||
name = "Tentacle"
|
||||
|
||||
/datum/sprite_accessory/vagina/dentata
|
||||
icon_state = "dentata"
|
||||
name = "Dentata"
|
||||
|
||||
/datum/sprite_accessory/vagina/hairy
|
||||
icon_state = "hairy"
|
||||
name = "Hairy"
|
||||
alt_aroused = FALSE
|
||||
|
||||
/datum/sprite_accessory/vagina/spade
|
||||
icon_state = "spade"
|
||||
name = "Spade"
|
||||
alt_aroused = FALSE
|
||||
|
||||
/datum/sprite_accessory/vagina/furred
|
||||
icon_state = "furred"
|
||||
name = "Furred"
|
||||
alt_aroused = FALSE
|
||||
|
||||
/datum/sprite_accessory/vagina/gaping
|
||||
icon_state = "gaping"
|
||||
name = "Gaping"
|
||||
|
||||
//BREASTS BE HERE
|
||||
/datum/sprite_accessory/breasts
|
||||
icon = 'icons/obj/genitals/breasts_onmob.dmi'
|
||||
name = "breasts"
|
||||
color_src = "breasts_color"
|
||||
|
||||
/datum/sprite_accessory/breasts/pair
|
||||
icon_state = "pair"
|
||||
name = "Pair"
|
||||
|
||||
/datum/sprite_accessory/breasts/quad
|
||||
icon_state = "quad"
|
||||
name = "Quad"
|
||||
|
||||
/datum/sprite_accessory/breasts/sextuple
|
||||
icon_state = "sextuple"
|
||||
name = "Sextuple"
|
||||
@@ -0,0 +1,137 @@
|
||||
#define BREASTS_ICON_MIN_SIZE 1
|
||||
#define BREASTS_ICON_MAX_SIZE 6
|
||||
|
||||
/obj/item/organ/genital/breasts
|
||||
name = "breasts"
|
||||
desc = "Female milk producing organs."
|
||||
icon_state = "breasts"
|
||||
icon = 'icons/obj/genitals/breasts.dmi'
|
||||
zone = BODY_ZONE_CHEST
|
||||
slot = ORGAN_SLOT_BREASTS
|
||||
size = BREASTS_SIZE_DEF // "c". Refer to the breast_values static list below for the cups associated number values
|
||||
fluid_id = /datum/reagent/consumable/milk
|
||||
fluid_rate = MILK_RATE
|
||||
shape = DEF_BREASTS_SHAPE
|
||||
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_FUID_PRODUCTION|GENITAL_CAN_AROUSE|UPDATE_OWNER_APPEARANCE|GENITAL_UNDIES_HIDDEN
|
||||
masturbation_verb = "massage"
|
||||
arousal_verb = "Your breasts start feeling sensitive"
|
||||
unarousal_verb = "Your breasts no longer feel sensitive"
|
||||
orgasm_verb = "leaking"
|
||||
fluid_transfer_factor = 0.5
|
||||
var/static/list/breast_values = list("a" = 1, "b" = 2, "c" = 3, "d" = 4, "e" = 5, "f" = 6, "g" = 7, "h" = 8, "i" = 9, "j" = 10, "k" = 11, "l" = 12, "m" = 13, "n" = 14, "o" = 15, "huge" = 16, "flat" = 0)
|
||||
var/cached_size //these two vars pertain size modifications and so should be expressed in NUMBERS.
|
||||
var/prev_size //former cached_size value, to allow update_size() to early return should be there no significant changes.
|
||||
|
||||
/obj/item/organ/genital/breasts/Initialize(mapload, do_update = TRUE)
|
||||
if(do_update)
|
||||
cached_size = breast_values[size]
|
||||
prev_size = cached_size
|
||||
return ..()
|
||||
|
||||
/obj/item/organ/genital/breasts/update_appearance()
|
||||
. = ..()
|
||||
var/lowershape = lowertext(shape)
|
||||
switch(lowershape)
|
||||
if("pair")
|
||||
desc = "You see a pair of breasts."
|
||||
if("quad")
|
||||
desc = "You see two pairs of breast, one just under the other."
|
||||
if("sextuple")
|
||||
desc = "You see three sets of breasts, running from their chest to their belly."
|
||||
else
|
||||
desc = "You see some breasts, they seem to be quite exotic."
|
||||
if(size == "huge")
|
||||
desc = "You see [pick("some serious honkers", "a real set of badonkers", "some dobonhonkeros", "massive dohoonkabhankoloos", "two big old tonhongerekoogers", "a couple of giant bonkhonagahoogs", "a pair of humongous hungolomghnonoloughongous")]. Their volume is way beyond cupsize now, measuring in about [round(cached_size)]cm in diameter."
|
||||
else
|
||||
if (size == "flat")
|
||||
desc += " They're very small and flatchested, however."
|
||||
else
|
||||
desc += " You estimate that they're [uppertext(size)]-cups."
|
||||
|
||||
if(CHECK_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION) && aroused_state)
|
||||
var/datum/reagent/R = GLOB.chemical_reagents_list[fluid_id]
|
||||
if(R)
|
||||
desc += " They're leaking [lowertext(R.name)]."
|
||||
var/datum/sprite_accessory/S = GLOB.breasts_shapes_list[shape]
|
||||
var/icon_shape = S ? S.icon_state : "pair"
|
||||
var/icon_size = clamp(breast_values[size], BREASTS_ICON_MIN_SIZE, BREASTS_ICON_MAX_SIZE)
|
||||
icon_state = "breasts_[icon_shape]_[icon_size]"
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
if(!H.dna.skin_tone_override)
|
||||
icon_state += "_s"
|
||||
else
|
||||
color = "#[owner.dna.features["breasts_color"]]"
|
||||
|
||||
//Allows breasts to grow and change size, with sprite changes too.
|
||||
//maximum wah
|
||||
//Comical sizes slow you down in movement and actions.
|
||||
//Ridiculous sizes makes you more cumbersome.
|
||||
//this is far too lewd wah
|
||||
|
||||
/obj/item/organ/genital/breasts/modify_size(modifier, min = -INFINITY, max = INFINITY)
|
||||
var/new_value = clamp(cached_size + modifier, min, max)
|
||||
if(new_value == cached_size)
|
||||
return
|
||||
prev_size = cached_size
|
||||
cached_size = new_value
|
||||
update()
|
||||
..()
|
||||
|
||||
/obj/item/organ/genital/breasts/update_size()//wah
|
||||
var/rounded_cached = round(cached_size)
|
||||
if(cached_size < 0)//I don't actually know what round() does to negative numbers, so to be safe!!fixed
|
||||
if(owner)
|
||||
to_chat(owner, "<span class='warning'>You feel your breasts shrinking away from your body as your chest flattens out.</span>")
|
||||
QDEL_IN(src, 1)
|
||||
return
|
||||
var/enlargement = FALSE
|
||||
switch(rounded_cached)
|
||||
if(0) //flatchested
|
||||
size = "flat"
|
||||
if(1 to 8) //modest
|
||||
size = breast_values[rounded_cached]
|
||||
if(9 to 15) //massive
|
||||
size = breast_values[rounded_cached]
|
||||
enlargement = TRUE
|
||||
if(16 to INFINITY) //rediculous
|
||||
size = "huge"
|
||||
enlargement = TRUE
|
||||
if(owner)
|
||||
var/status_effect = owner.has_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
|
||||
if(enlargement && !status_effect)
|
||||
owner.apply_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
|
||||
else if(!enlargement && status_effect)
|
||||
owner.remove_status_effect(STATUS_EFFECT_BREASTS_ENLARGEMENT)
|
||||
|
||||
if(rounded_cached < 16 && owner)//Because byond doesn't count from 0, I have to do this.
|
||||
var/mob/living/carbon/human/H = owner
|
||||
var/r_prev_size = round(prev_size)
|
||||
if (rounded_cached > r_prev_size)
|
||||
to_chat(H, "<span class='warning'>Your breasts [pick("swell up to", "flourish into", "expand into", "burst forth into", "grow eagerly into", "amplify into")] a [uppertext(size)]-cup.</span>")
|
||||
else if (rounded_cached < r_prev_size)
|
||||
to_chat(H, "<span class='warning'>Your breasts [pick("shrink down to", "decrease into", "diminish into", "deflate into", "shrivel regretfully into", "contracts into")] a [uppertext(size)]-cup.</span>")
|
||||
|
||||
/obj/item/organ/genital/breasts/get_features(mob/living/carbon/human/H)
|
||||
var/datum/dna/D = H.dna
|
||||
if(D.species.use_skintones && D.features["genitals_use_skintone"])
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
else
|
||||
color = "#[D.features["breasts_color"]]"
|
||||
size = D.features["breasts_size"]
|
||||
shape = D.features["breasts_shape"]
|
||||
if(!D.features["breasts_producing"])
|
||||
DISABLE_BITFIELD(genital_flags, GENITAL_FUID_PRODUCTION|CAN_CLIMAX_WITH|CAN_MASTURBATE_WITH)
|
||||
if(!isnum(size))
|
||||
cached_size = breast_values[size]
|
||||
else
|
||||
cached_size = size
|
||||
size = breast_values[size]
|
||||
prev_size = cached_size
|
||||
toggle_visibility(D.features["breasts_visibility"], FALSE)
|
||||
|
||||
#undef BREASTS_ICON_MIN_SIZE
|
||||
#undef BREASTS_ICON_MAX_SIZE
|
||||
@@ -0,0 +1,109 @@
|
||||
/obj/item/organ/genital/penis
|
||||
name = "penis"
|
||||
desc = "A male reproductive organ."
|
||||
icon_state = "penis"
|
||||
icon = 'icons/obj/genitals/penis.dmi'
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_PENIS
|
||||
masturbation_verb = "stroke"
|
||||
arousal_verb = "You pop a boner"
|
||||
unarousal_verb = "Your boner goes down"
|
||||
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_CAN_AROUSE|UPDATE_OWNER_APPEARANCE|GENITAL_UNDIES_HIDDEN|GENITAL_CAN_TAUR
|
||||
linked_organ_slot = ORGAN_SLOT_TESTICLES
|
||||
fluid_transfer_factor = 0.5
|
||||
shape = DEF_COCK_SHAPE
|
||||
size = 2 //arbitrary value derived from length and diameter for sprites.
|
||||
layer_index = PENIS_LAYER_INDEX
|
||||
var/length = 6 //inches
|
||||
|
||||
var/prev_length = 6 //really should be renamed to prev_length
|
||||
var/diameter = 4.38
|
||||
var/diameter_ratio = COCK_DIAMETER_RATIO_DEF //0.25; check citadel_defines.dm
|
||||
|
||||
/obj/item/organ/genital/penis/modify_size(modifier, min = -INFINITY, max = INFINITY)
|
||||
var/new_value = clamp(length + modifier, min, max)
|
||||
if(new_value == length)
|
||||
return
|
||||
prev_length = length
|
||||
length = clamp(length + modifier, min, max)
|
||||
update()
|
||||
..()
|
||||
|
||||
/obj/item/organ/genital/penis/update_size(modified = FALSE)
|
||||
if(length <= 0)//I don't actually know what round() does to negative numbers, so to be safe!!
|
||||
if(owner)
|
||||
to_chat(owner, "<span class='warning'>You feel your tallywacker shrinking away from your body as your groin flattens out!</b></span>")
|
||||
QDEL_IN(src, 1)
|
||||
if(linked_organ)
|
||||
QDEL_IN(linked_organ, 1)
|
||||
return
|
||||
var/rounded_length = round(length)
|
||||
var/new_size
|
||||
var/enlargement = FALSE
|
||||
switch(rounded_length)
|
||||
if(0 to 6) //If modest size
|
||||
new_size = 1
|
||||
if(7 to 11) //If large
|
||||
new_size = 2
|
||||
if(12 to 20) //If massive
|
||||
new_size = 3
|
||||
if(21 to 34) //If massive and due for large effects
|
||||
new_size = 3
|
||||
enlargement = TRUE
|
||||
if(35 to INFINITY) //If comical
|
||||
new_size = 4 //no new sprites for anything larger yet
|
||||
enlargement = TRUE
|
||||
if(owner)
|
||||
var/status_effect = owner.has_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
|
||||
if(enlargement && !status_effect)
|
||||
owner.apply_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
|
||||
else if(!enlargement && status_effect)
|
||||
owner.remove_status_effect(STATUS_EFFECT_PENIS_ENLARGEMENT)
|
||||
if(linked_organ)
|
||||
linked_organ.size = clamp(size + new_size, BALLS_SIZE_MIN, BALLS_SIZE_MAX)
|
||||
linked_organ.update()
|
||||
size = new_size
|
||||
|
||||
if(owner)
|
||||
if (round(length) > round(prev_length))
|
||||
to_chat(owner, "<span class='warning'>Your [pick(GLOB.dick_nouns)] [pick("swells up to", "flourishes into", "expands into", "bursts forth into", "grows eagerly into", "amplifys into")] a [uppertext(round(length))] inch penis.</b></span>")
|
||||
else if ((round(length) < round(prev_length)) && (length > 0.5))
|
||||
to_chat(owner, "<span class='warning'>Your [pick(GLOB.dick_nouns)] [pick("shrinks down to", "decreases into", "diminishes into", "deflates into", "shrivels regretfully into", "contracts into")] a [uppertext(round(length))] inch penis.</b></span>")
|
||||
icon_state = sanitize_text("penis_[shape]_[size]")
|
||||
diameter = (length * diameter_ratio)//Is it just me or is this ludicous, why not make it exponentially decay?
|
||||
|
||||
|
||||
/obj/item/organ/genital/penis/update_appearance()
|
||||
. = ..()
|
||||
var/datum/sprite_accessory/S = GLOB.cock_shapes_list[shape]
|
||||
var/icon_shape = S ? S.icon_state : "human"
|
||||
icon_state = "penis_[icon_shape]_[size]"
|
||||
var/lowershape = lowertext(shape)
|
||||
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
if(!H.dna.skin_tone_override)
|
||||
icon_state += "_s"
|
||||
else
|
||||
color = "#[owner.dna.features["cock_color"]]"
|
||||
if(genital_flags & GENITAL_CAN_TAUR && S?.taur_icon && (!S.feat_taur || owner.dna.features[S.feat_taur]) && owner.dna.species.mutant_bodyparts["taur"])
|
||||
var/datum/sprite_accessory/taur/T = GLOB.taur_list[owner.dna.features["taur"]]
|
||||
if(T.taur_mode & S.accepted_taurs) //looks out of place on those.
|
||||
lowershape = "taur, [lowershape]"
|
||||
|
||||
desc = "You see [aroused_state ? "an erect" : "a flaccid"] [lowershape] [name]. You estimate it's about [round(length, 0.25)] inch[round(length, 0.25) != 1 ? "es" : ""] long and [round(diameter, 0.25)] inch[round(diameter, 0.25) != 1 ? "es" : ""] in diameter."
|
||||
|
||||
/obj/item/organ/genital/penis/get_features(mob/living/carbon/human/H)
|
||||
var/datum/dna/D = H.dna
|
||||
if(D.species.use_skintones && D.features["genitals_use_skintone"])
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
else
|
||||
color = "#[D.features["cock_color"]]"
|
||||
length = D.features["cock_length"]
|
||||
diameter_ratio = D.features["cock_diameter_ratio"]
|
||||
shape = D.features["cock_shape"]
|
||||
prev_length = length
|
||||
toggle_visibility(D.features["cock_visibility"], FALSE)
|
||||
@@ -0,0 +1,67 @@
|
||||
/obj/item/organ/genital/testicles
|
||||
name = "testicles"
|
||||
desc = "A male reproductive organ."
|
||||
icon_state = "testicles"
|
||||
icon = 'icons/obj/genitals/testicles.dmi'
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_TESTICLES
|
||||
size = BALLS_SIZE_MIN
|
||||
arousal_verb = "Your balls ache a little"
|
||||
unarousal_verb = "Your balls finally stop aching, again"
|
||||
linked_organ_slot = ORGAN_SLOT_PENIS
|
||||
genital_flags = CAN_MASTURBATE_WITH|MASTURBATE_LINKED_ORGAN|GENITAL_FUID_PRODUCTION|UPDATE_OWNER_APPEARANCE|GENITAL_UNDIES_HIDDEN
|
||||
var/size_name = "average"
|
||||
shape = DEF_BALLS_SHAPE
|
||||
fluid_id = /datum/reagent/consumable/semen
|
||||
masturbation_verb = "massage"
|
||||
layer_index = TESTICLES_LAYER_INDEX
|
||||
|
||||
/obj/item/organ/genital/testicles/generate_fluid()
|
||||
if(!linked_organ && !update_link())
|
||||
return FALSE
|
||||
return ..()
|
||||
// in memoriam "Your balls finally feel full, again." ??-2020
|
||||
|
||||
/obj/item/organ/genital/testicles/upon_link()
|
||||
size = linked_organ.size
|
||||
update_size()
|
||||
update_appearance()
|
||||
|
||||
/obj/item/organ/genital/testicles/update_size(modified = FALSE)
|
||||
switch(size)
|
||||
if(BALLS_SIZE_MIN)
|
||||
size_name = "average"
|
||||
if(BALLS_SIZE_DEF)
|
||||
size_name = "enlarged"
|
||||
if(BALLS_SIZE_MAX)
|
||||
size_name = "engorged"
|
||||
else
|
||||
size_name = "nonexistant"
|
||||
|
||||
/obj/item/organ/genital/testicles/update_appearance()
|
||||
. = ..()
|
||||
desc = "You see an [size_name] pair of testicles."
|
||||
var/datum/sprite_accessory/S = GLOB.balls_shapes_list[shape]
|
||||
var/icon_shape = S ? S.icon_state : "single"
|
||||
icon_state = "testicles_[icon_shape]_[size]"
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
if(!H.dna.skin_tone_override)
|
||||
icon_state += "_s"
|
||||
else
|
||||
color = "#[owner.dna.features["balls_color"]]"
|
||||
|
||||
/obj/item/organ/genital/testicles/get_features(mob/living/carbon/human/H)
|
||||
var/datum/dna/D = H.dna
|
||||
if(D.species.use_skintones && D.features["genitals_use_skintone"])
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
else
|
||||
color = "#[D.features["balls_color"]]"
|
||||
shape = D.features["balls_shape"]
|
||||
fluid_rate = D.features["balls_cum_rate"]
|
||||
fluid_mult = D.features["balls_cum_mult"]
|
||||
fluid_efficiency = D.features["balls_efficiency"]
|
||||
toggle_visibility(D.features["balls_visibility"], FALSE)
|
||||
@@ -0,0 +1,74 @@
|
||||
/obj/item/organ/genital/vagina
|
||||
name = "vagina"
|
||||
desc = "A female reproductive organ."
|
||||
icon = 'icons/obj/genitals/vagina.dmi'
|
||||
icon_state = ORGAN_SLOT_VAGINA
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = "vagina"
|
||||
size = 1 //There is only 1 size right now
|
||||
shape = DEF_VAGINA_SHAPE
|
||||
genital_flags = CAN_MASTURBATE_WITH|CAN_CLIMAX_WITH|GENITAL_CAN_AROUSE|GENITAL_UNDIES_HIDDEN
|
||||
masturbation_verb = "finger"
|
||||
arousal_verb = "You feel wetness on your crotch"
|
||||
unarousal_verb = "You no longer feel wet"
|
||||
fluid_transfer_factor = 0.1 //Yes, some amount is exposed to you, go get your AIDS
|
||||
layer_index = VAGINA_LAYER_INDEX
|
||||
var/cap_length = 8//D E P T H (cap = capacity)
|
||||
var/cap_girth = 12
|
||||
var/cap_girth_ratio = 1.5
|
||||
var/clits = 1
|
||||
var/clit_diam = 0.25
|
||||
var/clit_len = 0.25
|
||||
var/list/vag_types = list("tentacle", "dentata", "hairy", "spade", "furred")
|
||||
|
||||
/obj/item/organ/genital/vagina/update_appearance()
|
||||
. = ..()
|
||||
icon_state = "vagina"
|
||||
var/lowershape = lowertext(shape)
|
||||
var/details
|
||||
|
||||
switch(lowershape)
|
||||
if("tentacle")
|
||||
details = "Its opening is lined with several tentacles and "
|
||||
if("dentata")
|
||||
details = "There's teeth inside it and it "
|
||||
if("hairy")
|
||||
details = "It has quite a bit of hair growing on it and "
|
||||
if("human")
|
||||
details = "It is taut with smooth skin, though without much hair and "
|
||||
if("gaping")
|
||||
details = "It is gaping slightly open, though without much hair and "
|
||||
if("spade")
|
||||
details = "It is a plush canine spade, it "
|
||||
if("furred")
|
||||
details = "It has neatly groomed fur around the outer folds, it "
|
||||
else
|
||||
details = "It has an exotic shape and "
|
||||
if(aroused_state)
|
||||
details += "is slick with female arousal."
|
||||
else
|
||||
details += "seems to be dry."
|
||||
|
||||
desc = "You see a vagina. [details]"
|
||||
|
||||
if(owner)
|
||||
if(owner.dna.species.use_skintones && owner.dna.features["genitals_use_skintone"])
|
||||
if(ishuman(owner)) // Check before recasting type, although someone fucked up if you're not human AND have use_skintones somehow...
|
||||
var/mob/living/carbon/human/H = owner // only human mobs have skin_tone, which we need.
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
if(!H.dna.skin_tone_override)
|
||||
icon_state += "_s"
|
||||
else
|
||||
color = "#[owner.dna.features["vag_color"]]"
|
||||
if(ishuman(owner))
|
||||
var/mob/living/carbon/human/H = owner
|
||||
H.update_genitals()
|
||||
|
||||
/obj/item/organ/genital/vagina/get_features(mob/living/carbon/human/H)
|
||||
var/datum/dna/D = H.dna
|
||||
if(D.species.use_skintones && D.features["genitals_use_skintone"])
|
||||
color = SKINTONE2HEX(H.skin_tone)
|
||||
else
|
||||
color = "[D.features["vag_color"]]"
|
||||
shape = "[D.features["vag_shape"]]"
|
||||
toggle_visibility(D.features["vag_visibility"], FALSE)
|
||||
@@ -0,0 +1,10 @@
|
||||
/obj/item/organ/genital/womb
|
||||
name = "womb"
|
||||
desc = "A female reproductive organ."
|
||||
icon = 'icons/obj/genitals/vagina.dmi'
|
||||
icon_state = "womb"
|
||||
zone = BODY_ZONE_PRECISE_GROIN
|
||||
slot = ORGAN_SLOT_WOMB
|
||||
genital_flags = GENITAL_INTERNAL|GENITAL_FUID_PRODUCTION
|
||||
fluid_id = /datum/reagent/consumable/femcum
|
||||
linked_organ_slot = ORGAN_SLOT_VAGINA
|
||||
@@ -0,0 +1,159 @@
|
||||
//////////
|
||||
//DILDOS//
|
||||
//////////
|
||||
/obj/item/dildo
|
||||
name = "dildo"
|
||||
desc = "Floppy!"
|
||||
icon = 'icons/obj/genitals/dildo.dmi'
|
||||
force = 0
|
||||
hitsound = 'sound/weapons/tap.ogg'
|
||||
throwforce = 0
|
||||
icon_state = "dildo_knotted_2"
|
||||
alpha = 192//transparent
|
||||
var/can_customize = FALSE
|
||||
var/dildo_shape = "human"
|
||||
var/dildo_size = 2
|
||||
var/dildo_type = "dildo"//pretty much just used for the icon state
|
||||
var/random_color = TRUE
|
||||
var/random_size = FALSE
|
||||
var/random_shape = FALSE
|
||||
var/is_knotted = FALSE
|
||||
//Lists moved to _cit_helpers.dm as globals so they're not instanced individually
|
||||
|
||||
/obj/item/dildo/proc/update_appearance()
|
||||
icon_state = "[dildo_type]_[dildo_shape]_[dildo_size]"
|
||||
var/sizeword = ""
|
||||
switch(dildo_size)
|
||||
if(1)
|
||||
sizeword = "small "
|
||||
if(3)
|
||||
sizeword = "big "
|
||||
if(4)
|
||||
sizeword = "huge "
|
||||
if(5)
|
||||
sizeword = "gigantic "
|
||||
|
||||
name = "[sizeword][dildo_shape] [can_customize ? "custom " : ""][dildo_type]"
|
||||
|
||||
/obj/item/dildo/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
|
||||
return
|
||||
customize(user)
|
||||
return TRUE
|
||||
|
||||
/obj/item/dildo/proc/customize(mob/living/user)
|
||||
if(!can_customize)
|
||||
return FALSE
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/color_choice = input(user,"Choose a color for your dildo.","Dildo Color") as null|anything in GLOB.dildo_colors
|
||||
if(src && color_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_inlist(color_choice, GLOB.dildo_colors, "Red")
|
||||
color = GLOB.dildo_colors[color_choice]
|
||||
update_appearance()
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/shape_choice = input(user,"Choose a shape for your dildo.","Dildo Shape") as null|anything in GLOB.dildo_shapes
|
||||
if(src && shape_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_inlist(shape_choice, GLOB.dildo_colors, "Knotted")
|
||||
dildo_shape = GLOB.dildo_shapes[shape_choice]
|
||||
update_appearance()
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/size_choice = input(user,"Choose the size for your dildo.","Dildo Size") as null|anything in GLOB.dildo_sizes
|
||||
if(src && size_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_inlist(size_choice, GLOB.dildo_colors, "Medium")
|
||||
dildo_size = GLOB.dildo_sizes[size_choice]
|
||||
update_appearance()
|
||||
if(src && !user.incapacitated() && in_range(user,src))
|
||||
var/transparency_choice = input(user,"Choose the transparency of your dildo. Lower is more transparent!(192-255)","Dildo Transparency") as null|num
|
||||
if(src && transparency_choice && !user.incapacitated() && in_range(user,src))
|
||||
sanitize_integer(transparency_choice, 192, 255, 192)
|
||||
alpha = transparency_choice
|
||||
update_appearance()
|
||||
return TRUE
|
||||
|
||||
/obj/item/dildo/Initialize()
|
||||
. = ..()
|
||||
if(random_color == TRUE)
|
||||
var/randcolor = pick(GLOB.dildo_colors)
|
||||
color = GLOB.dildo_colors[randcolor]
|
||||
if(random_shape == TRUE)
|
||||
var/randshape = pick(GLOB.dildo_shapes)
|
||||
dildo_shape = GLOB.dildo_shapes[randshape]
|
||||
if(random_size == TRUE)
|
||||
var/randsize = pick(GLOB.dildo_sizes)
|
||||
dildo_size = GLOB.dildo_sizes[randsize]
|
||||
update_appearance()
|
||||
alpha = rand(192, 255)
|
||||
pixel_y = rand(-7,7)
|
||||
pixel_x = rand(-7,7)
|
||||
|
||||
/obj/item/dildo/examine(mob/user)
|
||||
. = ..()
|
||||
if(can_customize)
|
||||
. += "<span class='notice'>Alt-Click \the [src.name] to customize it.</span>"
|
||||
|
||||
/obj/item/dildo/random//totally random
|
||||
name = "random dildo"//this name will show up in vendors and shit so you know what you're vending(or don't, i guess :^))
|
||||
random_color = TRUE
|
||||
random_shape = TRUE
|
||||
random_size = TRUE
|
||||
|
||||
/obj/item/dildo/knotted
|
||||
dildo_shape = "knotted"
|
||||
name = "knotted dildo"
|
||||
attack_verb = list("penetrated", "knotted", "slapped", "inseminated")
|
||||
|
||||
obj/item/dildo/human
|
||||
dildo_shape = "human"
|
||||
name = "human dildo"
|
||||
attack_verb = list("penetrated", "slapped", "inseminated")
|
||||
|
||||
obj/item/dildo/plain
|
||||
dildo_shape = "plain"
|
||||
name = "plain dildo"
|
||||
attack_verb = list("penetrated", "slapped", "inseminated")
|
||||
|
||||
obj/item/dildo/flared
|
||||
dildo_shape = "flared"
|
||||
name = "flared dildo"
|
||||
attack_verb = list("penetrated", "slapped", "neighed", "gaped", "prolapsed", "inseminated")
|
||||
|
||||
obj/item/dildo/flared/huge
|
||||
name = "literal horse cock"
|
||||
desc = "THIS THING IS HUGE!"
|
||||
dildo_size = 4
|
||||
|
||||
obj/item/dildo/custom
|
||||
name = "customizable dildo"
|
||||
desc = "Thanks to significant advances in synthetic nanomaterials, this dildo is capable of taking on many different forms to fit the user's preferences! Pricy!"
|
||||
can_customize = TRUE
|
||||
random_color = TRUE
|
||||
random_shape = TRUE
|
||||
random_size = TRUE
|
||||
|
||||
// Suicide acts, by request
|
||||
|
||||
/obj/item/dildo/proc/manual_suicide(mob/living/user)
|
||||
user.visible_message("<span class='suicide'>[user] finally finishes deepthroating the [src], and their life.</span>")
|
||||
user.adjustOxyLoss(200)
|
||||
user.death(0)
|
||||
|
||||
/obj/item/dildo/suicide_act(mob/living/user)
|
||||
// is_knotted = ((src.dildo_shape == "knotted")?"They swallowed the knot":"Their face is turning blue")
|
||||
if(do_after(user,17,target=src))
|
||||
user.visible_message("<span class='suicide'>[user] tears-up and gags as they shove [src] down their throat! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
playsound(loc, 'sound/weapons/gagging.ogg', 50, 1, -1)
|
||||
user.Stun(150)
|
||||
user.adjust_blurriness(8)
|
||||
var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES)
|
||||
eyes?.applyOrganDamage(10)
|
||||
return MANUAL_SUICIDE
|
||||
|
||||
/obj/item/dildo/flared/huge/suicide_act(mob/living/user)
|
||||
if(do_after(user,35,target=src))
|
||||
user.visible_message("<span class='suicide'>[user] tears-up and gags as they try to deepthroat the [src]! WHY WOULD THEY DO THAT? It looks like [user.p_theyre()] trying to commit suicide!!</span>")
|
||||
playsound(loc, 'sound/weapons/gagging.ogg', 50, 2, -1)
|
||||
user.Stun(300)
|
||||
user.adjust_blurriness(8)
|
||||
return MANUAL_SUICIDE
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
temperature_archived = temperature
|
||||
|
||||
/turf/open/archive()
|
||||
ARCHIVE_TEMPERATURE(air)
|
||||
ARCHIVE(air)
|
||||
archived_cycle = SSair.times_fired
|
||||
temperature_archived = temperature
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
if (planet_atmos) //share our air with the "atmosphere" "above" the turf
|
||||
var/datum/gas_mixture/G = new
|
||||
G.copy_from_turf(src)
|
||||
ARCHIVE_TEMPERATURE(G)
|
||||
ARCHIVE(G)
|
||||
if(our_air.compare(G))
|
||||
if(!our_excited_group)
|
||||
var/datum/excited_group/EG = new
|
||||
|
||||
@@ -17,6 +17,7 @@ GLOBAL_LIST_INIT(meta_gas_ids, meta_gas_id_list())
|
||||
GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
/datum/gas_mixture
|
||||
var/list/gases = list()
|
||||
var/list/gas_archive = list()
|
||||
var/temperature = 0 //kelvins
|
||||
var/tmp/temperature_archived = 0
|
||||
var/volume = CELL_VOLUME //liters
|
||||
@@ -31,13 +32,25 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
|
||||
//PV = nRT
|
||||
|
||||
/datum/gas_mixture/proc/heat_capacity() //joules per kelvin
|
||||
/datum/gas_mixture/proc/heat_capacity()
|
||||
|
||||
/datum/gas_mixture/proc/archived_heat_capacity()
|
||||
|
||||
/datum/gas_mixture/heat_capacity() //joules per kelvin
|
||||
var/list/cached_gases = gases
|
||||
var/list/cached_gasheats = GLOB.meta_gas_specific_heats
|
||||
. = 0
|
||||
for(var/id in cached_gases)
|
||||
. += cached_gases[id] * cached_gasheats[id]
|
||||
|
||||
/datum/gas_mixture/archived_heat_capacity()
|
||||
// lots of copypasta but heat_capacity is the single proc called the most in a regular round, bar none, so performance loss adds up
|
||||
var/list/cached_gases = gas_archive
|
||||
var/list/cached_gasheats = GLOB.meta_gas_specific_heats
|
||||
. = 0
|
||||
for(var/id in cached_gases)
|
||||
. += cached_gases[id] * cached_gasheats[id]
|
||||
|
||||
/datum/gas_mixture/turf/heat_capacity() // Same as above except vacuums return HEAT_CAPACITY_VACUUM
|
||||
var/list/cached_gases = gases
|
||||
var/list/cached_gasheats = GLOB.meta_gas_specific_heats
|
||||
@@ -46,6 +59,14 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
if(!.)
|
||||
. += HEAT_CAPACITY_VACUUM //we want vacuums in turfs to have the same heat capacity as space
|
||||
|
||||
/datum/gas_mixture/turf/archived_heat_capacity() // Same as above except vacuums return HEAT_CAPACITY_VACUUM
|
||||
var/list/cached_gases = gas_archive
|
||||
var/list/cached_gasheats = GLOB.meta_gas_specific_heats
|
||||
for(var/id in cached_gases)
|
||||
. += cached_gases[id] * cached_gasheats[id]
|
||||
if(!.)
|
||||
. += HEAT_CAPACITY_VACUUM //we want vacuums in turfs to have the same heat capacity as space
|
||||
|
||||
/datum/gas_mixture/proc/total_moles()
|
||||
var/cached_gases = gases
|
||||
TOTAL_MOLES(cached_gases, .)
|
||||
@@ -67,6 +88,10 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
/datum/gas_mixture/proc/thermal_energy() //joules
|
||||
return THERMAL_ENERGY(src) //see code/__DEFINES/atmospherics.dm; use the define in performance critical areas
|
||||
|
||||
/datum/gas_mixture/proc/archive()
|
||||
//Update archived versions of variables
|
||||
//Returns: 1 in all cases
|
||||
|
||||
/datum/gas_mixture/proc/merge(datum/gas_mixture/giver)
|
||||
//Merges all air from giver into self. Deletes giver.
|
||||
//Returns: 1 if we are mutable, 0 otherwise
|
||||
@@ -111,6 +136,10 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
//Performs various reactions such as combustion or fusion (LOL)
|
||||
//Returns: 1 if any reaction took place; 0 otherwise
|
||||
|
||||
/datum/gas_mixture/archive()
|
||||
temperature_archived = temperature
|
||||
gas_archive = gases.Copy()
|
||||
return 1
|
||||
|
||||
/datum/gas_mixture/merge(datum/gas_mixture/giver)
|
||||
if(!giver)
|
||||
@@ -245,7 +274,7 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
//GAS TRANSFER
|
||||
for(var/id in cached_gases | sharer_gases) // transfer gases
|
||||
|
||||
delta = QUANTIZE(cached_gases[id] - sharer_gases[id])/(atmos_adjacent_turfs+1) //the amount of gas that gets moved between the mixtures
|
||||
delta = QUANTIZE(gas_archive[id] - sharer.gas_archive[id])/(atmos_adjacent_turfs+1) //the amount of gas that gets moved between the mixtures
|
||||
|
||||
if(delta && abs_temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
|
||||
gas_heat_capacity = delta * cached_gasheats[id]
|
||||
@@ -293,8 +322,8 @@ GLOBAL_LIST_INIT(meta_gas_fusions, meta_gas_fusion_list())
|
||||
sharer_temperature = sharer.temperature_archived
|
||||
var/temperature_delta = temperature_archived - sharer_temperature
|
||||
if(abs(temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
|
||||
var/self_heat_capacity = heat_capacity()
|
||||
sharer_heat_capacity = sharer_heat_capacity || sharer.heat_capacity()
|
||||
var/self_heat_capacity = archived_heat_capacity()
|
||||
sharer_heat_capacity = sharer_heat_capacity || sharer.archived_heat_capacity()
|
||||
|
||||
if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
|
||||
var/heat = conduction_coefficient*temperature_delta* \
|
||||
|
||||
@@ -10,24 +10,13 @@
|
||||
group = "Science"
|
||||
crate_type = /obj/structure/closet/crate/science
|
||||
|
||||
/* For later
|
||||
/datum/supply_pack/science/monkey
|
||||
/datum/supply_pack/science/ape //Ape out!
|
||||
name = "Ape Cube Crate"
|
||||
desc = "Pss what a new test subject with supper strangth, speed, and love for bananas all at the same time? Say no more... Contains a single ape cube. Dont add water!"
|
||||
contraband = TRUE
|
||||
cost = 2500
|
||||
contains = list (/obj/item/reagent_containers/food/snacks/monkeycube/ape)
|
||||
contains = list (/obj/item/reagent_containers/food/snacks/cube/ape)
|
||||
crate_name = "ape cube crate"
|
||||
*/
|
||||
|
||||
/datum/supply_pack/science/aliens
|
||||
name = "Advanced Alien Alloy Crate Crate"
|
||||
desc = "Hello brothers from the stars!!! Our fellow brethren have made contact at long last and gave us gifts man! They really did build the prymi- Connection Error- Bro we’ll send you a sheet of advanced alien alloy."
|
||||
cost = 15000
|
||||
DropPodOnly = TRUE
|
||||
contraband = TRUE
|
||||
contains = list(/obj/item/stack/sheet/mineral/abductor)
|
||||
crate_name = "alien bro alloy crate"
|
||||
|
||||
/datum/supply_pack/science/beakers
|
||||
name = "Chemistry Beakers Crate"
|
||||
|
||||
@@ -153,6 +153,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
"ipc_screen" = "Sunburst",
|
||||
"ipc_antenna" = "None",
|
||||
"flavor_text" = "",
|
||||
"ooc_notes" = "",
|
||||
"meat_type" = "Mammalian",
|
||||
"body_model" = MALE,
|
||||
"body_size" = RESIZE_DEFAULT_SIZE
|
||||
@@ -348,6 +349,16 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "[features["flavor_text"]]"
|
||||
else
|
||||
dat += "[TextPreview(features["flavor_text"])]...<BR>"
|
||||
dat += "<h2>OOC notes</h2>"
|
||||
dat += "<a href='?_src_=prefs;preference=ooc_notes;task=input'><b>Set OOC notes</b></a><br>"
|
||||
var/ooc_notes_len = length(features["ooc_notes"])
|
||||
if(ooc_notes_len <= 40)
|
||||
if(!ooc_notes_len)
|
||||
dat += "\[...\]"
|
||||
else
|
||||
dat += "[features["ooc_notes"]]"
|
||||
else
|
||||
dat += "[TextPreview(features["ooc_notes"])]...<BR>"
|
||||
dat += "<h2>Body</h2>"
|
||||
dat += "<b>Gender:</b><a style='display:block;width:100px' href='?_src_=prefs;preference=gender;task=input'>[gender == MALE ? "Male" : (gender == FEMALE ? "Female" : (gender == PLURAL ? "Non-binary" : "Object"))]</a><BR>"
|
||||
if(gender != NEUTER && pref_species.sexes)
|
||||
@@ -1510,6 +1521,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if(!isnull(msg))
|
||||
features["flavor_text"] = html_decode(msg)
|
||||
|
||||
if("ooc_notes")
|
||||
var/msg = stripped_multiline_input(usr, "Set always-visible OOC notes related to content preferences. THIS IS NOT FOR CHARACTE DESCRIPTIONS!!", "OOC notes", features["ooc_notes"], MAX_FLAVOR_LEN, TRUE)
|
||||
if(!isnull(msg))
|
||||
features["ooc_notes"] = html_decode(msg)
|
||||
|
||||
if("hair")
|
||||
var/new_hair = input(user, "Choose your character's hair colour:", "Character Preference","#"+hair_color) as color|null
|
||||
if(new_hair)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// You do not need to raise this if you are adding new values that have sane defaults.
|
||||
// Only raise this value when changing the meaning/format/name/layout of an existing value
|
||||
// where you would want the updater procs below to run
|
||||
#define SAVEFILE_VERSION_MAX 30
|
||||
#define SAVEFILE_VERSION_MAX 31
|
||||
|
||||
/*
|
||||
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
|
||||
@@ -535,6 +535,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
else //We have no old flavortext, default to new
|
||||
S["feature_flavor_text"] >> features["flavor_text"]
|
||||
|
||||
S["feature_ooc_notes"] >> features["ooc_notes"]
|
||||
|
||||
S["vore_flags"] >> vore_flags
|
||||
S["vore_taste"] >> vore_taste
|
||||
S["belly_prefs"] >> belly_prefs
|
||||
@@ -643,6 +645,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
|
||||
|
||||
features["flavor_text"] = copytext(features["flavor_text"], 1, MAX_FLAVOR_LEN)
|
||||
features["ooc_notes"] = copytext(features["ooc_notes"], 1, MAX_FLAVOR_LEN)
|
||||
|
||||
joblessrole = sanitize_integer(joblessrole, 1, 3, initial(joblessrole))
|
||||
//Validate job prefs
|
||||
@@ -746,6 +749,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
|
||||
WRITE_FILE(S["feature_has_womb"], features["has_womb"])
|
||||
|
||||
WRITE_FILE(S["feature_ooc_notes"], features["ooc_notes"])
|
||||
|
||||
//Custom names
|
||||
for(var/custom_name_id in GLOB.preferences_custom_names)
|
||||
var/savefile_slot_name = custom_name_id + "_name" //TODO remove this
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
|
||||
//LightToggle
|
||||
|
||||
/obj/item/clothing/head/helment/ComponentInitialize()
|
||||
/obj/item/clothing/head/helmet/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/update_icon_updates_onmob)
|
||||
|
||||
|
||||
@@ -73,18 +73,26 @@
|
||||
resistance_flags = FLAMMABLE
|
||||
actions_types = list(/datum/action/item_action/adjust)
|
||||
dog_fashion = /datum/dog_fashion/head/clown
|
||||
var/list/clownmask_designs = list()
|
||||
|
||||
/obj/item/clothing/mask/gas/clown_hat/Initialize(mapload)
|
||||
.=..()
|
||||
clownmask_designs = list(
|
||||
"True Form" = image(icon = src.icon, icon_state = "clown"),
|
||||
"The Feminist" = image(icon = src.icon, icon_state = "sexyclown"),
|
||||
"The Jester" = image(icon = src.icon, icon_state = "chaos"),
|
||||
"The Madman" = image(icon = src.icon, icon_state = "joker"),
|
||||
"The Rainbow Color" = image(icon = src.icon, icon_state = "rainbow")
|
||||
)
|
||||
|
||||
/obj/item/clothing/mask/gas/clown_hat/ui_action_click(mob/user)
|
||||
if(!istype(user) || user.incapacitated())
|
||||
return
|
||||
|
||||
var/list/options = list()
|
||||
options["True Form"] = "clown"
|
||||
options["The Feminist"] = "sexyclown"
|
||||
options["The Madman"] = "joker"
|
||||
options["The Rainbow Color"] ="rainbow"
|
||||
var/static/list/options = list("True Form" = "clown", "The Feminist" = "sexyclown", "The Madman" = "joker",
|
||||
"The Rainbow Color" ="rainbow", "The Jester" = "chaos")
|
||||
|
||||
var/choice = input(user,"To what form do you wish to Morph this mask?","Morph Mask") in options
|
||||
var/choice = show_radial_menu(user,src, clownmask_designs, custom_check = FALSE, radius = 36, require_near = TRUE)
|
||||
|
||||
if(src && choice && !user.incapacitated() && in_range(user,src))
|
||||
icon_state = options[choice]
|
||||
@@ -93,7 +101,7 @@
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
to_chat(user, "<span class='notice'>Your Clown Mask has now morphed into [choice], all praise the Honkmother!</span>")
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/obj/item/clothing/mask/gas/sexyclown
|
||||
name = "sexy-clown wig and mask"
|
||||
@@ -113,19 +121,26 @@
|
||||
flags_cover = MASKCOVERSEYES
|
||||
resistance_flags = FLAMMABLE
|
||||
actions_types = list(/datum/action/item_action/adjust)
|
||||
var/list/mimemask_designs = list()
|
||||
|
||||
|
||||
/obj/item/clothing/mask/gas/mime/Initialize(mapload)
|
||||
.=..()
|
||||
mimemask_designs = list(
|
||||
"Blanc" = image(icon = src.icon, icon_state = "mime"),
|
||||
"Excité" = image(icon = src.icon, icon_state = "sexymime"),
|
||||
"Triste" = image(icon = src.icon, icon_state = "sadmime"),
|
||||
"Effrayé" = image(icon = src.icon, icon_state = "scaredmime")
|
||||
)
|
||||
|
||||
|
||||
/obj/item/clothing/mask/gas/mime/ui_action_click(mob/user)
|
||||
if(!istype(user) || user.incapacitated())
|
||||
return
|
||||
|
||||
var/list/options = list()
|
||||
options["Blanc"] = "mime"
|
||||
options["Triste"] = "sadmime"
|
||||
options["Effrayé"] = "scaredmime"
|
||||
options["Excité"] ="sexymime"
|
||||
var/static/list/options = list("Blanc" = "mime", "Triste" = "sadmime", "Effrayé" = "scaredmime", "Excité" ="sexymime")
|
||||
|
||||
var/choice = input(user,"To what form do you wish to Morph this mask?","Morph Mask") in options
|
||||
var/choice = show_radial_menu(user,src, mimemask_designs, custom_check = FALSE, radius = 36, require_near = TRUE)
|
||||
|
||||
if(src && choice && !user.incapacitated() && in_range(user,src))
|
||||
icon_state = options[choice]
|
||||
@@ -134,7 +149,7 @@
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
to_chat(user, "<span class='notice'>Your Mime Mask has now morphed into [choice]!</span>")
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/obj/item/clothing/mask/gas/monkeymask
|
||||
name = "monkey mask"
|
||||
@@ -187,18 +202,25 @@
|
||||
max_integrity = 100
|
||||
actions_types = list(/datum/action/item_action/adjust)
|
||||
dog_fashion = null
|
||||
var/list/tikimask_designs = list()
|
||||
|
||||
|
||||
/obj/item/clothing/mask/gas/tiki_mask/Initialize(mapload)
|
||||
.=..()
|
||||
tikimask_designs = list(
|
||||
"Original Tiki" = image(icon = src.icon, icon_state = "tiki_eyebrow"),
|
||||
"Happy Tiki" = image(icon = src.icon, icon_state = "tiki_happy"),
|
||||
"Confused Tiki" = image(icon = src.icon, icon_state = "tiki_confused"),
|
||||
"Angry Tiki" = image(icon = src.icon, icon_state = "tiki_angry")
|
||||
)
|
||||
|
||||
/obj/item/clothing/mask/gas/tiki_mask/ui_action_click(mob/user)
|
||||
|
||||
var/mob/M = usr
|
||||
var/list/options = list()
|
||||
options["Original Tiki"] = "tiki_eyebrow"
|
||||
options["Happy Tiki"] = "tiki_happy"
|
||||
options["Confused Tiki"] = "tiki_confused"
|
||||
options["Angry Tiki"] ="tiki_angry"
|
||||
var/static/list/options = list("Original Tiki" = "tiki_eyebrow", "Happy Tiki" = "tiki_happy", "Confused Tiki" = "tiki_confused",
|
||||
"Angry Tiki" = "tiki_angry")
|
||||
|
||||
var/choice = input(M,"To what form do you wish to change this mask?","Morph Mask") in options
|
||||
var/choice = show_radial_menu(user,src, tikimask_designs, custom_check = FALSE, radius = 36, require_near = TRUE)
|
||||
|
||||
if(src && choice && !M.stat && in_range(M,src))
|
||||
icon_state = options[choice]
|
||||
@@ -207,4 +229,4 @@
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
to_chat(M, "The Tiki Mask has now changed into the [choice] Mask!")
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
@@ -351,30 +351,40 @@
|
||||
resistance_flags = FLAMMABLE
|
||||
max_integrity = 100
|
||||
actions_types = list(/datum/action/item_action/adjust)
|
||||
var/list/papermask_designs = list()
|
||||
|
||||
|
||||
/obj/item/clothing/mask/paper/Initialize(mapload)
|
||||
.=..()
|
||||
papermask_designs = list(
|
||||
"Blank" = image(icon = src.icon, icon_state = "plainmask"),
|
||||
"Neutral" = image(icon = src.icon, icon_state = "neutralmask"),
|
||||
"Eyes" = image(icon = src.icon, icon_state = "eyemask"),
|
||||
"Sleeping" = image(icon = src.icon, icon_state = "sleepingmask"),
|
||||
"Heart" = image(icon = src.icon, icon_state = "heartmask"),
|
||||
"Core" = image(icon = src.icon, icon_state = "coremask"),
|
||||
"Plus" = image(icon = src.icon, icon_state = "plusmask"),
|
||||
"Square" = image(icon = src.icon, icon_state = "squaremask"),
|
||||
"Bullseye" = image(icon = src.icon, icon_state = "bullseyemask"),
|
||||
"Vertical" = image(icon = src.icon, icon_state = "verticalmask"),
|
||||
"Horizontal" = image(icon = src.icon, icon_state = "horizontalmask"),
|
||||
"X" = image(icon = src.icon, icon_state = "xmask"),
|
||||
"Bugeyes" = image(icon = src.icon, icon_state = "bugmask"),
|
||||
"Double" = image(icon = src.icon, icon_state = "doublemask"),
|
||||
"Mark" = image(icon = src.icon, icon_state = "markmask")
|
||||
)
|
||||
|
||||
/obj/item/clothing/mask/paper/ui_action_click(mob/user)
|
||||
if(!istype(user) || user.incapacitated())
|
||||
return
|
||||
|
||||
var/list/options = list()
|
||||
options["Blank"] = "plainmask"
|
||||
options["Neutral"] = "neutralmask"
|
||||
options["Eyes"] = "eyemask"
|
||||
options["Sleeping"] ="sleepingmask"
|
||||
options["Heart"] = "heartmask"
|
||||
options["Core"] = "coremask"
|
||||
options["Plus"] = "plusmask"
|
||||
options["Square"] ="squaremask"
|
||||
options["Bullseye"] = "bullseyemask"
|
||||
options["Vertical"] = "verticalmask"
|
||||
options["Horizontal"] = "horizontalmask"
|
||||
options["X"] ="xmask"
|
||||
options["Bugeyes"] = "bugmask"
|
||||
options["Double"] = "doublemask"
|
||||
options["Mark"] = "markmask"
|
||||
var/static/list/options = list("Blank" = "plainmask", "Neutral" = "neutralmask", "Eyes" = "eyemask",
|
||||
"Sleeping" ="sleepingmask", "Heart" = "heartmask", "Core" = "coremask",
|
||||
"Plus" = "plusmask", "Square" ="squaremask", "Bullseye" = "bullseyemask",
|
||||
"Vertical" = "verticalmask", "Horizontal" = "horizontalmask", "X" ="xmask",
|
||||
"Bugeyes" = "bugmask", "Double" = "doublemask", "Mark" = "markmask")
|
||||
|
||||
var/choice = input(user,"What symbol would you want on this mask?","Morph Mask") in options
|
||||
var/choice = show_radial_menu(user,src, papermask_designs, custom_check = FALSE, radius = 36, require_near = TRUE)
|
||||
|
||||
if(src && choice && !user.incapacitated() && in_range(user,src))
|
||||
icon_state = options[choice]
|
||||
|
||||
@@ -180,6 +180,10 @@
|
||||
name = "mining boots"
|
||||
desc = "Steel-toed mining boots for mining in hazardous environments. Very good at keeping toes uncrushed."
|
||||
icon_state = "explorer"
|
||||
cold_protection = FEET|LEGS
|
||||
min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
|
||||
heat_protection = FEET|LEGS
|
||||
max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT
|
||||
resistance_flags = FIRE_PROOF
|
||||
|
||||
/obj/item/clothing/shoes/cult
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/dropped(mob/user)
|
||||
..()
|
||||
if(suit)
|
||||
if(suit && !ismob(loc)) //equipped() will handle mob cases, so it doesn't disengage twice.
|
||||
suit.RemoveHelmet()
|
||||
soundloop.stop(user)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
role_name = "random animal"
|
||||
var/animals = 1
|
||||
var/one = "one"
|
||||
/// Blacklisted mob_biotypes - Hey can we like, not have player controlled megafauna?
|
||||
/// Blacklisted mob_biotypes - Hey can we like, not have player controlled megafauna?
|
||||
var/blacklisted_biotypes = MOB_EPIC
|
||||
fakeable = TRUE
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
SG.transfer_ckey(SA, FALSE)
|
||||
|
||||
SA.grant_all_languages(TRUE)
|
||||
SA.grant_all_languages(TRUE, FALSE, FALSE)
|
||||
|
||||
SA.sentience_act()
|
||||
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/sushi_basic
|
||||
name = "funa hosomaki"
|
||||
desc = "A small cylindrical kudzu skin, filled with rice and fish."
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "sushie_basic"
|
||||
bonus_reagents = list(/datum/reagent/consumable/nutriment/vitamin = 2)
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 4)
|
||||
@@ -69,7 +68,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/sushi_adv
|
||||
name = "funa nigiri"
|
||||
desc = "A peace of carp lightly placed on some rice."
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "sushie_adv"
|
||||
bonus_reagents = list(/datum/reagent/consumable/nutriment/vitamin = 2)
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 6)
|
||||
@@ -81,7 +79,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/sushi_pro
|
||||
name = "funa nigiri"
|
||||
desc = "A well prepared peace of the best of the carp fillet placed on rice. Looks fancy and fresh!"
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "sushie_pro"
|
||||
bonus_reagents = list(/datum/reagent/consumable/nutriment = 2, /datum/reagent/consumable/nutriment/vitamin = 2)
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 6, /datum/reagent/consumable/nutriment/vitamin = 2)
|
||||
@@ -398,6 +395,12 @@
|
||||
tastes = list("milk" = 1, "beef" = 1)
|
||||
dried_being = /mob/living/simple_animal/cow
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/cube/ape
|
||||
name = "ape cube"
|
||||
desc = "Don't add water."
|
||||
tastes = list("the jungle" = 1, "bananas" = 1, "jimmies" = 1)
|
||||
dried_being = /mob/living/simple_animal/hostile/gorilla
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/cube/egg
|
||||
//Well eggs normally are able to hatch into small birds, this one does not.
|
||||
//Also in order to have a normal egg hatch you need a hen to lay the egg that is able to hatch, meaning this one is for on-demand hen needs.
|
||||
|
||||
@@ -215,7 +215,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/tobiko
|
||||
name = "tobiko"
|
||||
desc = "Spider eggs wrapped in a thin salted Kudzu pod"
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "sushie_egg"
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 6, /datum/reagent/consumable/nutriment/vitamin = 2)
|
||||
filling_color = "#FF3333" // R225 G051 B051
|
||||
@@ -558,7 +557,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/riceball
|
||||
name = "onigiri"
|
||||
desc = "A ball of rice with some light salt and a wrap of Kudzu skin."
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "riceball"
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 6, /datum/reagent/consumable/sodiumchloride = 2)
|
||||
tastes = list("rice" = 3, "salt" = 1)
|
||||
|
||||
@@ -136,7 +136,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/tuna_sandwich
|
||||
name = "tuna sandwich"
|
||||
desc = "Both a salad and a sandwich in one."
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "tunasandwich"
|
||||
trash = /obj/item/trash/plate
|
||||
bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 3)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/sushi_rice
|
||||
name = "Sushi Rice"
|
||||
desc = "A bowl of sticky rice for making sushi."
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "sushi_rice"
|
||||
list_reagents = list(/datum/reagent/consumable/sodiumchloride = 5)
|
||||
tastes = list("rice" = 5, "salt" = 1)
|
||||
@@ -12,7 +11,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/sea_weed
|
||||
name = "Sea Weed Sheet"
|
||||
desc = "A thin, light salt sheet of plant mater. This is commenly used in sushi recipes,"
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "sea_weed"
|
||||
list_reagents = list(/datum/reagent/consumable/sodiumchloride = 2)
|
||||
tastes = list("plants" = 2, "salt" = 1)
|
||||
@@ -21,7 +19,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/tuna
|
||||
name = "Canned Tuna"
|
||||
desc = "A small can of tuna fish beloved by felines."
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "tuna_can"
|
||||
//trash = /obj/item/trash/tuna_used //I dont know if I like this idea - A Masked Cat
|
||||
list_reagents = list(/datum/reagent/consumable/sodiumchloride = 5, /datum/reagent/mercury = 2)
|
||||
@@ -32,7 +29,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/sushie_basic
|
||||
name = "Funa Hosomaki"
|
||||
desc = "A small cylindrical filled with rice and fish."
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "sushie_basic"
|
||||
bonus_reagents = list(/datum/reagent/consumable/nutriment/vitamin = 2)
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 1)
|
||||
@@ -44,7 +40,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/sushie_adv
|
||||
name = "Funa Nigiri"
|
||||
desc = "A pice of carp lightly placed on some rice."
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "sushie_adv"
|
||||
bonus_reagents = list(/datum/reagent/consumable/nutriment/vitamin = 2)
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 2)
|
||||
@@ -56,7 +51,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/sushie_pro
|
||||
name = "Funa Nigiri"
|
||||
desc = "A well prepared pice of the best of the carp fillet placed on rice. Looks fancy and fresh!"
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "sushie_pro"
|
||||
bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 2)
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 8, /datum/reagent/consumable/nutriment/vitamin = 1)
|
||||
@@ -68,7 +62,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/tobiko
|
||||
name = "Tobiko"
|
||||
desc = "Spider eggs wrapped in a thin salted Kudzu pod"
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "sushie_egg"
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 3, /datum/reagent/consumable/nutriment/vitamin = 2)
|
||||
filling_color = "#FF3333" // R225 G051 B051
|
||||
@@ -78,7 +71,6 @@
|
||||
/obj/item/reagent_containers/food/snacks/riceball
|
||||
name = "Onigiri"
|
||||
desc = "A ball of rice with some light salt and a wrap of Kudzu skin."
|
||||
icon = 'modular_citadel/icons/obj/food/food.dmi'
|
||||
icon_state = "riceball"
|
||||
list_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/consumable/sodiumchloride = 2)
|
||||
tastes = list("rice" = 4, "salt" = 1)
|
||||
|
||||
@@ -66,6 +66,11 @@
|
||||
/obj/item/instrument/proc/is_tuned()
|
||||
return tune_time_left > 0
|
||||
|
||||
/obj/item/instrument/dropped(mob/user)
|
||||
. = ..()
|
||||
if((loc != user) && (user.machine == src))
|
||||
user.set_machine(null)
|
||||
|
||||
/obj/item/instrument/interact(mob/user)
|
||||
ui_interact(user)
|
||||
|
||||
|
||||
@@ -222,7 +222,9 @@
|
||||
tempo = sanitize_tempo(600 / bpm)
|
||||
|
||||
/// Updates the window for our user. Override in subtypes.
|
||||
/datum/song/proc/updateDialog(mob/user)
|
||||
/datum/song/proc/updateDialog(mob/user = usr)
|
||||
if(user.machine != src)
|
||||
return
|
||||
ui_interact(user)
|
||||
|
||||
/datum/song/process(wait)
|
||||
@@ -277,8 +279,10 @@
|
||||
// subtype for handheld instruments, like violin
|
||||
/datum/song/handheld
|
||||
|
||||
/datum/song/handheld/updateDialog(mob/user)
|
||||
parent.ui_interact(user || usr)
|
||||
/datum/song/handheld/updateDialog(mob/user = usr)
|
||||
if(user.machine != src)
|
||||
return
|
||||
parent.ui_interact(user)
|
||||
|
||||
/datum/song/handheld/should_stop_playing(mob/user)
|
||||
. = ..()
|
||||
@@ -290,8 +294,10 @@
|
||||
// subtype for stationary structures, like pianos
|
||||
/datum/song/stationary
|
||||
|
||||
/datum/song/stationary/updateDialog(mob/user)
|
||||
parent.ui_interact(user || usr)
|
||||
/datum/song/stationary/updateDialog(mob/user = usr)
|
||||
if(user.machine != src)
|
||||
return
|
||||
parent.ui_interact(user)
|
||||
|
||||
/datum/song/stationary/should_stop_playing(mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -865,7 +865,7 @@
|
||||
var/translated = FALSE
|
||||
if(speaker && message)
|
||||
if(raw_message)
|
||||
if(message_langs != get_default_language())
|
||||
if(message_langs != get_selected_language())
|
||||
translated = TRUE
|
||||
set_pin_data(IC_OUTPUT, 1, speaker.GetVoice())
|
||||
set_pin_data(IC_OUTPUT, 2, raw_message)
|
||||
|
||||
@@ -41,4 +41,4 @@
|
||||
if(visualsOnly)
|
||||
return
|
||||
|
||||
H.grant_all_languages(omnitongue=TRUE)
|
||||
H.grant_all_languages(TRUE, TRUE, TRUE, LANGUAGE_CURATOR)
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
return
|
||||
|
||||
to_chat(user, "<span class='boldannounce'>You start skimming through [src], and suddenly your mind is filled with codewords and responses.</span>")
|
||||
user.grant_language(/datum/language/codespeak)
|
||||
user.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MIND)
|
||||
|
||||
use_charge(user)
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
M.visible_message("<span class='danger'>[user] beats [M] over the head with [src]!</span>", "<span class='userdanger'>[user] beats you over the head with [src]!</span>", "<span class='italics'>You hear smacking.</span>")
|
||||
else
|
||||
M.visible_message("<span class='notice'>[user] teaches [M] by beating [M.p_them()] over the head with [src]!</span>", "<span class='boldnotice'>As [user] hits you with [src], codewords and responses flow through your mind.</span>", "<span class='italics'>You hear smacking.</span>")
|
||||
M.grant_language(/datum/language/codespeak)
|
||||
M.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MIND)
|
||||
use_charge(user)
|
||||
|
||||
/obj/item/codespeak_manual/proc/use_charge(mob/user)
|
||||
|
||||
@@ -1,149 +1,332 @@
|
||||
/datum/language_holder
|
||||
var/list/languages = list(/datum/language/common)
|
||||
var/list/shadow_languages = list()
|
||||
var/only_speaks_language = null
|
||||
var/selected_default_language = null
|
||||
var/datum/language_menu/language_menu
|
||||
/*!Language holders will either exist in an atom/movable or a mind. Creation of language holders happens
|
||||
automatically when they are needed, for example when something tries to speak.
|
||||
Where a mind is available, the mind language holder will be the one "in charge". The mind holder
|
||||
will update its languages based on the atom holder, and will get updated as part of
|
||||
transformations and other events that cause new languages to become available.
|
||||
Every language holder has three lists of languages (and sources for each of them):
|
||||
- understood_languages
|
||||
- spoken_languages
|
||||
- blocked_languages
|
||||
Understood languages let you understand them, spoken languages lets you speak them
|
||||
(if your tongue is compatible), and blocked languages will let you do neither no matter
|
||||
what the source of the language is.
|
||||
Language holders are designed to mostly only ever require the use the helpers in atom/movable
|
||||
to achieve your goals, but it is also possible to work on them directly if needed. Any adding
|
||||
and removing of languages and sources should only happen through the procs, as directly changing
|
||||
these will mess something up somewhere down the line.
|
||||
All atom movables have the initial_language_holder var which allows you to set the default language
|
||||
holder to create. For example, /datum/language_holder/alien will give you xenocommon and a block for
|
||||
galactic common. Human species also have a default language holder var that will be updated on
|
||||
species change, initial_species_holder.
|
||||
Key procs
|
||||
* [grant_language](atom/movable.html#proc/grant_language)
|
||||
* [remove_language](atom/movable.html#proc/remove_language)
|
||||
* [add_blocked_language](atom/movable.html#proc/add_blocked_language)
|
||||
* [remove_blocked_language](atom/movable.html#proc/remove_blocked_language)
|
||||
* [grant_all_languages](atom/movable.html#proc/grant_all_languages)
|
||||
* [remove_all_languages](atom/movable.html#proc/remove_all_languages)
|
||||
* [has_language](atom/movable.html#proc/has_language)
|
||||
* [can_speak_language](atom/movable.html#proc/can_speak_language)
|
||||
* [get_selected_language](atom/movable.html#proc/get_selected_language)
|
||||
* [update_atom_languages](atom/movable.html#proc/update_atom_languages)
|
||||
*/
|
||||
|
||||
/datum/language_holder
|
||||
/// Understood languages.
|
||||
var/list/understood_languages = list(/datum/language/common = list(LANGUAGE_MIND))
|
||||
/// A list of languages that can be spoken. Tongue organ may also set limits beyond this list.
|
||||
var/list/spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
|
||||
/// A list of blocked languages. Used to prevent understanding and speaking certain languages, ie for certain mobs, mutations etc.
|
||||
var/list/blocked_languages = list()
|
||||
/// If true, overrides tongue limitations.
|
||||
var/omnitongue = FALSE
|
||||
/// Handles displaying the language menu UI.
|
||||
var/datum/language_menu/language_menu
|
||||
/// Currently spoken language
|
||||
var/selected_language
|
||||
/// Tracks the entity that owns the holder.
|
||||
var/owner
|
||||
|
||||
/datum/language_holder/New(owner)
|
||||
src.owner = owner
|
||||
|
||||
languages = typecacheof(languages)
|
||||
shadow_languages = typecacheof(shadow_languages)
|
||||
|
||||
/datum/language_holder/Destroy()
|
||||
owner = null
|
||||
QDEL_NULL(language_menu)
|
||||
languages.Cut()
|
||||
shadow_languages.Cut()
|
||||
return ..()
|
||||
|
||||
/datum/language_holder/proc/copy(newowner)
|
||||
var/datum/language_holder/copy = new(newowner)
|
||||
copy.languages = src.languages.Copy()
|
||||
// shadow languages are not copied.
|
||||
copy.only_speaks_language = src.only_speaks_language
|
||||
copy.selected_default_language = src.selected_default_language
|
||||
// language menu is not copied, that's tied to the holder.
|
||||
copy.omnitongue = src.omnitongue
|
||||
return copy
|
||||
|
||||
/datum/language_holder/proc/grant_language(datum/language/dt, shadow = FALSE)
|
||||
if(shadow)
|
||||
shadow_languages[dt] = TRUE
|
||||
else
|
||||
languages[dt] = TRUE
|
||||
|
||||
/datum/language_holder/proc/grant_all_languages(omnitongue=FALSE)
|
||||
for(var/la in GLOB.all_languages)
|
||||
grant_language(la)
|
||||
|
||||
if(omnitongue)
|
||||
src.omnitongue = TRUE
|
||||
|
||||
/datum/language_holder/proc/get_random_understood_language()
|
||||
var/list/possible = list()
|
||||
for(var/dt in languages)
|
||||
possible += dt
|
||||
. = safepick(possible)
|
||||
|
||||
/datum/language_holder/proc/remove_language(datum/language/dt, shadow = FALSE)
|
||||
if(shadow)
|
||||
shadow_languages -= dt
|
||||
else
|
||||
languages -= dt
|
||||
|
||||
/datum/language_holder/proc/remove_all_languages()
|
||||
languages.Cut()
|
||||
|
||||
/datum/language_holder/proc/has_language(datum/language/dt)
|
||||
if(is_type_in_typecache(dt, languages))
|
||||
return LANGUAGE_KNOWN
|
||||
else
|
||||
var/atom/movable/AM = get_atom()
|
||||
var/datum/language_holder/L = AM.get_language_holder(shadow=FALSE)
|
||||
if(L != src)
|
||||
if(is_type_in_typecache(dt, L.shadow_languages))
|
||||
return LANGUAGE_SHADOWED
|
||||
return FALSE
|
||||
|
||||
/datum/language_holder/proc/copy_known_languages_from(thing, replace=FALSE)
|
||||
var/datum/language_holder/other
|
||||
if(istype(thing, /datum/language_holder))
|
||||
other = thing
|
||||
else if(ismovable(thing))
|
||||
var/atom/movable/AM = thing
|
||||
other = AM.get_language_holder()
|
||||
else if(istype(thing, /datum/mind))
|
||||
var/datum/mind/M = thing
|
||||
other = M.get_language_holder()
|
||||
|
||||
if(replace)
|
||||
src.remove_all_languages()
|
||||
|
||||
for(var/l in other.languages)
|
||||
src.grant_language(l)
|
||||
|
||||
|
||||
/datum/language_holder/proc/open_language_menu(mob/user)
|
||||
if(!language_menu)
|
||||
language_menu = new(src)
|
||||
language_menu.ui_interact(user)
|
||||
|
||||
/datum/language_holder/proc/get_atom()
|
||||
if(ismovable(owner))
|
||||
. = owner
|
||||
else if(istype(owner, /datum/mind))
|
||||
/// Initializes, and copies in the languages from the current atom if available.
|
||||
/datum/language_holder/New(_owner)
|
||||
owner = _owner
|
||||
if(istype(owner, /datum/mind))
|
||||
var/datum/mind/M = owner
|
||||
if(M.current)
|
||||
. = M.current
|
||||
update_atom_languages(M.current)
|
||||
get_selected_language()
|
||||
|
||||
/datum/language_holder/Destroy()
|
||||
QDEL_NULL(language_menu)
|
||||
return ..()
|
||||
|
||||
/// Grants the supplied language.
|
||||
/datum/language_holder/proc/grant_language(language, understood = TRUE, spoken = TRUE, source = LANGUAGE_MIND)
|
||||
if(understood)
|
||||
if(!understood_languages[language])
|
||||
understood_languages[language] = list()
|
||||
understood_languages[language] |= source
|
||||
. = TRUE
|
||||
if(spoken)
|
||||
if(!spoken_languages[language])
|
||||
spoken_languages[language] = list()
|
||||
spoken_languages[language] |= source
|
||||
. = TRUE
|
||||
|
||||
/// Grants every language to understood and spoken, and gives omnitongue.
|
||||
/datum/language_holder/proc/grant_all_languages(understood = TRUE, spoken = TRUE, grant_omnitongue = TRUE, source = LANGUAGE_MIND)
|
||||
for(var/language in GLOB.all_languages)
|
||||
grant_language(language, understood, spoken, source)
|
||||
if(grant_omnitongue) // Overrides tongue limitations.
|
||||
omnitongue = TRUE
|
||||
return TRUE
|
||||
|
||||
/// Removes a single language or source, removing all sources returns the pre-removal state of the language.
|
||||
/datum/language_holder/proc/remove_language(language, understood = TRUE, spoken = TRUE, source = LANGUAGE_ALL)
|
||||
if(understood && understood_languages[language])
|
||||
if(source == LANGUAGE_ALL)
|
||||
understood_languages -= language
|
||||
else
|
||||
understood_languages[language] -= source
|
||||
if(!length(understood_languages[language]))
|
||||
understood_languages -= language
|
||||
. = TRUE
|
||||
|
||||
if(spoken && spoken_languages[language])
|
||||
if(source == LANGUAGE_ALL)
|
||||
spoken_languages -= language
|
||||
else
|
||||
spoken_languages[language] -= source
|
||||
if(!length(spoken_languages[language]))
|
||||
spoken_languages -= language
|
||||
. = TRUE
|
||||
|
||||
/// Removes every language and optionally sets omnitongue false. If a non default source is supplied, only removes that source.
|
||||
/datum/language_holder/proc/remove_all_languages(source = LANGUAGE_ALL, remove_omnitongue = FALSE)
|
||||
for(var/language in GLOB.all_languages)
|
||||
remove_language(language, TRUE, TRUE, source)
|
||||
if(remove_omnitongue)
|
||||
omnitongue = FALSE
|
||||
return TRUE
|
||||
|
||||
/// Adds a single language or list of languages to the blocked language list.
|
||||
/datum/language_holder/proc/add_blocked_language(languages, source = LANGUAGE_MIND)
|
||||
if(!islist(languages))
|
||||
languages = list(languages)
|
||||
for(var/language in languages)
|
||||
if(!blocked_languages[language])
|
||||
blocked_languages[language] = list()
|
||||
blocked_languages[language] |= source
|
||||
return TRUE
|
||||
|
||||
/// Removes a single language or list of languages from the blocked language list.
|
||||
/datum/language_holder/proc/remove_blocked_language(languages, source = LANGUAGE_MIND)
|
||||
if(!islist(languages))
|
||||
languages = list(languages)
|
||||
for(var/language in languages)
|
||||
if(blocked_languages[language])
|
||||
if(source == LANGUAGE_ALL)
|
||||
blocked_languages -= language
|
||||
else
|
||||
blocked_languages[language] -= source
|
||||
if(!length(blocked_languages[language]))
|
||||
blocked_languages -= language
|
||||
return TRUE
|
||||
|
||||
/// Checks if you have the language. If spoken is true, only checks if you can speak the language.
|
||||
/datum/language_holder/proc/has_language(language, spoken = FALSE)
|
||||
if(language in blocked_languages)
|
||||
return FALSE
|
||||
if(spoken)
|
||||
return language in spoken_languages
|
||||
return language in understood_languages
|
||||
|
||||
/// Checks if you can speak the language. Tongue limitations should be supplied as an argument.
|
||||
/datum/language_holder/proc/can_speak_language(language)
|
||||
var/atom/movable/ouratom = get_atom()
|
||||
var/tongue = ouratom.could_speak_language(language)
|
||||
if((omnitongue || tongue) && has_language(language, TRUE))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/// Returns selected language if it can be spoken, or decides, sets and returns a new selected language if possible.
|
||||
/datum/language_holder/proc/get_selected_language()
|
||||
if(selected_language && can_speak_language(selected_language))
|
||||
return selected_language
|
||||
selected_language = null
|
||||
var/highest_priority
|
||||
for(var/lang in spoken_languages)
|
||||
var/datum/language/language = lang
|
||||
var/priority = initial(language.default_priority)
|
||||
if((!highest_priority || (priority > highest_priority)) && !(language in blocked_languages))
|
||||
if(can_speak_language(language))
|
||||
selected_language = language
|
||||
highest_priority = priority
|
||||
return selected_language
|
||||
|
||||
/// Gets a random understood language, useful for hallucinations and such.
|
||||
/datum/language_holder/proc/get_random_understood_language()
|
||||
return pick(understood_languages)
|
||||
|
||||
/// Gets a random spoken language, useful for forced speech and such.
|
||||
/datum/language_holder/proc/get_random_spoken_language()
|
||||
return pick(spoken_languages)
|
||||
|
||||
/// Opens a language menu reading from the language holder.
|
||||
/datum/language_holder/proc/open_language_menu(mob/user)
|
||||
if(!language_menu)
|
||||
language_menu = new (src)
|
||||
language_menu.ui_interact(user)
|
||||
|
||||
/// Gets the atom, since we some times need to check if the tongue has limitations.
|
||||
/datum/language_holder/proc/get_atom()
|
||||
if(owner)
|
||||
if(istype(owner, /datum/mind))
|
||||
var/datum/mind/M = owner
|
||||
return M.current
|
||||
return owner
|
||||
return FALSE
|
||||
|
||||
/// Empties out the atom specific languages and updates them according to the supplied atoms language holder.
|
||||
/datum/language_holder/proc/update_atom_languages(atom/movable/thing)
|
||||
var/datum/language_holder/from_atom = thing.get_language_holder(FALSE) //Gets the atoms language holder
|
||||
if(from_atom == src) //This could happen if called on an atom without a mind.
|
||||
return FALSE
|
||||
for(var/language in understood_languages)
|
||||
remove_language(language, TRUE, FALSE, LANGUAGE_ATOM)
|
||||
for(var/language in spoken_languages)
|
||||
remove_language(language, FALSE, TRUE, LANGUAGE_ATOM)
|
||||
for(var/language in blocked_languages)
|
||||
remove_blocked_language(language, LANGUAGE_ATOM)
|
||||
|
||||
copy_languages(from_atom)
|
||||
get_selected_language()
|
||||
return TRUE
|
||||
|
||||
/// Copies all languages from 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.
|
||||
/datum/language_holder/proc/copy_languages(var/datum/language_holder/from_holder, source_override)
|
||||
if(source_override) //No blocked languages here, for now only used by ling absorb.
|
||||
for(var/language in from_holder.understood_languages)
|
||||
grant_language(language, TRUE, FALSE, source_override)
|
||||
for(var/language in from_holder.spoken_languages)
|
||||
grant_language(language, FALSE, TRUE, source_override)
|
||||
else
|
||||
for(var/language in from_holder.understood_languages)
|
||||
grant_language(language, TRUE, FALSE, from_holder.understood_languages[language])
|
||||
for(var/language in from_holder.spoken_languages)
|
||||
grant_language(language, FALSE, TRUE, from_holder.spoken_languages[language])
|
||||
for(var/language in from_holder.blocked_languages)
|
||||
add_blocked_language(language, from_holder.blocked_languages[language])
|
||||
return TRUE
|
||||
|
||||
|
||||
//************************************************
|
||||
//* Specific language holders *
|
||||
//* Use atom language sources only. *
|
||||
//************************************************/
|
||||
|
||||
|
||||
/datum/language_holder/alien
|
||||
languages = list(/datum/language/xenocommon)
|
||||
|
||||
/datum/language_holder/monkey
|
||||
languages = list(/datum/language/monkey)
|
||||
|
||||
/datum/language_holder/swarmer
|
||||
languages = list(/datum/language/swarmer)
|
||||
understood_languages = list(/datum/language/xenocommon = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/xenocommon = list(LANGUAGE_ATOM))
|
||||
blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/clockmob
|
||||
languages = list(/datum/language/common, /datum/language/ratvar)
|
||||
only_speaks_language = /datum/language/ratvar
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/ratvar = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/ratvar = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/construct
|
||||
languages = list(/datum/language/common, /datum/language/narsie)
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/narsie = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/narsie = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/drone
|
||||
languages = list(/datum/language/common, /datum/language/drone, /datum/language/machine)
|
||||
only_speaks_language = /datum/language/drone
|
||||
understood_languages = list(/datum/language/drone = list(LANGUAGE_ATOM),
|
||||
/datum/language/machine = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/drone = list(LANGUAGE_ATOM))
|
||||
blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/drone/syndicate
|
||||
only_speaks_language = null
|
||||
blocked_languages = null
|
||||
|
||||
/datum/language_holder/dwarf
|
||||
languages = list(/datum/language/common, /datum/language/dwarf)
|
||||
only_speaks_language = /datum/language/dwarf
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/dwarf = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/dwarf = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/slime
|
||||
languages = list(/datum/language/common, /datum/language/slime)
|
||||
only_speaks_language = /datum/language/slime
|
||||
/datum/language_holder/jelly
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/slime = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/slime = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/lightbringer
|
||||
// TODO change to a lightbringer specific sign language
|
||||
languages = list(/datum/language/slime)
|
||||
understood_languages = list(/datum/language/slime = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/slime = list(LANGUAGE_ATOM))
|
||||
blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/lizard
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/draconic = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/draconic = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/lizard/ash
|
||||
selected_language = /datum/language/draconic
|
||||
|
||||
/datum/language_holder/monkey
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/monkey = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/monkey = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/mushroom
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/mushroom = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/mushroom = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/slime
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/slime = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/slime = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/swarmer
|
||||
understood_languages = list(/datum/language/swarmer = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/swarmer = list(LANGUAGE_ATOM))
|
||||
blocked_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/sylvan
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/sylvan = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/sylvan = list(LANGUAGE_ATOM))
|
||||
|
||||
|
||||
/datum/language_holder/synthetic
|
||||
languages = list(/datum/language/common)
|
||||
shadow_languages = list(/datum/language/common, /datum/language/machine, /datum/language/draconic, /datum/language/slime, /datum/language/dwarf)
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/machine = list(LANGUAGE_ATOM),
|
||||
/datum/language/draconic = list(LANGUAGE_ATOM),
|
||||
/datum/language/slime = list(LANGUAGE_ATOM),
|
||||
/datum/language/dwarf = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/machine = list(LANGUAGE_ATOM),
|
||||
/datum/language/draconic = list(LANGUAGE_ATOM),
|
||||
/datum/language/slime = list(LANGUAGE_ATOM),
|
||||
/datum/language/dwarf = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/venus
|
||||
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
|
||||
/datum/language/sylvan = list(LANGUAGE_ATOM))
|
||||
spoken_languages = list(/datum/language/sylvan = list(LANGUAGE_ATOM))
|
||||
|
||||
/datum/language_holder/empty
|
||||
languages = list()
|
||||
shadow_languages = list()
|
||||
understood_languages = list()
|
||||
spoken_languages = list()
|
||||
|
||||
/datum/language_holder/universal/New()
|
||||
..()
|
||||
grant_all_languages(omnitongue=TRUE)
|
||||
grant_all_languages()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/datum/language_menu
|
||||
var/datum/language_holder/language_holder
|
||||
|
||||
/datum/language_menu/New(language_holder)
|
||||
src.language_holder = language_holder
|
||||
/datum/language_menu/New(_language_holder)
|
||||
language_holder = _language_holder
|
||||
|
||||
/datum/language_menu/Destroy()
|
||||
language_holder = null
|
||||
@@ -24,21 +24,20 @@
|
||||
data["is_living"] = FALSE
|
||||
|
||||
data["languages"] = list()
|
||||
for(var/ld in GLOB.all_languages)
|
||||
var/result = language_holder.has_language(ld)
|
||||
for(var/lang in GLOB.all_languages)
|
||||
var/result = language_holder.has_language(lang) || language_holder.has_language(lang, TRUE)
|
||||
if(!result)
|
||||
continue
|
||||
var/shadow = result == LANGUAGE_SHADOWED
|
||||
var/datum/language/LD = ld
|
||||
var/datum/language/language = lang
|
||||
var/list/L = list()
|
||||
|
||||
L["name"] = initial(LD.name)
|
||||
L["desc"] = initial(LD.desc)
|
||||
L["key"] = initial(LD.key)
|
||||
L["is_default"] = (LD == language_holder.selected_default_language)
|
||||
L["shadow"] = shadow
|
||||
L["name"] = initial(language.name)
|
||||
L["desc"] = initial(language.desc)
|
||||
L["key"] = initial(language.key)
|
||||
L["is_default"] = (language == language_holder.selected_language)
|
||||
if(AM)
|
||||
L["can_speak"] = AM.can_speak_in_language(LD)
|
||||
L["can_speak"] = AM.can_speak_language(language)
|
||||
L["can_understand"] = AM.has_language(language)
|
||||
|
||||
data["languages"] += list(L)
|
||||
|
||||
@@ -47,15 +46,15 @@
|
||||
data["omnitongue"] = language_holder.omnitongue
|
||||
|
||||
data["unknown_languages"] = list()
|
||||
for(var/ld in GLOB.all_languages)
|
||||
if(language_holder.has_language(ld))
|
||||
for(var/lang in GLOB.all_languages)
|
||||
if(language_holder.has_language(lang) || language_holder.has_language(lang, TRUE))
|
||||
continue
|
||||
var/datum/language/LD = ld
|
||||
var/datum/language/language = lang
|
||||
var/list/L = list()
|
||||
|
||||
L["name"] = initial(LD.name)
|
||||
L["desc"] = initial(LD.desc)
|
||||
L["key"] = initial(LD.key)
|
||||
L["name"] = initial(language.name)
|
||||
L["desc"] = initial(language.desc)
|
||||
L["key"] = initial(language.key)
|
||||
|
||||
data["unknown_languages"] += list(L)
|
||||
return data
|
||||
@@ -68,27 +67,51 @@
|
||||
|
||||
var/language_name = params["language_name"]
|
||||
var/datum/language/language_datum
|
||||
for(var/ld in GLOB.all_languages)
|
||||
var/datum/language/LD = ld
|
||||
if(language_name == initial(LD.name))
|
||||
language_datum = LD
|
||||
for(var/lang in GLOB.all_languages)
|
||||
var/datum/language/language = lang
|
||||
if(language_name == initial(language.name))
|
||||
language_datum = language
|
||||
var/is_admin = check_rights_for(user.client, R_ADMIN)
|
||||
|
||||
switch(action)
|
||||
if("select_default")
|
||||
if(language_datum && AM.can_speak_in_language(language_datum))
|
||||
language_holder.selected_default_language = language_datum
|
||||
if(language_datum && AM.can_speak_language(language_datum))
|
||||
language_holder.selected_language = language_datum
|
||||
. = TRUE
|
||||
if("grant_language")
|
||||
if((is_admin || isobserver(AM)) && language_datum)
|
||||
language_holder.grant_language(language_datum)
|
||||
var/list/choices = list("Only Spoken", "Only Understood", "Both")
|
||||
var/choice = input(user,"How do you want to add this language?","[language_datum]",null) as null|anything in choices
|
||||
var/spoken = FALSE
|
||||
var/understood = FALSE
|
||||
switch(choice)
|
||||
if("Only Spoken")
|
||||
spoken = TRUE
|
||||
if("Only Understood")
|
||||
understood = TRUE
|
||||
if("Both")
|
||||
spoken = TRUE
|
||||
understood = TRUE
|
||||
language_holder.grant_language(language_datum, understood, spoken)
|
||||
if(is_admin)
|
||||
message_admins("[key_name_admin(user)] granted the [language_name] language to [key_name_admin(AM)].")
|
||||
log_admin("[key_name(user)] granted the language [language_name] to [key_name(AM)].")
|
||||
. = TRUE
|
||||
if("remove_language")
|
||||
if((is_admin || isobserver(AM)) && language_datum)
|
||||
language_holder.remove_language(language_datum)
|
||||
var/list/choices = list("Only Spoken", "Only Understood", "Both")
|
||||
var/choice = input(user,"Which part do you wish to remove?","[language_datum]",null) as null|anything in choices
|
||||
var/spoken = FALSE
|
||||
var/understood = FALSE
|
||||
switch(choice)
|
||||
if("Only Spoken")
|
||||
spoken = TRUE
|
||||
if("Only Understood")
|
||||
understood = TRUE
|
||||
if("Both")
|
||||
spoken = TRUE
|
||||
understood = TRUE
|
||||
language_holder.remove_language(language_datum, understood, spoken)
|
||||
if(is_admin)
|
||||
message_admins("[key_name_admin(user)] removed the [language_name] language to [key_name_admin(AM)].")
|
||||
log_admin("[key_name(user)] removed the language [language_name] to [key_name(AM)].")
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// The language of the vinebings. Yes, it's a shameless ripoff of elvish.
|
||||
/datum/language/sylvan
|
||||
name = "Sylvan"
|
||||
desc = "A complicated, ancient language spoken by vine like beings."
|
||||
speech_verb = "expresses"
|
||||
ask_verb = "inquires"
|
||||
exclaim_verb = "declares"
|
||||
key = "h"
|
||||
space_chance = 20
|
||||
syllables = list(
|
||||
"fii", "sii", "rii", "rel", "maa", "ala", "san", "tol", "tok", "dia", "eres",
|
||||
"fal", "tis", "bis", "qel", "aras", "losk", "rasa", "eob", "hil", "tanl", "aere",
|
||||
"fer", "bal", "pii", "dala", "ban", "foe", "doa", "cii", "uis", "mel", "wex",
|
||||
"incas", "int", "elc", "ent", "aws", "qip", "nas", "vil", "jens", "dila", "fa",
|
||||
"la", "re", "do", "ji", "ae", "so", "qe", "ce", "na", "mo", "ha", "yu"
|
||||
)
|
||||
icon = 'icons/obj/hydroponics/harvest.dmi'
|
||||
icon_state = "lily"
|
||||
default_priority = 90
|
||||
@@ -1,6 +1,12 @@
|
||||
///////////////////////////////////////////////////////////////
|
||||
//SS13 Optimized Map loader
|
||||
//////////////////////////////////////////////////////////////
|
||||
/*
|
||||
* Notes:
|
||||
* This does NOT support map files where more than one Z is in each grid set, as defined in the file.
|
||||
* MultiZ map files ARE supported (however very much discouraged), as long as each gridset only contains one z.
|
||||
* This assumes that for the most part, map files are properly formed, either DMM or TGM standard formats. If you feed it bad data, expect to crash.
|
||||
*/
|
||||
#define SPACE_KEY "space"
|
||||
|
||||
/datum/grid_set
|
||||
@@ -9,6 +15,12 @@
|
||||
var/zcrd
|
||||
var/gridLines
|
||||
|
||||
/datum/grid_set/proc/height(key_len)
|
||||
return length(gridLines)
|
||||
|
||||
/datum/grid_set/proc/width(key_len)
|
||||
return gridLines[1] / key_len
|
||||
|
||||
/datum/parsed_map
|
||||
var/original_path
|
||||
var/key_len = 0
|
||||
@@ -43,7 +55,7 @@
|
||||
/// - `cropMap`: When true, the map will be cropped to fit the existing world dimensions (Optional).
|
||||
/// - `measureOnly`: When true, no changes will be made to the world (Optional).
|
||||
/// - `no_changeturf`: When true, [turf/AfterChange] won't be called on loaded turfs
|
||||
/// - `x_lower`, `x_upper`, `y_lower`, `y_upper`: Coordinates (relative to the map) to crop to (Optional).
|
||||
/// - `x_lower`, `x_upper`, `y_lower`, `y_upper`: Coordinates (relative to the game world) to crop to (Optional).
|
||||
/// - `placeOnTop`: Whether to use [turf/PlaceOnTop] rather than [turf/ChangeTurf] (Optional).
|
||||
/proc/load_map(
|
||||
dmm_file as file,
|
||||
@@ -60,25 +72,37 @@
|
||||
placeOnTop = FALSE as num,
|
||||
orientation = SOUTH as num,
|
||||
annihilate_tiles = FALSE,
|
||||
z_lower = -INFINITY as num,
|
||||
z_upper = INFINITY as num
|
||||
crop_relative_to_game_world = TRUE
|
||||
)
|
||||
var/datum/parsed_map/parsed = new(dmm_file, x_lower, x_upper, y_lower, y_upper, z_lower, z_upper, measureOnly)
|
||||
var/datum/parsed_map/parsed = new(dmm_file, measureOnly = measureOnly)
|
||||
if(parsed.bounds && !measureOnly)
|
||||
parsed.load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop, orientation, annihilate_tiles)
|
||||
return parsed
|
||||
|
||||
/// Parse a map, possibly cropping it.
|
||||
//WHY THE HECK DO WE EVEN SUPPORT NEGATIVE COORDINATES, ALL IT IS IS A WASTE OF TIME AND CPU!!!???
|
||||
//DO NOT USE THIS TO TRIM MAPS UNLESS STRICTLY NEEDED! IT IS EXTREMELY EXPENSIVE TO DO SO!
|
||||
/**
|
||||
* Parse a map, possibly cropping it.
|
||||
* Do not use the crop function unless strictly necessary.
|
||||
* WARNING: Crop function crops based on the tiles you'd see in the map editor. If you're planning to load it in in a different orientation later, you better have done the math.
|
||||
* It's recommended that you do not crop using this at all.
|
||||
*/
|
||||
/datum/parsed_map/New(tfile, x_lower = -INFINITY, x_upper = INFINITY, y_lower = -INFINITY, y_upper = INFINITY, z_lower = -INFINITY, z_upper = INFINITY, measureOnly = FALSE)
|
||||
_parse(tfile, x_lower, x_upper, y_lower, y_upper, z_lower, z_upper, measureOnly)
|
||||
|
||||
/datum/parsed_map/proc/_parse(tfile, x_lower, x_upper, y_lower, y_upper, z_lower, z_upper, measureOnly)
|
||||
var/static/parsing = FALSE
|
||||
UNTIL(!parsing)
|
||||
// do not multithread this or bad things happen
|
||||
parsing = TRUE
|
||||
_do_parse(tfile, x_lower, x_upper, y_lower, y_upper, z_lower, z_upper, measureOnly)
|
||||
parsing = FALSE
|
||||
|
||||
/datum/parsed_map/proc/_do_parse(tfile, x_lower, x_upper, y_lower, y_upper, z_lower, z_upper, measureOnly)
|
||||
if(isfile(tfile))
|
||||
original_path = "[tfile]"
|
||||
tfile = file2text(tfile)
|
||||
else if(isnull(tfile))
|
||||
// create a new datum without loading a map
|
||||
return
|
||||
|
||||
bounds = parsed_bounds = list(1.#INF, 1.#INF, 1.#INF, -1.#INF, -1.#INF, -1.#INF)
|
||||
ASSERT(x_upper >= x_lower)
|
||||
ASSERT(y_upper >= y_lower)
|
||||
@@ -107,12 +131,13 @@
|
||||
if(!key_len)
|
||||
CRASH("Coords before model definition in DMM")
|
||||
|
||||
// NOTE: We are assuming each coordset only contains one z.
|
||||
var/curr_z = text2num(dmmRegex.group[5])
|
||||
if(curr_z < z_lower || curr_z > z_upper)
|
||||
continue
|
||||
|
||||
var/curr_x = text2num(dmmRegex.group[3])
|
||||
var/curr_y = text2num(dmmRegex.group[4])
|
||||
var/curr_z = text2num(dmmRegex.group[5])
|
||||
|
||||
if(curr_x < x_lower || curr_y < y_lower || curr_z < z_lower || curr_z > z_upper)
|
||||
continue
|
||||
|
||||
var/datum/grid_set/gridSet = new
|
||||
|
||||
@@ -120,12 +145,6 @@
|
||||
gridSet.ycrd = curr_y
|
||||
gridSet.zcrd = curr_z
|
||||
|
||||
bounds[MAP_MINX] = min(bounds[MAP_MINX], curr_x) //since down is up for y/gridlines, we now know the lower left corner.
|
||||
bounds[MAP_MINY] = min(bounds[MAP_MINY], curr_y)
|
||||
bounds[MAP_MINZ] = min(bounds[MAP_MINZ], curr_z)
|
||||
|
||||
bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], curr_z) //we know max z now
|
||||
|
||||
var/list/gridLines = splittext(dmmRegex.group[6], "\n")
|
||||
gridSet.gridLines = gridLines
|
||||
|
||||
@@ -134,31 +153,36 @@
|
||||
if(leadingBlanks > 1)
|
||||
gridLines.Cut(1, leadingBlanks) // Remove all leading blank lines.
|
||||
|
||||
gridSets += gridSet
|
||||
|
||||
var/lines = length(gridLines)
|
||||
if(lines)
|
||||
if(gridLines[gridLines.len] == "")
|
||||
gridLines.Cut(gridLines.len) // Remove only one blank line at the end.
|
||||
var/right_length = y_upper - curr_y + 1
|
||||
if(lines > right_length)
|
||||
gridLines.len = right_length //this can't be negative due to our ASSERTions above, hopefully.
|
||||
|
||||
if(!gridLines.len) // Skip it if there's no content.
|
||||
if(lines && gridLines[lines] == "")
|
||||
// remove one trailing blank line
|
||||
gridLines.len--
|
||||
lines--
|
||||
// y crop
|
||||
var/right_length = y_upper - curr_y + 1
|
||||
if(lines > right_length)
|
||||
lines = gridLines.len = right_length
|
||||
if(!lines) // blank
|
||||
continue
|
||||
|
||||
//do not use curr_y after this point, ycrd has changed. use it before because local var.
|
||||
gridSet.ycrd += gridLines.len - 1 // Start at the top and work down
|
||||
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], gridSet.ycrd) //we know max y now
|
||||
var/width = length(gridLines[1]) / key_len
|
||||
// x crop
|
||||
var/right_width = x_upper - curr_x + 1
|
||||
if(width > right_width)
|
||||
for(var/i in 1 to lines)
|
||||
gridLines[i] = copytext(gridLines[i], 1, key_len * right_width)
|
||||
|
||||
// during the actual load we're starting at the top and working our way down
|
||||
gridSet.ycrd += lines - 1
|
||||
|
||||
var/linelength = length(gridLines[1]) //yes it only samples the first line, this is why you use TGM instead of DMM!
|
||||
var/xlength = linelength / key_len
|
||||
|
||||
var/maxx = gridSet.xcrd + xlength - 1
|
||||
if(maxx > x_upper)
|
||||
for(var/i in 1 to length(gridLines))
|
||||
gridLines[i] = copytext(gridLines[i], 1, key_len * (x_upper - curr_x + 1))
|
||||
bounds[MAP_MAXX] = max(bounds[MAP_MAXX], maxx)
|
||||
// Safe to proceed, commit gridset to list and record coords.
|
||||
gridSets += gridSet
|
||||
bounds[MAP_MINX] = min(bounds[MAP_MINX], curr_x)
|
||||
bounds[MAP_MAXX] = max(bounds[MAP_MAXX], curr_x + width - 1)
|
||||
bounds[MAP_MINY] = min(bounds[MAP_MINY], curr_y)
|
||||
bounds[MAP_MAXY] = max(bounds[MAP_MAXY], curr_y + lines - 1)
|
||||
bounds[MAP_MINZ] = min(bounds[MAP_MINZ], curr_z)
|
||||
bounds[MAP_MAXZ] = max(bounds[MAP_MAXZ], curr_z)
|
||||
CHECK_TICK
|
||||
|
||||
// Indicate failure to parse any coordinates by nulling bounds
|
||||
@@ -172,7 +196,8 @@
|
||||
/datum/parsed_map/Destroy()
|
||||
if(template_host && template_host.cached_map == src)
|
||||
template_host.cached_map = null
|
||||
return ..()
|
||||
. = ..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
/// Load the parsed map into the world. See [/proc/load_map] for arguments.
|
||||
/datum/parsed_map/proc/load(x_offset, y_offset, z_offset, cropMap, no_changeturf, x_lower, x_upper, y_lower, y_upper, placeOnTop, orientation, annihilate_tiles, datum/map_orientation_pattern/forced_pattern)
|
||||
@@ -197,6 +222,8 @@
|
||||
var/yi = mode.yi
|
||||
var/turn_angle = round(SIMPLIFY_DEGREES(mode.turn_angle), 90)
|
||||
var/delta_swap = x_offset - y_offset
|
||||
// less checks later
|
||||
var/do_crop = x_lower > -INFINITY || x_upper < INFINITY || y_lower > -INFINITY || y_upper < INFINITY
|
||||
|
||||
for(var/__I in gridSets)
|
||||
var/datum/grid_set/gridset = __I
|
||||
@@ -221,6 +248,8 @@
|
||||
for(var/pos = 1 to (length(line) - key_len + 1) step key_len)
|
||||
var/placement_x = swap_xy? (actual_y + delta_swap) : actual_x
|
||||
var/placement_y = swap_xy? (actual_x - delta_swap) : actual_y
|
||||
if(do_crop && ((placement_x < x_lower) || (placement_x > x_upper) || (placement_y < y_lower) || (placement_y > y_upper)))
|
||||
continue
|
||||
if(placement_x > world.maxx)
|
||||
if(cropMap)
|
||||
actual_x += xi
|
||||
@@ -532,6 +561,7 @@
|
||||
// fallback: string
|
||||
return text
|
||||
|
||||
/datum/parsed_map/Destroy()
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
/datum/parsed_map/vv_edit_var(var_name, var_value)
|
||||
if(var_name == NAMEOF(src, dmmRegex) || var_name == NAMEOF(src, trimQuotesRegex) || var_name == NAMEOF(src, trimRegex))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
body_parts_covered = HEAD
|
||||
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
|
||||
min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT
|
||||
cold_protection = HEAD
|
||||
max_heat_protection_temperature = FIRE_HELM_MAX_TEMP_PROTECT
|
||||
heat_protection = HEAD
|
||||
armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50)
|
||||
resistance_flags = FIRE_PROOF
|
||||
|
||||
|
||||
@@ -564,7 +564,7 @@
|
||||
|
||||
/obj/item/book_of_babel/attack_self(mob/user)
|
||||
to_chat(user, "You flip through the pages of the book, quickly and conveniently learning every language in existence. Somewhat less conveniently, the aging book crumbles to dust in the process. Whoops.")
|
||||
user.grant_all_languages(omnitongue=TRUE)
|
||||
user.grant_all_languages()
|
||||
new /obj/effect/decal/cleanable/ash(get_turf(user))
|
||||
qdel(src)
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
new /datum/data/mining_equipment("Spare Suit Voucher", /obj/item/suit_voucher, 2000),
|
||||
new /datum/data/mining_equipment("Super Resonator", /obj/item/resonator/upgraded, 2500),
|
||||
new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2500),
|
||||
new /datum/data/mining_equipment("Ice hiking boots", /obj/item/clothing/shoes/winterboots/ice_boots, 2500),
|
||||
new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000),
|
||||
new /datum/data/mining_equipment("Luxury Bar Capsule", /obj/item/survivalcapsule/luxuryelite, 10000),
|
||||
new /datum/data/mining_equipment("Nanotrasen Minebot", /mob/living/simple_animal/hostile/mining_drone, 800),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user