Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Mosley
2025-04-24 17:03:15 -05:00
402 changed files with 2262 additions and 1524 deletions
+3 -9
View File
@@ -13,9 +13,7 @@ GLOBAL_PROTECT(protected_ranks)
/datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights)
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
log_admin("[key_name(usr)][msg]")
alert_to_permissions_elevation_attempt(usr)
if (name == "NoRank") //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
QDEL_IN(src, 0)
CRASH("Admin proc call creation of admin datum")
@@ -35,9 +33,7 @@ GLOBAL_PROTECT(protected_ranks)
/datum/admin_rank/Destroy()
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
log_admin("[key_name(usr)][msg]")
alert_to_permissions_elevation_attempt(usr)
return QDEL_HINT_LETMELIVE
. = ..()
@@ -93,9 +89,7 @@ GLOBAL_PROTECT(protected_ranks)
// Adds/removes rights to this admin_rank
/datum/admin_rank/proc/process_keyword(word, previous_rights=0)
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
log_admin("[key_name(usr)][msg]")
alert_to_permissions_elevation_attempt(usr)
return
var/flag = admin_keyword_to_flag(word, previous_rights)
if(flag)
+6 -18
View File
@@ -40,9 +40,7 @@ GLOBAL_PROTECT(href_token)
/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected)
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
log_admin("[key_name(usr)][msg]")
alert_to_permissions_elevation_attempt(usr)
if (!target) //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
QDEL_IN(src, 0)
CRASH("Admin proc call creation of admin datum")
@@ -70,17 +68,13 @@ GLOBAL_PROTECT(href_token)
/datum/admins/Destroy()
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
log_admin("[key_name(usr)][msg]")
alert_to_permissions_elevation_attempt(usr)
return QDEL_HINT_LETMELIVE
. = ..()
/datum/admins/proc/activate()
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
log_admin("[key_name(usr)][msg]")
alert_to_permissions_elevation_attempt(usr)
return
GLOB.deadmins -= target
GLOB.admin_datums[target] = src
@@ -91,9 +85,7 @@ GLOBAL_PROTECT(href_token)
/datum/admins/proc/deactivate()
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
log_admin("[key_name(usr)][msg]")
alert_to_permissions_elevation_attempt(usr)
return
GLOB.deadmins[target] = src
GLOB.admin_datums -= target
@@ -105,9 +97,7 @@ GLOBAL_PROTECT(href_token)
/datum/admins/proc/associate(client/C)
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
log_admin("[key_name(usr)][msg]")
alert_to_permissions_elevation_attempt(usr)
return
if(istype(C))
@@ -127,9 +117,7 @@ GLOBAL_PROTECT(href_token)
/datum/admins/proc/disassociate()
if(IsAdminAdvancedProcCall())
var/msg = " has tried to elevate permissions!"
message_admins("[key_name_admin(usr)][msg]")
log_admin("[key_name(usr)][msg]")
alert_to_permissions_elevation_attempt(usr)
return
if(owner)
GLOB.admins -= owner
+1 -2
View File
@@ -2118,8 +2118,7 @@
if(!ismob(M))
to_chat(usr, "This can only be used on instances of type /mob.")
return
var/datum/language_holder/H = M.get_language_holder()
H.open_language_menu(usr)
M.get_language_holder().open_language_menu(usr)
else if(href_list["traitor"])
if(!check_rights(R_ADMIN))
+2 -2
View File
@@ -142,9 +142,9 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
/obj/effect/statclick/ticket_list
var/current_state
/obj/effect/statclick/ticket_list/New(loc, name, state)
/obj/effect/statclick/ticket_list/Initialize(mapload, name, state)
. = ..()
current_state = state
..()
/obj/effect/statclick/ticket_list/Click()
GLOB.ahelp_tickets.BrowseTickets(current_state)
@@ -10,12 +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, TRUE, TRUE, LANGUAGE_GLAND)
owner.grant_language(/datum/language/slime, source = 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)
owner.remove_language(/datum/language/slime, source = LANGUAGE_GLAND)
/obj/item/organ/heart/gland/slime/activate()
to_chat(owner, "<span class='warning'>You feel nauseated!</span>")
@@ -211,7 +211,7 @@
// Physiology
CheckVampOrgans() // Heart, Eyes
// Language
owner.current.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_BLOODSUCKER)
owner.current.grant_language(/datum/language/vampiric, source = 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
@@ -48,7 +48,7 @@
objectives += vassal_objective
objectives_given += vassal_objective
give_thrall_eyes()
owner.current.grant_language(/datum/language/vampiric, TRUE, TRUE, LANGUAGE_VASSAL)
owner.current.grant_language(/datum/language/vampiric, source = LANGUAGE_VASSAL)
// Add Antag HUD
update_vassal_icons_added(owner.current, "vassal")
. = ..()
@@ -84,7 +84,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.
owner.current.get_language_holder().omnitongue = TRUE
remove_clownmut()
. = ..()
@@ -96,7 +96,7 @@
current = mob_override
GLOB.all_clockwork_mobs += current
current.faction |= "ratvar"
current.grant_language(/datum/language/ratvar, TRUE, TRUE, LANGUAGE_CLOCKIE)
current.grant_language(/datum/language/ratvar, source = 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
+2 -2
View File
@@ -121,7 +121,7 @@
if(mob_override)
current = mob_override
current.faction |= "cult"
current.grant_language(/datum/language/narsie, TRUE, TRUE, LANGUAGE_CULTIST)
current.grant_language(/datum/language/narsie, source = LANGUAGE_CULTIST)
if(!cult_team?.cult_master)
vote.Grant(current)
communion.Grant(current)
@@ -139,7 +139,7 @@
if(mob_override)
current = mob_override
current.faction -= "cult"
current.remove_language(/datum/language/narsie, TRUE, TRUE, LANGUAGE_CULTIST)
current.remove_language(/datum/language/narsie, source = LANGUAGE_CULTIST)
vote.Remove(current)
communion.Remove(current)
magic.Remove(current)
+1 -2
View File
@@ -182,9 +182,8 @@
add_antag_hud(antag_hud_type, antag_hud_name, M)
if(M.hud_used)
var/datum/hud/H = M.hud_used
var/atom/movable/screen/wanted/giving_wanted_lvl = new /atom/movable/screen/wanted()
var/atom/movable/screen/wanted/giving_wanted_lvl = new /atom/movable/screen/wanted(null, H)
H.wanted_lvl = giving_wanted_lvl
giving_wanted_lvl.hud = H
H.infodisplay += giving_wanted_lvl
H.mymob.client.screen += giving_wanted_lvl
+1 -2
View File
@@ -113,9 +113,8 @@
add_antag_hud(antag_hud_type, antag_hud_name, M)
if(M.hud_used)
var/datum/hud/H = M.hud_used
var/atom/movable/screen/wanted/giving_wanted_lvl = new /atom/movable/screen/wanted()
var/atom/movable/screen/wanted/giving_wanted_lvl = new /atom/movable/screen/wanted(null, H)
H.wanted_lvl = giving_wanted_lvl
giving_wanted_lvl.hud = H
H.infodisplay += giving_wanted_lvl
H.mymob.client.screen += giving_wanted_lvl
@@ -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, TRUE, TRUE, LANGUAGE_MALF)
T.owner.current.grant_language(/datum/language/codespeak, source = LANGUAGE_MALF)
return FALSE
@@ -41,13 +41,13 @@
var/spawn_amt_left = 20
var/spawn_fast = 0
/obj/effect/rend/New(loc, var/spawn_type, var/spawn_amt, var/desc, var/spawn_fast)
/obj/effect/rend/Initialize(mapload, spawn_type, spawn_amt, desc, spawn_fast)
. = ..()
src.spawn_path = spawn_type
src.spawn_amt_left = spawn_amt
src.desc = desc
src.spawn_fast = spawn_fast
START_PROCESSING(SSobj, src)
return
/obj/effect/rend/process()
if(!spawn_fast)
@@ -287,9 +287,7 @@
S.real_name = "Shade of [T.real_name]"
T.transfer_ckey(S)
S.original_mind = T.mind
S.copy_languages(T, LANGUAGE_MIND)//Copies the old mobs languages into the new mob holder.
S.update_atom_languages()
grant_all_languages(FALSE, FALSE, TRUE) //Grants omnitongue
S.get_language_holder().omnitongue = TRUE //Grants omnitongue
if(user)
S.faction |= "[REF(user)]" //Add the master as a faction, allowing inter-mob cooperation
if(user && iscultist(user))
+2 -2
View File
@@ -131,10 +131,10 @@
return FALSE
/obj/item/assembly/mousetrap/hitby(atom/hit_atom, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
/obj/item/assembly/mousetrap/hitby(atom/movable/hit_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if(!armed)
return ..()
visible_message("<span class='warning'>[src] is triggered by [hit_atom].</span>")
visible_message(span_warning("[src] is triggered by [hit_atom]."))
triggered(null)
+9
View File
@@ -0,0 +1,9 @@
/datum/asset/simple/namespaced/fonts
parents = list("fonts.css" = 'interface/fonts/fonts.css')
/datum/asset/simple/namespaced/fonts/New()
for(var/datum/font/font as anything in subtypesof(/datum/font))
var/file = "[initial(font.font_family)]"
var/file_name = copytext(file, findlasttext(file, "/") + 1)
assets[file_name] = file(file)
return ..()
@@ -186,6 +186,6 @@ GLOBAL_DATUM_INIT(gas_data, /datum/auxgm, new)
appearance_flags = TILE_BOUND
vis_flags = NONE
/obj/effect/overlay/gas/New(state)
/obj/effect/overlay/gas/Initialize(mapload, state)
. = ..()
icon_state = state
+1
View File
@@ -1,5 +1,6 @@
/atom/movable/screen/buildmode
icon = 'icons/misc/buildmode.dmi'
mouse_over_pointer = MOUSE_HAND_POINTER
var/datum/buildmode/bd
// If we don't do this, we get occluded by item action buttons
layer = ABOVE_HUD_LAYER
+9
View File
@@ -171,6 +171,15 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
to_chat(src, "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. Only 10 bucks for 3 months! <a href=\"https://secure.byond.com/membership\">Click Here to find out more</a>.")
return FALSE
return TRUE
/client/proc/is_localhost()
var/static/localhost_addresses = list(
"127.0.0.1",
"::1",
null,
)
return address in localhost_addresses
/*
* Call back proc that should be checked in all paths where a client can send messages
*
+9 -6
View File
@@ -483,11 +483,14 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<HR>"
dat += "<center>"
var/savefile/client_file = new(user.client.Import())
var/file = user.client.Import()
var/savefile/client_file
var/savefile_name
if(istype(client_file, /savefile))
if(!client_file["deleted"] || savefile_needs_update(client_file) != -2)
client_file["real_name"] >> savefile_name
if(file)
client_file = new(file)
if(istype(client_file, /savefile))
if(!client_file["deleted"] || savefile_needs_update(client_file) != -2)
client_file["real_name"] >> savefile_name
dat += "Local storage: [savefile_name ? savefile_name : "Empty"]"
dat += "<br />"
dat += "<a href='?_src_=prefs;preference=export_slot'>Export current slot</a>"
@@ -1462,11 +1465,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(user.client)
if(unlock_content)
dat += "<b>BYOND Membership Publicity:</b> <a href='?_src_=prefs;preference=publicity'>[(toggles & MEMBER_PUBLIC) ? "Public" : "Hidden"]</a><br>"
if(unlock_content || check_rights_for(user.client, R_ADMIN))
if(unlock_content || is_admin(user.client))
dat += "<b>OOC Color:</b> <span style='border: 1px solid #161616; background-color: [ooccolor ? ooccolor : GLOB.normal_ooc_colour];'><font color='[color_hex2num(ooccolor ? ooccolor : GLOB.normal_ooc_colour) < 200 ? "FFFFFF" : "000000"]'>[ooccolor ? ooccolor : GLOB.normal_ooc_colour]</font></span> <a href='?_src_=prefs;preference=ooccolor;task=input'>Change</a><br>"
dat += "<b>Antag OOC Color:</b> <span style='border: 1px solid #161616; background-color: [aooccolor ? aooccolor : GLOB.normal_aooc_colour];'><font color='[color_hex2num(aooccolor ? aooccolor : GLOB.normal_aooc_colour) < 200 ? "FFFFFF" : "000000"]'>[aooccolor ? aooccolor : GLOB.normal_aooc_colour]</font></span> <a href='?_src_=prefs;preference=aooccolor;task=input'>Change</a><br>"
if(user.client.holder)
if(is_admin(user.client))
dat += "<h2>Admin Settings</h2>"
dat += "<b>Adminhelp Sounds:</b> <a href='?_src_=prefs;preference=hear_adminhelps'>[(toggles & SOUND_ADMINHELP)?"Enabled":"Disabled"]</a><br>"
dat += "<b>Announce Login:</b> <a href='?_src_=prefs;preference=announce_login'>[(toggles & ANNOUNCE_LOGIN)?"Enabled":"Disabled"]</a><br>"
+46 -48
View File
@@ -11,7 +11,7 @@
/obj/item/clothing/head/helmet/space/chronos/dropped(mob/user)
if(suit)
suit.deactivate(1, 1)
..()
return ..()
/obj/item/clothing/suit/space/chronos
name = "Chronosuit"
@@ -23,23 +23,22 @@
resistance_flags = FIRE_PROOF | ACID_PROOF
mutantrace_variation = STYLE_DIGITIGRADE
var/list/chronosafe_items = list(/obj/item/chrono_eraser, /obj/item/gun/energy/chrono_gun)
var/obj/item/clothing/head/helmet/space/chronos/helmet = null
var/obj/effect/chronos_cam/camera = null
var/datum/action/innate/chrono_teleport/teleport_now = new
var/obj/item/clothing/head/helmet/space/chronos/helmet
var/obj/effect/chronos_cam/camera
var/datum/action/innate/chrono_teleport/teleport_now
var/activating = 0
var/activated = 0
var/cooldowntime = 50 //deciseconds
var/cooldowntime = 5 SECONDS
var/teleporting = 0
var/phase_timer_id
/obj/item/clothing/suit/space/chronos/New()
..()
/obj/item/clothing/suit/space/chronos/Initialize(mapload)
. = ..()
teleport_now = new(src)
teleport_now.chronosuit = src
teleport_now.target = src
/obj/item/clothing/suit/space/chronos/proc/new_camera(mob/user)
if(camera)
qdel(camera)
QDEL_NULL(camera)
camera = new /obj/effect/chronos_cam(user)
camera.holder = user
camera.chronosuit = src
@@ -55,7 +54,7 @@
/obj/item/clothing/suit/space/chronos/dropped(mob/user)
if(activated)
deactivate()
..()
return ..()
/obj/item/clothing/suit/space/chronos/emp_act(severity)
. = ..()
@@ -64,8 +63,8 @@
var/mob/living/carbon/human/user = src.loc
if(severity >= 70)
if(activated && user && ishuman(user) && (user.wear_suit == src))
to_chat(user, "<span class='danger'>E:FATAL:RAM_READ_FAIL\nE:FATAL:STACK_EMPTY\nE:FATAL:READ_NULL_POINT\nE:FATAL:PWR_BUS_OVERLOAD</span>")
to_chat(user, "<span class='userdanger'>An electromagnetic pulse disrupts your [name] and violently tears you out of time-bluespace!</span>")
to_chat(user, span_danger("E:FATAL:RAM_READ_FAIL\nE:FATAL:STACK_EMPTY\nE:FATAL:READ_NULL_POINT\nE:FATAL:PWR_BUS_OVERLOAD"))
to_chat(user, span_userdanger("An electromagnetic pulse disrupts your [name] and violently tears you out of time-bluespace!"))
user.emote("scream")
deactivate(1, 1)
@@ -75,23 +74,24 @@
if(phase_timer_id)
deltimer(phase_timer_id)
phase_timer_id = 0
if(istype(user))
if(to_turf)
user.forceMove(to_turf)
user.SetStun(0)
user.SetNextAction(0, considered_action = FALSE, immediate = FALSE)
user.alpha = 255
user.update_atom_colour()
user.animate_movement = FORWARD_STEPS
user.mob_transforming = 0
user.anchored = FALSE
teleporting = 0
for(var/obj/item/I in user.held_items)
REMOVE_TRAIT(I, TRAIT_NODROP, CHRONOSUIT_TRAIT)
if(camera)
camera.remove_target_ui()
camera.forceMove(user)
teleport_now.UpdateButtons()
if(!istype(user))
return
if(to_turf)
user.forceMove(to_turf)
user.SetStun(0)
user.SetNextAction(0, considered_action = FALSE, immediate = FALSE)
user.alpha = 255
user.update_atom_colour()
user.animate_movement = FORWARD_STEPS
user.mob_transforming = 0
user.anchored = FALSE
teleporting = 0
for(var/obj/item/I in user.held_items)
REMOVE_TRAIT(I, TRAIT_NODROP, CHRONOSUIT_TRAIT)
if(camera)
camera.remove_target_ui()
camera.forceMove(user)
teleport_now.UpdateButtons()
/obj/item/clothing/suit/space/chronos/proc/chronowalk(atom/location)
var/mob/living/carbon/human/user = src.loc
@@ -238,7 +238,7 @@
var/mob/holder = null
var/phase_time = 0
var/phase_time_length = 3
var/atom/movable/screen/chronos_target/target_ui = null
var/atom/movable/screen/chronos_target/target_ui
var/obj/item/clothing/suit/space/chronos/chronosuit
/obj/effect/chronos_cam/singularity_act()
@@ -249,17 +249,14 @@
/obj/effect/chronos_cam/proc/create_target_ui()
if(holder && holder.client && chronosuit)
if(target_ui)
remove_target_ui()
target_ui = new(null, holder)
remove_target_ui()
target_ui = new(null, holder.hud_used, holder)
holder.client.screen += target_ui
/obj/effect/chronos_cam/proc/remove_target_ui()
if(target_ui)
qdel(target_ui)
target_ui = null
QDEL_NULL(target_ui)
/obj/effect/chronos_cam/relaymove(var/mob/user, direction)
/obj/effect/chronos_cam/relaymove(mob/user, direction)
if(!holder)
qdel(src)
return
@@ -305,13 +302,13 @@
color = "#ff3311"
blend_mode = BLEND_SUBTRACT
/atom/movable/screen/chronos_target/New(loc, var/mob/living/carbon/human/user)
if(user)
var/icon/user_icon = getFlatIcon(user)
icon = user_icon
transform = user.transform
else
qdel(src)
/atom/movable/screen/chronos_target/Initialize(mapload, datum/hud/hud_owner, mob/living/carbon/human/user)
. = ..()
if(!user)
return INITIALIZE_HINT_QDEL
var/icon/user_icon = getFlatIcon(user)
icon = user_icon
transform = user.transform
/datum/action/innate/chrono_teleport
name = "Teleport Now"
@@ -324,6 +321,7 @@
return (chronosuit && chronosuit.activated && chronosuit.camera && !chronosuit.teleporting)
/datum/action/innate/chrono_teleport/Activate()
if(IsAvailable())
if(chronosuit.camera)
chronosuit.chronowalk(chronosuit.camera)
if(!IsAvailable())
return
if(chronosuit.camera)
chronosuit.chronowalk(chronosuit.camera)
+5 -4
View File
@@ -27,10 +27,11 @@
if(ishuman(M) && M.w_uniform)
var/obj/item/clothing/under/U = M.w_uniform
//SANDSTORM EDIT
if(istype(U) && length(U.attached_accessories))
for(var/obj/item/clothing/accessory/attached in U.attached_accessories)
if(attached.above_suit)
. += U.accessory_overlays
if(istype(U) && !CHECK_BITFIELD(U.flags_inv, HIDEACCESSORY))
for(var/obj/item/clothing/accessory/attached as anything in U.attached_accessories)
if(CHECK_BITFIELD(attached.flags_inv, HIDEACCESSORY) || !attached.above_suit)
continue
. += attached.build_worn_icon()
//SANDSTORM EDIT END
/obj/item/clothing/suit/update_clothes_damaged_state()
+62 -69
View File
@@ -24,8 +24,7 @@
var/dummy_thick = FALSE // is able to hold accessories on its item
//SANDSTORM EDIT - Removed the old attached accessory system. We use a list of accessories instead.
var/max_accessories = 3
var/list/obj/item/clothing/accessory/attached_accessories = list()
var/list/mutable_appearance/accessory_overlays = list()
var/list/obj/item/clothing/accessory/attached_accessories
//SANDSTORM EDIT END
/obj/item/clothing/under/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE)
@@ -36,8 +35,12 @@
. += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform")
if(blood_DNA)
. += mutable_appearance('icons/effects/blood.dmi', "uniformblood", color = blood_DNA_to_color(), blend_mode = blood_DNA_to_blend())
if(length(accessory_overlays))
. += accessory_overlays
if(CHECK_BITFIELD(flags_inv, HIDEACCESSORY))
return
for(var/obj/item/clothing/accessory/attached_accessory as anything in attached_accessories)
if(CHECK_BITFIELD(attached_accessory.flags_inv, HIDEACCESSORY))
continue
. += attached_accessory.build_worn_icon()
/obj/item/clothing/under/attackby(obj/item/I, mob/user, params)
if((sensordamage || (has_sensor < HAS_SENSORS && has_sensor != NO_SENSORS)) && istype(I, /obj/item/stack/cable_coil))
@@ -125,68 +128,60 @@
body_parts_covered |= CHEST
// Sandstorm edit
for(var/obj/item/clothing/accessory/attached_accessory in attached_accessories)
if(attached_accessory && slot != ITEM_SLOT_HANDS && ishuman(user))
var/mob/living/carbon/human/H = user
var/must_update = FALSE
for(var/obj/item/clothing/accessory/attached_accessory as anything in attached_accessories)
if(attached_accessory && slot == ITEM_SLOT_ICLOTHING && ishuman(user))
attached_accessory.on_uniform_equip(src, user)
if(attached_accessory.above_suit)
H.update_inv_wear_suit()
if(attached_accessory.above_suit)
must_update = TRUE
if(must_update)
user.update_inv_wear_suit()
//
/obj/item/clothing/under/dropped(mob/user)
// Sandstorm edit
for(var/obj/item/clothing/accessory/attached_accessory in attached_accessories)
var/must_update = FALSE
for(var/obj/item/clothing/accessory/attached_accessory as anything in attached_accessories)
attached_accessory.on_uniform_dropped(src, user)
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(attached_accessory.above_suit)
H.update_inv_wear_suit()
if(attached_accessory.above_suit)
must_update = TRUE
if(must_update)
user.update_inv_wear_suit()
//
..()
/obj/item/clothing/under/proc/attach_accessory(obj/item/I, mob/user, notifyAttach = 1)
/obj/item/clothing/under/proc/attach_accessory(obj/item/clothing/accessory/accessory, mob/user, notifyAttach = 1)
. = FALSE
if(istype(I, /obj/item/clothing/accessory) && !istype(I, /obj/item/clothing/accessory/ring))
var/obj/item/clothing/accessory/A = I
if(length(attached_accessories) >= max_accessories)
if(user)
to_chat(user, "<span class='warning'>[src] already has [length(attached_accessories)] accessories.</span>")
return
if(dummy_thick)
if(user)
to_chat(user, "<span class='warning'>[src] is too bulky and cannot have accessories attached to it!</span>")
return
else
if(user && !user.temporarilyRemoveItemFromInventory(I))
return
if(!A.attach(src, user))
return
if(!istype(accessory))
return
if(istype(accessory, /obj/item/clothing/accessory/ring))
return
if(length(attached_accessories) >= max_accessories)
if(user)
to_chat(user, span_warning("[src] already has [length(attached_accessories)] accessories."))
return
if(dummy_thick)
if(user)
to_chat(user, span_warning("[src] is too bulky and cannot have accessories attached to it!"))
return
if(user && !user.temporarilyRemoveItemFromInventory(accessory))
return
if(!accessory.attach(src, user))
return
if(user && notifyAttach)
to_chat(user, "<span class='notice'>You attach [I] to [src].</span>")
if(user && notifyAttach)
to_chat(user, span_notice("You attach [accessory] to [src]."))
if((flags_inv & HIDEACCESSORY) || (A.flags_inv & HIDEACCESSORY))
return TRUE
if((flags_inv & HIDEACCESSORY) || (accessory.flags_inv & HIDEACCESSORY))
return TRUE
//SANDSTORM EDIT
accessory_overlays = list(mutable_appearance('icons/mob/clothing/accessories.dmi', "blank"))
for(var/obj/item/clothing/accessory/attached_accessory in attached_accessories)
var/datum/element/polychromic/polychromic = LAZYACCESS(attached_accessory.comp_lookup, "item_worn_overlays")
if(!polychromic)
var/mutable_appearance/accessory_overlay = mutable_appearance(attached_accessory.mob_overlay_icon, attached_accessory.item_state || attached_accessory.icon_state, ABOVE_HUD_LAYER)
accessory_overlay.alpha = attached_accessory.alpha
accessory_overlay.color = attached_accessory.color
accessory_overlays += accessory_overlay
else
polychromic.apply_worn_overlays(attached_accessory, FALSE, attached_accessory.mob_overlay_icon, attached_accessory.item_state || attached_accessory.icon_state, NONE, accessory_overlays)
//SANDSTORM EDIT END
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.update_inv_w_uniform()
if(accessory.above_suit)
H.update_inv_wear_suit()
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.update_inv_w_uniform()
H.update_inv_wear_suit()
return TRUE
return TRUE
/obj/item/clothing/under/proc/remove_accessory(mob/user)
if(!isliving(user))
@@ -194,22 +189,21 @@
if(!can_use(user))
return
//SKYRAT EDIT
if(length(attached_accessories))
var/obj/item/clothing/accessory/A = attached_accessories[length(attached_accessories)]
//SKYRAT EDIT END
A.detach(src, user)
if(user.put_in_hands(A))
to_chat(user, "<span class='notice'>You detach [A] from [src].</span>")
else
to_chat(user, "<span class='notice'>You detach [A] from [src] and it falls on the floor.</span>")
if(!LAZYLEN(attached_accessories))
return
var/obj/item/clothing/accessory/accessory = attached_accessories[length(attached_accessories)]
accessory.detach(src, user)
if(user.put_in_hands(accessory))
to_chat(user, span_notice("You detach [accessory] from [src]."))
else
to_chat(user, span_notice("You detach [accessory] from [src] and it falls on the floor."))
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.update_inv_w_uniform()
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
H.update_inv_w_uniform()
if(accessory.above_suit)
H.update_inv_wear_suit()
/obj/item/clothing/under/examine(mob/user)
. = ..()
if(can_adjust)
@@ -234,10 +228,9 @@
. += "Its vital tracker appears to be enabled."
if(SENSOR_COORDS)
. += "Its vital tracker and tracking beacon appear to be enabled."
if(length(attached_accessories))
for(var/obj/item/clothing/accessory/attached_accessory in attached_accessories)
. += "\A [attached_accessory] is attached to it."
//SKYRAT EDIT END
for(var/obj/item/clothing/accessory/attached_accessory as anything in attached_accessories)
. += "\A [attached_accessory] is attached to it."
//SANDSTORM EDIT END
/obj/item/clothing/under/verb/toggle()
set name = "Adjust Suit Sensors"
+39 -58
View File
@@ -2,7 +2,7 @@
name = "Accessory"
desc = "Something has gone wrong!"
icon = 'icons/obj/clothing/accessories.dmi'
//skyrat edit
//sandstorm edit
mob_overlay_icon = 'icons/mob/clothing/accessories.dmi'
//
icon_state = "plasma"
@@ -13,48 +13,41 @@
var/above_suit = FALSE
var/minimize_when_attached = TRUE // TRUE if shown as a small icon in corner, FALSE if overlayed
var/datum/component/storage/detached_pockets
//skyrat edit
var/current_uniform = null
//
/obj/item/clothing/accessory/proc/attach(obj/item/clothing/under/U, user)
/obj/item/clothing/accessory/proc/attach(obj/item/clothing/under/uniform, user)
var/datum/component/storage/storage = GetComponent(/datum/component/storage)
if(storage)
if(SEND_SIGNAL(U, COMSIG_CONTAINS_STORAGE))
if(SEND_SIGNAL(uniform, COMSIG_CONTAINS_STORAGE))
return FALSE
U.TakeComponent(storage)
uniform.TakeComponent(storage)
detached_pockets = storage
//SKYRAT EDIT
U.attached_accessories |= src
force_unto(U)
current_uniform = U
//SKYRAT EDIT END
forceMove(U)
//SANDSTORM EDIT
LAZYADD(uniform.attached_accessories, src)
force_unto(uniform)
//SANDSTORM EDIT END
forceMove(uniform)
if (islist(U.armor) || isnull(U.armor)) // This proc can run before /obj/Initialize has run for U and src,
U.armor = getArmor(arglist(U.armor)) // we have to check that the armor list has been transformed into a datum before we try to call a proc on it
if (islist(uniform.armor) || isnull(uniform.armor)) // This proc can run before /obj/Initialize has run for uniform and src,
uniform.armor = getArmor(arglist(uniform.armor)) // we have to check that the armor list has been transformed into a datum before we try to call a proc on it
// This is safe to do as /obj/Initialize only handles setting up the datum if actually needed.
if (islist(armor) || isnull(armor))
armor = getArmor(arglist(armor))
U.armor = U.armor.attachArmor(armor)
uniform.armor = uniform.armor.attachArmor(armor)
if(isliving(user))
on_uniform_equip(U, user)
on_uniform_equip(uniform, user)
return TRUE
/obj/item/clothing/accessory/proc/detach(obj/item/clothing/under/U, user)
if(detached_pockets && detached_pockets.parent == U)
/obj/item/clothing/accessory/proc/detach(obj/item/clothing/under/uniform, user)
if(detached_pockets && detached_pockets.parent == uniform)
TakeComponent(detached_pockets)
U.armor = U.armor.detachArmor(armor)
//SANDSTORM EDIT
current_uniform = null
//SANDSTORM EDIT END
uniform.armor = uniform.armor.detachArmor(armor)
if(isliving(user))
on_uniform_dropped(U, user)
on_uniform_dropped(uniform, user)
if(minimize_when_attached)
transform *= 2
@@ -62,48 +55,36 @@
pixel_y = 0
layer = initial(layer)
plane = initial(plane)
U.cut_overlays()
U.attached_accessories -= src
U.accessory_overlays = list()
if(length(U.attached_accessories))
U.accessory_overlays = list(mutable_appearance('icons/mob/clothing/accessories.dmi', "blank"))
for(var/obj/item/clothing/accessory/attached_accessory in U.attached_accessories)
attached_accessory.force_unto(U)
var/datum/element/polychromic/polychromic = LAZYACCESS(attached_accessory.comp_lookup, "item_worn_overlays")
if(!polychromic)
var/mutable_appearance/accessory_overlay = mutable_appearance(attached_accessory.mob_overlay_icon, attached_accessory.item_state || attached_accessory.icon_state, ABOVE_HUD_LAYER)
accessory_overlay.alpha = attached_accessory.alpha
accessory_overlay.color = attached_accessory.color
U.accessory_overlays += accessory_overlay
else
polychromic.apply_worn_overlays(attached_accessory, FALSE, attached_accessory.mob_overlay_icon, attached_accessory.item_state || attached_accessory.icon_state, NONE, U.accessory_overlays)
uniform.cut_overlays()
LAZYREMOVE(uniform.attached_accessories, src)
for(var/obj/item/clothing/accessory/attached_accessory as anything in uniform.attached_accessories)
uniform.add_overlay(attached_accessory)
//SANDSTORM EDIT
/obj/item/clothing/accessory/proc/force_unto(obj/item/clothing/under/U)
/obj/item/clothing/accessory/proc/force_unto(obj/item/clothing/under/uniform)
layer = FLOAT_LAYER
plane = FLOAT_PLANE
if(minimize_when_attached)
if(current_uniform != U)
transform *= 0.5 //halve the size so it doesn't overpower the under
pixel_x += 8
pixel_y -= 8
if(length(U.attached_accessories) > 1)
if(length(U.attached_accessories) <= 3 && !current_uniform)
pixel_y += 8 * (length(U.attached_accessories) - 1)
else if((length(U.attached_accessories) > 3) && (length(U.attached_accessories) <= 6) && !current_uniform)
pixel_x -= 8
pixel_y += 8 * (length(U.attached_accessories) - 4)
else if((length(U.attached_accessories) > 6) && (length(U.attached_accessories) <= 9) && !current_uniform)
pixel_x -= 16
pixel_y += 8 * (length(U.attached_accessories) - 7)
else
if(current_uniform != U)
transform *= 0.5 //halve the size so it doesn't overpower the under
pixel_x += 8
pixel_y -= 8
if(length(uniform.attached_accessories) > 1)
switch(LAZYLEN(uniform.attached_accessories))
if(2 to 3)
pixel_y += 8 * (length(uniform.attached_accessories) - 1)
if(4 to 6)
pixel_x -= 8
pixel_y += 8 * (length(uniform.attached_accessories) - 4)
if(7 to 9)
pixel_x -= 16
pixel_y += 8 * (length(uniform.attached_accessories) - 7)
else
//we ran out of space for accessories, so we just throw shit at the wall
pixel_x = 0
pixel_y = 0
pixel_x += rand(-16, 16)
pixel_y += rand(-16, 16)
U.add_overlay(src)
uniform.add_overlay(src)
//SANDSTORM EDIT END
/obj/item/clothing/accessory/proc/on_uniform_equip(obj/item/clothing/under/U, user)
@@ -122,9 +103,9 @@
/obj/item/clothing/accessory/examine(mob/user)
. = ..()
. += "<span class='notice'>\The [src] can be attached to [istype(src, /obj/item/clothing/accessory/ring) ? "gloves" : "a uniform"]. Alt-click to remove it once attached.</span>"
. += span_notice("\The [src] can be attached to [istype(src, /obj/item/clothing/accessory/ring) ? "gloves" : "a uniform"]. <b>Alt-click</b> to remove it once attached.")
if(initial(above_suit))
. += "<span class='notice'>\The [src] can be worn above or below your suit. Ctrl-click to toggle.</span>"
. += span_notice("\The [src] can be worn above or below your suit. <b>Ctrl-click</b> to toggle.")
//////////////
//Waistcoats//
+1 -1
View File
@@ -55,7 +55,7 @@
SG.transfer_ckey(SA, FALSE)
SA.grant_all_languages(TRUE, FALSE, FALSE)
SA.grant_all_languages(UNDERSTOOD_LANGUAGE, grant_omnitongue = FALSE, source = LANGUAGE_ATOM)
SA.sentience_act()
+2 -2
View File
@@ -122,10 +122,10 @@
if (isitem(AM) && !istype(AM,/obj/item/projectile))
if(prob(50))
AM.forceMove(get_turf(src))
visible_message("<span class='warning'>Swish! [AM] lands in [src].</span>")
visible_message(span_warning("Swish! [AM] lands in [src]."))
return
else
visible_message("<span class='danger'>[AM] bounces off of [src]'s rim!</span>")
visible_message(span_danger("[AM] bounces off of [src]'s rim!"))
return ..()
else
return ..()
+2 -1
View File
@@ -50,4 +50,5 @@
if(visualsOnly)
return
H.grant_all_languages(TRUE, TRUE, TRUE, LANGUAGE_CURATOR)
H.grant_all_languages(source = LANGUAGE_CURATOR)
H.remove_blocked_language(GLOB.all_languages, source=LANGUAGE_ALL)
+2 -2
View File
@@ -49,7 +49,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, TRUE, TRUE, LANGUAGE_MIND)
user.grant_language(/datum/language/codespeak, source = LANGUAGE_MIND)
use_charge(user)
@@ -68,7 +68,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, TRUE, TRUE, LANGUAGE_MIND)
M.grant_language(/datum/language/codespeak, source = LANGUAGE_MIND)
use_charge(user)
/obj/item/codespeak_manual/proc/use_charge(mob/user)
+133 -97
View File
@@ -1,23 +1,28 @@
/*!Language holders will either exist in an atom/movable or a mind. Creation of language holders happens
/*!Language holders will either exist in an atom/movable. 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)
@@ -28,83 +33,86 @@ Key procs
* [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.
/// Lazyassoclist of all understood languages
var/list/understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
/// Lazyassoclist 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.
/// Lazyassoclist of blocked languages.
/// Used to prevent understanding and speaking certain languages, ie for certain mobs, mutations etc.
var/list/blocked_languages
/// If true, overrides tongue aforementioned 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
var/atom/movable/owner
/// 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)
update_atom_languages(M.current)
get_selected_language()
/datum/language_holder/New(atom/new_owner)
if(new_owner)
if(QDELETED(new_owner))
CRASH("Langauge holder added to a qdeleting thing, what the fuck [text_ref(new_owner)]")
if(!ismovable(new_owner))
CRASH("Language holder being added to a non-movable thing, this is invalid (was: [new_owner] / [new_owner.type])")
owner = new_owner
// If we have an owner, we'll set a default selected language
if(owner)
get_selected_language()
/datum/language_holder/Destroy()
QDEL_NULL(language_menu)
owner = null
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
/datum/language_holder/proc/grant_language(language, language_flags = ALL, source = LANGUAGE_MIND)
if(language_flags & UNDERSTOOD_LANGUAGE)
LAZYORASSOCLIST(understood_languages, language, source)
. = TRUE
if(spoken)
if(!spoken_languages[language])
spoken_languages[language] = list()
spoken_languages[language] |= source
if(language_flags & SPOKEN_LANGUAGE)
LAZYORASSOCLIST(spoken_languages, language, source)
. = TRUE
return .
/// 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)
/datum/language_holder/proc/grant_all_languages(language_flags = ALL, 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.
grant_language(language, language_flags, 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])
/datum/language_holder/proc/remove_language(language, language_flags = ALL, source = LANGUAGE_ALL)
if(language_flags & UNDERSTOOD_LANGUAGE)
if(source == LANGUAGE_ALL)
understood_languages -= language
LAZYREMOVE(understood_languages, language)
else
understood_languages[language] -= source
if(!length(understood_languages[language]))
understood_languages -= language
LAZYREMOVEASSOC(understood_languages, language, source)
. = TRUE
if(spoken && spoken_languages[language])
if(language_flags & SPOKEN_LANGUAGE)
if(source == LANGUAGE_ALL)
spoken_languages -= language
LAZYREMOVE(spoken_languages, language)
else
spoken_languages[language] -= source
if(!length(spoken_languages[language]))
spoken_languages -= language
LAZYREMOVEASSOC(spoken_languages, language, source)
. = TRUE
return .
/// 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)
remove_language(language, ALL, source)
if(remove_omnitongue)
omnitongue = FALSE
return TRUE
@@ -113,41 +121,41 @@ Key procs
/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
LAZYORASSOCLIST(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
if(source == LANGUAGE_ALL)
LAZYREMOVE(blocked_languages, language)
else
LAZYREMOVEASSOC(blocked_languages, language, source)
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)
/// Checks if you have the language passed.
/datum/language_holder/proc/has_language(language, flag_to_check = UNDERSTOOD_LANGUAGE)
if(language in blocked_languages)
return FALSE
if(spoken)
return language in spoken_languages
return language in understood_languages
var/list/langs_to_check = list()
if(flag_to_check & SPOKEN_LANGUAGE)
langs_to_check |= spoken_languages
if(flag_to_check & UNDERSTOOD_LANGUAGE)
langs_to_check |= understood_languages
return language in langs_to_check
/// 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
var/can_speak_language_path = omnitongue || owner.could_speak_language(language)
return (can_speak_language_path && has_language(language, SPOKEN_LANGUAGE))
/// 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()
@@ -172,59 +180,79 @@ Key procs
/datum/language_holder/proc/get_random_spoken_language()
return pick(spoken_languages)
/// Gets a random spoken language, trying to get a non-common language.
/datum/language_holder/proc/get_random_spoken_uncommon_language()
var/list/languages_minus_common = assoc_to_keys(spoken_languages) - /datum/language/common
// They have a language other than common
if(length(languages_minus_common))
return pick(languages_minus_common)
// They can only speak common, oh well.
else
return /datum/language/common
/// 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.
/datum/language_holder/proc/copy_languages(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)
grant_language(language, UNDERSTOOD_LANGUAGE, source_override)
for(var/language in from_holder.spoken_languages)
grant_language(language, FALSE, TRUE, source_override)
grant_language(language, SPOKEN_LANGUAGE, source_override)
else
for(var/language in from_holder.understood_languages)
grant_language(language, TRUE, FALSE, from_holder.understood_languages[language])
grant_language(language, UNDERSTOOD_LANGUAGE, from_holder.understood_languages[language])
for(var/language in from_holder.spoken_languages)
grant_language(language, FALSE, TRUE, from_holder.spoken_languages[language])
grant_language(language, SPOKEN_LANGUAGE, from_holder.spoken_languages[language])
for(var/language in from_holder.blocked_languages)
add_blocked_language(language, from_holder.blocked_languages[language])
return TRUE
/// Transfers all mind languages to the supplied language holder.
/datum/language_holder/proc/transfer_mind_languages(datum/language_holder/to_holder)
for(var/language in understood_languages)
if(LANGUAGE_MIND in understood_languages[language])
remove_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_MIND)
to_holder.grant_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_MIND)
for(var/language in spoken_languages)
if(LANGUAGE_MIND in spoken_languages[language])
remove_language(language, SPOKEN_LANGUAGE, LANGUAGE_MIND)
to_holder.grant_language(language, SPOKEN_LANGUAGE, LANGUAGE_MIND)
for(var/language in blocked_languages)
if(LANGUAGE_MIND in blocked_languages[language])
remove_blocked_language(language, LANGUAGE_MIND)
to_holder.add_blocked_language(language, LANGUAGE_MIND)
//************************************************
//* Specific language holders *
//* Use atom language sources only. *
//************************************************/
if(owner)
get_selected_language()
if(to_holder.owner)
to_holder.get_selected_language()
/// A global assoc list containing prototypes of all language holders
/// [Language holder typepath] to [language holder instance]
/// Used for easy reference of what can speak what without needing to constantly recreate language holders.
GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes())
/// Inits the global list of language holder prototypes.
/proc/init_language_holder_prototypes()
var/list/prototypes = list()
for(var/holdertype in typesof(/datum/language_holder))
prototypes[holdertype] = new holdertype()
return prototypes
/*
* Specific language holders presets
*
* Prefer to use [LANGUGAE_ATOM]. Atom languages will stick through species changes but not mindswaps.
*/
/datum/language_holder/alien
@@ -252,6 +280,10 @@ Key procs
/datum/language_holder/drone/syndicate
blocked_languages = null
/datum/language_holder/human_basic
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
/datum/language_holder/dwarf
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
/datum/language/dwarf = list(LANGUAGE_ATOM))
@@ -353,8 +385,12 @@ Key procs
/datum/language/arachnid = list(LANGUAGE_ATOM))
/datum/language_holder/empty
understood_languages = list()
spoken_languages = list()
understood_languages = null
spoken_languages = null
/datum/language_holder/universal
understood_languages = null
spoken_languages = null
/datum/language_holder/universal/New()
..()
+59 -60
View File
@@ -20,109 +20,108 @@
/datum/language_menu/ui_data(mob/user)
var/list/data = list()
var/atom/movable/AM = language_holder.get_atom()
if(isliving(AM))
data["is_living"] = TRUE
else
data["is_living"] = FALSE
var/atom/movable/speaker = language_holder.owner
data["is_living"] = isliving(speaker)
data["languages"] = list()
for(var/lang in GLOB.all_languages)
var/result = language_holder.has_language(lang) || language_holder.has_language(lang, TRUE)
for(var/datum/language/language as anything in GLOB.all_languages)
var/result = language_holder.has_language(language) || language_holder.has_language(language, SPOKEN_LANGUAGE)
if(!result)
continue
var/datum/language/language = lang
var/list/L = list()
var/list/lang_data = list()
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_language(language)
L["can_understand"] = AM.has_language(language)
lang_data["name"] = initial(language.name)
lang_data["desc"] = initial(language.desc)
lang_data["key"] = initial(language.key)
lang_data["is_default"] = (language == language_holder.selected_language)
if(speaker)
lang_data["can_speak"] = speaker.can_speak_language(language)
lang_data["can_understand"] = speaker.has_language(language)
data["languages"] += list(L)
UNTYPED_LIST_ADD(data["languages"], lang_data)
if(check_rights_for(user.client, R_ADMIN) || isobserver(AM))
if(check_rights_for(user.client, R_ADMIN) || isobserver(speaker))
data["admin_mode"] = TRUE
data["omnitongue"] = language_holder.omnitongue
data["unknown_languages"] = list()
for(var/lang in GLOB.all_languages)
if(language_holder.has_language(lang) || language_holder.has_language(lang, TRUE))
for(var/datum/language/language as anything in GLOB.all_languages)
if(language_holder.has_language(language) || language_holder.has_language(language, SPOKEN_LANGUAGE))
continue
var/datum/language/language = lang
var/list/L = list()
var/list/lang_data = list()
L["name"] = initial(language.name)
L["desc"] = initial(language.desc)
L["key"] = initial(language.key)
lang_data["name"] = initial(language.name)
lang_data["desc"] = initial(language.desc)
lang_data["key"] = initial(language.key)
UNTYPED_LIST_ADD(data["unknown_languages"], lang_data)
data["unknown_languages"] += list(L)
return data
/datum/language_menu/ui_act(action, params)
if(..())
. = ..()
if(.)
return
var/mob/user = usr
var/atom/movable/AM = language_holder.get_atom()
var/atom/movable/speaker = language_holder.owner
var/is_admin = check_rights_for(user.client, R_ADMIN)
var/language_name = params["language_name"]
var/datum/language/language_datum
for(var/lang in GLOB.all_languages)
var/datum/language/language = lang
for(var/datum/language/language as anything in GLOB.all_languages)
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_language(language_datum))
if(language_datum && speaker.can_speak_language(language_datum))
language_holder.selected_language = language_datum
. = TRUE
if("grant_language")
if((is_admin || isobserver(AM)) && language_datum)
if((is_admin || isobserver(speaker)) && 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
var/choice = tgui_input_list(user, "How do you want to add this language?", "[language_datum]", choices)
if(isnull(choice))
return
var/adding_flags = NONE
switch(choice)
if("Only Spoken")
spoken = TRUE
adding_flags |= SPOKEN_LANGUAGE
if("Only Understood")
understood = TRUE
adding_flags |= UNDERSTOOD_LANGUAGE
if("Both")
spoken = TRUE
understood = TRUE
language_holder.grant_language(language_datum, understood, spoken)
adding_flags |= ALL
if(LAZYACCESS(language_holder.blocked_languages, language_datum))
choice = tgui_alert(user, "Do you want to lift the blockage that's also preventing the language to be spoken or understood?", "[language_datum]", list("Yes", "No"))
if(choice == "Yes")
language_holder.remove_blocked_language(language_datum, LANGUAGE_ALL)
language_holder.grant_language(language_datum, adding_flags)
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)].")
message_admins("[key_name_admin(user)] granted the [language_name] language to [key_name_admin(speaker)].")
log_admin("[key_name(user)] granted the language [language_name] to [key_name(speaker)].")
. = TRUE
if("remove_language")
if((is_admin || isobserver(AM)) && language_datum)
if((is_admin || isobserver(speaker)) && 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
var/choice = tgui_input_list(user, "Which part do you wish to remove?", "[language_datum]", choices)
if(isnull(choice))
return
var/removing_flags = NONE
switch(choice)
if("Only Spoken")
spoken = TRUE
removing_flags |= SPOKEN_LANGUAGE
if("Only Understood")
understood = TRUE
removing_flags |= UNDERSTOOD_LANGUAGE
if("Both")
spoken = TRUE
understood = TRUE
language_holder.remove_language(language_datum, understood, spoken)
removing_flags |= ALL
language_holder.remove_language(language_datum, removing_flags)
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)].")
message_admins("[key_name_admin(user)] removed the [language_name] language to [key_name_admin(speaker)].")
log_admin("[key_name(user)] removed the language [language_name] to [key_name(speaker)].")
. = TRUE
if("toggle_omnitongue")
if(is_admin || isobserver(AM))
if(is_admin || isobserver(speaker))
language_holder.omnitongue = !language_holder.omnitongue
if(is_admin)
message_admins("[key_name_admin(user)] [language_holder.omnitongue ? "enabled" : "disabled"] the ability to speak all languages (that they know) of [key_name_admin(AM)].")
log_admin("[key_name(user)] [language_holder.omnitongue ? "enabled" : "disabled"] the ability to speak all languages (that_they know) of [key_name(AM)].")
message_admins("[key_name_admin(user)] [language_holder.omnitongue ? "enabled" : "disabled"] the ability to speak all languages (that they know) of [key_name_admin(speaker)].")
log_admin("[key_name(user)] [language_holder.omnitongue ? "enabled" : "disabled"] the ability to speak all languages (that_they know) of [key_name(speaker)].")
. = TRUE
@@ -678,6 +678,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()
user.remove_blocked_language(GLOB.all_languages, source = LANGUAGE_ALL)
new /obj/effect/decal/cleanable/ash(get_turf(user))
qdel(src)
+6 -6
View File
@@ -89,20 +89,20 @@ INITIALIZE_IMMEDIATE(/mob/dead)
if(alert(src, "Jump to server [pick] ([addr])?", "Server Hop", "Yes", "No") != "Yes")
return
var/client/C = client
to_chat(C, "<span class='notice'>Sending you to [pick].</span>")
new /atom/movable/screen/splash(C)
var/client/hopper = client
to_chat(hopper, "<span class='notice'>Sending you to [pick].</span>")
new /atom/movable/screen/splash(null, src, hopper, FALSE)
mob_transforming = TRUE
sleep(29) //let the animation play
sleep(2.9 SECONDS) //let the animation play
mob_transforming = FALSE
if(!C)
if(!hopper)
return
winset(src, null, "command=.options") //other wise the user never knows if byond is downloading resources
C << link("[addr]")
hopper << link("[addr]")
/mob/dead/proc/update_z(new_z) // 1+ to register, null to unregister
if (registered_z != new_z)
@@ -41,7 +41,3 @@
else
postfix = "soon"
to_chat(src, "Please set up your character and select \"Ready\". The game will start [postfix].")
var/datum/hud/new_player/NH = hud_used
if(istype(NH))
NH.populate_buttons(src)
@@ -27,7 +27,7 @@
/mob/dead/new_player/Initialize(mapload)
if(client && SSticker.state == GAME_STATE_STARTUP)
var/atom/movable/screen/splash/S = new(client, TRUE, TRUE)
var/atom/movable/screen/splash/S = new(null, null, client, TRUE)
S.Fade(TRUE)
if(length(GLOB.newplayer_start))
@@ -496,7 +496,7 @@
if(job && !job.override_latejoin_spawn(character))
SSjob.SendToLateJoin(character)
if(!arrivals_docked)
var/atom/movable/screen/splash/Spl = new(character.client, TRUE)
var/atom/movable/screen/splash/Spl = new(null, character, character.client, TRUE)
Spl.Fade(TRUE)
character.playsound_local(get_turf(character), 'sound/voice/ApproachingTG.ogg', 25)
-4
View File
@@ -794,10 +794,6 @@
else
hud_used.healths.icon_state = "health7"
/mob/living/carbon/proc/update_internals_hud_icon(internal_state = 0)
if(hud_used && hud_used.internals)
hud_used.internals.icon_state = "internal[internal_state]"
/mob/living/carbon/update_stat()
if(status_flags & GODMODE)
return
+23 -17
View File
@@ -50,22 +50,15 @@
//accessory
var/accessory_msg
if(istype(w_uniform, /obj/item/clothing/under))
var/obj/item/clothing/under/U = w_uniform
if(length(U.attached_accessories) && !(U.flags_inv & HIDEACCESSORY))
var/list/weehoo = list()
var/dumb_icons = ""
for(var/obj/item/clothing/accessory/attached_accessory in U.attached_accessories)
if(!(attached_accessory.flags_inv & HIDEACCESSORY))
weehoo += "\a [attached_accessory]"
dumb_icons = "[dumb_icons][icon2html(attached_accessory, user)]"
if(length(weehoo))
accessory_msg += " with [dumb_icons]"
if(length(U.attached_accessories) >= 2)
accessory_msg += jointext(weehoo, ", ", 1, length(weehoo) - 1)
accessory_msg += " and [weehoo[length(weehoo)]]"
else
accessory_msg += weehoo[1]
var/obj/item/clothing/under/worn_thing = w_uniform
if(!CHECK_BITFIELD(worn_thing.flags_inv, HIDEACCESSORY))
var/list/accessory_preparation
for(var/obj/item/clothing/accessory/attached_accessory as anything in worn_thing.attached_accessories)
if(CHECK_BITFIELD(attached_accessory.flags_inv, HIDEACCESSORY))
continue
LAZYADD(accessory_preparation, "[icon2html(attached_accessory, user)] [attached_accessory]")
if(length(accessory_preparation))
accessory_msg = " with [english_list(accessory_preparation)]"
. += "[t_He] [t_is] wearing [w_uniform.get_examine_string(user)][accessory_msg]."
//head
@@ -88,7 +81,20 @@
//gloves
if(gloves && !(ITEM_SLOT_GLOVES in obscured))
. += "[t_He] [t_has] [gloves.get_examine_string(user)] on [t_his] hands."
//accessory
var/accessory_msg
if(istype(gloves, /obj/item/clothing/gloves))
var/obj/item/clothing/gloves/worn_thing = gloves
if(!CHECK_BITFIELD(worn_thing.flags_inv, HIDEACCESSORY))
var/list/accessory_preparation
for(var/obj/item/clothing/accessory/ring/attached_accessory as anything in worn_thing.attached_accessories)
if(CHECK_BITFIELD(attached_accessory.flags_inv, HIDEACCESSORY))
continue
LAZYADD(accessory_preparation, "[icon2html(attached_accessory, user)] [attached_accessory]")
if(length(accessory_preparation))
accessory_msg = " with [english_list(accessory_preparation)] on [t_his] fingers"
. += "[t_He] [t_has] [gloves.get_examine_string(user)] on [t_his] hands[accessory_msg]."
else if(length(blood_DNA))
var/hand_number = get_num_arms(FALSE)
if(hand_number)
@@ -678,11 +678,11 @@
if(hal_screwyhud == SCREWYHUD_HEALTHY)
icon_num = 0
if(icon_num)
hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[BP.body_zone][icon_num]"))
hud_used.healthdoll.add_overlay(mutable_appearance(ui_style_modular(hud_used.ui_style, "health"), "[BP.body_zone][icon_num]"))
for(var/t in get_missing_limbs()) //Missing limbs
hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]6"))
hud_used.healthdoll.add_overlay(mutable_appearance(ui_style_modular(hud_used.ui_style, "health"), "[t]6"))
for(var/t in get_disabled_limbs()) //Disabled limbs
hud_used.healthdoll.add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[t]7"))
hud_used.healthdoll.add_overlay(mutable_appearance(ui_style_modular(hud_used.ui_style, "health"), "[t]7"))
else
hud_used.healthdoll.icon_state = "healthdoll_DEAD"
@@ -6,6 +6,7 @@
can_buckle = TRUE
buckle_lying = FALSE
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
initial_language_holder = /datum/language_holder/empty // We get stuff from our species
/// Enable stamina combat
combat_flags = COMBAT_FLAGS_STAMINA_COMBAT | COMBAT_FLAG_UNARMED_PARRY
status_flags = CANSTUN|CANKNOCKDOWN|CANUNCONSCIOUS|CANPUSH|CANSTAGGER
@@ -292,12 +292,9 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list(
if(carbon_source.internal)
carbon_source.internal = null
// This isn't meant to be FALSE, it correlates to the icon's name.
carbon_source.update_internals_hud_icon(0)
else if (!QDELETED(item))
if(internals || carbon_source.getorganslot(ORGAN_SLOT_BREATHING_TUBE))
carbon_source.internal = item
carbon_source.update_internals_hud_icon(1)
carbon_source.visible_message(
span_danger("[user] [isnull(carbon_source.internal) ? "closes": "opens"] the valve on [source]'s [item.name]."),
@@ -319,7 +319,6 @@
if((C.flags_inv & (HIDEHAIR|HIDEFACIALHAIR)) || (initial(C.flags_inv) & (HIDEHAIR|HIDEFACIALHAIR)))
update_hair()
if(toggle_off && internal && !getorganslot(ORGAN_SLOT_BREATHING_TUBE))
update_internals_hud_icon(0)
internal = null
if(C.flags_inv & HIDEEYES)
update_inv_glasses()
+15 -11
View File
@@ -90,8 +90,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/dangerous_existence //A flag for transformation spells that tells them "hey if you turn a person into one of these without preperation, they'll probably die!"
///Affects the speech message, for example: Motharula flutters, "My speech message is flutters!"
var/say_mod = "says"
///What languages this species can understand and say. Use a [language holder datum][/datum/language_holder] in this var.
var/species_language_holder = /datum/language_holder
/// What languages this species can understand and say.
/// Use a [language holder datum][/datum/language_holder] typepath in this var.
/// Should never be null.
var/datum/language_holder/species_language_holder = /datum/language_holder/human_basic
/**
* Visible CURRENT bodyparts that are unique to a species.
* DO NOT USE THIS AS A LIST OF ALL POSSIBLE BODYPARTS AS IT WILL FUCK
@@ -558,6 +560,16 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
for(var/obj/item/bodypart/B in C.bodyparts)
B.change_bodypart_status(BODYPART_HYBRID, FALSE, TRUE) // Makes all Bodyparts 'robotic'.
// All languages associated with this language holder are added with source [LANGUAGE_SPECIES]
// rather than source [LANGUAGE_ATOM], so we can track what to remove if our species changes again
var/datum/language_holder/gaining_holder = GLOB.prototype_language_holders[species_language_holder]
for(var/language in gaining_holder.understood_languages)
C.grant_language(language, UNDERSTOOD_LANGUAGE, LANGUAGE_SPECIES)
for(var/language in gaining_holder.spoken_languages)
C.grant_language(language, SPOKEN_LANGUAGE, LANGUAGE_SPECIES)
for(var/language in gaining_holder.blocked_languages)
C.add_blocked_language(language, LANGUAGE_SPECIES)
SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species)
/datum/species/proc/update_species_slowdown(mob/living/carbon/human/H)
@@ -1650,15 +1662,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.Jitter(5)
hunger_rate = 3 * HUNGER_FACTOR
hunger_rate *= H.physiology.hunger_mod
// SANDSTORM EDIT
if (H.client)
H.adjust_nutrition(-hunger_rate)
else
// Do not allow SSD players to get too hungry.
if (H.nutrition >= NUTRITION_LEVEL_FED)
H.adjust_nutrition(-hunger_rate)
// End of sandstorm edit
H.adjust_nutrition(-hunger_rate)
if (H.nutrition > NUTRITION_LEVEL_FULL)
@@ -70,7 +70,6 @@
H.equipOutfit(O, visualsOnly)
H.internal = H.get_item_for_held_index(2)
H.update_internals_hud_icon(1)
return FALSE
/datum/species/plasmaman/random_name(gender,unique,lastname)
@@ -34,11 +34,11 @@
/mob/living/carbon/human/set_drugginess(amount)
..()
if(!amount)
remove_language(/datum/language/beachbum, TRUE, TRUE, LANGUAGE_HIGH)
remove_language(/datum/language/beachbum, source = LANGUAGE_HIGH)
/mob/living/carbon/human/adjust_drugginess(amount)
..()
if(druggy)
grant_language(/datum/language/beachbum, TRUE, TRUE, LANGUAGE_HIGH)
grant_language(/datum/language/beachbum, source = LANGUAGE_HIGH)
else
remove_language(/datum/language/beachbum, TRUE, TRUE, LANGUAGE_HIGH)
remove_language(/datum/language/beachbum, source = LANGUAGE_HIGH)
-3
View File
@@ -340,12 +340,9 @@
if(internal)
if(internal.loc != src)
internal = null
update_internals_hud_icon(0)
else if (!internals && !getorganslot(ORGAN_SLOT_BREATHING_TUBE))
internal = null
update_internals_hud_icon(0)
else
update_internals_hud_icon(1)
. = internal.remove_air_volume(volume_needed)
if(!.)
return FALSE //to differentiate between no internals and active, but empty internals
@@ -397,7 +397,7 @@
return ..()
/mob/living/carbon/monkey/hitby(atom/movable/hitting_atom, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(istype(hitting_atom, /obj/item))
if(isitem(hitting_atom))
var/obj/item/item_hitby = hitting_atom
var/mob/thrown_by = item_hitby.thrownby?.resolve()
if(item_hitby.throwforce < src.health && thrown_by && ishuman(thrown_by))
+8 -3
View File
@@ -107,14 +107,17 @@
return FALSE
/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
var/zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
if(!isitem(AM))
// Filled with made up numbers for non-items.
if(mob_run_block(AM, 30, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, throwingdatum?.thrower?.zone_selected, list()))
if(mob_run_block(AM, 30, "\the [AM.name]", ATTACK_TYPE_THROWN, 0, throwingdatum?.thrower, throwingdatum?.thrower?.zone_selected, list()) & BLOCK_SUCCESS)
hitpush = FALSE
skipcatch = TRUE
blocked = TRUE
return TRUE
else
playsound(loc, 'sound/weapons/genhit.ogg', 50, TRUE, -1) //Item sounds are handled in the item itself
log_combat(AM, src, "hit ")
return ..()
var/obj/item/thrown_item = AM
@@ -126,18 +129,20 @@
skipcatch = TRUE
blocked = TRUE
var/zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
// zone moved up because things need it early while checking it from the thrower is unnecessary
var/nosell_hit = SEND_SIGNAL(thrown_item, COMSIG_MOVABLE_IMPACT_ZONE, src, zone, throwingdatum, blocked, FALSE)
if(nosell_hit)
skipcatch = TRUE
hitpush = FALSE
if(blocked)
return TRUE
return BLOCK_SUCCESS
var/mob/thrown_by = thrown_item.thrownby?.resolve()
if(thrown_by)
log_combat(thrown_by, src, "threw and hit", thrown_item)
else
log_combat(thrown_item, src, "hit ")
if(nosell_hit)
return ..()
visible_message(span_danger("[src] is hit by [thrown_item]!"), \
-5
View File
@@ -501,8 +501,3 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
// End of Skyrat edits
/mob/living/whisper(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
say("#[message]", bubble_type, spans, sanitize, language, ignore_spam, forced)
/mob/living/get_language_holder(get_minds = TRUE)
if(get_minds && mind)
return mind.get_language_holder()
. = ..()
@@ -6,7 +6,7 @@
var/highlighted = FALSE
var/mob/camera/aiEye/pic_in_pic/aiEye
/atom/movable/screen/movable/pic_in_pic/ai/Initialize(mapload)
/atom/movable/screen/movable/pic_in_pic/ai/Initialize(mapload, datum/hud/hud_owner)
. = ..()
aiEye = new /mob/camera/aiEye/pic_in_pic()
aiEye.screen = src
@@ -138,13 +138,11 @@
var/datum/action/innate/pai/light/AL = new /datum/action/innate/pai/light
var/datum/action/innate/custom_holoform/custom_holoform = new /datum/action/innate/custom_holoform
var/datum/action/language_menu/ALM = new
SW.Grant(src)
AS.Grant(src)
AC.Grant(src)
AR.Grant(src)
AL.Grant(src)
ALM.Grant(src)
custom_holoform.Grant(src)
emitter_next_use = world.time + 10 SECONDS
@@ -270,7 +270,7 @@
encryptmod = TRUE
if("translator")
if(href_list["toggle"]) //This is permanent.
grant_all_languages(TRUE, TRUE, TRUE, LANGUAGE_SOFTWARE)
grant_all_languages(source = LANGUAGE_SOFTWARE)
if("doorjack")
if(href_list["jack"])
if(cable && cable.machine)
@@ -24,9 +24,9 @@
robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer ABOVE_HUD_LAYER, UI should be just below it.
robot_modules_background.plane = HUD_PLANE
inv1 = new /atom/movable/screen/robot/module1()
inv2 = new /atom/movable/screen/robot/module2()
inv3 = new /atom/movable/screen/robot/module3()
inv1 = new /atom/movable/screen/robot/module1(null, src)
inv2 = new /atom/movable/screen/robot/module2(null, src)
inv3 = new /atom/movable/screen/robot/module3(null, src)
ident = rand(1, 999)
@@ -139,7 +139,7 @@
/mob/living/simple_animal/bot/honkbot/hitby(atom/movable/hitting_atom, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(istype(hitting_atom, /obj/item))
if(isitem(hitting_atom))
playsound(src, honksound, 50, TRUE, -1)
var/obj/item/item_hitby = hitting_atom
var/mob/thrown_by = item_hitby.thrownby?.resolve()
@@ -352,7 +352,7 @@
/mob/living/simple_animal/bot/secbot/hitby(atom/movable/hitting_atom, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
if(istype(hitting_atom, /obj/item))
if(isitem(hitting_atom))
var/obj/item/item_hitby = hitting_atom
var/mob/thrown_by = item_hitby.thrownby?.resolve()
if(item_hitby.throwforce < src.health && thrown_by && ishuman(thrown_by))
@@ -44,13 +44,13 @@
visible_message("<span class='danger'>[P] has a reduced effect on [src]!</span>")
return ..()
/mob/living/simple_animal/hostile/asteroid/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)//No floor tiling them to death, wiseguy
if(istype(AM, /obj/item))
var/obj/item/T = AM
/mob/living/simple_animal/hostile/asteroid/hitby(atom/movable/hitting_atom, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)//No floor tiling them to death, wiseguy
if(isitem(hitting_atom))
var/obj/item/item_hitby = hitting_atom
if(!stat)
Aggro()
if(T.throwforce <= 20)
visible_message(span_notice("The [T.name] [throw_message] [src.name]!"))
if(item_hitby.throwforce <= 20)
visible_message(span_notice("\The [item_hitby] [throw_message] [src]!"))
return
..()
@@ -180,11 +180,11 @@
if(M.a_intent == INTENT_HARM)
Bruise()
/mob/living/simple_animal/hostile/mushroom/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
/mob/living/simple_animal/hostile/mushroom/hitby(atom/movable/hitting_atom, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum)
..()
if(istype(AM, /obj/item))
var/obj/item/T = AM
if(T.throwforce)
if(isitem(hitting_atom))
var/obj/item/item_hitby = hitting_atom
if(item_hitby.throwforce)
Bruise()
/mob/living/simple_animal/hostile/mushroom/bullet_act(obj/item/projectile/P)
+3 -4
View File
@@ -49,7 +49,7 @@
dispose_rendering()
qdel(hud_used)
QDEL_LIST(client_colours)
ghostize(can_reenter_corpse = FALSE) //False, since we're deleting it currently
ghostize()
if(mind?.current == src) //Let's just be safe yeah? This will occasionally be cleared, but not always. Can't do it with ghostize without changing behavior
mind.set_current(null)
// if(mock_client)
@@ -1051,12 +1051,11 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
//Do not do parent's actions, as we *usually* do this differently.
fully_replace_character_name(real_name, new_name)
/mob/verb/open_language_menu()
/mob/verb/open_language_menu_verb()
set name = "Open Language Menu"
set category = "IC"
var/datum/language_holder/H = get_language_holder()
H.open_language_menu(usr)
get_language_holder().open_language_menu(usr)
///Adjust the nutrition of a mob
/mob/proc/adjust_nutrition(change, max = INFINITY) //Honestly FUCK the oldcoders for putting nutrition on /mob someone else can move it up because holy hell I'd have to fix SO many typechecks
+1 -1
View File
@@ -308,7 +308,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
a_intent = possible_a_intents[current_intent]
if(hud_used && hud_used.action_intent)
hud_used.action_intent.icon_state = "[a_intent]"
hud_used.action_intent.update_icon()
/proc/is_blind(A)
-1
View File
@@ -438,7 +438,6 @@
mind.transfer_to(new_xeno)
else
transfer_ckey(new_xeno)
update_atom_languages()
to_chat(new_xeno, "<B>You are now an alien.</B>")
. = new_xeno
@@ -58,9 +58,9 @@
build_path = /obj/item/construction/rcd
category = list("hacked", "Construction")
/datum/design/rpd
/datum/design/rpd_autolathe
name = "Rapid Pipe Dispenser (RPD)"
id = "rpd"
id = "rpd_autolathe"
build_type = AUTOLATHE | NO_PUBLIC_LATHE
materials = list(/datum/material/iron = 75000, /datum/material/glass = 37500)
build_path = /obj/item/pipe_dispenser
@@ -552,7 +552,7 @@
if(S.amount < S.max_amount)
sheets += S
if(sheets.len > 0)
if(length(sheets))
var/obj/item/stack/sheet/S = pick(sheets)
S.add(1) // Dare var edit directly again and i'll strangle you.
to_chat(owner, "<span class='notice'>[linked_extract] adds a layer of slime to [S], which metamorphosizes into another sheet of material!</span>")
@@ -587,6 +587,7 @@
/obj/item/hothands
name = "burning fingertips"
desc = "You shouldn't see this."
item_flags = ABSTRACT
/obj/item/hothands/get_temperature()
return 290 //Below what's required to ignite plasma.
@@ -595,11 +596,11 @@
id = "stabilizeddarkpurple"
colour = "dark purple"
var/obj/item/hothands/fire
examine_text = "<span class='notice'>Their fingertips burn brightly!</span>"
examine_text = span_notice("Their fingertips burn brightly!")
/datum/status_effect/stabilized/darkpurple/on_apply()
ADD_TRAIT(owner, TRAIT_RESISTHEATHANDS, "slimestatus")
fire = new(owner)
fire = new()
return ..()
/datum/status_effect/stabilized/darkpurple/tick()
@@ -607,7 +608,7 @@
if(item)
var/obj/item/reagent_containers/food/snacks/F = item
if(istype(F) && F.cooked_type)
to_chat(owner, "<span class='warning'>[linked_extract] flares up brightly, and your hands alone are enough cook [F]!</span>")
to_chat(owner, span_warning("[linked_extract] flares up brightly, and your hands alone are enough cook [F]!"))
F.microwave_act()
else
item.attackby(fire, owner)
@@ -980,7 +981,6 @@
familiar.copy_languages(owner, LANGUAGE_MASTER)
if(linked.saved_mind)
linked.saved_mind.transfer_to(familiar)
familiar.update_atom_languages()
familiar.ckey = linked.saved_mind.key
else
if(familiar.mind)
@@ -35,7 +35,7 @@
icon_state = "wasteland"
environment_type = "wasteland"
baseturfs = /turf/open/floor/plating/asteroid/basalt/wasteland
digResult = /obj/item/stack/ore/glass/basalt
dig_result = /obj/item/stack/ore/glass/basalt
initial_gas_mix = LAVALAND_DEFAULT_ATMOS
slowdown = 0.5
floor_variance = 30
@@ -159,7 +159,7 @@
to_chat(user, "<span class='boldannounce'>You start skimming through [src], but you already know dronespeak.</span>")
else
to_chat(user, "<span class='boldannounce'>You start skimming through [src], and suddenly the drone chittering makes sense.</span>")
user.grant_language(/datum/language/drone, TRUE, TRUE)//, LANGUAGE_MIND)
user.grant_language(/datum/language/drone, source = LANGUAGE_MIND)
return
if(user.has_language(/datum/language/drone))
@@ -180,7 +180,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='hear'>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], chitters resonate in your mind.</span>", "<span class='hear'>You hear smacking.</span>")
M.grant_language(/datum/language/drone, TRUE, TRUE) //, LANGUAGE_MIND)
M.grant_language(/datum/language/drone, source = LANGUAGE_MIND)
return
/obj/structure/fluff/oldturret
+1
View File
@@ -69,6 +69,7 @@ All ShuttleMove procs go here
/turf/proc/afterShuttleMove(turf/oldT, rotation)
//Dealing with the turf we left behind
oldT.TransferComponents(src)
SEND_SIGNAL(oldT, COMSIG_TURF_AFTER_SHUTTLE_MOVE, src) //Mostly for decals
var/shuttle_boundary = baseturfs.Find(/turf/baseturf_skipover/shuttle)
if(shuttle_boundary)
oldT.ScrapeAway(baseturfs.len - shuttle_boundary + 1)
+2 -2
View File
@@ -14,6 +14,6 @@ GLOBAL_DATUM_INIT(language_menu_state, /datum/ui_state/language_menu, new)
if(check_rights_for(user.client, R_ADMIN))
. = UI_INTERACTIVE
else if(istype(src_object, /datum/language_menu))
var/datum/language_menu/LM = src_object
if(LM.language_holder.get_atom() == user)
var/datum/language_menu/my_languages = src_object
if(my_languages.language_holder.owner == user)
. = UI_INTERACTIVE
+7 -1
View File
@@ -44,7 +44,13 @@ Notes:
owner = C
var/datum/asset/stuff = get_asset_datum(/datum/asset/simple/jquery)
stuff.send(owner)
owner << browse(file2text('code/modules/tooltip/tooltip.html'), "window=[control]")
var/datum/asset/simple/namespaced/fonts/fonts = get_asset_datum(/datum/asset/simple/namespaced/fonts)
fonts.send(owner)
var/static/file2send
if(!file2send)
file2send = replacetext(file2text('code/modules/tooltip/tooltip.html'), "THE_FONT_GOES_HERE!!!!!!!!!!!!!!!", SSassets.transport.get_asset_url("fonts.css"))
owner << browse(file2send, "window=[control]")
..()
+8
View File
@@ -4,6 +4,7 @@
<title>Tooltip</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel='stylesheet' href='THE_FONT_GOES_HERE!!!!!!!!!!!!!!!' >
<style type="text/css">
body, html {
margin: 0;
@@ -104,6 +105,13 @@
#1b1d1a 4px
); background-size: 100% 4px;}
.corru .wrap {border: 2px dashed #FFFF00; background-color: #000000;}
.corru .content {color: #FFFFFF; border-color: #000000; background-color: #000000; font-family: "Space Mono";}
.corru .content h1::before {content: "NOTE::'"; display: inline;}
.corru .content h1::after {content: "'"; display: inline;}
.corru .content p::before {content: "INHERITED CONTEXT::'"; display: inline;}
.corru .content p::after {content: "'"; display: inline;}
</style>
</head>
<body>
+1
View File
@@ -93,6 +93,7 @@
// #include "holidays.dm"
// #include "hydroponics_harvest.dm"
// #include "keybinding_init.dm"
// #include "language_transfer.dm"
#include "machine_disassembly.dm"
#include "medical_wounds.dm"
#include "merge_type.dm"
@@ -0,0 +1,191 @@
// Dedicated to testing language holders
/// Simply tests that swapping to a new species gives you the languages of that species and removes the languages of the old species
/datum/unit_test/language_species_swap_simple
/datum/unit_test/language_species_swap_simple/Run()
var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
var/datum/language_holder/holder = dummy.get_language_holder()
var/list/initial_spoken = holder.spoken_languages.Copy()
var/list/initial_understood = holder.understood_languages.Copy()
TEST_ASSERT(length(initial_spoken) == 1, \
"Dummy should only speak one language! Instead, it knew the following: [print_language_list(initial_spoken)]")
TEST_ASSERT(length(initial_understood) == 1, \
"Dummy should only understand one language! Instead, it knew the following: [print_language_list(initial_understood)]")
dummy.set_species(/datum/species/lizard)
TEST_ASSERT(length(holder.spoken_languages) == 2, \
"Dummy should speak two languages - Common and Draconic! Instead, it knew the following: [print_language_list(holder.spoken_languages)]")
TEST_ASSERT(length(holder.understood_languages) == 2, \
"Dummy should understand two languages - Common and Draconic! Instead, it knew the following: [print_language_list(holder.understood_languages)]")
dummy.set_species(/datum/species/human)
TEST_ASSERT(length(initial_spoken & holder.spoken_languages) == 1, \
"Dummy did not speak Common after returning to human! Instead, it knew the following: [print_language_list(holder.spoken_languages)]")
TEST_ASSERT(length(initial_understood & holder.understood_languages) == 1, \
"Dummy did not understand Common after returning to human! Instead, it knew the following: [print_language_list(holder.understood_languages)]")
/// Tests species changes which are more complex are functional (e.g. from a species which speaks common to one which does not)
/datum/unit_test/language_species_swap_complex
/datum/unit_test/language_species_swap_complex/Run()
var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
var/datum/language_holder/holder = dummy.get_language_holder()
dummy.set_species(/datum/species/lizard/silverscale)
TEST_ASSERT(!dummy.has_language(/datum/language/common, SPOKEN_LANGUAGE), \
"Changing a mob's species from one which speaks common to one which does not should remove the language!")
TEST_ASSERT(dummy.has_language(/datum/language/common, UNDERSTOOD_LANGUAGE), \
"Changing a mob's species from one which understands common another which does should not remove the language!")
TEST_ASSERT(length(holder.spoken_languages) == 2, \
"Dummy should speak two languages - Uncommon and Draconic! Instead, it knew the following: [print_language_list(holder.spoken_languages)]")
TEST_ASSERT(length(holder.understood_languages) == 3, \
"Dummy should understand two languages - Common, Uncommon and Draconic! Instead, it knew the following: [print_language_list(holder.understood_languages)]")
/// Test that other random languages known are not lost on species change
/datum/unit_test/language_species_change_other_known
/datum/unit_test/language_species_change_other_known/Run()
var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
dummy.grant_language(/datum/language/piratespeak, source = LANGUAGE_MIND)
dummy.grant_language(/datum/language/draconic, source = LANGUAGE_ATOM)
dummy.set_species(/datum/species/lizard)
TEST_ASSERT(dummy.has_language(/datum/language/piratespeak, SPOKEN_LANGUAGE), \
"Dummy should still speak Pirate after changing species, as it's a mind language!")
TEST_ASSERT(dummy.has_language(/datum/language/piratespeak, UNDERSTOOD_LANGUAGE), \
"Dummy should still understand Pirate after changing species, as it's a mind language!")
dummy.set_species(/datum/species/human)
TEST_ASSERT(dummy.has_language(/datum/language/draconic, SPOKEN_LANGUAGE), \
"Dummy should still speak Draconic after changing species, as it's an atom language!")
TEST_ASSERT(dummy.has_language(/datum/language/draconic, UNDERSTOOD_LANGUAGE), \
"Dummy should still understand Draconic after changing species, as it's an atom language!")
/// Tests that mind bound languages are not lost swapping into a new mob, but other languages are
/datum/unit_test/language_mind_transfer
/datum/unit_test/language_mind_transfer/Run()
var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
var/mob/living/basic/pet/dog/corgi/transfer_target = allocate(/mob/living/basic/pet/dog/corgi)
dummy.mind_initialize()
dummy.grant_language(/datum/language/piratespeak, source = LANGUAGE_MIND)
dummy.grant_language(/datum/language/draconic, source = LANGUAGE_ATOM)
dummy.set_species(/datum/species/lizard/silverscale)
dummy.mind.transfer_to(transfer_target)
// transfer_target should speak and understand pirate
TEST_ASSERT(!dummy.has_language(/datum/language/piratespeak, SPOKEN_LANGUAGE), \
"Dummy should no longer be speaking Pirate after losing their mind!")
TEST_ASSERT(transfer_target.has_language(/datum/language/piratespeak, SPOKEN_LANGUAGE), \
"Dummy's new mob should be capable of speaking Pirate!")
TEST_ASSERT(!dummy.has_language(/datum/language/piratespeak, UNDERSTOOD_LANGUAGE), \
"Dummy should no longer be understanding Pirate after losing their mind!")
TEST_ASSERT(transfer_target.has_language(/datum/language/piratespeak, UNDERSTOOD_LANGUAGE), \
"Dummy's new mob should be capable of understanding Pirate!")
// transfer_target should NOT speak and understand draconic
TEST_ASSERT(dummy.has_language(/datum/language/draconic, SPOKEN_LANGUAGE), \
"Dummy should still understand Draconic after losing their mind - it's an atom language!")
TEST_ASSERT(!transfer_target.has_language(/datum/language/draconic, SPOKEN_LANGUAGE), \
"Dummy's new mob should not understand Draconic - it's an atom language!")
TEST_ASSERT(dummy.has_language(/datum/language/draconic, UNDERSTOOD_LANGUAGE), \
"Dummy should still understand Draconic after losing their mind - it's an atom language!")
TEST_ASSERT(!transfer_target.has_language(/datum/language/draconic, UNDERSTOOD_LANGUAGE), \
"Dummy's new mob should not understand Draconic - it's an atom language!")
// transfer_target should NOT speak and understand uncommon
TEST_ASSERT(dummy.has_language(/datum/language/uncommon, SPOKEN_LANGUAGE), \
"Dummy should still understand Uncommon after losing their mind - it's a species language!")
TEST_ASSERT(!transfer_target.has_language(/datum/language/uncommon, SPOKEN_LANGUAGE), \
"Dummy's new mob should not understand Uncommon - it's a species language!")
TEST_ASSERT(dummy.has_language(/datum/language/uncommon, UNDERSTOOD_LANGUAGE), \
"Dummy should still understand Uncommon after losing their mind - it's a species language!")
TEST_ASSERT(!transfer_target.has_language(/datum/language/uncommon, UNDERSTOOD_LANGUAGE), \
"Dummy's new mob should not understand Uncommon - it's a species language!")
/// Tests that mind bound languages are not lost when swapping with another person (wiz mindswap)
/datum/unit_test/language_mind_swap
/datum/unit_test/language_mind_swap/Run()
var/mob/living/carbon/human/dummy_A = allocate(/mob/living/carbon/human/consistent)
var/mob/living/carbon/human/dummy_B = allocate(/mob/living/carbon/human/consistent)
dummy_A.mind_initialize()
dummy_B.mind_initialize()
var/datum/mind/dummy_A_mind = dummy_A.mind
var/datum/mind/dummy_B_mind = dummy_B.mind
dummy_A.set_species(/datum/species/lizard)
dummy_B.grant_language(/datum/language/piratespeak, source = LANGUAGE_MIND)
dummy_A_mind.transfer_to(dummy_B)
dummy_B_mind.transfer_to(dummy_A)
var/datum/language_holder/holder_A = dummy_A.get_language_holder()
var/datum/language_holder/holder_B = dummy_B.get_language_holder()
// Holder A is a lizard: starts with 2 languages (common, draconic)
// Holder B is a human with a mind language: starts with 2 language (common, pirate)
// Swap occurs
// Holder A is a lizard with 2 languages, but should now also have pirate: 3 languages (common, draconic, pirate)
// Holder B is a human with just 1 language left over (common)
TEST_ASSERT_EQUAL(length(holder_A.spoken_languages), 3, \
"Holder A / Dummy A / Dummy B mind should speak Common, Draconic, and Pirate! \
Instead, it knew the following: [print_language_list(holder_A.spoken_languages)]")
TEST_ASSERT_EQUAL(length(holder_A.understood_languages), 3, \
"Holder A / Dummy A / Dummy B mind should only understand Common, Draconic, and Pirate! \
Instead, it knew the following: [print_language_list(holder_A.understood_languages)]")
TEST_ASSERT_EQUAL(length(holder_B.spoken_languages), 1, \
"Holder B / Dummy B / Dummy A mind should only speak 1 language - Common! \
Instead, it knew the following: [print_language_list(holder_B.spoken_languages)]")
TEST_ASSERT_EQUAL(length(holder_B.understood_languages), 1, \
"Holder B / Dummy B / Dummy A mind only understand 1 language - Common! \
Instead, it knew the following: [print_language_list(holder_B.understood_languages)]")
/// Tests that the book of babel, and by extension grant_all_languages, works as intended
/datum/unit_test/book_of_babel
/datum/unit_test/book_of_babel/Run()
var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
var/obj/item/book_of_babel/book = allocate(/obj/item/book_of_babel)
var/datum/language_holder/holder = dummy.get_language_holder()
var/expected_amount = length(GLOB.all_languages)
book.attack_self(dummy)
TEST_ASSERT_EQUAL(length(holder.spoken_languages), expected_amount, "Book of Babel failed to give all languages out!")
dummy.set_species(/datum/species/lizard)
TEST_ASSERT_EQUAL(length(holder.spoken_languages), expected_amount, "Changing species after using the Book of Bable should not remove languages!")
/// Helper proc to print a list of languages in a human readable format
/proc/print_language_list(list/languages_to_print)
var/list/printed_languages = list()
for(var/datum/language/language as anything in languages_to_print)
printed_languages += initial(language.name)
return english_list(printed_languages)
+4 -4
View File
@@ -1027,13 +1027,13 @@ GLOBAL_LIST_EMPTY(vending_products)
/obj/machinery/vending/proc/canLoadItem(obj/item/I, mob/user)
return FALSE
/obj/machinery/vending/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
/obj/machinery/vending/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
. = ..()
var/mob/living/L = AM
if(tilted || !istype(L) || !prob(20 * (throwingdatum.speed - L.throw_speed))) // hulk throw = +20%, neckgrab throw = +20%
var/mob/living/living_mob = hitting_atom
if(tilted || !istype(living_mob) || !prob(20 * (throwingdatum.speed - living_mob.throw_speed))) // hulk throw = +20%, neckgrab throw = +20%
return
tilt(L)
tilt(living_mob)
/obj/machinery/vending/attack_tk_grab(mob/user)
to_chat(user, span_warning("[src] seems to resist your mental grasp!"))
+2 -8
View File
@@ -369,10 +369,7 @@
switch(intent)
if("Examine") //Examine a mob inside another mob
var/list/results = target.examine(host)
if(!results || !results.len)
results = list("You were unable to examine that. Tell a developer!")
to_chat(user, jointext(results, "<br>"))
host.examinate(target)
return TRUE
if("Use Hand")
@@ -469,10 +466,7 @@
intent = tgui_alert(user, "What would you like to do with [target]?", "Vore Pick", available_options)
switch(intent)
if("Examine")
var/list/results = target.examine(host)
if(!results || !results.len)
results = list("You were unable to examine that. Tell a developer!")
to_chat(user, jointext(results, "<br>"))
host.examinate(target)
return TRUE
if("Eject")