Merge remote-tracking branch 'origin/master' into fake_blood
This commit is contained in:
@@ -793,6 +793,15 @@
|
||||
small_icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
small_icon_state = "ash_whelp"
|
||||
|
||||
/datum/action/small_sprite/megafauna/colossus
|
||||
small_icon_state = "Basilisk"
|
||||
|
||||
/datum/action/small_sprite/megafauna/bubblegum
|
||||
small_icon_state = "goliath2"
|
||||
|
||||
/datum/action/small_sprite/megafauna/legion
|
||||
small_icon_state = "mega_legion"
|
||||
|
||||
/datum/action/small_sprite/Trigger()
|
||||
..()
|
||||
if(!small)
|
||||
|
||||
@@ -176,6 +176,11 @@
|
||||
scheduled_destruction = world.time + (lifespan - CHAT_MESSAGE_EOL_FADE)
|
||||
enter_subsystem()
|
||||
|
||||
var/mob/living/silicon/robot/R = target
|
||||
if(iscyborg(R))
|
||||
if(R.module.dogborg == TRUE || R.dogborg == TRUE) //I hate whoever that thought that putting two types of dogborg that don't even sync up properly was good
|
||||
message.pixel_x = 16
|
||||
|
||||
/**
|
||||
* Applies final animations to overlay CHAT_MESSAGE_EOL_FADE deciseconds prior to message deletion
|
||||
* Arguments:
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/datum/component/acid
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
|
||||
var/level = 0
|
||||
|
||||
/datum/component/acid/Initialize(acidpwr, acid_volume)
|
||||
if(!isobj(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
var/obj/O = parent
|
||||
var/acid_cap = acidpwr * 300
|
||||
level = min(acidpwr * acid_volume, acid_cap)
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, .proc/add_acid_overlay)
|
||||
if(isitem(parent))
|
||||
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, .proc/on_attack_hand)
|
||||
O.update_icon()
|
||||
|
||||
/datum/component/acid/proc/on_attack_hand(datum/source, mob/user)
|
||||
var/obj/item/I = parent
|
||||
if(istype(I) && level > 20 && !ismob(I.loc))// so we can still remove the clothes on us that have acid.
|
||||
var/mob/living/carbon/C = user
|
||||
if(istype(C))
|
||||
if(!C.gloves || (!(C.gloves.resistance_flags & (UNACIDABLE|ACID_PROOF))))
|
||||
to_chat(user, "<span class='warning'>The acid on [I] burns your hand!</span>")
|
||||
var/obj/item/bodypart/affecting = C.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm")
|
||||
if(affecting && affecting.receive_damage( 0, 5 )) // 5 burn damage
|
||||
C.update_damage_overlays()
|
||||
|
||||
/datum/component/acid/InheritComponent(datum/component/C, i_am_original, acidpwr, acid_volume)
|
||||
if(!i_am_original)
|
||||
return
|
||||
var/acid_cap = acidpwr * 300
|
||||
if(level < acid_cap)
|
||||
if(C)
|
||||
var/datum/component/acid/other = C
|
||||
level = min(level + other.level, acid_cap)
|
||||
else
|
||||
level = min(level + acidpwr * acid_volume, acid_cap)
|
||||
|
||||
/datum/component/acid/Destroy()
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
var/obj/O = parent
|
||||
level = 0
|
||||
O.update_overlays()
|
||||
return ..()
|
||||
|
||||
/datum/component/acid/process()
|
||||
var/obj/O = parent
|
||||
if(!istype(O))
|
||||
qdel(src)
|
||||
return PROCESS_KILL
|
||||
if(!(O.resistance_flags & ACID_PROOF))
|
||||
if(prob(33))
|
||||
playsound(O.loc, 'sound/items/welder.ogg', 150, 1)
|
||||
O.take_damage(min(1 + round(sqrt(level)*0.3), 300), BURN, "acid", 0)
|
||||
|
||||
level = max(level - (5 + 3*round(sqrt(level))), 0)
|
||||
if(level <= 0)
|
||||
qdel(src)
|
||||
return PROCESS_KILL
|
||||
else
|
||||
O.update_icon()
|
||||
return TRUE
|
||||
|
||||
/datum/component/acid/proc/add_acid_overlay(atom/source, list/overlay_list)
|
||||
overlay_list += GLOB.acid_overlay
|
||||
@@ -0,0 +1,63 @@
|
||||
/datum/component/activity
|
||||
var/activity_level = 0
|
||||
var/not_moved_counter = 0
|
||||
var/list/historical_activity_levels = list()
|
||||
|
||||
/datum/component/activity/Initialize()
|
||||
if(!isliving(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
var/mob/living/L = parent
|
||||
|
||||
RegisterSignal(L, COMSIG_LIVING_SET_AS_ATTACKER, .proc/on_set_as_attacker)
|
||||
RegisterSignal(L, COMSIG_LIVING_ATTACKER_SET, .proc/on_attacker_set)
|
||||
RegisterSignal(L, COMSIG_MOB_DEATH, .proc/on_death)
|
||||
RegisterSignal(L, COMSIG_EXIT_AREA, .proc/on_exit_area)
|
||||
RegisterSignal(L, COMSIG_LIVING_LIFE, .proc/on_life)
|
||||
RegisterSignal(L, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE, COMSIG_MOB_APPLY_DAMAGE), .proc/minor_activity)
|
||||
|
||||
/datum/component/activity/proc/log_activity()
|
||||
historical_activity_levels["[world.time]"] = activity_level
|
||||
|
||||
/datum/component/activity/proc/minor_activity(datum/source)
|
||||
activity_level += 1
|
||||
|
||||
/datum/component/activity/proc/on_attacker_set(datum/source, mob/attacker)
|
||||
activity_level += 10
|
||||
if(attacker?.mind)
|
||||
activity_level += 10
|
||||
log_activity()
|
||||
|
||||
/datum/component/activity/proc/on_set_as_attacker(datum/source, mob/target)
|
||||
activity_level += 10
|
||||
if(target?.mind)
|
||||
activity_level += 20
|
||||
log_activity()
|
||||
|
||||
/datum/component/activity/proc/on_death(datum/source)
|
||||
activity_level += 100 // dying means you're doing SOMETHING
|
||||
log_activity()
|
||||
|
||||
/datum/component/activity/proc/on_exit_area(datum/source)
|
||||
activity_level += 1
|
||||
not_moved_counter = 0
|
||||
|
||||
/datum/component/activity/proc/on_life(datum/source, seconds, times_fired)
|
||||
var/mob/living/L = source
|
||||
if(L.stat >= UNCONSCIOUS) // can't expect the unconscious to move
|
||||
return
|
||||
not_moved_counter += seconds
|
||||
var/should_log = FALSE
|
||||
switch(not_moved_counter)
|
||||
if(60 to 120)
|
||||
activity_level -= 1
|
||||
if(120 to 600)
|
||||
activity_level -= 5
|
||||
if(600 to 1200)
|
||||
activity_level -= 10
|
||||
should_log = TRUE
|
||||
if(1200 to INFINITY)
|
||||
activity_level -= 20
|
||||
should_log = TRUE
|
||||
activity_level = max(activity_level, 0)
|
||||
if(should_log)
|
||||
log_activity()
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
/datum/component/butchering/proc/Butcher(mob/living/butcher, mob/living/meat)
|
||||
var/meat_quality = 50 + (bonus_modifier/10) //increases through quality of butchering tool, and through if it was butchered in the kitchen or not
|
||||
if(istype(get_area(butcher), /area/crew_quarters/kitchen))
|
||||
if(istype(get_area(butcher), /area/service/kitchen))
|
||||
meat_quality = meat_quality + 10
|
||||
var/turf/T = meat.drop_location()
|
||||
var/final_effectiveness = effectiveness - meat.butcher_difficulty
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
source.playsound_local(source, 'sound/misc/ui_toggle.ogg', 50, FALSE, pressure_affected = FALSE) //Sound from interbay!
|
||||
RegisterSignal(source, COMSIG_MOB_CLIENT_MOUSEMOVE, .proc/onMouseMove)
|
||||
RegisterSignal(source, COMSIG_MOVABLE_MOVED, .proc/on_move)
|
||||
RegisterSignal(source, COMSIG_MOB_CLIENT_MOVE, .proc/on_client_move)
|
||||
if(hud_icon)
|
||||
hud_icon.combat_on = TRUE
|
||||
hud_icon.update_icon()
|
||||
@@ -115,7 +114,7 @@
|
||||
to_chat(source, self_message)
|
||||
if(playsound)
|
||||
source.playsound_local(source, 'sound/misc/ui_toggleoff.ogg', 50, FALSE, pressure_affected = FALSE) //Slightly modified version of the toggleon sound!
|
||||
UnregisterSignal(source, list(COMSIG_MOB_CLIENT_MOUSEMOVE, COMSIG_MOVABLE_MOVED, COMSIG_MOB_CLIENT_MOVE))
|
||||
UnregisterSignal(source, list(COMSIG_MOB_CLIENT_MOUSEMOVE, COMSIG_MOVABLE_MOVED))
|
||||
if(hud_icon)
|
||||
hud_icon.combat_on = FALSE
|
||||
hud_icon.update_icon()
|
||||
@@ -128,11 +127,6 @@
|
||||
if((mode_flags & COMBAT_MODE_ACTIVE) && L.client)
|
||||
L.setDir(lastmousedir, ismousemovement = TRUE)
|
||||
|
||||
/// Added movement delay if moving backward.
|
||||
/datum/component/combat_mode/proc/on_client_move(mob/source, client/client, direction, n, oldloc, added_delay)
|
||||
if(oldloc != n && direction == REVERSE_DIR(source.dir))
|
||||
client.move_delay += added_delay*0.5
|
||||
|
||||
///Changes the user direction to (try) match the pointer.
|
||||
/datum/component/combat_mode/proc/onMouseMove(mob/source, object, location, control, params)
|
||||
if(source.client.show_popup_menus)
|
||||
|
||||
@@ -1,491 +0,0 @@
|
||||
/datum/component/personal_crafting/Initialize()
|
||||
if(!ismob(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
RegisterSignal(parent, COMSIG_MOB_CLIENT_LOGIN, .proc/create_mob_button)
|
||||
|
||||
/datum/component/personal_crafting/proc/create_mob_button(mob/user, client/CL)
|
||||
var/datum/hud/H = user.hud_used
|
||||
var/obj/screen/craft/C = new()
|
||||
C.icon = H.ui_style
|
||||
H.static_inventory += C
|
||||
if(!CL.prefs.widescreenpref)
|
||||
C.screen_loc = ui_boxcraft
|
||||
CL.screen += C
|
||||
RegisterSignal(C, COMSIG_CLICK, .proc/component_ui_interact)
|
||||
|
||||
/datum/component/personal_crafting
|
||||
var/busy
|
||||
var/viewing_category = 1
|
||||
var/viewing_subcategory = 1
|
||||
var/list/categories = list(
|
||||
CAT_WEAPONRY = list(
|
||||
CAT_WEAPON,
|
||||
CAT_AMMO,
|
||||
),
|
||||
CAT_ROBOT = CAT_NONE,
|
||||
CAT_MISC = list(
|
||||
CAT_MISCELLANEOUS,
|
||||
CAT_TOOL,
|
||||
CAT_FURNITURE,
|
||||
),
|
||||
CAT_PRIMAL = CAT_NONE,
|
||||
CAT_FOOD = list(
|
||||
CAT_BREAD,
|
||||
CAT_BURGER,
|
||||
CAT_CAKE,
|
||||
CAT_DONUT,
|
||||
CAT_EGG,
|
||||
CAT_ICE,
|
||||
CAT_MEAT,
|
||||
CAT_MEXICAN,
|
||||
CAT_MISCFOOD,
|
||||
CAT_PASTRY,
|
||||
CAT_PIE,
|
||||
CAT_PIZZA,
|
||||
CAT_SEAFOOD,
|
||||
CAT_SALAD,
|
||||
CAT_SANDWICH,
|
||||
CAT_SOUP,
|
||||
CAT_SPAGHETTI,
|
||||
),
|
||||
CAT_DRINK = CAT_NONE,
|
||||
CAT_CLOTHING = CAT_NONE,
|
||||
)
|
||||
|
||||
var/cur_category = CAT_NONE
|
||||
var/cur_subcategory = CAT_NONE
|
||||
var/datum/action/innate/crafting/button
|
||||
var/display_craftable_only = FALSE
|
||||
var/display_compact = TRUE
|
||||
|
||||
|
||||
|
||||
|
||||
/* This is what procs do:
|
||||
get_environment - gets a list of things accessable for crafting by user
|
||||
get_surroundings - takes a list of things and makes a list of key-types to values-amounts of said type in the list
|
||||
check_contents - takes a recipe and a key-type list and checks if said recipe can be done with available stuff
|
||||
check_tools - takes recipe, a key-type list, and a user and checks if there are enough tools to do the stuff, checks bugs one level deep
|
||||
construct_item - takes a recipe and a user, call all the checking procs, calls do_after, checks all the things again, calls del_reqs, creates result, calls CheckParts of said result with argument being list returned by deel_reqs
|
||||
del_reqs - takes recipe and a user, loops over the recipes reqs var and tries to find everything in the list make by get_environment and delete it/add to parts list, then returns the said list
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check that the contents of the recipe meet the requirements.
|
||||
*
|
||||
* user: The /mob that initated the crafting.
|
||||
* R: The /datum/crafting_recipe being attempted.
|
||||
* contents: List of items to search for R's reqs.
|
||||
*/
|
||||
/datum/component/personal_crafting/proc/check_contents(mob/user, datum/crafting_recipe/R, list/contents)
|
||||
var/list/item_instances = contents["instances"]
|
||||
contents = contents["other"]
|
||||
|
||||
var/list/requirements_list = list()
|
||||
|
||||
// Process all requirements
|
||||
for(var/requirement_path in R.reqs)
|
||||
// Check we have the appropriate amount available in the contents list
|
||||
var/needed_amount = R.reqs[requirement_path]
|
||||
for(var/content_item_path in contents)
|
||||
// Right path and not blacklisted
|
||||
if(!ispath(content_item_path, requirement_path) || R.blacklist.Find(requirement_path))
|
||||
continue
|
||||
|
||||
needed_amount -= contents[content_item_path]
|
||||
if(needed_amount <= 0)
|
||||
break
|
||||
|
||||
if(needed_amount > 0)
|
||||
return FALSE
|
||||
|
||||
// Store the instances of what we will use for R.check_requirements() for requirement_path
|
||||
var/list/instances_list = list()
|
||||
for(var/instance_path in item_instances)
|
||||
if(ispath(instance_path, requirement_path))
|
||||
instances_list += item_instances[instance_path]
|
||||
|
||||
requirements_list[requirement_path] = instances_list
|
||||
|
||||
for(var/requirement_path in R.chem_catalysts)
|
||||
if(contents[requirement_path] < R.chem_catalysts[requirement_path])
|
||||
return FALSE
|
||||
|
||||
return R.check_requirements(user, requirements_list)
|
||||
|
||||
/datum/component/personal_crafting/proc/get_environment(mob/user)
|
||||
. = list()
|
||||
for(var/obj/item/I in user.held_items)
|
||||
. += I
|
||||
if(!isturf(user.loc))
|
||||
return
|
||||
var/list/L = block(get_step(user, SOUTHWEST), get_step(user, NORTHEAST))
|
||||
for(var/A in L)
|
||||
var/turf/T = A
|
||||
if(T.Adjacent(user))
|
||||
for(var/B in T)
|
||||
var/atom/movable/AM = B
|
||||
if(AM.flags_1 & HOLOGRAM_1)
|
||||
continue
|
||||
. += AM
|
||||
for(var/slot in list(SLOT_R_STORE, SLOT_L_STORE))
|
||||
. += user.get_item_by_slot(slot)
|
||||
|
||||
/datum/component/personal_crafting/proc/get_surroundings(mob/user)
|
||||
. = list()
|
||||
.["tool_behaviour"] = list()
|
||||
.["other"] = list()
|
||||
.["instances"] = list()
|
||||
for(var/obj/item/I in get_environment(user))
|
||||
if(I.flags_1 & HOLOGRAM_1)
|
||||
continue
|
||||
if(.["instances"][I.type])
|
||||
.["instances"][I.type] += I
|
||||
else
|
||||
.["instances"][I.type] = list(I)
|
||||
if(istype(I, /obj/item/stack))
|
||||
var/obj/item/stack/S = I
|
||||
.["other"][I.type] += S.amount
|
||||
else if(I.tool_behaviour)
|
||||
.["tool_behaviour"] += I.tool_behaviour
|
||||
.["other"][I.type] += 1
|
||||
else
|
||||
if(istype(I, /obj/item/reagent_containers))
|
||||
var/obj/item/reagent_containers/RC = I
|
||||
if(RC.is_drainable())
|
||||
for(var/datum/reagent/A in RC.reagents.reagent_list)
|
||||
.["other"][A.type] += A.volume
|
||||
.["other"][I.type] += 1
|
||||
|
||||
/datum/component/personal_crafting/proc/check_tools(mob/user, datum/crafting_recipe/R, list/contents)
|
||||
if(!R.tools.len)
|
||||
return TRUE
|
||||
var/list/possible_tools = list()
|
||||
var/list/present_qualities = list()
|
||||
present_qualities |= contents["tool_behaviour"]
|
||||
for(var/obj/item/I in user.contents)
|
||||
if(istype(I, /obj/item/storage))
|
||||
for(var/obj/item/SI in I.contents)
|
||||
possible_tools += SI.type
|
||||
if(SI.tool_behaviour)
|
||||
present_qualities.Add(SI.tool_behaviour)
|
||||
|
||||
possible_tools += I.type
|
||||
|
||||
if(I.tool_behaviour)
|
||||
present_qualities.Add(I.tool_behaviour)
|
||||
|
||||
possible_tools |= contents["other"]
|
||||
|
||||
main_loop:
|
||||
for(var/A in R.tools)
|
||||
if(A in present_qualities)
|
||||
continue
|
||||
else
|
||||
for(var/I in possible_tools)
|
||||
if(ispath(I, A))
|
||||
continue main_loop
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/component/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R)
|
||||
var/list/contents = get_surroundings(user)
|
||||
var/send_feedback = TRUE
|
||||
if(check_contents(user, R, contents))
|
||||
if(check_tools(user, R, contents))
|
||||
if(do_after(user, R.time, target = user))
|
||||
contents = get_surroundings(user)
|
||||
if(!check_contents(user, R, contents))
|
||||
return ", missing component."
|
||||
if(!check_tools(user, R, contents))
|
||||
return ", missing tool."
|
||||
var/list/parts = del_reqs(R, user)
|
||||
var/atom/movable/I = new R.result (get_turf(user.loc))
|
||||
I.CheckParts(parts, R)
|
||||
if(isitem(I))
|
||||
if(isfood(I))
|
||||
var/obj/item/reagent_containers/food/food_result = I
|
||||
var/total_quality = 0
|
||||
var/total_items = 0
|
||||
for(var/obj/item/reagent_containers/food/ingredient in parts)
|
||||
total_items += 1
|
||||
total_quality += ingredient.food_quality
|
||||
if(total_items == 0)
|
||||
food_result.adjust_food_quality(50)
|
||||
else
|
||||
food_result.adjust_food_quality(total_quality / total_items)
|
||||
user.put_in_hands(I)
|
||||
if(send_feedback)
|
||||
SSblackbox.record_feedback("tally", "object_crafted", 1, I.type)
|
||||
log_craft("[I] crafted by [user] at [loc_name(I.loc)]")
|
||||
return FALSE
|
||||
return "."
|
||||
return ", missing tool."
|
||||
return ", missing component."
|
||||
|
||||
|
||||
/*Del reqs works like this:
|
||||
|
||||
Loop over reqs var of the recipe
|
||||
Set var amt to the value current cycle req is pointing to, its amount of type we need to delete
|
||||
Get var/surroundings list of things accessable to crafting by get_environment()
|
||||
Check the type of the current cycle req
|
||||
If its reagent then do a while loop, inside it try to locate() reagent containers, inside such containers try to locate needed reagent, if there isnt remove thing from surroundings
|
||||
If there is enough reagent in the search result then delete the needed amount, create the same type of reagent with the same data var and put it into deletion list
|
||||
If there isnt enough take all of that reagent from the container, put into deletion list, substract the amt var by the volume of reagent, remove the container from surroundings list and keep searching
|
||||
While doing above stuff check deletion list if it already has such reagnet, if yes merge instead of adding second one
|
||||
If its stack check if it has enough amount
|
||||
If yes create new stack with the needed amount and put in into deletion list, substract taken amount from the stack
|
||||
If no put all of the stack in the deletion list, substract its amount from amt and keep searching
|
||||
While doing above stuff check deletion list if it already has such stack type, if yes try to merge them instead of adding new one
|
||||
If its anything else just locate() in in the list in a while loop, each find --s the amt var and puts the found stuff in deletion loop
|
||||
|
||||
Then do a loop over parts var of the recipe
|
||||
Do similar stuff to what we have done above, but now in deletion list, until the parts conditions are satisfied keep taking from the deletion list and putting it into parts list for return
|
||||
|
||||
After its done loop over deletion list and delete all the shit that wasnt taken by parts loop
|
||||
|
||||
del_reqs return the list of parts resulting object will receive as argument of CheckParts proc, on the atom level it will add them all to the contents, on all other levels it calls ..() and does whatever is needed afterwards but from contents list already
|
||||
*/
|
||||
|
||||
/datum/component/personal_crafting/proc/del_reqs(datum/crafting_recipe/R, mob/user)
|
||||
var/list/surroundings
|
||||
var/list/Deletion = list()
|
||||
. = list()
|
||||
var/data
|
||||
var/amt
|
||||
main_loop:
|
||||
for(var/A in R.reqs)
|
||||
amt = R.reqs[A]
|
||||
surroundings = get_environment(user)
|
||||
surroundings -= Deletion
|
||||
if(ispath(A, /datum/reagent))
|
||||
var/datum/reagent/RG = new A
|
||||
var/datum/reagent/RGNT
|
||||
while(amt > 0)
|
||||
var/obj/item/reagent_containers/RC = locate() in surroundings
|
||||
RG = RC.reagents.get_reagent(A)
|
||||
if(RG)
|
||||
if(!locate(RG.type) in Deletion)
|
||||
Deletion += new RG.type()
|
||||
if(RG.volume > amt)
|
||||
RG.volume -= amt
|
||||
data = RG.data
|
||||
RC.reagents.conditional_update(RC)
|
||||
RG = locate(RG.type) in Deletion
|
||||
RG.volume = amt
|
||||
RG.data += data
|
||||
continue main_loop
|
||||
else
|
||||
surroundings -= RC
|
||||
amt -= RG.volume
|
||||
RC.reagents.reagent_list -= RG
|
||||
RC.reagents.conditional_update(RC)
|
||||
RGNT = locate(RG.type) in Deletion
|
||||
RGNT.volume += RG.volume
|
||||
RGNT.data += RG.data
|
||||
qdel(RG)
|
||||
RC.on_reagent_change()
|
||||
else
|
||||
surroundings -= RC
|
||||
else if(ispath(A, /obj/item/stack))
|
||||
var/obj/item/stack/S
|
||||
var/obj/item/stack/SD
|
||||
while(amt > 0)
|
||||
S = locate(A) in surroundings
|
||||
if(S.amount >= amt)
|
||||
if(!locate(S.type) in Deletion)
|
||||
SD = new S.type()
|
||||
Deletion += SD
|
||||
S.use(amt)
|
||||
SD = locate(S.type) in Deletion
|
||||
SD.amount += amt
|
||||
continue main_loop
|
||||
else
|
||||
amt -= S.amount
|
||||
if(!locate(S.type) in Deletion)
|
||||
Deletion += S
|
||||
else
|
||||
data = S.amount
|
||||
S = locate(S.type) in Deletion
|
||||
S.add(data)
|
||||
surroundings -= S
|
||||
else
|
||||
var/atom/movable/I
|
||||
while(amt > 0)
|
||||
I = locate(A) in surroundings
|
||||
Deletion += I
|
||||
surroundings -= I
|
||||
amt--
|
||||
var/list/partlist = list(R.parts.len)
|
||||
for(var/M in R.parts)
|
||||
partlist[M] = R.parts[M]
|
||||
for(var/A in R.parts)
|
||||
if(istype(A, /datum/reagent))
|
||||
var/datum/reagent/RG = locate(A) in Deletion
|
||||
if(RG.volume > partlist[A])
|
||||
RG.volume = partlist[A]
|
||||
. += RG
|
||||
Deletion -= RG
|
||||
continue
|
||||
else if(istype(A, /obj/item/stack))
|
||||
var/obj/item/stack/ST = locate(A) in Deletion
|
||||
if(ST.amount > partlist[A])
|
||||
ST.amount = partlist[A]
|
||||
. += ST
|
||||
Deletion -= ST
|
||||
continue
|
||||
else
|
||||
while(partlist[A] > 0)
|
||||
var/atom/movable/AM = locate(A) in Deletion
|
||||
. += AM
|
||||
Deletion -= AM
|
||||
partlist[A] -= 1
|
||||
while(Deletion.len)
|
||||
var/DL = Deletion[Deletion.len]
|
||||
Deletion.Cut(Deletion.len)
|
||||
qdel(DL)
|
||||
|
||||
/datum/component/personal_crafting/proc/component_ui_interact(obj/screen/craft/image, location, control, params, user)
|
||||
if(user == parent)
|
||||
ui_interact(user)
|
||||
|
||||
/datum/component/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.not_incapacitated_turf_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
cur_category = categories[1]
|
||||
if(islist(categories[cur_category]))
|
||||
var/list/subcats = categories[cur_category]
|
||||
cur_subcategory = subcats[1]
|
||||
else
|
||||
cur_subcategory = CAT_NONE
|
||||
ui = new(user, src, ui_key, "personal_crafting", "Crafting Menu", 700, 800, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
|
||||
/datum/component/personal_crafting/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["busy"] = busy
|
||||
data["category"] = cur_category
|
||||
data["subcategory"] = cur_subcategory
|
||||
data["display_craftable_only"] = display_craftable_only
|
||||
data["display_compact"] = display_compact
|
||||
|
||||
var/list/surroundings = get_surroundings(user)
|
||||
var/list/craftability = list()
|
||||
for(var/rec in GLOB.crafting_recipes)
|
||||
var/datum/crafting_recipe/R = rec
|
||||
|
||||
if(!R.always_availible && !(R.type in user?.mind?.learned_recipes)) //User doesn't actually know how to make this.
|
||||
continue
|
||||
|
||||
if((R.category != cur_category) || (R.subcategory != cur_subcategory))
|
||||
continue
|
||||
|
||||
craftability["[REF(R)]"] = check_contents(user, R, surroundings)
|
||||
|
||||
data["craftability"] = craftability
|
||||
return data
|
||||
|
||||
/datum/component/personal_crafting/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/list/crafting_recipes = list()
|
||||
for(var/rec in GLOB.crafting_recipes)
|
||||
var/datum/crafting_recipe/R = rec
|
||||
|
||||
if(R.name == "") //This is one of the invalid parents that sneaks in
|
||||
continue
|
||||
|
||||
if(!R.always_availible && !(R.type in user?.mind?.learned_recipes)) //User doesn't actually know how to make this.
|
||||
continue
|
||||
|
||||
if(isnull(crafting_recipes[R.category]))
|
||||
crafting_recipes[R.category] = list()
|
||||
|
||||
if(R.subcategory == CAT_NONE)
|
||||
crafting_recipes[R.category] += list(build_recipe_data(R))
|
||||
else
|
||||
if(isnull(crafting_recipes[R.category][R.subcategory]))
|
||||
crafting_recipes[R.category][R.subcategory] = list()
|
||||
crafting_recipes[R.category]["has_subcats"] = TRUE
|
||||
crafting_recipes[R.category][R.subcategory] += list(build_recipe_data(R))
|
||||
|
||||
data["crafting_recipes"] = crafting_recipes
|
||||
return data
|
||||
|
||||
|
||||
/datum/component/personal_crafting/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
switch(action)
|
||||
if("make")
|
||||
var/datum/crafting_recipe/TR = locate(params["recipe"]) in GLOB.crafting_recipes
|
||||
ui_interact(usr)
|
||||
if(busy)
|
||||
to_chat(usr, "<span class='warning'>You are already making something!</span>")
|
||||
return
|
||||
busy = TRUE
|
||||
var/fail_msg = construct_item(usr, TR)
|
||||
if(!fail_msg)
|
||||
to_chat(usr, "<span class='notice'>[TR.name] constructed.</span>")
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>Construction failed[fail_msg]</span>")
|
||||
busy = FALSE
|
||||
if("toggle_recipes")
|
||||
display_craftable_only = !display_craftable_only
|
||||
. = TRUE
|
||||
if("toggle_compact")
|
||||
display_compact = !display_compact
|
||||
. = TRUE
|
||||
if("set_category")
|
||||
if(!isnull(params["category"]))
|
||||
cur_category = params["category"]
|
||||
if(!isnull(params["subcategory"]))
|
||||
if(params["subcategory"] == "0")
|
||||
cur_subcategory = ""
|
||||
else
|
||||
cur_subcategory = params["subcategory"]
|
||||
. = TRUE
|
||||
|
||||
/datum/component/personal_crafting/proc/build_recipe_data(datum/crafting_recipe/R)
|
||||
var/list/data = list()
|
||||
data["name"] = R.name
|
||||
data["ref"] = "[REF(R)]"
|
||||
var/req_text = ""
|
||||
var/tool_text = ""
|
||||
var/catalyst_text = ""
|
||||
|
||||
for(var/a in R.reqs)
|
||||
//We just need the name, so cheat-typecast to /atom for speed (even tho Reagents are /datum they DO have a "name" var)
|
||||
//Also these are typepaths so sadly we can't just do "[a]"
|
||||
var/atom/A = a
|
||||
req_text += " [R.reqs[A]] [initial(A.name)],"
|
||||
req_text = replacetext(req_text,",","",-1)
|
||||
data["req_text"] = req_text
|
||||
|
||||
for(var/a in R.chem_catalysts)
|
||||
var/atom/A = a //cheat-typecast
|
||||
catalyst_text += " [R.chem_catalysts[A]] [initial(A.name)],"
|
||||
catalyst_text = replacetext(catalyst_text,",","",-1)
|
||||
data["catalyst_text"] = catalyst_text
|
||||
|
||||
for(var/a in R.tools)
|
||||
if(ispath(a, /obj/item))
|
||||
var/obj/item/b = a
|
||||
tool_text += " [initial(b.name)],"
|
||||
else
|
||||
tool_text += " [a],"
|
||||
tool_text = replacetext(tool_text,",","",-1)
|
||||
data["tool_text"] = tool_text
|
||||
|
||||
return data
|
||||
|
||||
//Mind helpers
|
||||
|
||||
/datum/mind/proc/teach_crafting_recipe(R)
|
||||
if(!learned_recipes)
|
||||
learned_recipes = list()
|
||||
learned_recipes |= R
|
||||
@@ -4,6 +4,10 @@
|
||||
|
||||
/datum/component/personal_crafting/proc/create_mob_button(mob/user, client/CL)
|
||||
var/datum/hud/H = user.hud_used
|
||||
for(var/huds in H.static_inventory)
|
||||
if(istype(huds, /obj/screen/craft))
|
||||
return
|
||||
//We don't want to be stacking multiple crafting huds on relogs
|
||||
var/obj/screen/craft/C = new()
|
||||
C.icon = H.ui_style
|
||||
H.static_inventory += C
|
||||
@@ -20,7 +24,7 @@
|
||||
CAT_AMMO,
|
||||
),
|
||||
CAT_ROBOT = CAT_NONE,
|
||||
CAT_MISC = list(
|
||||
CAT_MISCELLANEOUS = list(
|
||||
CAT_MISCELLANEOUS,
|
||||
CAT_TOOL,
|
||||
CAT_FURNITURE,
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
/datum/crafting_recipe/armwraps
|
||||
name = "Armwraps"
|
||||
result = /obj/item/clothing/gloves/fingerless/pugilist
|
||||
result = /obj/item/clothing/gloves/fingerless/pugilist/crafted
|
||||
time = 60
|
||||
tools = list(TOOL_WIRECUTTER)
|
||||
reqs = list(/obj/item/stack/sheet/cloth = 4,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
time = 1
|
||||
reqs = list(/obj/item/stack/sheet/plastic = 1,
|
||||
/obj/item/stack/sheet/mineral/wood = 1)
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
subcategory = CAT_TOOL
|
||||
|
||||
/datum/crafting_recipe/showercurtain
|
||||
@@ -18,7 +18,7 @@
|
||||
/obj/item/stack/rods = 1)
|
||||
result = /obj/structure/curtain
|
||||
subcategory = CAT_FURNITURE
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/guillotine
|
||||
name = "Guillotine"
|
||||
@@ -29,7 +29,7 @@
|
||||
/obj/item/stack/cable_coil = 10)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH, TOOL_WELDER)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/femur_breaker
|
||||
name = "Femur Breaker"
|
||||
@@ -39,7 +39,7 @@
|
||||
/obj/item/stack/cable_coil = 30)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH, TOOL_WELDER)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
// Blood Sucker stuff //
|
||||
/datum/crafting_recipe/bloodsucker/blackcoffin
|
||||
@@ -54,7 +54,7 @@
|
||||
///obj/item/pipe = 2)
|
||||
time = 150
|
||||
subcategory = CAT_FURNITURE
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
always_availible = TRUE
|
||||
|
||||
/datum/crafting_recipe/bloodsucker/meatcoffin
|
||||
@@ -66,7 +66,7 @@
|
||||
/obj/item/restraints/handcuffs/cable = 1)
|
||||
time = 150
|
||||
subcategory = CAT_FURNITURE
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
always_availible = TRUE
|
||||
|
||||
/datum/crafting_recipe/bloodsucker/metalcoffin
|
||||
@@ -77,7 +77,7 @@
|
||||
reqs = list(/obj/item/stack/sheet/metal = 5)
|
||||
time = 100
|
||||
subcategory = CAT_FURNITURE
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
always_availible = TRUE
|
||||
|
||||
/datum/crafting_recipe/bloodsucker/vassalrack
|
||||
@@ -100,7 +100,7 @@
|
||||
// )
|
||||
time = 150
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
always_availible = FALSE // Disabled until learned
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
)
|
||||
time = 100
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
always_availible = FALSE // Disabled til learned
|
||||
|
||||
/datum/crafting_recipe/furnace
|
||||
@@ -129,7 +129,7 @@
|
||||
/obj/item/stack/rods = 2)
|
||||
tools = list(TOOL_CROWBAR)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/tableanvil
|
||||
name = "Table Anvil"
|
||||
@@ -139,7 +139,7 @@
|
||||
/obj/item/stack/rods = 2)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH, TOOL_WELDER)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/sandvil
|
||||
name = "Sandstone Anvil"
|
||||
@@ -148,7 +148,7 @@
|
||||
reqs = list(/obj/item/stack/sheet/mineral/sandstone = 24)
|
||||
tools = list(TOOL_CROWBAR)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/basaltblock
|
||||
name = "Sintered Basalt Block"
|
||||
@@ -157,7 +157,7 @@
|
||||
reqs = list(/obj/item/stack/ore/glass/basalt = 50)
|
||||
tools = list(TOOL_WELDER)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/basaltanvil
|
||||
name = "Basalt Anvil"
|
||||
@@ -166,7 +166,7 @@
|
||||
reqs = list(/obj/item/basaltblock = 5)
|
||||
tools = list(TOOL_CROWBAR)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
///////////////////
|
||||
//Tools & Storage//
|
||||
///////////////////
|
||||
@@ -177,7 +177,7 @@
|
||||
time = 1
|
||||
reqs = list(/obj/item/stack/medical/gauze = 1,
|
||||
/datum/reagent/space_cleaner/sterilizine = 5)
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
subcategory = CAT_TOOL
|
||||
|
||||
/datum/crafting_recipe/brute_pack
|
||||
@@ -186,7 +186,7 @@
|
||||
time = 1
|
||||
reqs = list(/obj/item/stack/medical/gauze/adv = 1,
|
||||
/datum/reagent/medicine/styptic_powder = 10)
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
subcategory = CAT_TOOL
|
||||
|
||||
/datum/crafting_recipe/burn_pack
|
||||
@@ -195,7 +195,7 @@
|
||||
time = 1
|
||||
reqs = list(/obj/item/stack/medical/gauze/adv = 1,
|
||||
/datum/reagent/medicine/silver_sulfadiazine = 10)
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
subcategory = CAT_TOOL
|
||||
|
||||
/datum/crafting_recipe/ghettojetpack
|
||||
@@ -206,7 +206,7 @@
|
||||
/obj/item/extinguisher = 1,
|
||||
/obj/item/pipe = 3,
|
||||
/obj/item/stack/cable_coil = 30)
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
subcategory = CAT_TOOL
|
||||
tools = list(TOOL_WRENCH, TOOL_WELDER, TOOL_WIRECUTTER)
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
/datum/reagent/water = 15)
|
||||
time = 40
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/toolboxhammer
|
||||
name = "Toolbox Hammer"
|
||||
@@ -231,7 +231,7 @@
|
||||
/obj/item/stack/rods = 2)
|
||||
time = 40
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/papersack
|
||||
name = "Paper Sack"
|
||||
@@ -239,7 +239,7 @@
|
||||
time = 10
|
||||
reqs = list(/obj/item/paper = 5)
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/smallcarton
|
||||
name = "Small Carton"
|
||||
@@ -247,7 +247,7 @@
|
||||
time = 10
|
||||
reqs = list(/obj/item/stack/sheet/cardboard = 1)
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/bronze_driver
|
||||
name = "Bronze Plated Screwdriver"
|
||||
@@ -259,7 +259,7 @@
|
||||
/datum/reagent/water = 15)
|
||||
time = 40
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/bronze_welder
|
||||
name = "Bronze Plated Welding Tool"
|
||||
@@ -271,7 +271,7 @@
|
||||
/datum/reagent/water = 15)
|
||||
time = 40
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/bronze_wirecutters
|
||||
name = "Bronze Plated Wirecutters"
|
||||
@@ -283,7 +283,7 @@
|
||||
/datum/reagent/water = 15)
|
||||
time = 40
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/bronze_crowbar
|
||||
name = "Bronze Plated Crowbar"
|
||||
@@ -295,7 +295,7 @@
|
||||
/datum/reagent/water = 15)
|
||||
time = 40
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/bronze_wrench
|
||||
name = "Bronze Plated Wrench"
|
||||
@@ -307,7 +307,7 @@
|
||||
/datum/reagent/water = 15)
|
||||
time = 40
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/rcl
|
||||
name = "Makeshift Rapid Cable Layer"
|
||||
@@ -316,7 +316,7 @@
|
||||
tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WRENCH)
|
||||
reqs = list(/obj/item/stack/sheet/metal = 15)
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/picket_sign
|
||||
name = "Picket Sign"
|
||||
@@ -325,7 +325,7 @@
|
||||
/obj/item/stack/sheet/cardboard = 2)
|
||||
time = 80
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/electrochromatic_kit
|
||||
name = "Electrochromatic Kit"
|
||||
@@ -334,7 +334,19 @@
|
||||
/obj/item/stack/cable_coil = 1)
|
||||
time = 5
|
||||
subcategory = CAT_TOOL
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/blackmarket_uplink
|
||||
name = "Black Market Uplink"
|
||||
result = /obj/item/blackmarket_uplink
|
||||
time = 20
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
|
||||
reqs = list(/obj/item/stock_parts/subspace/amplifier = 1,
|
||||
/obj/item/stack/cable_coil = 15,
|
||||
/obj/item/radio = 1,
|
||||
/obj/item/analyzer = 1)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/heretic/codex
|
||||
name = "Codex Cicatrix"
|
||||
@@ -346,7 +358,7 @@
|
||||
/obj/item/stack/sheet/animalhide/human = 1)
|
||||
time = 150
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
always_availible = FALSE
|
||||
|
||||
////////////
|
||||
@@ -360,7 +372,21 @@
|
||||
/obj/item/stack/rods = 8)
|
||||
time = 100
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/motorized_wheelchair
|
||||
name = "Hoverchair"
|
||||
result = /obj/vehicle/ridden/wheelchair/motorized
|
||||
reqs = list(/obj/item/stack/sheet/plasteel = 10,
|
||||
/obj/item/stack/rods = 8,
|
||||
/obj/item/stock_parts/manipulator = 2,
|
||||
/obj/item/stock_parts/capacitor = 1)
|
||||
parts = list(/obj/item/stock_parts/manipulator = 2,
|
||||
/obj/item/stock_parts/capacitor = 1)
|
||||
tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WRENCH)
|
||||
time = 200
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/skateboard
|
||||
name = "Skateboard"
|
||||
@@ -369,7 +395,7 @@
|
||||
reqs = list(/obj/item/stack/sheet/metal = 5,
|
||||
/obj/item/stack/rods = 10)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/scooter
|
||||
name = "Scooter"
|
||||
@@ -378,7 +404,7 @@
|
||||
reqs = list(/obj/item/stack/sheet/metal = 5,
|
||||
/obj/item/stack/rods = 12)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/////////
|
||||
//Toys///
|
||||
@@ -389,28 +415,28 @@
|
||||
reqs = list(/obj/item/light/bulb = 1, /obj/item/stack/cable_coil = 1, /obj/item/stack/sheet/plastic = 4)
|
||||
result = /obj/item/toy/sword
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/extendohand
|
||||
name = "Extendo-Hand"
|
||||
reqs = list(/obj/item/bodypart/r_arm/robot = 1, /obj/item/clothing/gloves/boxing = 1)
|
||||
result = /obj/item/extendohand
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/toyneb
|
||||
name = "Non-Euplastic Blade"
|
||||
reqs = list(/obj/item/light/tube = 1, /obj/item/stack/cable_coil = 1, /obj/item/stack/sheet/plastic = 4)
|
||||
result = /obj/item/toy/sword/cx
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/catgirlplushie
|
||||
name = "Catgirl Plushie"
|
||||
reqs = list(/obj/item/toy/plush/hairball = 3)
|
||||
result = /obj/item/toy/plush/catgirl
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
////////////
|
||||
//Unsorted//
|
||||
@@ -424,7 +450,7 @@
|
||||
reqs = list(/obj/item/stack/sheet/mineral/wood = 1)
|
||||
result = /obj/item/stick
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
|
||||
/datum/crafting_recipe/swordhilt
|
||||
@@ -433,14 +459,14 @@
|
||||
reqs = list(/obj/item/stack/sheet/mineral/wood = 2)
|
||||
result = /obj/item/swordhandle
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/blackcarpet
|
||||
name = "Black Carpet"
|
||||
reqs = list(/obj/item/stack/tile/carpet = 50, /obj/item/toy/crayon/black = 1)
|
||||
result = /obj/item/stack/tile/carpet/black/fifty
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/paperframes
|
||||
name = "Paper Frames"
|
||||
@@ -448,7 +474,7 @@
|
||||
time = 10
|
||||
reqs = list(/obj/item/stack/sheet/mineral/wood = 5, /obj/item/paper = 20)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/naturalpaper
|
||||
name = "Hand-Pressed Paper"
|
||||
@@ -457,7 +483,7 @@
|
||||
tools = list(/obj/item/hatchet)
|
||||
result = /obj/item/paper_bin/bundlenatural
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/bluespacehonker
|
||||
name = "Bluespace Bike horn"
|
||||
@@ -467,7 +493,7 @@
|
||||
/obj/item/toy/crayon/blue = 1,
|
||||
/obj/item/bikehorn = 1)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/mousetrap
|
||||
name = "Mouse Trap"
|
||||
@@ -476,7 +502,7 @@
|
||||
reqs = list(/obj/item/stack/sheet/cardboard = 1,
|
||||
/obj/item/stack/rods = 1)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/flashlight_eyes
|
||||
name = "Flashlight Eyes"
|
||||
@@ -487,7 +513,7 @@
|
||||
/obj/item/restraints/handcuffs/cable = 1
|
||||
)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/pressureplate
|
||||
name = "Pressure Plate"
|
||||
@@ -498,7 +524,7 @@
|
||||
/obj/item/stack/cable_coil = 2,
|
||||
/obj/item/assembly/igniter = 1)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/gold_horn
|
||||
name = "Golden Bike Horn"
|
||||
@@ -507,7 +533,7 @@
|
||||
reqs = list(/obj/item/stack/sheet/mineral/bananium = 5,
|
||||
/obj/item/bikehorn = 1)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/spooky_camera
|
||||
name = "Camera Obscura"
|
||||
@@ -517,7 +543,7 @@
|
||||
/datum/reagent/water/holywater = 10)
|
||||
parts = list(/obj/item/camera = 1)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/coconut_bong
|
||||
name = "Coconut Bong"
|
||||
@@ -526,7 +552,7 @@
|
||||
/obj/item/reagent_containers/food/snacks/grown/coconut = 1)
|
||||
time = 70
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
//////////////
|
||||
//Banners/////
|
||||
@@ -539,7 +565,7 @@
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/captain/parade = 1)
|
||||
subcategory = CAT_FURNITURE
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/engineering_banner
|
||||
name = "Engitopia Banner"
|
||||
@@ -548,7 +574,7 @@
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/engineering/engineer = 1)
|
||||
subcategory = CAT_FURNITURE
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/cargo_banner
|
||||
name = "Cargonia Banner"
|
||||
@@ -557,7 +583,7 @@
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/cargo/tech = 1)
|
||||
subcategory = CAT_FURNITURE
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/science_banner
|
||||
name = "Sciencia Banner"
|
||||
@@ -566,7 +592,7 @@
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/rnd/scientist = 1)
|
||||
subcategory = CAT_FURNITURE
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/medical_banner
|
||||
name = "Meditopia Banner"
|
||||
@@ -575,7 +601,7 @@
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/medical/doctor = 1)
|
||||
subcategory = CAT_FURNITURE
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
/datum/crafting_recipe/security_banner
|
||||
name = "Securistan Banner"
|
||||
@@ -584,4 +610,4 @@
|
||||
reqs = list(/obj/item/stack/rods = 2,
|
||||
/obj/item/clothing/under/rank/security/officer = 1)
|
||||
subcategory = CAT_FURNITURE
|
||||
category = CAT_MISC
|
||||
category = CAT_MISCELLANEOUS
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
/datum/crafting_recipe/strobeshield
|
||||
name = "Strobe Shield"
|
||||
result = /obj/item/assembly/flash/shield
|
||||
result = /obj/item/shield/riot/flash
|
||||
reqs = list(/obj/item/wallframe/flasher = 1,
|
||||
/obj/item/assembly/flash/handheld = 1,
|
||||
/obj/item/shield/riot = 1)
|
||||
@@ -72,6 +72,16 @@
|
||||
category = CAT_WEAPONRY
|
||||
subcategory = CAT_MELEE
|
||||
|
||||
/datum/crafting_recipe/newsbaton
|
||||
name = "Newspaper Baton"
|
||||
result = /obj/item/melee/classic_baton/telescopic/newspaper
|
||||
reqs = list(/obj/item/melee/classic_baton/telescopic = 1,
|
||||
/obj/item/newspaper = 1,
|
||||
/obj/item/stack/sticky_tape = 2)
|
||||
time = 40
|
||||
category = CAT_WEAPONRY
|
||||
subcategory = CAT_MELEE
|
||||
|
||||
/datum/crafting_recipe/bokken
|
||||
name = "Training Bokken"
|
||||
result = /obj/item/melee/bokken
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
/datum/component/embedded/proc/jostleCheck()
|
||||
var/mob/living/carbon/victim = parent
|
||||
|
||||
var/damage = weapon.w_class * pain_mult
|
||||
var/damage = weapon.w_class * jostle_pain_mult
|
||||
var/pain_chance_current = jostle_chance
|
||||
if(victim.m_intent == MOVE_INTENT_WALK || !(victim.mobility_flags & MOBILITY_STAND))
|
||||
pain_chance_current *= 0.5
|
||||
@@ -265,6 +265,7 @@
|
||||
/// Items embedded/stuck to carbons both check whether they randomly fall out (if applicable), as well as if the target mob and limb still exists.
|
||||
/// Items harmfully embedded in carbons have an additional check for random pain (if applicable)
|
||||
/datum/component/embedded/proc/processCarbon()
|
||||
set waitfor = FALSE
|
||||
var/mob/living/carbon/victim = parent
|
||||
|
||||
if(!victim || !limb) // in case the victim and/or their limbs exploded (say, due to a sticky bomb)
|
||||
|
||||
@@ -56,10 +56,10 @@
|
||||
detonate()
|
||||
|
||||
/datum/component/explodable/proc/on_equip(datum/source, mob/equipper, slot)
|
||||
RegisterSignal(equipper, COMSIG_MOB_APPLY_DAMGE, .proc/explodable_attack_zone, TRUE)
|
||||
RegisterSignal(equipper, COMSIG_MOB_APPLY_DAMAGE, .proc/explodable_attack_zone, TRUE)
|
||||
|
||||
/datum/component/explodable/proc/on_drop(datum/source, mob/user)
|
||||
UnregisterSignal(user, COMSIG_MOB_APPLY_DAMGE)
|
||||
UnregisterSignal(user, COMSIG_MOB_APPLY_DAMAGE)
|
||||
|
||||
/// Checks if we're hitting the zone this component is covering
|
||||
/datum/component/explodable/proc/is_hitting_zone(def_zone)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
identification_method_flags = id_method_flags
|
||||
|
||||
/datum/component/identification/RegisterWithParent()
|
||||
RegisterSignal(parent, COMSIG_IDENTIFICATION_KNOWLEDGE_CHECK, .proc/check_knowledge)
|
||||
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine)
|
||||
if(identification_effect_flags & ID_COMPONENT_EFFECT_NO_ACTIONS)
|
||||
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
|
||||
|
||||
@@ -158,6 +158,7 @@
|
||||
autolock()
|
||||
|
||||
/datum/component/lockon_aiming/proc/autolock()
|
||||
set waitfor = FALSE
|
||||
var/mob/M = parent
|
||||
if(!M.client)
|
||||
return FALSE
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
RegisterSignal(parent, COMSIG_LIVING_REVIVE, .proc/on_revive)
|
||||
RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, .proc/modify_hud)
|
||||
RegisterSignal(parent, COMSIG_MOB_DEATH, .proc/stop_processing)
|
||||
RegisterSignal(parent, COMSIG_VOID_MASK_ACT, .proc/direct_sanity_drain)
|
||||
|
||||
|
||||
if(owner.hud_used)
|
||||
modify_hud()
|
||||
@@ -377,6 +379,10 @@
|
||||
remove_temp_moods()
|
||||
setSanity(initial(sanity))
|
||||
|
||||
///Causes direct drain of someone's sanity, call it with a numerical value corresponding how badly you want to hurt their sanity
|
||||
/datum/component/mood/proc/direct_sanity_drain(datum/source, amount)
|
||||
setSanity(sanity + amount)
|
||||
|
||||
#undef ECSTATIC_SANITY_PEN
|
||||
#undef SLIGHT_INSANITY_PEN
|
||||
#undef MINOR_INSANITY_PEN
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
/**
|
||||
* Used to rid ourselves
|
||||
*/
|
||||
///Deletes nanites!
|
||||
/datum/component/nanites/proc/delete_nanites()
|
||||
if(can_be_deleted)
|
||||
qdel(src)
|
||||
@@ -213,7 +214,7 @@
|
||||
/datum/component/nanites/proc/check_viral_prevention()
|
||||
return SEND_SIGNAL(src, COMSIG_NANITE_INTERNAL_VIRAL_PREVENTION_CHECK)
|
||||
|
||||
//Syncs the nanite component to another, making it so programs are the same with the same programming (except activation status)
|
||||
///Syncs the nanite component to another, making it so programs are the same with the same programming (except activation status)
|
||||
/datum/component/nanites/proc/sync(datum/signal_source, datum/component/nanites/source, full_overwrite = TRUE, copy_activation = FALSE)
|
||||
var/list/programs_to_remove = programs.Copy() - permanent_programs
|
||||
var/list/programs_to_add = source.programs.Copy()
|
||||
@@ -233,6 +234,7 @@
|
||||
var/datum/nanite_program/SNP = X
|
||||
add_program(null, SNP.copy())
|
||||
|
||||
///Syncs the nanites to their assigned cloud copy, if it is available. If it is not, there is a small chance of a software error instead.
|
||||
/datum/component/nanites/proc/cloud_sync()
|
||||
if(cloud_id)
|
||||
var/datum/nanite_cloud_backup/backup = SSnanites.get_cloud_backup(cloud_id)
|
||||
@@ -246,6 +248,7 @@
|
||||
var/datum/nanite_program/NP = pick(programs)
|
||||
NP.software_error()
|
||||
|
||||
///Adds a nanite program, replacing existing unique programs of the same type. A source program can be specified to copy its programming onto the new one.
|
||||
/datum/component/nanites/proc/add_program(datum/source, datum/nanite_program/new_program, datum/nanite_program/source_program)
|
||||
for(var/X in programs)
|
||||
var/datum/nanite_program/NP = X
|
||||
@@ -268,11 +271,67 @@
|
||||
adjust_nanites(null, -amount)
|
||||
return (nanite_volume > 0)
|
||||
|
||||
///Modifies the current nanite volume, then checks if the nanites are depleted or exceeding the maximum amount
|
||||
/datum/component/nanites/proc/adjust_nanites(datum/source, amount)
|
||||
nanite_volume = clamp(nanite_volume + amount, 0, max_nanites)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
nanite_volume = max(nanite_volume + amount, 0) //Lets not have negative nanite counts on permanent ones.
|
||||
if(nanite_volume > max_nanites)
|
||||
reject_excess_nanites()
|
||||
if(nanite_volume <= 0) //oops we ran out
|
||||
nanites_depleted()
|
||||
|
||||
/**
|
||||
* Handles how nanites leave the host's body if they find out that they're currently exceeding the maximum supported amount
|
||||
*
|
||||
* IC explanation:
|
||||
* Normally nanites simply discard excess volume by slowing replication or 'sweating' it out in imperceptible amounts,
|
||||
* but if there is a large excess volume, likely due to a programming change that leaves them unable to support their current volume,
|
||||
* the nanites attempt to leave the host as fast as necessary to prevent nanite poisoning. This can range from minor oozing to nanites
|
||||
* rapidly bursting out from every possible pathway, causing temporary inconvenience to the host.
|
||||
*/
|
||||
/datum/component/nanites/proc/reject_excess_nanites()
|
||||
var/excess = nanite_volume - max_nanites
|
||||
nanite_volume = max_nanites
|
||||
|
||||
switch(excess)
|
||||
if(0 to NANITE_EXCESS_MINOR) //Minor excess amount, the extra nanites are quietly expelled without visible effects
|
||||
return
|
||||
if((NANITE_EXCESS_MINOR + 0.1) to NANITE_EXCESS_VOMIT) //Enough nanites getting rejected at once to be visible to the naked eye
|
||||
host_mob.visible_message("<span class='warning'>A grainy grey slurry starts oozing out of [host_mob].</span>", "<span class='warning'>A grainy grey slurry starts oozing out of your skin.</span>", null, 4);
|
||||
if((NANITE_EXCESS_VOMIT + 0.1) to NANITE_EXCESS_BURST) //Nanites getting rejected in massive amounts, but still enough to make a semi-orderly exit through vomit
|
||||
if(iscarbon(host_mob))
|
||||
var/mob/living/carbon/C = host_mob
|
||||
host_mob.visible_message("<span class='warning'>[host_mob] vomits a grainy grey slurry!</span>", "<span class='warning'>You suddenly vomit a metallic-tasting grainy grey slurry!</span>", null);
|
||||
C.vomit(0, FALSE, TRUE, FLOOR(excess / 100, 1), FALSE, VOMIT_NANITE, FALSE, TRUE, 0)
|
||||
else
|
||||
host_mob.visible_message("<span class='warning'>A metallic grey slurry bursts out of [host_mob]'s skin!</span>", "<span class='userdanger'>A metallic grey slurry violently bursts out of your skin!</span>", null);
|
||||
if(isturf(host_mob.drop_location()))
|
||||
var/turf/T = host_mob.drop_location()
|
||||
T.add_vomit_floor(host_mob, VOMIT_NANITE, 0)
|
||||
if((NANITE_EXCESS_BURST + 0.1) to INFINITY) //Way too many nanites, they just leave through the closest exit before they harm/poison the host
|
||||
host_mob.visible_message("<span class='warning'>A torrent of metallic grey slurry violently bursts out of [host_mob]'s face and floods out of [host_mob.p_their()] skin!</span>",
|
||||
"<span class='userdanger'>A torrent of metallic grey slurry violently bursts out of your eyes, ears, and mouth, and floods out of your skin!</span>");
|
||||
|
||||
host_mob.blind_eyes(15) //nanites coming out of your eyes
|
||||
host_mob.Paralyze(120)
|
||||
if(iscarbon(host_mob))
|
||||
var/mob/living/carbon/C = host_mob
|
||||
var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS)
|
||||
if(ears)
|
||||
ears.adjustEarDamage(0, 30) //nanites coming out of your ears
|
||||
C.vomit(0, FALSE, TRUE, 2, FALSE, VOMIT_NANITE, FALSE, TRUE, 0) //nanites coming out of your mouth
|
||||
|
||||
//nanites everywhere
|
||||
if(isturf(host_mob.drop_location()))
|
||||
var/turf/T = host_mob.drop_location()
|
||||
T.add_vomit_floor(host_mob, VOMIT_NANITE, 0)
|
||||
for(var/turf/adjacent_turf in oview(host_mob, 1))
|
||||
if(adjacent_turf.density || !adjacent_turf.Adjacent(T))
|
||||
continue
|
||||
adjacent_turf.add_vomit_floor(host_mob, VOMIT_NANITE, 0)
|
||||
|
||||
///Updates the nanite volume bar visible in diagnostic HUDs
|
||||
/datum/component/nanites/proc/set_nanite_bar(remove = FALSE)
|
||||
var/image/holder = host_mob.hud_list[DIAG_NANITE_FULL_HUD]
|
||||
var/icon/I = icon(host_mob.icon, host_mob.icon_state, host_mob.dir)
|
||||
@@ -346,7 +405,9 @@
|
||||
nanite_volume = clamp(amount, 0, max_nanites)
|
||||
|
||||
/datum/component/nanites/proc/set_max_volume(datum/source, amount)
|
||||
max_nanites = max(1, max_nanites)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
max_nanites = max(1, amount)
|
||||
|
||||
/datum/component/nanites/proc/set_cloud(datum/source, amount)
|
||||
cloud_id = clamp(amount, 0, 100)
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
return
|
||||
SEND_SIGNAL(parent, COMSIG_ATOM_ORBIT_END, orbiter, refreshing)
|
||||
UnregisterSignal(orbiter, COMSIG_MOVABLE_MOVED)
|
||||
orbiter.SpinAnimation(0, 0)
|
||||
orbiter.SpinAnimation(0, 0, parallel = FALSE)
|
||||
if(istype(orbiters[orbiter],/matrix)) //This is ugly.
|
||||
orbiter.transform = orbiters[orbiter]
|
||||
orbiters -= orbiter
|
||||
|
||||
@@ -52,8 +52,15 @@
|
||||
return
|
||||
strength -= strength / hl3_release_date
|
||||
if(strength <= RAD_BACKGROUND_RADIATION)
|
||||
addtimer(CALLBACK(src, .proc/check_dissipate), 5 SECONDS)
|
||||
return PROCESS_KILL
|
||||
|
||||
/datum/component/radioactive/proc/check_dissipate()
|
||||
if(strength <= RAD_BACKGROUND_RADIATION)
|
||||
qdel(src)
|
||||
return
|
||||
if(!(datum_flags & DF_ISPROCESSING)) // keep going
|
||||
START_PROCESSING(SSradiation, src)
|
||||
|
||||
/datum/component/radioactive/proc/glow_loop(atom/movable/master)
|
||||
var/filter = master.get_filter("rad_glow")
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper,
|
||||
/obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini,
|
||||
/obj/item/firing_pin, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/automatic/magrifle/pistol,
|
||||
/obj/item/toy/plush/snakeplushie, /obj/item/gun/energy/e_gun/mini
|
||||
/obj/item/toy/plush/snakeplushie, /obj/item/gun/energy/e_gun/mini, /obj/item/gun/ballistic/derringer
|
||||
))
|
||||
|
||||
/datum/component/storage/concrete/pockets/shoes/clown/Initialize()
|
||||
@@ -97,3 +97,11 @@
|
||||
. = ..()
|
||||
can_hold = typecacheof(list(/obj/item/reagent_containers/glass/bottle,
|
||||
/obj/item/ammo_box/a762))
|
||||
|
||||
/datum/component/storage/concrete/pockets/void_cloak
|
||||
quickdraw = TRUE
|
||||
max_items = 3
|
||||
|
||||
/datum/component/storage/concrete/pockets/void_cloak/Initialize()
|
||||
. = ..()
|
||||
var/static/list/exception_cache = typecacheof(list(/obj/item/living_heart,/obj/item/forbidden_book))
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
to_chat(M, "<span class='notice'>You start dumping out tier/cell rating [lowest_rating] parts from [parent].</span>")
|
||||
var/turf/T = get_turf(A)
|
||||
var/datum/progressbar/progress = new(M, length(things), T)
|
||||
while (do_after(M, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress)))
|
||||
while (do_after(M, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress, TRUE, M)))
|
||||
stoplag(1)
|
||||
qdel(progress)
|
||||
A.do_squish(0.8, 1.2)
|
||||
@@ -81,7 +81,7 @@
|
||||
to_chat(M, "<span class='notice'>You start dumping out tier/cell rating [lowest_rating] parts from [parent].</span>")
|
||||
var/turf/T = get_turf(A)
|
||||
var/datum/progressbar/progress = new(M, length(things), T)
|
||||
while (do_after(M, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress)))
|
||||
while (do_after(M, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress, TRUE, M)))
|
||||
stoplag(1)
|
||||
qdel(progress)
|
||||
A.do_squish(0.8, 1.2)
|
||||
|
||||
@@ -47,18 +47,8 @@
|
||||
|
||||
var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number.
|
||||
|
||||
/// "legacy"/default view mode's storage "boxes"
|
||||
var/obj/screen/storage/boxes/ui_boxes
|
||||
/// New volumetric storage display mode's left side
|
||||
var/obj/screen/storage/left/ui_left
|
||||
/// New volumetric storage display mode's center 'blocks'
|
||||
var/obj/screen/storage/continuous/ui_continuous
|
||||
/// The close button, used in all modes. Frames right side in volumetric mode.
|
||||
var/obj/screen/storage/close/ui_close
|
||||
/// Associative list of list(item = screen object) for volumetric storage item screen blocks
|
||||
var/list/ui_item_blocks
|
||||
|
||||
var/current_maxscreensize
|
||||
/// Ui objects by person. mob = list(objects)
|
||||
var/list/ui_by_mob = list()
|
||||
|
||||
var/allow_big_nesting = FALSE //allow storage objects of the same or greater size.
|
||||
|
||||
@@ -125,18 +115,16 @@
|
||||
|
||||
/datum/component/storage/Destroy()
|
||||
close_all()
|
||||
QDEL_NULL(ui_boxes)
|
||||
QDEL_NULL(ui_close)
|
||||
QDEL_NULL(ui_continuous)
|
||||
QDEL_NULL(ui_left)
|
||||
// DO NOT USE QDEL_LIST_ASSOC.
|
||||
if(ui_item_blocks)
|
||||
for(var/i in ui_item_blocks)
|
||||
qdel(ui_item_blocks[i]) //qdel the screen object not the item
|
||||
ui_item_blocks.Cut()
|
||||
wipe_ui_objects()
|
||||
LAZYCLEARLIST(is_using)
|
||||
return ..()
|
||||
|
||||
/datum/component/storage/proc/wipe_ui_objects()
|
||||
for(var/i in ui_by_mob)
|
||||
var/list/objects = ui_by_mob[i]
|
||||
QDEL_LIST(objects)
|
||||
ui_by_mob.Cut()
|
||||
|
||||
/datum/component/storage/PreTransfer()
|
||||
update_actions()
|
||||
|
||||
@@ -283,20 +271,20 @@
|
||||
var/turf/T = get_turf(A)
|
||||
var/list/things = contents()
|
||||
var/datum/progressbar/progress = new(M, length(things), T)
|
||||
while (do_after(M, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress)))
|
||||
while (do_after(M, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress, TRUE, M)))
|
||||
stoplag(1)
|
||||
qdel(progress)
|
||||
A.do_squish(0.8, 1.2)
|
||||
|
||||
/datum/component/storage/proc/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress, trigger_on_found = TRUE)
|
||||
/datum/component/storage/proc/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress, trigger_on_found = TRUE, mob/user)
|
||||
var/atom/real_location = real_location()
|
||||
for(var/obj/item/I in things)
|
||||
things -= I
|
||||
if(I.loc != real_location)
|
||||
continue
|
||||
remove_from_storage(I, target)
|
||||
if(trigger_on_found && I.on_found())
|
||||
if(trigger_on_found && user && (user.active_storage != src) && I.on_found(user))
|
||||
return FALSE
|
||||
remove_from_storage(I, target)
|
||||
if(TICK_CHECK)
|
||||
progress.update(progress.goal - length(things))
|
||||
return TRUE
|
||||
@@ -351,13 +339,6 @@
|
||||
return master._removal_reset(thing)
|
||||
|
||||
/datum/component/storage/proc/_remove_and_refresh(datum/source, atom/movable/thing)
|
||||
if(LAZYACCESS(ui_item_blocks, thing))
|
||||
var/obj/screen/storage/volumetric_box/center/C = ui_item_blocks[thing]
|
||||
for(var/i in can_see_contents()) //runtimes result if mobs can access post deletion.
|
||||
var/mob/M = i
|
||||
M.client?.screen -= C.on_screen_objects()
|
||||
ui_item_blocks -= thing
|
||||
qdel(C)
|
||||
_removal_reset(thing) // THIS NEEDS TO HAPPEN AFTER SO LAYERING DOESN'T BREAK!
|
||||
refresh_mob_views()
|
||||
|
||||
@@ -448,7 +429,7 @@
|
||||
return FALSE
|
||||
// this must come before the screen objects only block, dunno why it wasn't before
|
||||
if(over_object == M)
|
||||
user_show_to_mob(M)
|
||||
user_show_to_mob(M, trigger_on_found = TRUE)
|
||||
return
|
||||
if(isrevenant(M))
|
||||
RevenantThrow(over_object, M, source)
|
||||
@@ -467,14 +448,27 @@
|
||||
return
|
||||
A.add_fingerprint(M)
|
||||
|
||||
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE, ghost = FALSE)
|
||||
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE, trigger_on_found = FALSE)
|
||||
var/atom/A = parent
|
||||
if(!istype(M))
|
||||
return FALSE
|
||||
A.add_fingerprint(M)
|
||||
if(!force && (check_locked(null, M) || !M.CanReach(parent, view_only = TRUE)))
|
||||
return FALSE
|
||||
ui_show(M, !ghost)
|
||||
if(trigger_on_found)
|
||||
if(check_on_found(M))
|
||||
return
|
||||
ui_show(M)
|
||||
|
||||
/**
|
||||
* Check if we should trigger on_found()
|
||||
* If this returns TRUE, it means an on_found() returned TRUE and immediately broke the chain.
|
||||
* In most contexts, this should mean to stop.
|
||||
*/
|
||||
/datum/component/storage/proc/check_on_found(mob/user)
|
||||
for(var/obj/item/I in contents())
|
||||
if(I.on_found(user))
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M)
|
||||
if(isitem(O))
|
||||
@@ -596,10 +590,10 @@
|
||||
return can_be_inserted(I, silent, M)
|
||||
|
||||
/datum/component/storage/proc/show_to_ghost(datum/source, mob/dead/observer/M)
|
||||
return user_show_to_mob(M, TRUE, TRUE)
|
||||
return user_show_to_mob(M, TRUE)
|
||||
|
||||
/datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE)
|
||||
return user_show_to_mob(showto, force)
|
||||
/datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE, trigger_on_found = TRUE)
|
||||
return user_show_to_mob(showto, force, trigger_on_found = trigger_on_found)
|
||||
|
||||
/datum/component/storage/proc/on_check()
|
||||
return TRUE
|
||||
@@ -668,7 +662,7 @@
|
||||
if(A.loc == user)
|
||||
. = COMPONENT_NO_ATTACK_HAND
|
||||
if(!check_locked(source, user, TRUE))
|
||||
ui_show(user)
|
||||
user_show_to_mob(user, trigger_on_found = TRUE)
|
||||
A.do_jiggle()
|
||||
|
||||
/datum/component/storage/proc/signal_on_pickup(datum/source, mob/user)
|
||||
@@ -698,7 +692,7 @@
|
||||
var/atom/A = parent
|
||||
if(!quickdraw)
|
||||
A.add_fingerprint(user)
|
||||
user_show_to_mob(user)
|
||||
user_show_to_mob(user, trigger_on_found = TRUE)
|
||||
if(rustle_sound)
|
||||
playsound(A, "rustle", 50, 1, -5)
|
||||
return TRUE
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
if(QDELETED(I))
|
||||
continue
|
||||
if(!.[I.type])
|
||||
.[I.type] = new /datum/numbered_display(I, 1)
|
||||
.[I.type] = new /datum/numbered_display(I, 1, src)
|
||||
else
|
||||
var/datum/numbered_display/ND = .[I.type]
|
||||
ND.number++
|
||||
@@ -20,6 +20,8 @@
|
||||
. = list()
|
||||
var/list/accessible_contents = accessible_items()
|
||||
var/adjusted_contents = length(accessible_contents)
|
||||
var/obj/screen/storage/close/ui_close
|
||||
var/obj/screen/storage/boxes/ui_boxes
|
||||
|
||||
//Numbered contents display
|
||||
var/list/datum/numbered_display/numbered_contents
|
||||
@@ -60,12 +62,13 @@
|
||||
for(var/obj/O in accessible_items())
|
||||
if(QDELETED(O))
|
||||
continue
|
||||
O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip"
|
||||
O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
|
||||
var/obj/screen/storage/item_holder/D = new(null, src, O)
|
||||
D.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip"
|
||||
D.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
|
||||
O.maptext = ""
|
||||
O.layer = ABOVE_HUD_LAYER
|
||||
O.plane = ABOVE_HUD_PLANE
|
||||
. += O
|
||||
. += D
|
||||
cx++
|
||||
if(cx - screen_start_x >= columns)
|
||||
cx = screen_start_x
|
||||
@@ -78,6 +81,9 @@
|
||||
*/
|
||||
/datum/component/storage/proc/orient2hud_volumetric(mob/user, maxcolumns)
|
||||
. = list()
|
||||
var/obj/screen/storage/left/ui_left
|
||||
var/obj/screen/storage/continuous/ui_continuous
|
||||
var/obj/screen/storage/close/ui_close
|
||||
|
||||
// Generate ui_item_blocks for missing ones and render+orient.
|
||||
var/list/atom/contents = accessible_items()
|
||||
@@ -128,14 +134,10 @@
|
||||
var/first = TRUE
|
||||
var/row = 1
|
||||
|
||||
LAZYINITLIST(ui_item_blocks)
|
||||
|
||||
for(var/i in percentage_by_item)
|
||||
I = i
|
||||
var/percent = percentage_by_item[I]
|
||||
if(!ui_item_blocks[I])
|
||||
ui_item_blocks[I] = new /obj/screen/storage/volumetric_box/center(null, src, I)
|
||||
var/obj/screen/storage/volumetric_box/center/B = ui_item_blocks[I]
|
||||
var/obj/screen/storage/volumetric_box/center/B = new /obj/screen/storage/volumetric_box/center(null, src, I)
|
||||
var/pixels_to_use = overrun? MINIMUM_PIXELS_PER_ITEM : max(using_horizontal_pixels * percent, MINIMUM_PIXELS_PER_ITEM)
|
||||
var/addrow = FALSE
|
||||
if(CEILING(pixels_to_use, 1) >= FLOOR(horizontal_pixels - current_pixel - VOLUMETRIC_STORAGE_EDGE_PADDING, 1))
|
||||
@@ -143,25 +145,17 @@
|
||||
addrow = TRUE
|
||||
|
||||
// now that we have pixels_to_use, place our thing and add it to the returned list.
|
||||
B.screen_loc = I.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING), 1)],[screen_start_y+row-1]:[screen_pixel_y]"
|
||||
B.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING), 1)],[screen_start_y+row-1]:[screen_pixel_y]"
|
||||
// add the used pixels to pixel after we place the object
|
||||
current_pixel += pixels_to_use + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING)
|
||||
first = FALSE //apply padding to everything after this
|
||||
|
||||
// set various things
|
||||
B.set_pixel_size(pixels_to_use)
|
||||
B.layer = VOLUMETRIC_STORAGE_BOX_LAYER
|
||||
B.plane = VOLUMETRIC_STORAGE_BOX_PLANE
|
||||
B.name = I.name
|
||||
|
||||
I.mouse_opacity = MOUSE_OPACITY_ICON
|
||||
I.maptext = ""
|
||||
I.layer = VOLUMETRIC_STORAGE_ITEM_LAYER
|
||||
I.plane = VOLUMETRIC_STORAGE_ITEM_PLANE
|
||||
|
||||
// finally add our things.
|
||||
. += B.on_screen_objects()
|
||||
. += I
|
||||
|
||||
// go up a row if needed
|
||||
if(addrow)
|
||||
@@ -185,18 +179,19 @@
|
||||
/**
|
||||
* Shows our UI to a mob.
|
||||
*/
|
||||
/datum/component/storage/proc/ui_show(mob/M, set_screen_size = TRUE)
|
||||
/datum/component/storage/proc/ui_show(mob/M)
|
||||
if(!M.client)
|
||||
return FALSE
|
||||
if(ui_by_mob[M] || LAZYFIND(is_using, M))
|
||||
// something went horribly wrong
|
||||
// hide first
|
||||
ui_hide(M)
|
||||
var/list/cview = getviewsize(M.client.view)
|
||||
// in tiles
|
||||
var/maxallowedscreensize = cview[1]-8
|
||||
if(set_screen_size)
|
||||
current_maxscreensize = maxallowedscreensize
|
||||
else if(current_maxscreensize)
|
||||
maxallowedscreensize = current_maxscreensize
|
||||
// we got screen size, register signal
|
||||
RegisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT, .proc/on_logout, override = TRUE)
|
||||
RegisterSignal(M, COMSIG_PARENT_QDELETING, .proc/on_logout, override = TRUE)
|
||||
if(M.active_storage != src)
|
||||
if(M.active_storage)
|
||||
M.active_storage.ui_hide(M)
|
||||
@@ -204,10 +199,14 @@
|
||||
LAZYOR(is_using, M)
|
||||
if(!M.client?.prefs?.no_tetris_storage && volumetric_ui())
|
||||
//new volumetric ui bay-style
|
||||
M.client.screen |= orient2hud_volumetric(M, maxallowedscreensize)
|
||||
var/list/objects = orient2hud_volumetric(M, maxallowedscreensize)
|
||||
M.client.screen |= objects
|
||||
ui_by_mob[M] = objects
|
||||
else
|
||||
//old ui
|
||||
M.client.screen |= orient2hud_legacy(M, maxallowedscreensize)
|
||||
var/list/objects = orient2hud_legacy(M, maxallowedscreensize)
|
||||
M.client.screen |= objects
|
||||
ui_by_mob[M] = objects
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
@@ -236,8 +235,10 @@
|
||||
/datum/component/storage/proc/ui_hide(mob/M)
|
||||
if(!M.client)
|
||||
return TRUE
|
||||
UnregisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT)
|
||||
M.client.screen -= list(ui_boxes, ui_close, ui_left, ui_continuous) + get_ui_item_objects_hide(M)
|
||||
UnregisterSignal(M, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_CLIENT_LOGOUT))
|
||||
M.client.screen -= ui_by_mob[M]
|
||||
var/list/objects = ui_by_mob[M]
|
||||
QDEL_LIST(objects)
|
||||
if(M.active_storage == src)
|
||||
M.active_storage = null
|
||||
LAZYREMOVE(is_using, M)
|
||||
@@ -250,48 +251,26 @@
|
||||
var/atom/real_location = real_location()
|
||||
return (storage_flags & STORAGE_LIMIT_VOLUME) && (length(real_location.contents) <= MAXIMUM_VOLUMETRIC_ITEMS) && !display_numerical_stacking
|
||||
|
||||
/**
|
||||
* Gets the ui item objects to ui_hide.
|
||||
*/
|
||||
/datum/component/storage/proc/get_ui_item_objects_hide(mob/M)
|
||||
if(!volumetric_ui() || M.client?.prefs?.no_tetris_storage)
|
||||
var/atom/real_location = real_location()
|
||||
return real_location.contents
|
||||
else
|
||||
. = list()
|
||||
for(var/i in ui_item_blocks)
|
||||
// get both the box and the item
|
||||
. += ui_item_blocks[i]
|
||||
. += i
|
||||
|
||||
/**
|
||||
* Gets our ui_boxes, making it if it doesn't exist.
|
||||
*/
|
||||
/datum/component/storage/proc/get_ui_boxes()
|
||||
if(!ui_boxes)
|
||||
ui_boxes = new(null, src)
|
||||
return ui_boxes
|
||||
return new /obj/screen/storage/boxes(null, src)
|
||||
|
||||
/**
|
||||
* Gets our ui_left, making it if it doesn't exist.
|
||||
*/
|
||||
/datum/component/storage/proc/get_ui_left()
|
||||
if(!ui_left)
|
||||
ui_left = new(null, src)
|
||||
return ui_left
|
||||
return new /obj/screen/storage/left(null, src)
|
||||
|
||||
/**
|
||||
* Gets our ui_close, making it if it doesn't exist.
|
||||
*/
|
||||
/datum/component/storage/proc/get_ui_close()
|
||||
if(!ui_close)
|
||||
ui_close = new(null, src)
|
||||
return ui_close
|
||||
return new /obj/screen/storage/close(null, src)
|
||||
|
||||
/**
|
||||
* Gets our ui_continuous, making it if it doesn't exist.
|
||||
*/
|
||||
/datum/component/storage/proc/get_ui_continuous()
|
||||
if(!ui_continuous)
|
||||
ui_continuous = new(null, src)
|
||||
return ui_continuous
|
||||
return new /obj/screen/storage/continuous(null, src)
|
||||
|
||||
@@ -159,9 +159,9 @@
|
||||
/datum/symptom/heal/metabolism/Heal(mob/living/carbon/C, datum/disease/advance/A, actual_power)
|
||||
if(!istype(C))
|
||||
return
|
||||
C.reagents.metabolize(C, can_overdose=TRUE) //this works even without a liver; it's intentional since the virus is metabolizing by itself
|
||||
C.reagents.metabolize(C, SSMOBS_DT, 0, can_overdose=TRUE) //this works even without a liver; it's intentional since the virus is metabolizing by itself
|
||||
if(triple_metabolism)
|
||||
C.reagents.metabolize(C, can_overdose=TRUE)
|
||||
C.reagents.metabolize(C, SSMOBS_DT, 0, can_overdose=TRUE)
|
||||
C.overeatduration = max(C.overeatduration - 2, 0)
|
||||
var/lost_nutrition = 9 - (reduced_hunger * 5)
|
||||
C.adjust_nutrition(-lost_nutrition * HUNGER_FACTOR) //Hunger depletes at 10x the normal speed
|
||||
@@ -384,7 +384,7 @@
|
||||
var/temp_rate = 1
|
||||
threshold_desc = list(
|
||||
"Transmission 6" = "Additionally increases temperature adjustment rate and heals those who love toxins",
|
||||
"Resistance 7" = "Increases healing speed.",
|
||||
"Stage Speed 7" = "Increases healing speed.",
|
||||
)
|
||||
/datum/symptom/heal/plasma/Start(datum/disease/advance/A)
|
||||
if(!..())
|
||||
@@ -404,8 +404,8 @@
|
||||
if(M.loc)
|
||||
environment = M.loc.return_air()
|
||||
if(environment)
|
||||
plasmamount = environment.get_moles(/datum/gas/plasma)
|
||||
if(plasmamount && plasmamount > GLOB.meta_gas_visibility[/datum/gas/plasma]) //if there's enough plasma in the air to see
|
||||
plasmamount = environment.get_moles(GAS_PLASMA)
|
||||
if(plasmamount && plasmamount > GLOB.gas_data.visibility[GAS_PLASMA]) //if there's enough plasma in the air to see
|
||||
. += power * 0.5
|
||||
if(M.reagents.has_reagent(/datum/reagent/toxin/plasma))
|
||||
. += power * 0.75
|
||||
|
||||
+4
-3
@@ -329,12 +329,13 @@
|
||||
uni_identity = generate_uni_identity()
|
||||
unique_enzymes = generate_unique_enzymes()
|
||||
|
||||
/datum/dna/proc/initialize_dna(newblood_type)
|
||||
/datum/dna/proc/initialize_dna(newblood_type, skip_index = FALSE)
|
||||
if(newblood_type)
|
||||
blood_type = newblood_type
|
||||
unique_enzymes = generate_unique_enzymes()
|
||||
uni_identity = generate_uni_identity()
|
||||
generate_dna_blocks()
|
||||
if(!skip_index) //I hate this
|
||||
generate_dna_blocks()
|
||||
features = random_features(species?.id, holder?.gender)
|
||||
|
||||
|
||||
@@ -404,7 +405,7 @@
|
||||
|
||||
|
||||
/mob/living/carbon/human/proc/hardset_dna(ui, list/mutation_index, newreal_name, newblood_type, datum/species/mrace, newfeatures, list/default_mutation_genes)
|
||||
|
||||
set waitfor = FALSE
|
||||
if(newreal_name)
|
||||
real_name = newreal_name
|
||||
dna.generate_unique_enzymes()
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
/**
|
||||
* A holder for simple behaviour that can be attached to many different types
|
||||
*
|
||||
* Only one element of each type is instanced during game init.
|
||||
* Otherwise acts basically like a lightweight component.
|
||||
*/
|
||||
* A holder for simple behaviour that can be attached to many different types
|
||||
*
|
||||
* Only one element of each type is instanced during game init.
|
||||
* Otherwise acts basically like a lightweight component.
|
||||
*/
|
||||
/datum/element
|
||||
/// Option flags for element behaviour
|
||||
var/element_flags = NONE
|
||||
/**
|
||||
* The index of the first attach argument to consider for duplicate elements
|
||||
* Is only used when flags contains ELEMENT_BESPOKE
|
||||
*
|
||||
* Is only used when flags contains [ELEMENT_BESPOKE]
|
||||
*
|
||||
* This is infinity so you must explicitly set this
|
||||
*/
|
||||
var/id_arg_index = INFINITY
|
||||
|
||||
/// Activates the functionality defined by the element on the given target datum
|
||||
/datum/element/proc/Attach(datum/target)
|
||||
SHOULD_CALL_PARENT(1)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
if(type == /datum/element)
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
SEND_SIGNAL(target, COMSIG_ELEMENT_ATTACH, src)
|
||||
@@ -25,8 +27,10 @@
|
||||
|
||||
/// Deactivates the functionality defines by the element on the given datum
|
||||
/datum/element/proc/Detach(datum/source, force)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
SEND_SIGNAL(source, COMSIG_ELEMENT_DETACH, src)
|
||||
SHOULD_CALL_PARENT(1)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
UnregisterSignal(source, COMSIG_PARENT_QDELETING)
|
||||
|
||||
/datum/element/Destroy(force)
|
||||
@@ -45,9 +49,9 @@
|
||||
CRASH("Incompatible [arguments[1]] assigned to a [type]! args: [json_encode(args)]")
|
||||
|
||||
/**
|
||||
* Finds the singleton for the element type given and detaches it from src
|
||||
* You only need additional arguments beyond the type if you're using ELEMENT_BESPOKE
|
||||
*/
|
||||
* Finds the singleton for the element type given and detaches it from src
|
||||
* You only need additional arguments beyond the type if you're using [ELEMENT_BESPOKE]
|
||||
*/
|
||||
/datum/proc/_RemoveElement(list/arguments)
|
||||
var/datum/element/ele = SSdcs.GetElement(arguments)
|
||||
ele.Detach(src)
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
else
|
||||
user_by_item -= source
|
||||
|
||||
/datum/element/earhealing/process()
|
||||
/datum/element/earhealing/proc/do_process()
|
||||
set waitfor = FALSE
|
||||
for(var/i in user_by_item)
|
||||
var/mob/living/carbon/user = user_by_item[i]
|
||||
if(HAS_TRAIT(user, TRAIT_DEAF))
|
||||
@@ -35,3 +36,6 @@
|
||||
ears.deaf = max(ears.deaf - 0.25, (ears.damage < ears.maxHealth ? 0 : 1)) // Do not clear deafness if our ears are too damaged
|
||||
ears.damage = max(ears.damage - 0.025, 0)
|
||||
CHECK_TICK
|
||||
|
||||
/datum/element/earhealing/process()
|
||||
do_process()
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
var/left_hand
|
||||
var/inv_slots
|
||||
var/proctype //if present, will be invoked on headwear generation.
|
||||
var/escape_on_find = FALSE //if present, will be released upon the item being 'found' (i.e. opening a container or pocket with it present)
|
||||
|
||||
/datum/element/mob_holder/Attach(datum/target, worn_state, alt_worn, right_hand, left_hand, inv_slots = NONE, proctype)
|
||||
/datum/element/mob_holder/Attach(datum/target, worn_state, alt_worn, right_hand, left_hand, inv_slots = NONE, proctype, escape_on_find)
|
||||
. = ..()
|
||||
|
||||
if(!isliving(target))
|
||||
@@ -20,6 +21,7 @@
|
||||
src.left_hand = left_hand
|
||||
src.inv_slots = inv_slots
|
||||
src.proctype = proctype
|
||||
src.escape_on_find = escape_on_find
|
||||
|
||||
RegisterSignal(target, COMSIG_CLICK_ALT, .proc/mob_try_pickup)
|
||||
RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine)
|
||||
@@ -55,6 +57,8 @@
|
||||
to_chat(user, "<span class='notice'>You pick [source] up.</span>")
|
||||
source.drop_all_held_items()
|
||||
var/obj/item/clothing/head/mob_holder/holder = new(get_turf(source), source, worn_state, alt_worn, right_hand, left_hand, inv_slots)
|
||||
holder.escape_on_find = escape_on_find
|
||||
|
||||
if(proctype)
|
||||
INVOKE_ASYNC(src, proctype, source, holder, user)
|
||||
user.put_in_hands(holder)
|
||||
@@ -78,6 +82,7 @@
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
dynamic_hair_suffix = ""
|
||||
var/mob/living/held_mob
|
||||
var/escape_on_find
|
||||
|
||||
/obj/item/clothing/head/mob_holder/Initialize(mapload, mob/living/target, worn_state, alt_worn, right_hand, left_hand, slots = NONE)
|
||||
. = ..()
|
||||
@@ -141,7 +146,7 @@
|
||||
|
||||
/obj/item/clothing/head/mob_holder/dropped(mob/user)
|
||||
. = ..()
|
||||
if(held_mob && !ismob(loc))//don't release on soft-drops
|
||||
if(held_mob && !ismob(loc) && !istype(loc,/obj/item/storage))//don't release on soft-drops
|
||||
release()
|
||||
|
||||
/obj/item/clothing/head/mob_holder/proc/release()
|
||||
@@ -179,7 +184,7 @@
|
||||
return location.loc.assume_air(env)
|
||||
return location.assume_air(env)
|
||||
|
||||
/obj/item/clothing/head/mob_holder/remove_air(amount)
|
||||
/obj/item/clothing/head/mob_holder/proc/get_loc_for_air()
|
||||
var/atom/location = loc
|
||||
if(!loc)
|
||||
return //null
|
||||
@@ -187,5 +192,35 @@
|
||||
while(location != T)
|
||||
location = location.loc
|
||||
if(ismob(location))
|
||||
return location.loc.remove_air(amount)
|
||||
return location.loc
|
||||
return location
|
||||
|
||||
/obj/item/clothing/head/mob_holder/assume_air_moles(datum/gas_mixture/env, moles)
|
||||
var/atom/location = get_loc_for_air()
|
||||
return location.assume_air_moles(env, moles)
|
||||
|
||||
/obj/item/clothing/head/mob_holder/assume_air_ratio(datum/gas_mixture/env, ratio)
|
||||
var/atom/location = get_loc_for_air()
|
||||
return location.assume_air_ratio(env, ratio)
|
||||
|
||||
/obj/item/clothing/head/mob_holder/remove_air(amount)
|
||||
var/atom/location = get_loc_for_air()
|
||||
return location.remove_air(amount)
|
||||
|
||||
/obj/item/clothing/head/mob_holder/remove_air_ratio(ratio)
|
||||
var/atom/location = get_loc_for_air()
|
||||
return location.remove_air_ratio(ratio)
|
||||
|
||||
/obj/item/clothing/head/mob_holder/transfer_air(datum/gas_mixture/taker, amount)
|
||||
var/atom/location = get_loc_for_air()
|
||||
return location.transfer_air(taker, amount)
|
||||
|
||||
/obj/item/clothing/head/mob_holder/transfer_air_ratio(datum/gas_mixture/taker, ratio)
|
||||
var/atom/location = get_loc_for_air()
|
||||
return location.transfer_air(taker, ratio)
|
||||
|
||||
// escape when found if applicable
|
||||
/obj/item/clothing/head/mob_holder/on_found(mob/living/finder)
|
||||
if(escape_on_find)
|
||||
finder.visible_message("[finder] accidentally releases the [held_mob]!")
|
||||
release()
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
var/mob/living/L = AM
|
||||
if(L.stat == DEAD)
|
||||
continue
|
||||
if(light_nutrition_gain)
|
||||
if(light_nutrition_gain && L.nutrition < NUTRITION_LEVEL_WELL_FED)
|
||||
L.adjust_nutrition(light_amount * light_nutrition_gain * attached_atoms[AM], NUTRITION_LEVEL_WELL_FED)
|
||||
if(light_amount > bonus_lum || light_amount < malus_lum)
|
||||
var/mult = ((light_amount > bonus_lum) ? 1 : -1) * attached_atoms[AM]
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/datum/element/trash
|
||||
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
|
||||
|
||||
/datum/element/trash/Attach(datum/target)
|
||||
. = ..()
|
||||
RegisterSignal(target, COMSIG_ITEM_ATTACK, .proc/UseFromHand)
|
||||
|
||||
/datum/element/trash/proc/UseFromHand(obj/item/source, mob/living/M, mob/living/user)
|
||||
if((M == user || user.vore_flags & TRASH_FORCEFEED) && ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(HAS_TRAIT(H, TRAIT_TRASHCAN))
|
||||
playsound(H.loc,'sound/items/eatfood.ogg', rand(10,50), 1)
|
||||
if(H.vore_selected)
|
||||
H.visible_message("<span class='notice'>[H] [H.vore_selected.vore_verb]s the [source] into their [H.vore_selected]</span>",
|
||||
"<span class='notice'>You [H.vore_selected.vore_verb]s the [source] into your [H.vore_selected]</span>")
|
||||
source.forceMove(H.vore_selected)
|
||||
else
|
||||
H.visible_message("<span class='notice'>[H] consumes the [source].")
|
||||
qdel(source)
|
||||
@@ -0,0 +1,75 @@
|
||||
/datum/element/turf_z_transparency
|
||||
var/show_bottom_level = FALSE
|
||||
|
||||
///This proc sets up the signals to handle updating viscontents when turfs above/below update. Handle plane and layer here too so that they don't cover other obs/turfs in Dream Maker
|
||||
/datum/element/turf_z_transparency/Attach(datum/target, show_bottom_level = TRUE)
|
||||
. = ..()
|
||||
if(!isturf(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
var/turf/our_turf = target
|
||||
|
||||
src.show_bottom_level = show_bottom_level
|
||||
|
||||
our_turf.plane = OPENSPACE_PLANE
|
||||
our_turf.layer = OPENSPACE_LAYER
|
||||
|
||||
RegisterSignal(target, COMSIG_TURF_MULTIZ_DEL, .proc/on_multiz_turf_del)
|
||||
RegisterSignal(target, COMSIG_TURF_MULTIZ_NEW, .proc/on_multiz_turf_new)
|
||||
|
||||
ADD_TRAIT(our_turf, TURF_Z_TRANSPARENT_TRAIT, TURF_TRAIT)
|
||||
|
||||
|
||||
update_multiz(our_turf, TRUE, TRUE)
|
||||
|
||||
/datum/element/turf_z_transparency/Detach(datum/source, force)
|
||||
. = ..()
|
||||
var/turf/our_turf = source
|
||||
our_turf.vis_contents.len = 0
|
||||
REMOVE_TRAIT(our_turf, TURF_Z_TRANSPARENT_TRAIT, TURF_TRAIT)
|
||||
|
||||
///Updates the viscontents or underlays below this tile.
|
||||
/datum/element/turf_z_transparency/proc/update_multiz(turf/our_turf, prune_on_fail = FALSE, init = FALSE)
|
||||
var/turf/below_turf = our_turf.below()
|
||||
if(!below_turf)
|
||||
our_turf.vis_contents.len = 0
|
||||
if(!show_bottom_level(our_turf) && prune_on_fail) //If we cant show whats below, and we prune on fail, change the turf to plating as a fallback
|
||||
our_turf.ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
return FALSE
|
||||
if(init)
|
||||
our_turf.vis_contents += below_turf
|
||||
if(isclosedturf(our_turf)) //Show girders below closed turfs
|
||||
var/mutable_appearance/girder_underlay = mutable_appearance('icons/obj/structures.dmi', "girder", layer = TURF_LAYER-0.01)
|
||||
girder_underlay.appearance_flags = RESET_ALPHA | RESET_COLOR
|
||||
our_turf.underlays += girder_underlay
|
||||
var/mutable_appearance/plating_underlay = mutable_appearance('icons/turf/floors.dmi', "plating", layer = TURF_LAYER-0.02)
|
||||
plating_underlay = RESET_ALPHA | RESET_COLOR
|
||||
our_turf.underlays += plating_underlay
|
||||
return TRUE
|
||||
|
||||
/datum/element/turf_z_transparency/proc/on_multiz_turf_del(turf/our_turf, turf/T, dir)
|
||||
SIGNAL_HANDLER
|
||||
if(dir != DOWN)
|
||||
return
|
||||
update_multiz(our_turf)
|
||||
|
||||
/datum/element/turf_z_transparency/proc/on_multiz_turf_new(turf/our_turf, turf/T, dir)
|
||||
SIGNAL_HANDLER
|
||||
if(dir != DOWN)
|
||||
return
|
||||
update_multiz(our_turf)
|
||||
|
||||
///Called when there is no real turf below this turf
|
||||
/datum/element/turf_z_transparency/proc/show_bottom_level(turf/our_turf)
|
||||
if(!show_bottom_level)
|
||||
return FALSE
|
||||
var/turf/path = SSmapping.level_trait(our_turf.z, ZTRAIT_BASETURF) || /turf/open/space
|
||||
if(!ispath(path))
|
||||
path = text2path(path)
|
||||
if(!ispath(path))
|
||||
warning("Z-level [our_turf.z] has invalid baseturf '[SSmapping.level_trait(our_turf.z, ZTRAIT_BASETURF)]'")
|
||||
path = /turf/open/space
|
||||
var/mutable_appearance/underlay_appearance = mutable_appearance(initial(path.icon), initial(path.icon_state), layer = TURF_LAYER-0.02, plane = PLANE_SPACE)
|
||||
underlay_appearance.appearance_flags = RESET_ALPHA | RESET_COLOR
|
||||
our_turf.underlays += underlay_appearance
|
||||
return TRUE
|
||||
@@ -34,13 +34,13 @@
|
||||
. = ..()
|
||||
UnregisterSignal(source, COMSIG_MOB_ATTACK_HAND)
|
||||
|
||||
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user)
|
||||
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user, act_intent)
|
||||
var/mob/living/L = source
|
||||
|
||||
if(L.stat == DEAD)
|
||||
return
|
||||
//we want to delay the effect to be displayed after the mob is petted, not before.
|
||||
switch(user.a_intent)
|
||||
switch(act_intent)
|
||||
if(INTENT_HARM)
|
||||
addtimer(CALLBACK(src, .proc/kick_the_dog, source, user), 1)
|
||||
if(INTENT_HELP)
|
||||
|
||||
@@ -317,6 +317,8 @@ GLOBAL_LIST_EMPTY(explosions)
|
||||
|
||||
var/took = (REALTIMEOFDAY - started_at) / 10
|
||||
|
||||
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_EXPLOSION,epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
|
||||
|
||||
//You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare
|
||||
if(GLOB.Debug2)
|
||||
log_world("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.")
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
/// Creates a wave explosion at a certain place
|
||||
/proc/wave_explosion(turf/target, power, factor = EXPLOSION_DEFAULT_FALLOFF_MULTIPLY, constant = EXPLOSION_DEFAULT_FALLOFF_SUBTRACT, flash = 0, fire = 0, atom/source, speed = 0,
|
||||
silent = FALSE, bypass_logging = FALSE, block_resistance = 1, start_immediately = TRUE)
|
||||
if(!istype(target) || (power <= EXPLOSION_POWER_DEAD))
|
||||
return
|
||||
if(!bypass_logging)
|
||||
var/logstring = "Wave explosion at [COORD(target)]: [power]/[factor]/[constant]/[flash]/[fire]/[speed] initial/factor/constant/flash/fire/speed"
|
||||
log_game(logstring)
|
||||
message_admins(logstring)
|
||||
return new /datum/wave_explosion(target, power, factor, constant, flash, fire, source, speed, silent, start_immediately, block_resistance)
|
||||
|
||||
/**
|
||||
* New force-blastwave explosion system
|
||||
*/
|
||||
/datum/wave_explosion
|
||||
/// Next unique numerical ID
|
||||
var/static/next_id = 0
|
||||
/// Our unique nuumerical ID
|
||||
var/id
|
||||
/// world.time we started at
|
||||
var/start_time
|
||||
/// Are we currently running?
|
||||
var/running = FALSE
|
||||
/// Are we currently finished?
|
||||
var/finished = FALSE
|
||||
/// What atom we originated from, if any
|
||||
var/atom/source
|
||||
|
||||
/// Explosion power at which point to consider to be a dead expansion
|
||||
var/power_considered_dead = EXPLOSION_POWER_DEAD
|
||||
/// Explosion power we were initially at
|
||||
var/power_initial
|
||||
/// Base explosion power falloff multiplier (applied first)
|
||||
var/power_falloff_factor = EXPLOSION_DEFAULT_FALLOFF_MULTIPLY
|
||||
/// Base explosion power falloff subtract (applied second)
|
||||
var/power_falloff_constant = EXPLOSION_DEFAULT_FALLOFF_SUBTRACT
|
||||
/// Flash range
|
||||
var/flash_range = 0
|
||||
/// Fire probability per tile
|
||||
var/fire_probability = 0
|
||||
/// Are we silent/do we make the screenshake/sounds?
|
||||
var/silent = FALSE
|
||||
|
||||
// Modifications
|
||||
/// Object damage mod
|
||||
var/object_damage_mod = 1
|
||||
/// Hard obstcales get this mod INSTEAD of object damage mod
|
||||
var/hard_obstacle_mod = 1
|
||||
/// Window shatter mod. Overrides both [hard_obstcale_mod] and [object_damage_mod]
|
||||
var/window_shatter_mod = 1
|
||||
/// Wall destruction mod
|
||||
var/wall_destroy_mod = 1
|
||||
/// Mob damage mod
|
||||
var/mob_damage_mod = 1
|
||||
/// Mob gib mod
|
||||
var/mob_gib_mod = 1
|
||||
/// Mob deafen mod
|
||||
var/mob_deafen_mod = 1
|
||||
/// block = block / this, if 0 any block is absolute
|
||||
var/block_resistance = 1
|
||||
|
||||
// Rewrite count: 2
|
||||
// Each cycle is a "perfect ring".
|
||||
// We run into the problem that diagonal hitboxes don't exist on 2d grid games.
|
||||
// How we deal with this is this:
|
||||
// The first half of each cycle explodes cardinal directions awaiting expansion first
|
||||
// Diagonals get added to a potential diagonals list.
|
||||
// The second half of each cycle checks the potential diagonals list. If something isn't on the exploded list,
|
||||
// we know it's a valid diagonal and explode it.
|
||||
// Then all exploded turfs are flushed to exploded_last and it continues.
|
||||
// Direction bitflags use the WEX_DIR_X flags so we can keep track of more than one direction in a single field
|
||||
// The insanity begins when I realized that doing cardinals are easy but diagonals require:
|
||||
// - Tallying the explosive power that should go into it
|
||||
// - Exploding it afterwards using the tallied power rather than passed power (so corners aren't far weaker unless there's one side of it blocked)
|
||||
// Expanding the explosion power of the now exploded diagonal into the two dirs its cardinals are in
|
||||
// If this is done using a perfect algorithm it should be relatively efficient and result in a near-perfect shockwave simulation.
|
||||
|
||||
/// The last ring that's been exploded. Any turfs in this will completely ignore the current cycle. Turf = TRUE
|
||||
var/list/turf/exploded_last = list()
|
||||
/// The "edges" + dirs that need to be processed this cycle. turf = dir flags
|
||||
var/list/turf/edges = list()
|
||||
/// The powers of the current turf edges. turf = power
|
||||
var/list/turf/powers = list()
|
||||
|
||||
/// What cycle are we on?
|
||||
var/cycle
|
||||
/// When we started the current cycle
|
||||
var/cycle_start
|
||||
/// Time to wait between cycles
|
||||
var/cycle_speed = 0
|
||||
/// Current index for list
|
||||
var/index = 1
|
||||
|
||||
/datum/wave_explosion/New(turf/initial, power, factor = EXPLOSION_DEFAULT_FALLOFF_MULTIPLY, constant = EXPLOSION_DEFAULT_FALLOFF_SUBTRACT, flash = 0, fire = 0, atom/source, speed = 0, silent = FALSE, autostart = TRUE, block_resistance = 1)
|
||||
id = ++next_id
|
||||
if(next_id > SHORT_REAL_LIMIT)
|
||||
next_id = 0
|
||||
SSexplosions.wave_explosions += src
|
||||
src.power_initial = power
|
||||
src.power_falloff_factor = factor
|
||||
src.power_falloff_constant = constant
|
||||
src.flash_range = flash
|
||||
src.fire_probability = fire
|
||||
src.source = source
|
||||
src.cycle_speed = speed
|
||||
src.silent = silent
|
||||
src.block_resistance = block_resistance
|
||||
if(!istype(initial))
|
||||
stack_trace("Wave explosion created without a turf. This better be for debugging purposes.")
|
||||
return
|
||||
if(autostart)
|
||||
start(initial)
|
||||
|
||||
/datum/wave_explosion/Destroy()
|
||||
if(running)
|
||||
stop(FALSE)
|
||||
return ..()
|
||||
|
||||
/datum/wave_explosion/proc/start(list/turf/_starting)
|
||||
if(running)
|
||||
CRASH("Attempted to start() a running wave explosion")
|
||||
if(!islist(_starting))
|
||||
_starting = list(_starting)
|
||||
var/list/mob/to_flash = list()
|
||||
var/list/feedback = list()
|
||||
var/list/mob/mob_potential_shake = list()
|
||||
var/list/mob/closest_to = list()
|
||||
for(var/i in 1 to _starting.len)
|
||||
var/turf/starting = _starting[i]
|
||||
edges[starting] = WEX_ALLDIRS
|
||||
powers[starting] = power_initial
|
||||
var/x0 = starting.x
|
||||
var/y0 = starting.y
|
||||
var/z0 = starting.z
|
||||
var/area/areatype = get_area(starting)
|
||||
feedback += list(list("power" = power_initial, factor = "factor", constant = "constant", flash = "flash", fire = "fire", speed = "speed", "x" = x0, "y" = y0, "z" = z0, "area" = areatype.type, "time" = TIME_STAMP("YYYY-MM-DD hh:mm:ss", 1)))
|
||||
// Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves.
|
||||
// Stereo users will also hear the direction of the explosion!
|
||||
|
||||
// Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions.
|
||||
// 3/7/14 will calculate to 80 + 35
|
||||
|
||||
if(!silent)
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
// Double check for client
|
||||
var/turf/M_turf = get_turf(M)
|
||||
if(M_turf && M_turf.z == z0)
|
||||
var/dist = get_dist(M_turf, starting)
|
||||
if(isnull(mob_potential_shake[M]))
|
||||
mob_potential_shake[M] = dist
|
||||
closest_to[M] = starting
|
||||
else if(mob_potential_shake[M] < dist)
|
||||
mob_potential_shake[M] = dist
|
||||
closest_to[M] = starting
|
||||
|
||||
for(var/array in GLOB.doppler_arrays)
|
||||
var/obj/machinery/doppler_array/A = array
|
||||
A.sense_wave_explosion(starting, power_initial, cycle_speed)
|
||||
|
||||
// Flash mobs
|
||||
if(flash_range)
|
||||
for(var/mob/living/L in viewers(flash_range, starting))
|
||||
to_flash |= L
|
||||
|
||||
if(!silent)
|
||||
var/frequency = get_rand_frequency()
|
||||
var/sound/explosion_sound = sound(get_sfx("explosion"))
|
||||
var/sound/far_explosion_sound = sound('sound/effects/explosionfar.ogg')
|
||||
|
||||
var/far_dist = sqrt(power_initial) * 7.5
|
||||
|
||||
for(var/mob/M in mob_potential_shake)
|
||||
var/dist = mob_potential_shake[M]
|
||||
var/baseshakeamount
|
||||
if(sqrt(power_initial) - dist > 0)
|
||||
baseshakeamount = sqrt((sqrt(power_initial) - dist)*0.1)
|
||||
// If inside the blast radius + world.view - 2
|
||||
if(dist <= round(2 * sqrt(power_initial) + world.view - 2, 1))
|
||||
M.playsound_local(closest_to[M], null, 100, 1, frequency, max_distance = 5, S = explosion_sound)
|
||||
if(baseshakeamount > 0)
|
||||
shake_camera(M, 25, clamp(baseshakeamount, 0, 10))
|
||||
// You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
|
||||
else if(dist <= far_dist)
|
||||
var/far_volume = clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
|
||||
far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
|
||||
M.playsound_local(closest_to[M], null, far_volume, 1, frequency, max_distance = 5, S = far_explosion_sound)
|
||||
if(baseshakeamount > 0)
|
||||
shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, 2.5))
|
||||
|
||||
for(var/i in 1 to to_flash.len)
|
||||
var/mob/living/L = to_flash[i]
|
||||
L.flash_act()
|
||||
|
||||
SSblackbox.record_feedback("associative", "wave_explosion", 1, feedback)
|
||||
|
||||
if(!cycle)
|
||||
cycle = 1
|
||||
SSexplosions.active_wave_explosions += src
|
||||
running = TRUE
|
||||
cycle_start = world.time - cycle_speed
|
||||
tick()
|
||||
|
||||
/datum/wave_explosion/proc/stop(delete = TRUE)
|
||||
SSexplosions.active_wave_explosions -= src
|
||||
SSexplosions.currentrun -= src
|
||||
edges = null
|
||||
powers = null
|
||||
exploded_last = null
|
||||
cycle = null
|
||||
running = FALSE
|
||||
qdel(src)
|
||||
|
||||
#define SHOULD_SUSPEND ((cycle_start + cycle_speed) > world.time)
|
||||
|
||||
/**
|
||||
* Called by SSexplosions to propagate this.
|
||||
* Return TRUE if postponed
|
||||
*/
|
||||
/datum/wave_explosion/proc/tick()
|
||||
/// Each tick goes through one full cycle.
|
||||
// This can be changed to a "continuous process" system where indexes are tracked if needed.
|
||||
if(!src.edges.len)
|
||||
// we're done
|
||||
finished = TRUE
|
||||
stop(TRUE)
|
||||
return TRUE
|
||||
if(SHOULD_SUSPEND)
|
||||
return TRUE
|
||||
// Set up variables
|
||||
var/turf/T
|
||||
var/turf/expanding
|
||||
var/power
|
||||
var/returned
|
||||
var/blocked
|
||||
var/dir
|
||||
// insanity define to explode a turf with a certain amount of power, direction, and set returned.
|
||||
#define WEX_ACT(_T, _P, _D) \
|
||||
returned = max(0, _T.wave_explode(_P, src, _D)); \
|
||||
blocked = _P - returned; \
|
||||
if(!block_resistance) { \
|
||||
if(blocked > EXPLOSION_POWER_NO_RESIST_THRESHOLD) { \
|
||||
returned = 0; \
|
||||
} \
|
||||
} \
|
||||
else if(blocked) { \
|
||||
returned = _P - (blocked / block_resistance); \
|
||||
}; \
|
||||
returned = round((returned * power_falloff_factor) - power_falloff_constant, EXPLOSION_POWER_QUANTIZATION_ACCURACY); \
|
||||
if(prob(fire_probability)) { \
|
||||
new /obj/effect/hotspot(_T); \
|
||||
};
|
||||
|
||||
// Cache hot lists
|
||||
var/list/turf/edges = src.edges
|
||||
var/list/turf/powers = src.powers
|
||||
var/list/turf/exploded_last = src.exploded_last
|
||||
|
||||
// prepare expansions
|
||||
var/list/turf/edges_next = list()
|
||||
var/list/turf/powers_next = list()
|
||||
var/list/turf/powers_returned = list()
|
||||
var/list/turf/diagonals = list()
|
||||
var/list/turf/diagonal_powers = list()
|
||||
var/list/turf/diagonal_powers_max = list()
|
||||
|
||||
// to_chat(world, "DEBUG: cycle start edges [english_list_assoc(edges)]")
|
||||
|
||||
// Process cardinals:
|
||||
// Explode all cardinals and expand in directions, gathering all cardinals it should go to.
|
||||
// Power for when things meet in the middle should be the greatest of the two.
|
||||
for(var/i in edges)
|
||||
T = i
|
||||
power = powers[T]
|
||||
dir = edges[T]
|
||||
WEX_ACT(T, power, dir)
|
||||
if(returned < power_considered_dead)
|
||||
continue
|
||||
powers_returned[T] = returned
|
||||
// diagonal power calc when multiple things hit one diagonal
|
||||
#define CALCULATE_DIAGONAL_POWER(existing, adding, maximum) min(maximum, existing + adding)
|
||||
// diagonal hitting cardinal expansion
|
||||
#define CALCULATE_DIAGONAL_CROSS_POWER(existing, adding) max(existing, adding)
|
||||
// insanity define to mark the next set of cardinals.
|
||||
#define CARDINAL_MARK(ndir, cdir, edir) \
|
||||
if(edir & cdir) { \
|
||||
expanding = get_step(T,ndir); \
|
||||
if(expanding && !exploded_last[expanding] && !edges[expanding]) { \
|
||||
powers_next[expanding] = max(powers_next[expanding], returned); \
|
||||
edges_next[expanding] = (cdir | edges_next[expanding]); \
|
||||
}; \
|
||||
};
|
||||
|
||||
// insanity define to do diagonal marking as 2 substeps
|
||||
#define DIAGONAL_SUBSTEP(ndir, cdir, edir) \
|
||||
expanding = get_step(T,ndir); \
|
||||
if(expanding && !exploded_last[expanding] && !edges[expanding]) { \
|
||||
if(!edges_next[expanding]) { \
|
||||
diagonal_powers_max[expanding] = max(diagonal_powers_max[expanding], returned, powers[T]); \
|
||||
diagonal_powers[expanding] = CALCULATE_DIAGONAL_POWER(diagonal_powers[expanding], returned, diagonal_powers_max[expanding]); \
|
||||
diagonals[expanding] = (cdir | diagonals[expanding]); \
|
||||
}; \
|
||||
else { \
|
||||
powers_next[expanding] = CALCULATE_DIAGONAL_CROSS_POWER(powers_next[expanding], returned); \
|
||||
}; \
|
||||
};
|
||||
|
||||
// insanity define to mark the diagonals that would otherwise be missed
|
||||
// this only works because right now, WEX_DIR_X is the same as a byond dir
|
||||
// and we know we're only passing in one dir at a time.
|
||||
// if this ever stops being the case, and explosions break when you touch this, now you know why.
|
||||
#define DIAGONAL_MARK(ndir, cdir, edir) \
|
||||
if(edir & cdir) { \
|
||||
DIAGONAL_SUBSTEP(turn(ndir, 90), turn(cdir, 90), edir); \
|
||||
DIAGONAL_SUBSTEP(turn(ndir, -90), turn(cdir, -90), edir); \
|
||||
};
|
||||
|
||||
CARDINAL_MARK(NORTH, WEX_DIR_NORTH, dir)
|
||||
CARDINAL_MARK(SOUTH, WEX_DIR_SOUTH, dir)
|
||||
CARDINAL_MARK(EAST, WEX_DIR_EAST, dir)
|
||||
CARDINAL_MARK(WEST, WEX_DIR_WEST, dir)
|
||||
|
||||
// to_chat(world, "DEBUG: cycle mid edges_next [english_list_assoc(edges_next)]")
|
||||
|
||||
// Sweep after cardinals for diagonals
|
||||
for(var/i in edges)
|
||||
T = i
|
||||
power = powers[T]
|
||||
dir = edges[T]
|
||||
returned = powers_returned[T]
|
||||
DIAGONAL_MARK(NORTH, WEX_DIR_NORTH, dir)
|
||||
DIAGONAL_MARK(SOUTH, WEX_DIR_SOUTH, dir)
|
||||
DIAGONAL_MARK(EAST, WEX_DIR_EAST, dir)
|
||||
DIAGONAL_MARK(WEST, WEX_DIR_WEST, dir)
|
||||
|
||||
// to_chat(world, "DEBUG: cycle mid diagonals [english_list_assoc(diagonals)]")
|
||||
|
||||
// Process diagonals:
|
||||
for(var/i in diagonals)
|
||||
T = i
|
||||
power = diagonal_powers[T]
|
||||
dir = diagonals[T]
|
||||
WEX_ACT(T, power, dir)
|
||||
if(returned < power_considered_dead)
|
||||
continue
|
||||
CARDINAL_MARK(NORTH, WEX_DIR_NORTH, dir)
|
||||
CARDINAL_MARK(SOUTH, WEX_DIR_SOUTH, dir)
|
||||
CARDINAL_MARK(EAST, WEX_DIR_EAST, dir)
|
||||
CARDINAL_MARK(WEST, WEX_DIR_WEST, dir)
|
||||
|
||||
// to_chat(world, "DEBUG: cycle end edges_next [english_list_assoc(edges_next)]")
|
||||
|
||||
// flush lists
|
||||
src.exploded_last = edges + diagonals
|
||||
src.edges = edges_next
|
||||
src.powers = powers_next
|
||||
cycle++
|
||||
cycle_start = world.time
|
||||
|
||||
#undef SHOULD_SUSPEND
|
||||
|
||||
#undef WEX_ACT
|
||||
|
||||
#undef CALCULATE_DIAGONAL_POWER
|
||||
#undef CALCULATE_DIAGONAL_CROSS_POWER
|
||||
|
||||
#undef DIAGONAL_SUBSTEP
|
||||
#undef DIAGONAL_MARK
|
||||
#undef CARDINAL_MARK
|
||||
@@ -16,7 +16,7 @@
|
||||
if(revinfo)
|
||||
commit = revinfo.commit
|
||||
originmastercommit = revinfo.origin_commit
|
||||
date = rustg_git_commit_date(commit)
|
||||
date = revinfo.timestamp || rustg_git_commit_date(commit)
|
||||
|
||||
// goes to DD log and config_error.txt
|
||||
log_world(get_log_message())
|
||||
@@ -29,8 +29,8 @@
|
||||
|
||||
for(var/line in testmerge)
|
||||
var/datum/tgs_revision_information/test_merge/tm = line
|
||||
msg += "Test merge active of PR #[tm.number] commit [tm.pull_request_commit]"
|
||||
SSblackbox.record_feedback("associative", "testmerged_prs", 1, list("number" = "[tm.number]", "commit" = "[tm.pull_request_commit]", "title" = "[tm.title]", "author" = "[tm.author]"))
|
||||
msg += "Test merge active of PR #[tm.number] commit [tm.head_commit]"
|
||||
SSblackbox.record_feedback("associative", "testmerged_prs", 1, list("number" = "[tm.number]", "commit" = "[tm.head_commit]", "title" = "[tm.title]", "author" = "[tm.author]"))
|
||||
|
||||
if(commit && commit != originmastercommit)
|
||||
msg += "HEAD: [commit]"
|
||||
@@ -45,7 +45,7 @@
|
||||
. = header ? "The following pull requests are currently test merged:<br>" : ""
|
||||
for(var/line in testmerge)
|
||||
var/datum/tgs_revision_information/test_merge/tm = line
|
||||
var/cm = tm.pull_request_commit
|
||||
var/cm = tm.head_commit
|
||||
var/details = ": '" + html_encode(tm.title) + "' by " + html_encode(tm.author) + " at commit " + html_encode(copytext_char(cm, 1, 11))
|
||||
if(details && findtext(details, "\[s\]") && (!usr || !usr.client.holder))
|
||||
continue
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
// effectout: effect to show right after teleportation
|
||||
// asoundin: soundfile to play before teleportation
|
||||
// asoundout: soundfile to play after teleportation
|
||||
// no_effects: disable the default effectin/effectout of sparks
|
||||
// forceMove: if false, teleport will use Move() proc (dense objects will prevent teleportation)
|
||||
// no_effects: disable the default effectin/effectout of sparks
|
||||
// forced: whether or not to ignore no_teleport
|
||||
/proc/do_teleport(atom/movable/teleatom, atom/destination, precision=null, forceMove = TRUE, datum/effect_system/effectin=null, datum/effect_system/effectout=null, asoundin=null, asoundout=null, no_effects=FALSE, channel=TELEPORT_CHANNEL_BLUESPACE, forced = FALSE)
|
||||
// teleporting most effects just deletes them
|
||||
@@ -15,7 +15,8 @@
|
||||
)) - typecacheof(list(
|
||||
/obj/effect/dummy/chameleon,
|
||||
/obj/effect/wisp,
|
||||
/obj/effect/mob_spawn
|
||||
/obj/effect/mob_spawn,
|
||||
/obj/effect/immovablerod,
|
||||
))
|
||||
if(delete_atoms[teleatom.type])
|
||||
qdel(teleatom)
|
||||
@@ -66,7 +67,7 @@
|
||||
|
||||
var/area/A = get_area(curturf)
|
||||
var/area/B = get_area(destturf)
|
||||
if(!forced && (HAS_TRAIT(teleatom, TRAIT_NO_TELEPORT) || A.noteleport || B.noteleport))
|
||||
if(!forced && (HAS_TRAIT(teleatom, TRAIT_NO_TELEPORT) || (A.area_flags & NOTELEPORT) || (B.area_flags & NOTELEPORT)))
|
||||
return FALSE
|
||||
|
||||
if(SEND_SIGNAL(destturf, COMSIG_ATOM_INTERCEPT_TELEPORT, channel, curturf, destturf))
|
||||
@@ -99,13 +100,13 @@
|
||||
/proc/tele_play_specials(atom/movable/teleatom, atom/location, datum/effect_system/effect, sound)
|
||||
if (location && !isobserver(teleatom))
|
||||
if (sound)
|
||||
playsound(location, sound, 60, 1)
|
||||
playsound(location, sound, 60, TRUE)
|
||||
if (effect)
|
||||
effect.attach(location)
|
||||
effect.start()
|
||||
|
||||
// Safe location finder
|
||||
/proc/find_safe_turf(zlevel, list/zlevels, extended_safety_checks = FALSE)
|
||||
/proc/find_safe_turf(zlevel, list/zlevels, extended_safety_checks = FALSE, dense_atoms = TRUE)
|
||||
if(!zlevels)
|
||||
if (zlevel)
|
||||
zlevels = list(zlevel)
|
||||
@@ -122,12 +123,17 @@
|
||||
if(!isfloorturf(random_location))
|
||||
continue
|
||||
var/turf/open/floor/F = random_location
|
||||
var/area/destination_area = F.loc
|
||||
|
||||
if(cycle < 300 && destination_area.area_flags & NOTELEPORT)//if the area is mostly NOTELEPORT (centcom) we gotta give up on this fantasy at some point.
|
||||
continue
|
||||
if(!F.air)
|
||||
continue
|
||||
|
||||
var/datum/gas_mixture/A = F.air
|
||||
var/list/A_gases = A.get_gases()
|
||||
var/trace_gases
|
||||
for(var/id in A.get_gases())
|
||||
for(var/id in A_gases)
|
||||
if(id in GLOB.hardcoded_gases)
|
||||
continue
|
||||
trace_gases = TRUE
|
||||
@@ -136,11 +142,12 @@
|
||||
// Can most things breathe?
|
||||
if(trace_gases)
|
||||
continue
|
||||
if(A.get_moles(/datum/gas/oxygen) < 16)
|
||||
var/oxy_moles = A.get_moles(GAS_O2)
|
||||
if(oxy_moles < 16 || oxy_moles > 50)
|
||||
continue
|
||||
if(A.get_moles(/datum/gas/plasma))
|
||||
if(A.get_moles(GAS_PLASMA))
|
||||
continue
|
||||
if(A.get_moles(/datum/gas/carbon_dioxide) >= 10)
|
||||
if(A.get_moles(GAS_CO2) >= 10)
|
||||
continue
|
||||
|
||||
// Aim for goldilocks temperatures and pressure
|
||||
@@ -156,6 +163,16 @@
|
||||
if(!L.is_safe())
|
||||
continue
|
||||
|
||||
// Check that we're not warping onto a table or window
|
||||
if(!dense_atoms)
|
||||
var/density_found = FALSE
|
||||
for(var/atom/movable/found_movable in F)
|
||||
if(found_movable.density)
|
||||
density_found = TRUE
|
||||
break
|
||||
if(density_found)
|
||||
continue
|
||||
|
||||
// DING! You have passed the gauntlet, and are "probably" safe.
|
||||
return F
|
||||
|
||||
@@ -167,9 +184,11 @@
|
||||
if(T.is_transition_turf())
|
||||
continue // Avoid picking these.
|
||||
var/area/A = T.loc
|
||||
if(!A.noteleport)
|
||||
if(!(A.area_flags & NOTELEPORT))
|
||||
posturfs.Add(T)
|
||||
return posturfs
|
||||
|
||||
/proc/get_teleport_turf(turf/center, precision = 0)
|
||||
return safepick(get_teleport_turfs(center, precision))
|
||||
var/list/turfs = get_teleport_turfs(center, precision)
|
||||
if (length(turfs))
|
||||
return pick(turfs)
|
||||
|
||||
@@ -45,3 +45,12 @@
|
||||
start_length = 130
|
||||
end_sound = 'sound/weather/ashstorm/inside/weak_end.ogg'
|
||||
volume = 30
|
||||
|
||||
/datum/looping_sound/void_loop
|
||||
mid_sounds = list('sound/ambience/VoidsEmbrace.ogg'=1)
|
||||
mid_length = 1669 // exact length of the music in ticks
|
||||
volume = 100
|
||||
extra_range = 30
|
||||
|
||||
/datum/looping_sound/void_loop/start(atom/add_thing)
|
||||
. = ..()
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/datum/map_generator/cave_generator
|
||||
var/name = "Cave Generator"
|
||||
///Weighted list of the types that spawns if the turf is open
|
||||
var/open_turf_types = list(/turf/open/floor/plating/asteroid/airless = 1)
|
||||
///Weighted list of the types that spawns if the turf is closed
|
||||
var/closed_turf_types = list(/turf/closed/mineral/random = 1)
|
||||
|
||||
|
||||
///Weighted list of mobs that can spawn in the area.
|
||||
var/list/mob_spawn_list
|
||||
// Weighted list of Megafauna that can spawn in the caves
|
||||
var/list/megafauna_spawn_list
|
||||
///Weighted list of flora that can spawn in the area.
|
||||
var/list/flora_spawn_list
|
||||
///Weighted list of extra features that can spawn in the area, such as geysers.
|
||||
var/list/feature_spawn_list
|
||||
|
||||
|
||||
///Base chance of spawning a mob
|
||||
var/mob_spawn_chance = 6
|
||||
///Base chance of spawning flora
|
||||
var/flora_spawn_chance = 2
|
||||
///Base chance of spawning features
|
||||
var/feature_spawn_chance = 0.1
|
||||
///Unique ID for this spawner
|
||||
var/string_gen
|
||||
|
||||
///Chance of cells starting closed
|
||||
var/initial_closed_chance = 45
|
||||
///Amount of smoothing iterations
|
||||
var/smoothing_iterations = 20
|
||||
///How much neighbours does a dead cell need to become alive
|
||||
var/birth_limit = 4
|
||||
///How little neighbours does a alive cell need to die
|
||||
var/death_limit = 3
|
||||
|
||||
/datum/map_generator/cave_generator/New()
|
||||
. = ..()
|
||||
if(!mob_spawn_list)
|
||||
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goldgrub = 1, /mob/living/simple_animal/hostile/asteroid/goliath = 5, /mob/living/simple_animal/hostile/asteroid/basilisk = 4, /mob/living/simple_animal/hostile/asteroid/hivelord = 3)
|
||||
if(!megafauna_spawn_list)
|
||||
megafauna_spawn_list = GLOB.megafauna_spawn_list
|
||||
if(!flora_spawn_list)
|
||||
flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2)
|
||||
if(!feature_spawn_list)
|
||||
feature_spawn_list = list(/obj/structure/geyser/random = 1)
|
||||
|
||||
/datum/map_generator/cave_generator/generate_terrain(list/turfs)
|
||||
. = ..()
|
||||
var/start_time = REALTIMEOFDAY
|
||||
string_gen = rustg_cnoise_generate("[initial_closed_chance]", "[smoothing_iterations]", "[birth_limit]", "[death_limit]", "[world.maxx]", "[world.maxy]") //Generate the raw CA data
|
||||
|
||||
for(var/i in turfs) //Go through all the turfs and generate them
|
||||
var/turf/gen_turf = i
|
||||
|
||||
var/area/A = gen_turf.loc
|
||||
if(!(A.area_flags & CAVES_ALLOWED))
|
||||
continue
|
||||
|
||||
var/closed = text2num(string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x])
|
||||
|
||||
var/stored_flags
|
||||
if(gen_turf.flags_1 & NO_RUINS_1)
|
||||
stored_flags |= NO_RUINS_1
|
||||
|
||||
var/turf/new_turf = pickweight(closed ? closed_turf_types : open_turf_types)
|
||||
|
||||
new_turf = gen_turf.ChangeTurf(new_turf, initial(new_turf.baseturfs), CHANGETURF_DEFER_CHANGE)
|
||||
|
||||
new_turf.flags_1 |= stored_flags
|
||||
|
||||
if(!closed)//Open turfs have some special behavior related to spawning flora and mobs.
|
||||
|
||||
var/turf/open/new_open_turf = new_turf
|
||||
|
||||
///Spawning isn't done in procs to save on overhead on the 60k turfs we're going through.
|
||||
|
||||
//FLORA SPAWNING HERE
|
||||
var/atom/spawned_flora
|
||||
if(flora_spawn_list && prob(flora_spawn_chance))
|
||||
var/can_spawn = TRUE
|
||||
|
||||
if(!(A.area_flags & FLORA_ALLOWED))
|
||||
can_spawn = FALSE
|
||||
if(can_spawn)
|
||||
spawned_flora = pickweight(flora_spawn_list)
|
||||
spawned_flora = new spawned_flora(new_open_turf)
|
||||
|
||||
//FEATURE SPAWNING HERE
|
||||
var/atom/spawned_feature
|
||||
if(feature_spawn_list && prob(feature_spawn_chance))
|
||||
var/can_spawn = TRUE
|
||||
|
||||
if(!(A.area_flags & FLORA_ALLOWED)) //checks the same flag because lol dunno
|
||||
can_spawn = FALSE
|
||||
|
||||
var/atom/picked_feature = pickweight(feature_spawn_list)
|
||||
|
||||
for(var/obj/structure/F in range(7, new_open_turf))
|
||||
if(istype(F, picked_feature))
|
||||
can_spawn = FALSE
|
||||
|
||||
if(can_spawn)
|
||||
spawned_feature = new picked_feature(new_open_turf)
|
||||
|
||||
//MOB SPAWNING HERE
|
||||
|
||||
if(mob_spawn_list && !spawned_flora && !spawned_feature && prob(mob_spawn_chance))
|
||||
var/can_spawn = TRUE
|
||||
|
||||
if(!(A.area_flags & MOB_SPAWN_ALLOWED))
|
||||
can_spawn = FALSE
|
||||
|
||||
var/atom/picked_mob = pickweight(mob_spawn_list)
|
||||
|
||||
if(picked_mob == SPAWN_MEGAFAUNA) //
|
||||
if((A.area_flags & MEGAFAUNA_SPAWN_ALLOWED) && megafauna_spawn_list?.len) //this is danger. it's boss time.
|
||||
picked_mob = pickweight(megafauna_spawn_list)
|
||||
else //this is not danger, don't spawn a boss, spawn something else
|
||||
picked_mob = pickweight(mob_spawn_list - SPAWN_MEGAFAUNA) //What if we used 100% of the brain...and did something (slightly) less shit than a while loop?
|
||||
|
||||
for(var/thing in urange(12, new_open_turf)) //prevents mob clumps
|
||||
if(!ishostile(thing) && !istype(thing, /obj/structure/spawner))
|
||||
continue
|
||||
if((ispath(picked_mob, /mob/living/simple_animal/hostile/megafauna) || ismegafauna(thing)) && get_dist(new_open_turf, thing) <= 7)
|
||||
can_spawn = FALSE //if there's a megafauna within standard view don't spawn anything at all
|
||||
break
|
||||
if(ispath(picked_mob, /mob/living/simple_animal/hostile/asteroid) || istype(thing, /mob/living/simple_animal/hostile/asteroid))
|
||||
can_spawn = FALSE //if the random is a standard mob, avoid spawning if there's another one within 12 tiles
|
||||
break
|
||||
if((ispath(picked_mob, /obj/structure/spawner/lavaland) || istype(thing, /obj/structure/spawner/lavaland)) && get_dist(new_open_turf, thing) <= 2)
|
||||
can_spawn = FALSE //prevents tendrils spawning in each other's collapse range
|
||||
break
|
||||
|
||||
if(can_spawn)
|
||||
if(ispath(picked_mob, /mob/living/simple_animal/hostile/megafauna/bubblegum)) //there can be only one bubblegum, so don't waste spawns on it
|
||||
megafauna_spawn_list.Remove(picked_mob)
|
||||
|
||||
new picked_mob(new_open_turf)
|
||||
CHECK_TICK
|
||||
|
||||
var/message = "[name] finished in [(REALTIMEOFDAY - start_time)/10]s!"
|
||||
to_chat(world, "<span class='boldannounce'>[message]</span>")
|
||||
log_world(message)
|
||||
@@ -0,0 +1,28 @@
|
||||
/datum/map_generator/cave_generator/icemoon
|
||||
open_turf_types = list(/turf/open/floor/plating/asteroid/snow/icemoon = 19, /turf/open/floor/plating/ice/icemoon = 1)
|
||||
closed_turf_types = list(/turf/closed/mineral/random/snow = 1)
|
||||
|
||||
|
||||
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, \
|
||||
/mob/living/simple_animal/hostile/asteroid/lobstrosity = 15)
|
||||
flora_spawn_list = list(/obj/structure/flora/tree/pine = 2, /obj/structure/flora/rock/icy = 2, /obj/structure/flora/rock/pile/icy = 2, /obj/structure/flora/grass/both = 6, /obj/structure/flora/ash = 2)
|
||||
feature_spawn_list = list(/obj/structure/geyser/random = 1)
|
||||
|
||||
/datum/map_generator/cave_generator/icemoon/surface
|
||||
flora_spawn_chance = 4
|
||||
mob_spawn_list = null
|
||||
initial_closed_chance = 53
|
||||
birth_limit = 5
|
||||
death_limit = 4
|
||||
smoothing_iterations = 10
|
||||
|
||||
/datum/map_generator/cave_generator/icemoon/deep
|
||||
closed_turf_types = list(/turf/closed/mineral/random/snow/underground = 1)
|
||||
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/ice_demon = 50, /obj/structure/spawner/ice_moon/demonic_portal = 3, \
|
||||
/mob/living/simple_animal/hostile/asteroid/ice_whelp = 30, /obj/structure/spawner/ice_moon/demonic_portal/ice_whelp = 3, \
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/snow = 50, /obj/structure/spawner/ice_moon/demonic_portal/snowlegion = 3, \
|
||||
SPAWN_MEGAFAUNA = 2)
|
||||
megafauna_spawn_list = list(/mob/living/simple_animal/hostile/megafauna/colossus = 1)
|
||||
flora_spawn_list = list(/obj/structure/flora/rock/icy = 6, /obj/structure/flora/rock/pile/icy = 6, /obj/structure/flora/ash = 1)
|
||||
@@ -0,0 +1,16 @@
|
||||
/datum/map_generator/cave_generator/lavaland
|
||||
open_turf_types = list(/turf/open/floor/plating/asteroid/basalt/lava_land_surface = 1)
|
||||
closed_turf_types = list(/turf/closed/mineral/random/volcanic = 1)
|
||||
|
||||
|
||||
mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast/random = 50, /obj/structure/spawner/lavaland/goliath = 3, \
|
||||
/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/random = 40, /obj/structure/spawner/lavaland = 2, \
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelord/legion/random = 30, /obj/structure/spawner/lavaland/legion = 3, \
|
||||
SPAWN_MEGAFAUNA = 4, /mob/living/simple_animal/hostile/asteroid/goldgrub = 10)
|
||||
flora_spawn_list = list(/obj/structure/flora/ash/leaf_shroom = 2 , /obj/structure/flora/ash/cap_shroom = 2 , /obj/structure/flora/ash/stem_shroom = 2 , /obj/structure/flora/ash/cacti = 1, /obj/structure/flora/ash/tall_shroom = 2)
|
||||
feature_spawn_list = list(/obj/structure/geyser/random = 1)
|
||||
|
||||
initial_closed_chance = 45
|
||||
smoothing_iterations = 50
|
||||
birth_limit = 4
|
||||
death_limit = 3
|
||||
@@ -0,0 +1,91 @@
|
||||
//the random offset applied to square coordinates, causes intermingling at biome borders
|
||||
#define BIOME_RANDOM_SQUARE_DRIFT 2
|
||||
|
||||
/datum/map_generator/jungle_generator
|
||||
///2D list of all biomes based on heat and humidity combos.
|
||||
var/list/possible_biomes = list(
|
||||
BIOME_LOW_HEAT = list(
|
||||
BIOME_LOW_HUMIDITY = /datum/biome/plains,
|
||||
BIOME_LOWMEDIUM_HUMIDITY = /datum/biome/mudlands,
|
||||
BIOME_HIGHMEDIUM_HUMIDITY = /datum/biome/mudlands,
|
||||
BIOME_HIGH_HUMIDITY = /datum/biome/water
|
||||
),
|
||||
BIOME_LOWMEDIUM_HEAT = list(
|
||||
BIOME_LOW_HUMIDITY = /datum/biome/plains,
|
||||
BIOME_LOWMEDIUM_HUMIDITY = /datum/biome/jungle,
|
||||
BIOME_HIGHMEDIUM_HUMIDITY = /datum/biome/jungle,
|
||||
BIOME_HIGH_HUMIDITY = /datum/biome/mudlands
|
||||
),
|
||||
BIOME_HIGHMEDIUM_HEAT = list(
|
||||
BIOME_LOW_HUMIDITY = /datum/biome/plains,
|
||||
BIOME_LOWMEDIUM_HUMIDITY = /datum/biome/plains,
|
||||
BIOME_HIGHMEDIUM_HUMIDITY = /datum/biome/jungle/deep,
|
||||
BIOME_HIGH_HUMIDITY = /datum/biome/jungle
|
||||
),
|
||||
BIOME_HIGH_HEAT = list(
|
||||
BIOME_LOW_HUMIDITY = /datum/biome/wasteland,
|
||||
BIOME_LOWMEDIUM_HUMIDITY = /datum/biome/plains,
|
||||
BIOME_HIGHMEDIUM_HUMIDITY = /datum/biome/jungle,
|
||||
BIOME_HIGH_HUMIDITY = /datum/biome/jungle/deep
|
||||
)
|
||||
)
|
||||
///Used to select "zoom" level into the perlin noise, higher numbers result in slower transitions
|
||||
var/perlin_zoom = 65
|
||||
|
||||
///Seeds the rust-g perlin noise with a random number.
|
||||
/datum/map_generator/jungle_generator/generate_terrain(list/turfs)
|
||||
. = ..()
|
||||
var/height_seed = rand(0, 50000)
|
||||
var/humidity_seed = rand(0, 50000)
|
||||
var/heat_seed = rand(0, 50000)
|
||||
|
||||
for(var/t in turfs) //Go through all the turfs and generate them
|
||||
var/turf/gen_turf = t
|
||||
var/drift_x = (gen_turf.x + rand(-BIOME_RANDOM_SQUARE_DRIFT, BIOME_RANDOM_SQUARE_DRIFT)) / perlin_zoom
|
||||
var/drift_y = (gen_turf.y + rand(-BIOME_RANDOM_SQUARE_DRIFT, BIOME_RANDOM_SQUARE_DRIFT)) / perlin_zoom
|
||||
|
||||
var/height = text2num(rustg_noise_get_at_coordinates("[height_seed]", "[drift_x]", "[drift_y]"))
|
||||
|
||||
|
||||
var/datum/biome/selected_biome
|
||||
if(height <= 0.85) //If height is less than 0.85, we generate biomes based on the heat and humidity of the area.
|
||||
var/humidity = text2num(rustg_noise_get_at_coordinates("[humidity_seed]", "[drift_x]", "[drift_y]"))
|
||||
var/heat = text2num(rustg_noise_get_at_coordinates("[heat_seed]", "[drift_x]", "[drift_y]"))
|
||||
var/heat_level //Type of heat zone we're in LOW-MEDIUM-HIGH
|
||||
var/humidity_level //Type of humidity zone we're in LOW-MEDIUM-HIGH
|
||||
|
||||
switch(heat)
|
||||
if(0 to 0.25)
|
||||
heat_level = BIOME_LOW_HEAT
|
||||
if(0.25 to 0.5)
|
||||
heat_level = BIOME_LOWMEDIUM_HEAT
|
||||
if(0.5 to 0.75)
|
||||
heat_level = BIOME_HIGHMEDIUM_HEAT
|
||||
if(0.75 to 1)
|
||||
heat_level = BIOME_HIGH_HEAT
|
||||
switch(humidity)
|
||||
if(0 to 0.25)
|
||||
humidity_level = BIOME_LOW_HUMIDITY
|
||||
if(0.25 to 0.5)
|
||||
humidity_level = BIOME_LOWMEDIUM_HUMIDITY
|
||||
if(0.5 to 0.75)
|
||||
humidity_level = BIOME_HIGHMEDIUM_HUMIDITY
|
||||
if(0.75 to 1)
|
||||
humidity_level = BIOME_HIGH_HUMIDITY
|
||||
selected_biome = possible_biomes[heat_level][humidity_level]
|
||||
else //Over 0.85; It's a mountain
|
||||
selected_biome = /datum/biome/mountain
|
||||
selected_biome = SSmapping.biomes[selected_biome] //Get the instance of this biome from SSmapping
|
||||
selected_biome.generate_turf(gen_turf)
|
||||
CHECK_TICK
|
||||
|
||||
/turf/open/genturf
|
||||
name = "ungenerated turf"
|
||||
desc = "If you see this, and you're not a ghost, yell at coders"
|
||||
icon = 'icons/turf/debug.dmi'
|
||||
icon_state = "genturf"
|
||||
|
||||
/area/mine/planetgeneration
|
||||
name = "planet generation area"
|
||||
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
|
||||
map_generator = /datum/map_generator/jungle_generator
|
||||
@@ -0,0 +1,6 @@
|
||||
///This type is responsible for any map generation behavior that is done in areas, override this to allow for area-specific map generation. This generation is ran by areas in initialize.
|
||||
/datum/map_generator
|
||||
|
||||
///This proc will be ran by areas on Initialize, and provides the areas turfs as argument to allow for generation.
|
||||
/datum/map_generator/proc/generate_terrain(list/turfs)
|
||||
return
|
||||
@@ -0,0 +1,53 @@
|
||||
///This datum handles the transitioning from a turf to a specific biome, and handles spawning decorative structures and mobs.
|
||||
/datum/biome
|
||||
///Type of turf this biome creates
|
||||
var/turf_type
|
||||
///Chance of having a structure from the flora types list spawn
|
||||
var/flora_density = 0
|
||||
///Chance of having a mob from the fauna types list spawn
|
||||
var/fauna_density = 0
|
||||
///list of type paths of objects that can be spawned when the turf spawns flora
|
||||
var/list/flora_types = list(/obj/structure/flora/grass/jungle)
|
||||
///list of type paths of mobs that can be spawned when the turf spawns fauna
|
||||
var/list/fauna_types = list()
|
||||
|
||||
///This proc handles the creation of a turf of a specific biome type
|
||||
/datum/biome/proc/generate_turf(turf/gen_turf)
|
||||
gen_turf.ChangeTurf(turf_type, null, CHANGETURF_DEFER_CHANGE)
|
||||
if(length(fauna_types) && prob(fauna_density))
|
||||
var/mob/fauna = pick(fauna_types)
|
||||
new fauna(gen_turf)
|
||||
|
||||
if(length(flora_types) && prob(flora_density))
|
||||
var/obj/structure/flora = pick(flora_types)
|
||||
new flora(gen_turf)
|
||||
|
||||
/datum/biome/mudlands
|
||||
// turf_type = /turf/open/floor/plating/dirt/jungle/dark
|
||||
turf_type = /turf/open/floor/plating/dirt/jungle
|
||||
flora_types = list(/obj/structure/flora/grass/jungle,/obj/structure/flora/grass/jungle/b, /obj/structure/flora/rock/jungle, /obj/structure/flora/rock/pile/largejungle)
|
||||
flora_density = 3
|
||||
|
||||
/datum/biome/plains
|
||||
// turf_type = /turf/open/floor/plating/grass/jungle
|
||||
turf_type = /turf/open/floor/grass
|
||||
flora_types = list(/obj/structure/flora/grass/jungle,/obj/structure/flora/grass/jungle/b, /obj/structure/flora/tree/jungle, /obj/structure/flora/rock/jungle, /obj/structure/flora/junglebush, /obj/structure/flora/junglebush/b, /obj/structure/flora/junglebush/c, /obj/structure/flora/junglebush/large, /obj/structure/flora/rock/pile/largejungle)
|
||||
flora_density = 15
|
||||
|
||||
/datum/biome/jungle
|
||||
// turf_type = /turf/open/floor/plating/grass/jungle
|
||||
turf_type = /turf/open/floor/grass
|
||||
flora_types = list(/obj/structure/flora/grass/jungle,/obj/structure/flora/grass/jungle/b, /obj/structure/flora/tree/jungle, /obj/structure/flora/rock/jungle, /obj/structure/flora/junglebush, /obj/structure/flora/junglebush/b, /obj/structure/flora/junglebush/c, /obj/structure/flora/junglebush/large, /obj/structure/flora/rock/pile/largejungle)
|
||||
flora_density = 40
|
||||
|
||||
/datum/biome/jungle/deep
|
||||
flora_density = 65
|
||||
|
||||
/datum/biome/wasteland
|
||||
// turf_type = /turf/open/floor/plating/dirt/jungle/wasteland
|
||||
turf_type = /turf/open/floor/plating/dirt/jungle
|
||||
/datum/biome/water
|
||||
turf_type = /turf/open/water/jungle
|
||||
|
||||
/datum/biome/mountain
|
||||
turf_type = /turf/closed/mineral/random //jungle
|
||||
@@ -43,12 +43,8 @@
|
||||
/datum/martial_art/proc/damage_roll(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
//Here we roll for our damage to be added into the damage var in the various attack procs. This is changed depending on whether we are in combat mode, lying down, or if our target is in combat mode.
|
||||
var/damage = rand(A.dna.species.punchdamagelow, A.dna.species.punchdamagehigh)
|
||||
if(SEND_SIGNAL(D, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
damage *= 1.2
|
||||
if(!CHECK_MOBILITY(A, MOBILITY_STAND))
|
||||
damage *= 0.7
|
||||
if(SEND_SIGNAL(A, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
||||
damage *= 0.8
|
||||
return damage
|
||||
|
||||
/datum/martial_art/proc/teach(mob/living/carbon/human/H, make_temporary = FALSE)
|
||||
|
||||
@@ -223,9 +223,10 @@
|
||||
///Subtype of CQC. Only used for the chef.
|
||||
/datum/martial_art/cqc/under_siege
|
||||
name = "Close Quarters Cooking"
|
||||
var/list/valid_areas = list(/area/service/kitchen)
|
||||
|
||||
///Prevents use if the cook is not in the kitchen.
|
||||
/datum/martial_art/cqc/under_siege/can_use(mob/living/carbon/human/H) //this is used to make chef CQC only work in kitchen
|
||||
if(!istype(get_area(H), /area/crew_quarters/kitchen))
|
||||
/datum/martial_art/cqc/under_siege/can_use(mob/living/owner) //this is used to make chef CQC only work in kitchen
|
||||
if(!is_type_in_list(get_area(owner), valid_areas))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -144,14 +144,16 @@
|
||||
parry_time_active_visual_override = 3
|
||||
parry_time_spindown_visual_override = 12
|
||||
parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK //can attack while
|
||||
parry_time_perfect = 2.5 // first ds isn't perfect
|
||||
parry_time_perfect_leeway = 1.5
|
||||
parry_time_perfect = 2.5
|
||||
parry_time_perfect_leeway = 2.5
|
||||
parry_imperfect_falloff_percent = 5
|
||||
parry_efficiency_to_counterattack = 100
|
||||
parry_efficiency_considered_successful = 65 // VERY generous
|
||||
parry_efficiency_perfect = 100
|
||||
parry_failed_stagger_duration = 4 SECONDS
|
||||
parry_failed_cooldown_duration = 2 SECONDS
|
||||
parry_cooldown = 0.5 SECONDS
|
||||
parry_flags = NONE
|
||||
|
||||
/mob/living/carbon/human/UseStaminaBuffer(amount, warn = FALSE, considered_action = TRUE)
|
||||
amount *= physiology? physiology.stamina_buffer_mod : 1
|
||||
@@ -165,6 +167,7 @@
|
||||
ADD_TRAIT(H, TRAIT_PIERCEIMMUNE, SLEEPING_CARP_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_NODISMEMBER, SLEEPING_CARP_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_TASED_RESISTANCE, SLEEPING_CARP_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_NOPUGILIST, SLEEPING_CARP_TRAIT) // cqc doesn't get this as it's intended to be able to stack with northstar gloves
|
||||
H.physiology.brute_mod *= 0.4 //brute is really not gonna cut it
|
||||
H.physiology.burn_mod *= 0.7 //burn is distinctly more useful against them than brute but they're still resistant
|
||||
H.physiology.stamina_mod *= 0.4 //You take less stamina damage overall, but you do not reduce the damage from stun batons as much
|
||||
@@ -181,6 +184,7 @@
|
||||
REMOVE_TRAIT(H, TRAIT_PIERCEIMMUNE, SLEEPING_CARP_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_NODISMEMBER, SLEEPING_CARP_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_TASED_RESISTANCE, SLEEPING_CARP_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_NOPUGILIST, SLEEPING_CARP_TRAIT)
|
||||
H.physiology.brute_mod = initial(H.physiology.brute_mod)
|
||||
H.physiology.burn_mod = initial(H.physiology.burn_mod)
|
||||
H.physiology.stamina_mod = initial(H.physiology.stamina_mod)
|
||||
|
||||
@@ -52,7 +52,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
|
||||
name = "diamond"
|
||||
desc = "Highly pressurized carbon"
|
||||
color = list(48/255, 272/255, 301/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
|
||||
strength_modifier = 1.1
|
||||
strength_modifier = 1.2
|
||||
alpha = 132
|
||||
categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
|
||||
sheet_type = /obj/item/stack/sheet/mineral/diamond
|
||||
@@ -85,6 +85,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
|
||||
name = "plasma"
|
||||
desc = "Isn't plasma a state of matter? Oh whatever."
|
||||
color = list(298/255, 46/255, 352/255,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
|
||||
strength_modifier = 0.7
|
||||
categories = list(MAT_CATEGORY_ORE = TRUE, MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE)
|
||||
sheet_type = /obj/item/stack/sheet/mineral/plasma
|
||||
value_per_unit = 0.1
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
/datum/mutation/human/proc/get_spans()
|
||||
return list()
|
||||
|
||||
/mob/living/carbon/proc/update_mutations_overlay()
|
||||
/mob/living/proc/update_mutations_overlay()
|
||||
return
|
||||
|
||||
/mob/living/carbon/human/update_mutations_overlay()
|
||||
@@ -142,13 +142,12 @@
|
||||
if(overlays_standing[CM.layer_used])
|
||||
mut_overlay = overlays_standing[CM.layer_used]
|
||||
var/mutable_appearance/V = CM.get_visual_indicator()
|
||||
if(!mut_overlay.Find(V)) //either we lack the visual indicator or we have the wrong one
|
||||
remove_overlay(CM.layer_used)
|
||||
for(var/mutable_appearance/MA in CM.visual_indicators[CM.type])
|
||||
mut_overlay.Remove(MA)
|
||||
mut_overlay |= V
|
||||
overlays_standing[CM.layer_used] = mut_overlay
|
||||
apply_overlay(CM.layer_used)
|
||||
remove_overlay(CM.layer_used) //trying to find its existence defeats the point because if cut_overlays is called it doesn't bother reloading it.
|
||||
for(var/mutable_appearance/MA in CM.visual_indicators[CM.type])
|
||||
mut_overlay.Remove(MA)
|
||||
mut_overlay |= V
|
||||
overlays_standing[CM.layer_used] = mut_overlay
|
||||
apply_overlay(CM.layer_used)
|
||||
|
||||
/datum/mutation/human/proc/modify() //called when a genome is applied so we can properly update some stats without having to remove and reapply the mutation from someone
|
||||
if(modified || !power || !owner)
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
//can we sniff? is there miasma in the air?
|
||||
var/datum/gas_mixture/air = user.loc.return_air()
|
||||
|
||||
if(air.get_moles(/datum/gas/miasma))
|
||||
if(air.get_moles(GAS_MIASMA))
|
||||
user.adjust_disgust(sensitivity * 45)
|
||||
to_chat(user, "<span class='warning'>With your overly sensitive nose, you get a whiff of stench and feel sick! Try moving to a cleaner area!</span>")
|
||||
return
|
||||
@@ -283,7 +283,7 @@
|
||||
desc = "Allows a creature to voluntary discard a random appendage."
|
||||
quality = POSITIVE
|
||||
text_gain_indication = "<span class='notice'>Your joints feel loose.</span>"
|
||||
instability = 30
|
||||
instability = 20
|
||||
power = /obj/effect/proc_holder/spell/self/self_amputation
|
||||
|
||||
energy_coeff = 1
|
||||
@@ -316,7 +316,7 @@
|
||||
return
|
||||
|
||||
var/obj/item/bodypart/BP = pick(parts)
|
||||
BP.dismember()
|
||||
BP.dismember(harmless=TRUE)
|
||||
|
||||
//spider webs
|
||||
/datum/mutation/human/webbing
|
||||
|
||||
@@ -261,7 +261,7 @@
|
||||
desc = "The user's chemical balance is more robust."
|
||||
quality = POSITIVE
|
||||
text_gain_indication = "<span class='notice'>You feel stimmed.</span>"
|
||||
difficulty = 16
|
||||
difficulty = 18
|
||||
|
||||
/datum/mutation/human/paranoia
|
||||
name = "Paranoia"
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/datum/mutation/human/cluwne
|
||||
|
||||
name = "Cluwne"
|
||||
quality = NEGATIVE
|
||||
locked = TRUE
|
||||
text_gain_indication = "<span class='danger'>You feel like your brain is tearing itself apart.</span>"
|
||||
|
||||
/datum/mutation/human/cluwne/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
owner.dna.add_mutation(CLOWNMUT)
|
||||
owner.dna.add_mutation(EPILEPSY)
|
||||
owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 199, 199)
|
||||
|
||||
var/mob/living/carbon/human/H = owner
|
||||
|
||||
if(!istype(H.wear_mask, /obj/item/clothing/mask/gas/cluwne))
|
||||
if(!H.dropItemToGround(H.wear_mask))
|
||||
qdel(H.wear_mask)
|
||||
H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/cluwne(H), SLOT_WEAR_MASK)
|
||||
if(!istype(H.w_uniform, /obj/item/clothing/under/cluwne))
|
||||
if(!H.dropItemToGround(H.w_uniform))
|
||||
qdel(H.w_uniform)
|
||||
H.equip_to_slot_or_del(new /obj/item/clothing/under/cluwne(H), SLOT_W_UNIFORM)
|
||||
if(!istype(H.shoes, /obj/item/clothing/shoes/clown_shoes/cluwne))
|
||||
if(!H.dropItemToGround(H.shoes))
|
||||
qdel(H.shoes)
|
||||
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/clown_shoes/cluwne(H), SLOT_SHOES)
|
||||
|
||||
owner.equip_to_slot_or_del(new /obj/item/clothing/gloves/color/white(owner), SLOT_GLOVES) // this is purely for cosmetic purposes incase they aren't wearing anything in that slot
|
||||
owner.equip_to_slot_or_del(new /obj/item/storage/backpack/clown(owner), SLOT_BACK) // ditto
|
||||
|
||||
/datum/mutation/human/cluwne/on_life(mob/living/carbon/human/owner)
|
||||
if((prob(15) && owner.IsUnconscious()))
|
||||
owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, 199, 199) // there I changed it to setBrainLoss
|
||||
switch(rand(1, 6))
|
||||
if(1)
|
||||
owner.say("HONK")
|
||||
if(2 to 5)
|
||||
owner.emote("scream")
|
||||
if(6)
|
||||
owner.Stun(1)
|
||||
owner.Knockdown(20)
|
||||
owner.Jitter(500)
|
||||
|
||||
/datum/mutation/human/cluwne/on_losing(mob/living/carbon/human/owner)
|
||||
owner.adjust_fire_stacks(1)
|
||||
owner.IgniteMob()
|
||||
owner.dna.add_mutation(CLUWNEMUT)
|
||||
|
||||
/mob/living/carbon/human/proc/cluwneify()
|
||||
dna.add_mutation(CLUWNEMUT)
|
||||
emote("scream")
|
||||
regenerate_icons()
|
||||
visible_message("<span class='danger'>[src]'s body glows green, the glow dissipating only to leave behind a cluwne formerly known as [src]!</span>", \
|
||||
"<span class='danger'>Your brain feels like it's being torn apart, there is only the honkmother now.</span>")
|
||||
flash_act()
|
||||
@@ -30,3 +30,7 @@
|
||||
/datum/generecipe/hulk
|
||||
required = "/datum/mutation/human/strong; /datum/mutation/human/radioactive"
|
||||
result = HULK
|
||||
|
||||
/datum/generecipe/thermal
|
||||
required = "/datum/mutation/human/nearsight; /datum/mutation/human/stimmed"
|
||||
result = THERMAL
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/datum/mutation/human/radioactive
|
||||
name = "Radioactivity"
|
||||
desc = "A volatile mutation that causes the host to sent out deadly beta radiation. This affects both the hosts and their surroundings."
|
||||
desc = "A volatile mutation that causes the host to send out deadly beta radiation. This affects both the hosts and their surroundings."
|
||||
quality = NEGATIVE
|
||||
text_gain_indication = "<span class='warning'>You can feel it in your bones!</span>"
|
||||
time_coeff = 5
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
difficulty = 18
|
||||
text_gain_indication = "<span class='notice'>You can see the heat rising off of your skin...</span>"
|
||||
time_coeff = 2
|
||||
instability = 25
|
||||
instability = 40
|
||||
locked = TRUE
|
||||
var/visionflag = TRAIT_THERMAL_VISION
|
||||
|
||||
/datum/mutation/human/thermal/on_acquiring(mob/living/carbon/human/owner)
|
||||
@@ -63,7 +64,7 @@
|
||||
name = "X Ray Vision"
|
||||
desc = "A strange genome that allows the user to see between the spaces of walls." //actual x-ray would mean you'd constantly be blasting rads, wich might be fun for later //hmb
|
||||
text_gain_indication = "<span class='notice'>The walls suddenly disappear!</span>"
|
||||
instability = 35
|
||||
instability = 50
|
||||
locked = TRUE
|
||||
visionflag = TRAIT_XRAY_VISION
|
||||
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
//Cold Resistance gives your entire body an orange halo, and makes you immune to the effects of vacuum and cold.
|
||||
/datum/mutation/human/space_adaptation
|
||||
name = "Space Adaptation"
|
||||
desc = "A strange mutation that renders the host immune to the vacuum if space. Will still need an oxygen supply."
|
||||
desc = "A strange mutation that renders the host immune to the vacuum of space. Will still need an oxygen supply."
|
||||
quality = POSITIVE
|
||||
difficulty = 16
|
||||
text_gain_indication = "<span class='notice'>Your body feels warm!</span>"
|
||||
time_coeff = 5
|
||||
instability = 30
|
||||
|
||||
/datum/mutation/human/space_adaptation/New(class_ = MUT_OTHER, timer, datum/mutation/human/copymut)
|
||||
..()
|
||||
if(!(type in visual_indicators))
|
||||
visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "fire", -MUTATIONS_LAYER))
|
||||
|
||||
/datum/mutation/human/space_adaptation/get_visual_indicator()
|
||||
return visual_indicators[type][1]
|
||||
|
||||
/datum/mutation/human/space_adaptation/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
ADD_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
|
||||
ADD_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance")
|
||||
owner.add_filter("space_glow", 2, list("type" = "outline", "color" = "#ffe46bd8", "size" = 1))
|
||||
addtimer(CALLBACK(src, .proc/glow_loop, owner), rand(1,19))
|
||||
|
||||
/datum/mutation/human/space_adaptation/proc/glow_loop(mob/living/carbon/human/owner)
|
||||
var/filter = owner.get_filter("space_glow")
|
||||
if(filter)
|
||||
animate(filter, alpha = 190, time = 15, loop = -1)
|
||||
animate(alpha = 110, time = 25)
|
||||
|
||||
/datum/mutation/human/space_adaptation/on_losing(mob/living/carbon/human/owner)
|
||||
if(..())
|
||||
return
|
||||
REMOVE_TRAIT(owner, TRAIT_RESISTCOLD, "cold_resistance")
|
||||
REMOVE_TRAIT(owner, TRAIT_RESISTLOWPRESSURE, "cold_resistance")
|
||||
owner.remove_filter("space_glow")
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
var/obj/item/sample_object
|
||||
var/number
|
||||
|
||||
/datum/numbered_display/New(obj/item/sample, _number = 1)
|
||||
/datum/numbered_display/New(obj/item/sample, _number = 1, datum/component/storage/parent)
|
||||
if(!istype(sample))
|
||||
qdel(src)
|
||||
sample_object = sample
|
||||
sample_object = new /obj/screen/storage/item_holder(null, parent, sample)
|
||||
number = _number
|
||||
|
||||
+294
-57
@@ -1,71 +1,185 @@
|
||||
/**
|
||||
* # Outfit datums
|
||||
*
|
||||
* This is a clean system of applying outfits to mobs, if you need to equip someone in a uniform
|
||||
* this is the way to do it cleanly and properly.
|
||||
*
|
||||
* You can also specify an outfit datum on a job to have it auto equipped to the mob on join
|
||||
*
|
||||
* /mob/living/carbon/human/proc/equipOutfit(outfit) is the mob level proc to equip an outfit
|
||||
* and you pass it the relevant datum outfit
|
||||
*
|
||||
* outfits can also be saved as json blobs downloadable by a client and then can be uploaded
|
||||
* by that user to recreate the outfit, this is used by admins to allow for custom event outfits
|
||||
* that can be restored at a later date
|
||||
*/
|
||||
/datum/outfit
|
||||
///Name of the outfit (shows up in the equip admin verb)
|
||||
var/name = "Naked"
|
||||
|
||||
var/uniform = null
|
||||
var/suit = null
|
||||
var/toggle_helmet = TRUE
|
||||
var/back = null
|
||||
var/belt = null
|
||||
var/gloves = null
|
||||
var/shoes = null
|
||||
var/head = null
|
||||
var/mask = null
|
||||
var/neck = null
|
||||
var/ears = null
|
||||
var/glasses = null
|
||||
/// Type path of item to go in the idcard slot
|
||||
var/id = null
|
||||
var/l_pocket = null
|
||||
var/r_pocket = null
|
||||
|
||||
/// Type path of item to go in uniform slot
|
||||
var/uniform = null
|
||||
|
||||
/// Type path of item to go in suit slot
|
||||
var/suit = null
|
||||
|
||||
/**
|
||||
* Type path of item to go in suit storage slot
|
||||
*
|
||||
* (make sure it's valid for that suit)
|
||||
*/
|
||||
var/suit_store = null
|
||||
var/r_hand = null
|
||||
|
||||
/// Type path of item to go in back slot
|
||||
var/back = null
|
||||
|
||||
/**
|
||||
* list of items that should go in the backpack of the user
|
||||
*
|
||||
* Format of this list should be: list(path=count,otherpath=count)
|
||||
*/
|
||||
var/list/backpack_contents = null
|
||||
|
||||
/// Type path of item to go in belt slot
|
||||
var/belt = null
|
||||
|
||||
/// Type path of item to go in ears slot
|
||||
var/ears = null
|
||||
|
||||
/// Type path of item to go in the glasses slot
|
||||
var/glasses = null
|
||||
|
||||
/// Type path of item to go in gloves slot
|
||||
var/gloves = null
|
||||
|
||||
/// Type path of item to go in head slot
|
||||
var/head = null
|
||||
|
||||
/// Type path of item to go in mask slot
|
||||
var/mask = null
|
||||
|
||||
/// Type path of item to go in neck slot
|
||||
var/neck = null
|
||||
|
||||
/// Type path of item to go in shoes slot
|
||||
var/shoes = null
|
||||
|
||||
/// Type path of item for left pocket slot
|
||||
var/l_pocket = null
|
||||
|
||||
/// Type path of item for right pocket slot
|
||||
var/r_pocket = null
|
||||
|
||||
///Type path of item to go in the right hand
|
||||
var/l_hand = null
|
||||
var/internals_slot = null //ID of slot containing a gas tank
|
||||
var/list/backpack_contents = null // In the list(path=count,otherpath=count) format
|
||||
var/box // Internals box. Will be inserted at the start of backpack_contents
|
||||
var/list/implants = null
|
||||
|
||||
//Type path of item to go in left hand
|
||||
var/r_hand = null
|
||||
|
||||
/// Any clothing accessory item
|
||||
var/accessory = null
|
||||
|
||||
var/can_be_admin_equipped = TRUE // Set to FALSE if your outfit requires runtime parameters
|
||||
var/list/chameleon_extras //extra types for chameleon outfit changes, mostly guns
|
||||
/// Internals box. Will be inserted at the start of backpack_contents
|
||||
var/box
|
||||
|
||||
/**
|
||||
* extra types for chameleon outfit changes, mostly guns
|
||||
*
|
||||
* Format of this list is (typepath, typepath, typepath)
|
||||
*
|
||||
* These are all added and returns in the list for get_chamelon_diguise_info proc
|
||||
*/
|
||||
var/list/chameleon_extras
|
||||
|
||||
/**
|
||||
* Any implants the mob should start implanted with
|
||||
*
|
||||
* Format of this list is (typepath, typepath, typepath)
|
||||
*/
|
||||
var/list/implants = null
|
||||
|
||||
///ID of the slot containing a gas tank
|
||||
var/internals_slot = null
|
||||
|
||||
/// Should the toggle helmet proc be called on the helmet during equip
|
||||
var/toggle_helmet = TRUE
|
||||
|
||||
/// Any undershirt. While on humans it is a string, here we use paths to stay consistent with the rest of the equips.
|
||||
var/datum/sprite_accessory/undershirt = null
|
||||
|
||||
/**
|
||||
* Called at the start of the equip proc
|
||||
*
|
||||
* Override to change the value of the slots depending on client prefs, species and
|
||||
* other such sources of change
|
||||
*
|
||||
* Extra Arguments
|
||||
* * visualsOnly true if this is only for display (in the character setup screen)
|
||||
*
|
||||
* If visualsOnly is true, you can omit any work that doesn't visually appear on the character sprite
|
||||
*/
|
||||
/datum/outfit/proc/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
//to be overridden for customization depending on client prefs,species etc
|
||||
return
|
||||
|
||||
/**
|
||||
* Called after the equip proc has finished
|
||||
*
|
||||
* All items are on the mob at this point, use this proc to toggle internals
|
||||
* fiddle with id bindings and accesses etc
|
||||
*
|
||||
* Extra Arguments
|
||||
* * visualsOnly true if this is only for display (in the character setup screen)
|
||||
*
|
||||
* If visualsOnly is true, you can omit any work that doesn't visually appear on the character sprite
|
||||
*/
|
||||
/datum/outfit/proc/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
//to be overridden for toggling internals, id binding, access etc
|
||||
return
|
||||
|
||||
/**
|
||||
* Equips all defined types and paths to the mob passed in
|
||||
*
|
||||
* Extra Arguments
|
||||
* * visualsOnly true if this is only for display (in the character setup screen)
|
||||
*
|
||||
* If visualsOnly is true, you can omit any work that doesn't visually appear on the character sprite
|
||||
*/
|
||||
/datum/outfit/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
pre_equip(H, visualsOnly, preference_source)
|
||||
|
||||
//Start with uniform,suit,backpack for additional slots
|
||||
if(uniform)
|
||||
H.equip_to_slot_or_del(new uniform(H),SLOT_W_UNIFORM)
|
||||
H.equip_to_slot_or_del(new uniform(H), SLOT_W_UNIFORM, TRUE)
|
||||
if(suit)
|
||||
H.equip_to_slot_or_del(new suit(H),SLOT_WEAR_SUIT)
|
||||
H.equip_to_slot_or_del(new suit(H), SLOT_WEAR_SUIT, TRUE)
|
||||
if(back)
|
||||
H.equip_to_slot_or_del(new back(H),SLOT_BACK)
|
||||
H.equip_to_slot_or_del(new back(H), SLOT_BACK, TRUE)
|
||||
if(belt)
|
||||
H.equip_to_slot_or_del(new belt(H),SLOT_BELT)
|
||||
H.equip_to_slot_or_del(new belt(H), SLOT_BELT, TRUE)
|
||||
if(gloves)
|
||||
H.equip_to_slot_or_del(new gloves(H),SLOT_GLOVES)
|
||||
H.equip_to_slot_or_del(new gloves(H), SLOT_GLOVES, TRUE)
|
||||
if(shoes)
|
||||
H.equip_to_slot_or_del(new shoes(H),SLOT_SHOES)
|
||||
H.equip_to_slot_or_del(new shoes(H), SLOT_SHOES, TRUE)
|
||||
if(head)
|
||||
H.equip_to_slot_or_del(new head(H),SLOT_HEAD)
|
||||
H.equip_to_slot_or_del(new head(H), SLOT_HEAD, TRUE)
|
||||
if(mask)
|
||||
H.equip_to_slot_or_del(new mask(H),SLOT_WEAR_MASK)
|
||||
H.equip_to_slot_or_del(new mask(H), SLOT_WEAR_MASK, TRUE)
|
||||
if(neck)
|
||||
H.equip_to_slot_or_del(new neck(H),SLOT_NECK)
|
||||
H.equip_to_slot_or_del(new neck(H), SLOT_NECK, TRUE)
|
||||
if(ears)
|
||||
H.equip_to_slot_or_del(new ears(H),SLOT_EARS)
|
||||
H.equip_to_slot_or_del(new ears(H), SLOT_EARS, TRUE)
|
||||
if(glasses)
|
||||
H.equip_to_slot_or_del(new glasses(H),SLOT_GLASSES)
|
||||
H.equip_to_slot_or_del(new glasses(H), SLOT_GLASSES, TRUE)
|
||||
if(id)
|
||||
H.equip_to_slot_or_del(new id(H),SLOT_WEAR_ID)
|
||||
H.equip_to_slot_or_del(new id(H), SLOT_WEAR_ID, TRUE)
|
||||
if(suit_store)
|
||||
H.equip_to_slot_or_del(new suit_store(H),SLOT_S_STORE)
|
||||
H.equip_to_slot_or_del(new suit_store(H), SLOT_S_STORE, TRUE)
|
||||
if(undershirt)
|
||||
H.undershirt = initial(undershirt.name)
|
||||
|
||||
if(accessory)
|
||||
var/obj/item/clothing/under/U = H.w_uniform
|
||||
@@ -81,9 +195,9 @@
|
||||
|
||||
if(!visualsOnly) // Items in pockets or backpack don't show up on mob's icon.
|
||||
if(l_pocket)
|
||||
H.equip_to_slot_or_del(new l_pocket(H),SLOT_L_STORE)
|
||||
H.equip_to_slot_or_del(new l_pocket(H), SLOT_L_STORE, TRUE)
|
||||
if(r_pocket)
|
||||
H.equip_to_slot_or_del(new r_pocket(H),SLOT_R_STORE)
|
||||
H.equip_to_slot_or_del(new r_pocket(H), SLOT_R_STORE, TRUE)
|
||||
|
||||
if(box)
|
||||
if(!backpack_contents)
|
||||
@@ -97,7 +211,7 @@
|
||||
if(!isnum(number))//Default to 1
|
||||
number = 1
|
||||
for(var/i in 1 to number)
|
||||
H.equip_to_slot_or_del(new path(H),SLOT_IN_BACKPACK)
|
||||
H.equip_to_slot_or_del(new path(H), SLOT_IN_BACKPACK, TRUE)
|
||||
|
||||
if(!H.head && toggle_helmet && istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit))
|
||||
var/obj/item/clothing/suit/space/hardsuit/HS = H.wear_suit
|
||||
@@ -112,55 +226,178 @@
|
||||
H.update_action_buttons_icon()
|
||||
if(implants)
|
||||
for(var/implant_type in implants)
|
||||
var/obj/item/implant/I = new implant_type
|
||||
var/obj/item/implant/I = new implant_type(H)
|
||||
I.implant(H, null, TRUE)
|
||||
|
||||
H.update_body()
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Apply a fingerprint from the passed in human to all items in the outfit
|
||||
*
|
||||
* Used for forensics setup when the mob is first equipped at roundstart
|
||||
* essentially calls add_fingerprint to every defined item on the human
|
||||
*
|
||||
*/
|
||||
/datum/outfit/proc/apply_fingerprints(mob/living/carbon/human/H)
|
||||
if(!istype(H))
|
||||
return
|
||||
if(H.back)
|
||||
H.back.add_fingerprint(H,1) //The 1 sets a flag to ignore gloves
|
||||
H.back.add_fingerprint(H, ignoregloves = TRUE)
|
||||
for(var/obj/item/I in H.back.contents)
|
||||
I.add_fingerprint(H,1)
|
||||
I.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.wear_id)
|
||||
H.wear_id.add_fingerprint(H,1)
|
||||
H.wear_id.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.w_uniform)
|
||||
H.w_uniform.add_fingerprint(H,1)
|
||||
H.w_uniform.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.wear_suit)
|
||||
H.wear_suit.add_fingerprint(H,1)
|
||||
H.wear_suit.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.wear_mask)
|
||||
H.wear_mask.add_fingerprint(H,1)
|
||||
H.wear_mask.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.wear_neck)
|
||||
H.wear_neck.add_fingerprint(H,1)
|
||||
H.wear_neck.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.head)
|
||||
H.head.add_fingerprint(H,1)
|
||||
H.head.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.shoes)
|
||||
H.shoes.add_fingerprint(H,1)
|
||||
H.shoes.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.gloves)
|
||||
H.gloves.add_fingerprint(H,1)
|
||||
H.gloves.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.ears)
|
||||
H.ears.add_fingerprint(H,1)
|
||||
H.ears.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.glasses)
|
||||
H.glasses.add_fingerprint(H,1)
|
||||
H.glasses.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.belt)
|
||||
H.belt.add_fingerprint(H,1)
|
||||
H.belt.add_fingerprint(H, ignoregloves = TRUE)
|
||||
for(var/obj/item/I in H.belt.contents)
|
||||
I.add_fingerprint(H,1)
|
||||
I.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.s_store)
|
||||
H.s_store.add_fingerprint(H,1)
|
||||
H.s_store.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.l_store)
|
||||
H.l_store.add_fingerprint(H,1)
|
||||
H.l_store.add_fingerprint(H, ignoregloves = TRUE)
|
||||
if(H.r_store)
|
||||
H.r_store.add_fingerprint(H,1)
|
||||
H.r_store.add_fingerprint(H, ignoregloves = TRUE)
|
||||
for(var/obj/item/I in H.held_items)
|
||||
I.add_fingerprint(H,1)
|
||||
return 1
|
||||
I.add_fingerprint(H, ignoregloves = TRUE)
|
||||
return TRUE
|
||||
|
||||
/// Return a list of all the types that are required to disguise as this outfit type
|
||||
/datum/outfit/proc/get_chameleon_disguise_info()
|
||||
var/list/types = list(uniform, suit, back, belt, gloves, shoes, head, mask, neck, ears, glasses, id, l_pocket, r_pocket, suit_store, r_hand, l_hand)
|
||||
types += chameleon_extras
|
||||
listclearnulls(types)
|
||||
return types
|
||||
|
||||
/// Return a json list of this outfit
|
||||
/datum/outfit/proc/get_json_data()
|
||||
. = list()
|
||||
.["outfit_type"] = type
|
||||
.["name"] = name
|
||||
.["uniform"] = uniform
|
||||
.["suit"] = suit
|
||||
.["toggle_helmet"] = toggle_helmet
|
||||
.["back"] = back
|
||||
.["belt"] = belt
|
||||
.["gloves"] = gloves
|
||||
.["shoes"] = shoes
|
||||
.["head"] = head
|
||||
.["mask"] = mask
|
||||
.["neck"] = neck
|
||||
.["ears"] = ears
|
||||
.["glasses"] = glasses
|
||||
.["id"] = id
|
||||
.["l_pocket"] = l_pocket
|
||||
.["r_pocket"] = r_pocket
|
||||
.["suit_store"] = suit_store
|
||||
.["r_hand"] = r_hand
|
||||
.["l_hand"] = l_hand
|
||||
.["internals_slot"] = internals_slot
|
||||
.["backpack_contents"] = backpack_contents
|
||||
.["box"] = box
|
||||
.["implants"] = implants
|
||||
.["accessory"] = accessory
|
||||
|
||||
/// Copy most vars from another outfit to this one
|
||||
/datum/outfit/proc/copy_from(datum/outfit/target)
|
||||
name = target.name
|
||||
uniform = target.uniform
|
||||
suit = target.suit
|
||||
toggle_helmet = target.toggle_helmet
|
||||
back = target.back
|
||||
belt = target.belt
|
||||
gloves = target.gloves
|
||||
shoes = target.shoes
|
||||
head = target.head
|
||||
mask = target.mask
|
||||
neck = target.neck
|
||||
ears = target.ears
|
||||
glasses = target.glasses
|
||||
id = target.id
|
||||
l_pocket = target.l_pocket
|
||||
r_pocket = target.r_pocket
|
||||
suit_store = target.suit_store
|
||||
r_hand = target.r_hand
|
||||
l_hand = target.l_hand
|
||||
internals_slot = target.internals_slot
|
||||
backpack_contents = target.backpack_contents
|
||||
box = target.box
|
||||
implants = target.implants
|
||||
accessory = target.accessory
|
||||
|
||||
/// Prompt the passed in mob client to download this outfit as a json blob
|
||||
/datum/outfit/proc/save_to_file(mob/admin)
|
||||
var/stored_data = get_json_data()
|
||||
var/json = json_encode(stored_data)
|
||||
//Kinda annoying but as far as i can tell you need to make actual file.
|
||||
var/f = file("data/TempOutfitUpload")
|
||||
fdel(f)
|
||||
WRITE_FILE(f,json)
|
||||
admin << ftp(f,"[name].json")
|
||||
|
||||
/// Create an outfit datum from a list of json data
|
||||
/datum/outfit/proc/load_from(list/outfit_data)
|
||||
//This could probably use more strict validation
|
||||
name = outfit_data["name"]
|
||||
uniform = text2path(outfit_data["uniform"])
|
||||
suit = text2path(outfit_data["suit"])
|
||||
toggle_helmet = outfit_data["toggle_helmet"]
|
||||
back = text2path(outfit_data["back"])
|
||||
belt = text2path(outfit_data["belt"])
|
||||
gloves = text2path(outfit_data["gloves"])
|
||||
shoes = text2path(outfit_data["shoes"])
|
||||
head = text2path(outfit_data["head"])
|
||||
mask = text2path(outfit_data["mask"])
|
||||
neck = text2path(outfit_data["neck"])
|
||||
ears = text2path(outfit_data["ears"])
|
||||
glasses = text2path(outfit_data["glasses"])
|
||||
id = text2path(outfit_data["id"])
|
||||
l_pocket = text2path(outfit_data["l_pocket"])
|
||||
r_pocket = text2path(outfit_data["r_pocket"])
|
||||
suit_store = text2path(outfit_data["suit_store"])
|
||||
r_hand = text2path(outfit_data["r_hand"])
|
||||
l_hand = text2path(outfit_data["l_hand"])
|
||||
internals_slot = outfit_data["internals_slot"]
|
||||
var/list/backpack = outfit_data["backpack_contents"]
|
||||
backpack_contents = list()
|
||||
for(var/item in backpack)
|
||||
var/itype = text2path(item)
|
||||
if(itype)
|
||||
backpack_contents[itype] = backpack[item]
|
||||
box = text2path(outfit_data["box"])
|
||||
var/list/impl = outfit_data["implants"]
|
||||
implants = list()
|
||||
for(var/I in impl)
|
||||
var/imptype = text2path(I)
|
||||
if(imptype)
|
||||
implants += imptype
|
||||
accessory = text2path(outfit_data["accessory"])
|
||||
return TRUE
|
||||
|
||||
/datum/outfit/vv_get_dropdown()
|
||||
. = ..()
|
||||
VV_DROPDOWN_OPTION("", "---")
|
||||
VV_DROPDOWN_OPTION(VV_HK_TO_OUTFIT_EDITOR, "Outfit Editor")
|
||||
|
||||
/datum/outfit/vv_do_topic(list/href_list)
|
||||
. = ..()
|
||||
if(href_list[VV_HK_TO_OUTFIT_EDITOR])
|
||||
usr.client.open_outfit_editor(src)
|
||||
|
||||
@@ -204,12 +204,11 @@
|
||||
// first 10 minutes only
|
||||
return world.time - SSticker.round_start_time < 6000
|
||||
|
||||
// this is broken and does not work. Thanks TG
|
||||
// /datum/map_template/shuttle/emergency/airless/post_load()
|
||||
// . = ..()
|
||||
// //enable buying engines from cargo
|
||||
// var/datum/supply_pack/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shuttle_engine]
|
||||
// P.special_enabled = TRUE
|
||||
/datum/map_template/shuttle/emergency/construction/post_load()
|
||||
. = ..()
|
||||
//enable buying engines from cargo
|
||||
var/datum/supply_pack/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shuttle_engine]
|
||||
P.special_enabled = TRUE
|
||||
|
||||
|
||||
/datum/map_template/shuttle/emergency/asteroid
|
||||
|
||||
@@ -642,7 +642,7 @@
|
||||
O.Remove()
|
||||
if(iscarbon(owner))
|
||||
var/mob/living/carbon/C = owner
|
||||
C.vomit(0, toxic = TRUE)
|
||||
C.vomit(0)
|
||||
O.forceMove(get_turf(owner))
|
||||
if(isliving(owner))
|
||||
var/mob/living/L = owner
|
||||
@@ -768,3 +768,98 @@
|
||||
M.dna.species.punchdamagelow -= damageboost
|
||||
M.dna.species.punchwoundbonus -= woundboost
|
||||
to_chat(M, "<span class='notice'>You calm yourself, and your unnatural strength dissipates.</span>")
|
||||
|
||||
/datum/status_effect/crucible_soul
|
||||
id = "Blessing of Crucible Soul"
|
||||
status_type = STATUS_EFFECT_REFRESH
|
||||
duration = 15 SECONDS
|
||||
examine_text = "<span class='notice'>They don't seem to be all here.</span>"
|
||||
alert_type = /obj/screen/alert/status_effect/crucible_soul
|
||||
var/turf/location
|
||||
|
||||
/datum/status_effect/crucible_soul/on_apply()
|
||||
. = ..()
|
||||
to_chat(owner,"<span class='notice'>You phase through reality, nothing is out of bounds!</span>")
|
||||
owner.alpha = 180
|
||||
owner.pass_flags |= PASSCLOSEDTURF | PASSGLASS | PASSGRILLE | PASSTABLE | PASSMOB
|
||||
location = get_turf(owner)
|
||||
|
||||
/datum/status_effect/crucible_soul/on_remove()
|
||||
to_chat(owner,"<span class='notice'>You regain your physicality, returning you to your original location...</span>")
|
||||
owner.alpha = initial(owner.alpha)
|
||||
owner.pass_flags &= ~(PASSCLOSEDTURF | PASSGLASS | PASSGRILLE | PASSTABLE | PASSMOB)
|
||||
owner.forceMove(location)
|
||||
location = null
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/duskndawn
|
||||
id = "Blessing of Dusk and Dawn"
|
||||
status_type = STATUS_EFFECT_REFRESH
|
||||
duration = 60 SECONDS
|
||||
alert_type =/obj/screen/alert/status_effect/duskndawn
|
||||
|
||||
/datum/status_effect/duskndawn/on_apply()
|
||||
. = ..()
|
||||
ADD_TRAIT(owner,TRAIT_XRAY_VISION,type)
|
||||
owner.update_sight()
|
||||
|
||||
/datum/status_effect/duskndawn/on_remove()
|
||||
REMOVE_TRAIT(owner,TRAIT_XRAY_VISION,type)
|
||||
owner.update_sight()
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/marshal
|
||||
id = "Blessing of Wounded Soldier"
|
||||
status_type = STATUS_EFFECT_REFRESH
|
||||
duration = 60 SECONDS
|
||||
tick_interval = 1 SECONDS
|
||||
alert_type = /obj/screen/alert/status_effect/marshal
|
||||
|
||||
/datum/status_effect/marshal/on_apply()
|
||||
. = ..()
|
||||
ADD_TRAIT(owner,TRAIT_IGNOREDAMAGESLOWDOWN,type)
|
||||
|
||||
/datum/status_effect/marshal/on_remove()
|
||||
. = ..()
|
||||
REMOVE_TRAIT(owner,TRAIT_IGNOREDAMAGESLOWDOWN,type)
|
||||
|
||||
/datum/status_effect/marshal/tick()
|
||||
. = ..()
|
||||
if(!iscarbon(owner))
|
||||
return
|
||||
var/mob/living/carbon/carbie = owner
|
||||
|
||||
for(var/BP in carbie.bodyparts)
|
||||
var/obj/item/bodypart/part = BP
|
||||
for(var/W in part.wounds)
|
||||
var/datum/wound/wound = W
|
||||
var/heal_amt = 0
|
||||
|
||||
switch(wound.severity)
|
||||
if(WOUND_SEVERITY_MODERATE)
|
||||
heal_amt = 1
|
||||
if(WOUND_SEVERITY_SEVERE)
|
||||
heal_amt = 3
|
||||
if(WOUND_SEVERITY_CRITICAL)
|
||||
heal_amt = 6
|
||||
if(wound.wound_type == WOUND_BURN)
|
||||
carbie.adjustFireLoss(-heal_amt)
|
||||
else
|
||||
carbie.adjustBruteLoss(-heal_amt)
|
||||
carbie.blood_volume += carbie.blood_volume >= BLOOD_VOLUME_NORMAL ? 0 : heal_amt*3
|
||||
|
||||
|
||||
/obj/screen/alert/status_effect/crucible_soul
|
||||
name = "Blessing of Crucible Soul"
|
||||
desc = "You phased through the reality, you are halfway to your final destination..."
|
||||
icon_state = "crucible"
|
||||
|
||||
/obj/screen/alert/status_effect/duskndawn
|
||||
name = "Blessing of Dusk and Dawn"
|
||||
desc = "Many things hide beyond the horizon, with Owl's help i managed to slip past sun's guard and moon's watch."
|
||||
icon_state = "duskndawn"
|
||||
|
||||
/obj/screen/alert/status_effect/marshal
|
||||
name = "Blessing of Wounded Soldier"
|
||||
desc = "Some people seek power through redemption, one thing many people don't know is that battle is the ultimate redemption and wounds let you bask in eternal glory."
|
||||
icon_state = "wounded_soldier"
|
||||
|
||||
@@ -95,21 +95,31 @@
|
||||
/datum/status_effect/staggered/on_creation(mob/living/new_owner, set_duration)
|
||||
if(isnum(set_duration))
|
||||
duration = set_duration
|
||||
if(!CONFIG_GET(flag/sprint_enabled))
|
||||
new_owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/status_effect/stagger, TRUE, CONFIG_GET(number/sprintless_stagger_slowdown))
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/staggered/on_remove()
|
||||
owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/stagger)
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/off_balance
|
||||
id = "offbalance"
|
||||
blocks_sprint = TRUE
|
||||
alert_type = null
|
||||
|
||||
/datum/status_effect/off_balance/on_creation(mob/living/new_owner, set_duration)
|
||||
if(isnum(set_duration))
|
||||
duration = set_duration
|
||||
if(!CONFIG_GET(flag/sprint_enabled))
|
||||
new_owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/status_effect/off_balance, TRUE, CONFIG_GET(number/sprintless_off_balance_slowdown))
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/off_balance/on_remove()
|
||||
var/active_item = owner.get_active_held_item()
|
||||
if(is_type_in_typecache(active_item, GLOB.shove_disarming_types))
|
||||
if(active_item)
|
||||
owner.visible_message("<span class='warning'>[owner.name] regains their grip on \the [active_item]!</span>", "<span class='warning'>You regain your grip on \the [active_item]</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/off_balance)
|
||||
return ..()
|
||||
|
||||
/obj/screen/alert/status_effect/asleep
|
||||
@@ -117,18 +127,14 @@
|
||||
desc = "You've fallen asleep. Wait a bit and you should wake up. Unless you don't, considering how helpless you are."
|
||||
icon_state = "asleep"
|
||||
|
||||
/datum/status_effect/no_combat_mode
|
||||
id = "no_combat_mode"
|
||||
alert_type = null
|
||||
status_type = STATUS_EFFECT_REPLACE
|
||||
blocks_combatmode = TRUE
|
||||
|
||||
/datum/status_effect/no_combat_mode/on_creation(mob/living/new_owner, set_duration)
|
||||
if(isnum(set_duration))
|
||||
duration = set_duration
|
||||
. = ..()
|
||||
/datum/status_effect/grouped/stasis
|
||||
id = "stasis"
|
||||
duration = -1
|
||||
tick_interval = 10
|
||||
var/last_dead_time
|
||||
|
||||
/datum/status_effect/no_combat_mode/robotic_emp
|
||||
/datum/status_effect/robotic_emp
|
||||
id = "emp_no_combat_mode"
|
||||
|
||||
/datum/status_effect/mesmerize
|
||||
@@ -138,13 +144,11 @@
|
||||
/datum/status_effect/mesmerize/on_creation(mob/living/new_owner, set_duration)
|
||||
. = ..()
|
||||
ADD_TRAIT(owner, TRAIT_MUTE, "mesmerize")
|
||||
ADD_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, "mesmerize")
|
||||
owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/mesmerize)
|
||||
|
||||
/datum/status_effect/mesmerize/on_remove()
|
||||
. = ..()
|
||||
REMOVE_TRAIT(owner, TRAIT_MUTE, "mesmerize")
|
||||
REMOVE_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, "mesmerize")
|
||||
owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/mesmerize)
|
||||
|
||||
/datum/status_effect/mesmerize/on_creation(mob/living/new_owner, set_duration)
|
||||
@@ -154,7 +158,7 @@
|
||||
|
||||
/obj/screen/alert/status_effect/mesmerized
|
||||
name = "Mesmerized"
|
||||
desc = "You cant tear your sight from who is in front of you... their gaze is simply too enthralling.."
|
||||
desc = "You can't tear your sight from who is in front of you... their gaze is simply too enthralling.."
|
||||
icon = 'icons/mob/actions/bloodsucker.dmi'
|
||||
icon_state = "power_mez"
|
||||
|
||||
@@ -162,7 +166,7 @@
|
||||
id = "tased"
|
||||
alert_type = null
|
||||
var/movespeed_mod = /datum/movespeed_modifier/status_effect/tased
|
||||
var/stamdmg_per_ds = 0 //a 20 duration would do 20 stamdmg, disablers do 24 or something
|
||||
var/stamdmg_per_ds = 1 //a 20 duration would do 20 stamdmg, disablers do 24 or something
|
||||
var/last_tick = 0 //fastprocess processing speed is a goddamn sham, don't trust it.
|
||||
|
||||
/datum/status_effect/electrode/on_creation(mob/living/new_owner, set_duration)
|
||||
@@ -193,9 +197,26 @@
|
||||
/datum/status_effect/electrode/no_combat_mode
|
||||
id = "tased_strong"
|
||||
movespeed_mod = /datum/movespeed_modifier/status_effect/tased/no_combat_mode
|
||||
blocks_combatmode = TRUE
|
||||
stamdmg_per_ds = 1
|
||||
|
||||
/datum/status_effect/vtec_disabled
|
||||
id = "vtec_disable"
|
||||
tick = FALSE
|
||||
|
||||
/datum/status_effect/vtec_disabled/on_creation(mob/living/new_owner, set_duration)
|
||||
if(isnum(set_duration))
|
||||
duration = set_duration
|
||||
. = ..()
|
||||
if(iscyborg(owner))
|
||||
var/mob/living/silicon/robot/R = owner
|
||||
R.vtec_disabled = TRUE
|
||||
|
||||
/datum/status_effect/vtec_disabled/on_remove()
|
||||
if(iscyborg(owner))
|
||||
var/mob/living/silicon/robot/R = owner
|
||||
R.vtec_disabled = FALSE
|
||||
return ..()
|
||||
|
||||
//OTHER DEBUFFS
|
||||
/datum/status_effect/his_wrath //does minor damage over time unless holding His Grace
|
||||
id = "his_wrath"
|
||||
@@ -498,6 +519,32 @@
|
||||
I.take_damage(100)
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/eldritch/void
|
||||
id = "void_mark"
|
||||
effect_sprite = "emark4"
|
||||
|
||||
/datum/status_effect/eldritch/void/on_effect()
|
||||
var/turf/open/turfie = get_turf(owner)
|
||||
turfie.TakeTemperature(-40)
|
||||
owner.adjust_bodytemperature(-60)
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/domain
|
||||
id = "domain"
|
||||
alert_type = null
|
||||
var/movespeed_mod = /datum/movespeed_modifier/status_effect/domain
|
||||
|
||||
/datum/status_effect/domain/on_creation(mob/living/new_owner, set_duration)
|
||||
if(isliving(owner))
|
||||
var/mob/living/carbon/C = owner
|
||||
C.add_movespeed_modifier(movespeed_mod)
|
||||
|
||||
/datum/status_effect/electrode/on_remove()
|
||||
if(isliving(owner))
|
||||
var/mob/living/carbon/C = owner
|
||||
C.remove_movespeed_modifier(movespeed_mod)
|
||||
. = ..()
|
||||
|
||||
/datum/status_effect/corrosion_curse
|
||||
id = "corrosion_curse"
|
||||
status_type = STATUS_EFFECT_REPLACE
|
||||
@@ -506,7 +553,7 @@
|
||||
|
||||
/datum/status_effect/corrosion_curse/on_creation(mob/living/new_owner, ...)
|
||||
. = ..()
|
||||
to_chat(owner, "<span class='danger'>Your feel your body starting to break apart...</span>")
|
||||
to_chat(owner, "<span class='danger'>You feel your body starting to break apart...</span>")
|
||||
|
||||
/datum/status_effect/corrosion_curse/tick()
|
||||
. = ..()
|
||||
@@ -577,7 +624,7 @@
|
||||
|
||||
/datum/status_effect/amok/on_apply(mob/living/afflicted)
|
||||
. = ..()
|
||||
to_chat(owner, "<span class='boldwarning'>Your feel filled with a rage that is not your own!</span>")
|
||||
to_chat(owner, "<span class='boldwarning'>You feel filled with a rage that is not your own!</span>")
|
||||
|
||||
/datum/status_effect/amok/tick()
|
||||
. = ..()
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
/datum/status_effect
|
||||
var/id = "effect" //Used for screen alerts.
|
||||
var/duration = -1 //How long the status effect lasts in DECISECONDS. Enter -1 for an effect that never ends unless removed through some means.
|
||||
/// do we tick()?
|
||||
var/tick = TRUE
|
||||
var/tick_interval = 10 //How many deciseconds between ticks, approximately. Leave at 10 for every second.
|
||||
var/next_tick //The scheduled time for the next tick.
|
||||
var/mob/living/owner //The mob affected by the status effect.
|
||||
var/on_remove_on_mob_delete = FALSE //if we call on_remove() when the mob is deleted
|
||||
var/examine_text //If defined, this text will appear when the mob is examined - to use he, she etc. use "SUBJECTPRONOUN" and replace it in the examines themselves
|
||||
var/alert_type = /obj/screen/alert/status_effect //the alert thrown by the status effect, contains name and description
|
||||
/// If this is TRUE, the user will have combt mode forcefully disabled while this is active.
|
||||
var/blocks_combatmode = FALSE
|
||||
/// If this is TRUE, the user will have sprint forcefully disabled while this is active.
|
||||
var/blocks_sprint = FALSE
|
||||
var/obj/screen/alert/status_effect/linked_alert = null //the alert itself, if it exists
|
||||
@@ -61,8 +61,6 @@
|
||||
|
||||
/datum/status_effect/proc/on_apply() //Called whenever the buff is applied; returning FALSE will cause it to autoremove itself.
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
if(blocks_combatmode)
|
||||
ADD_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, src)
|
||||
if(blocks_sprint)
|
||||
ADD_TRAIT(owner, TRAIT_SPRINT_LOCKED, src)
|
||||
return TRUE
|
||||
@@ -74,8 +72,6 @@
|
||||
|
||||
/datum/status_effect/proc/on_remove() //Called whenever the buff expires or is removed; do note that at the point this is called, it is out of the owner's status_effects but owner is not yet null
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
if(blocks_combatmode)
|
||||
REMOVE_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, src)
|
||||
if(blocks_sprint)
|
||||
REMOVE_TRAIT(owner, TRAIT_SPRINT_LOCKED, src)
|
||||
return TRUE
|
||||
@@ -83,8 +79,6 @@
|
||||
/datum/status_effect/proc/be_replaced() //Called instead of on_remove when a status effect is replaced by itself or when a status effect with on_remove_on_mob_delete = FALSE has its mob deleted
|
||||
owner.clear_alert(id)
|
||||
LAZYREMOVE(owner.status_effects, src)
|
||||
if(blocks_combatmode)
|
||||
REMOVE_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, src)
|
||||
if(blocks_sprint)
|
||||
REMOVE_TRAIT(owner, TRAIT_SPRINT_LOCKED, src)
|
||||
owner = null
|
||||
|
||||
@@ -219,19 +219,3 @@
|
||||
/datum/quirk/night_vision/on_spawn()
|
||||
var/mob/living/carbon/human/H = quirk_holder
|
||||
H.update_sight()
|
||||
|
||||
/datum/quirk/multilingual
|
||||
name = "Multi-Lingual"
|
||||
desc = "You spent a portion of your life learning to understand an additional language. You may or may not be able to speak it based on your anatomy."
|
||||
value = 1
|
||||
mob_trait = TRAIT_MULTILINGUAL
|
||||
gain_text = "You've learned an extra language!"
|
||||
lose_text = "You've forgotten your extra language."
|
||||
|
||||
/datum/quirk/multilingual/post_add()
|
||||
var/mob/living/carbon/human/H = quirk_holder
|
||||
H.grant_language(H.client.prefs.language, TRUE, TRUE, LANGUAGE_MULTILINGUAL)
|
||||
|
||||
/datum/quirk/multilingual/remove()
|
||||
var/mob/living/carbon/human/H = quirk_holder
|
||||
H.remove_language(H.client.prefs.language, TRUE, TRUE, LANGUAGE_MULTILINGUAL)
|
||||
|
||||
@@ -154,3 +154,11 @@
|
||||
/datum/quirk/longtimer/on_spawn()
|
||||
var/mob/living/carbon/C = quirk_holder
|
||||
C.generate_fake_scars(rand(min_scars, max_scars))
|
||||
|
||||
/datum/quirk/trashcan
|
||||
name = "Trashcan"
|
||||
desc = "You are able to consume and digest trash."
|
||||
value = 0
|
||||
gain_text = "<span class='notice'>You feel like munching on a can of soda.</span>"
|
||||
lose_text = "<span class='notice'>You no longer feel like you should be eating trash.</span>"
|
||||
mob_trait = TRAIT_TRASHCAN
|
||||
|
||||
@@ -58,13 +58,13 @@
|
||||
/// The list of z-levels that this weather is actively affecting
|
||||
var/impacted_z_levels
|
||||
|
||||
/// Since it's above everything else, this is the layer used by default. TURF_LAYER is below mobs and walls if you need to use that.
|
||||
var/overlay_layer = AREA_LAYER
|
||||
/// Since it's above everything else, this is the layer used by default. TURF_LAYER is below mobs and walls if you need to use that.
|
||||
var/overlay_layer = AREA_LAYER
|
||||
/// Plane for the overlay
|
||||
var/overlay_plane = BLACKNESS_PLANE
|
||||
/// If the weather has no purpose but aesthetics.
|
||||
/// If the weather has no purpose but aesthetics.
|
||||
var/aesthetic = FALSE
|
||||
/// Used by mobs to prevent them from being affected by the weather
|
||||
/// Used by mobs to prevent them from being affected by the weather
|
||||
var/immunity_type = "storm"
|
||||
|
||||
/// The stage of the weather, from 1-4
|
||||
@@ -79,6 +79,8 @@
|
||||
var/barometer_predictable = FALSE
|
||||
/// For barometers to know when the next storm will hit
|
||||
var/next_hit_time = 0
|
||||
/// This causes the weather to only end if forced to
|
||||
var/perpetual = FALSE
|
||||
|
||||
/datum/weather/New(z_levels)
|
||||
..()
|
||||
@@ -140,7 +142,8 @@
|
||||
to_chat(M, weather_message)
|
||||
if(weather_sound)
|
||||
SEND_SOUND(M, sound(weather_sound))
|
||||
addtimer(CALLBACK(src, .proc/wind_down), weather_duration)
|
||||
if(!perpetual)
|
||||
addtimer(CALLBACK(src, .proc/wind_down), weather_duration)
|
||||
|
||||
/**
|
||||
* Weather enters the winding down phase, stops effects
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
area_type = /area
|
||||
protected_areas = list(/area/maintenance, /area/ai_monitored/turret_protected/ai_upload, /area/ai_monitored/turret_protected/ai_upload_foyer,
|
||||
/area/ai_monitored/turret_protected/ai, /area/storage/emergency/starboard, /area/storage/emergency/port, /area/shuttle, /area/security/prison, /area/ruin, /area/space/nearstation, /area/icemoon)
|
||||
/area/ai_monitored/turret_protected/ai, /area/commons/storage/emergency/starboard, /area/commons/storage/emergency/port, /area/shuttle, /area/security/prison/safe, /area/security/prison/toilet)
|
||||
target_trait = ZTRAIT_STATION
|
||||
|
||||
immunity_type = "rad"
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/datum/weather/void_storm
|
||||
name = "void storm"
|
||||
desc = "A rare and highly anomalous event often accompanied by unknown entities shredding spacetime continouum. We'd advise you to start running."
|
||||
|
||||
telegraph_duration = 2 SECONDS
|
||||
telegraph_overlay = "void"
|
||||
|
||||
weather_message = "<span class='danger'><i>You feel air around you getting colder... and void's sweet embrace...</i></span>"
|
||||
weather_overlay = "void_storm"
|
||||
weather_duration_lower = 60 SECONDS
|
||||
weather_duration_upper = 120 SECONDS
|
||||
|
||||
|
||||
end_duration = 10 SECONDS
|
||||
|
||||
area_type = /area
|
||||
protect_indoors = FALSE
|
||||
target_trait = ZTRAIT_VOIDSTORM
|
||||
|
||||
immunity_type = "void"
|
||||
|
||||
barometer_predictable = FALSE
|
||||
perpetual = TRUE
|
||||
|
||||
/datum/weather/void_storm/weather_act(mob/living/L)
|
||||
if(IS_HERETIC(L) || IS_HERETIC_MONSTER(L))
|
||||
return
|
||||
L.adjustOxyLoss(rand(1,3))
|
||||
L.adjustFireLoss(rand(1,3))
|
||||
L.adjust_blurriness(rand(0,1))
|
||||
L.adjust_bodytemperature(-rand(5,15))
|
||||
@@ -166,6 +166,7 @@
|
||||
on_pulse(wire, user)
|
||||
|
||||
/datum/wires/proc/pulse_color(color, mob/living/user)
|
||||
set waitfor = FALSE
|
||||
LAZYINITLIST(current_users)
|
||||
if(current_users[user])
|
||||
return FALSE
|
||||
|
||||
@@ -106,12 +106,7 @@
|
||||
A.aiControlDisabled = 1
|
||||
else if(A.aiControlDisabled == -1)
|
||||
A.aiControlDisabled = 2
|
||||
sleep(10)
|
||||
if(A)
|
||||
if(A.aiControlDisabled == 1)
|
||||
A.aiControlDisabled = 0
|
||||
else if(A.aiControlDisabled == 2)
|
||||
A.aiControlDisabled = -1
|
||||
addtimer(CALLBACK(A, /obj/machinery/door/airlock.proc/reset_ai_wire), 1 SECONDS)
|
||||
if(WIRE_SHOCK) // Pulse to shock the door for 10 ticks.
|
||||
if(!A.secondsElectrified)
|
||||
A.set_electrified(30, usr)
|
||||
@@ -125,6 +120,12 @@
|
||||
A.lights = !A.lights
|
||||
A.update_icon()
|
||||
|
||||
/obj/machinery/door/airlock/proc/reset_ai_wire()
|
||||
if(aiControlDisabled == 1)
|
||||
aiControlDisabled = 0
|
||||
else if(aiControlDisabled == 2)
|
||||
aiControlDisabled = -1
|
||||
|
||||
/datum/wires/airlock/on_cut(wire, mend)
|
||||
var/obj/machinery/door/airlock/A = holder
|
||||
if(usr && !A.hasSiliconAccessInArea(usr) && A.isElectrified() && A.shock(usr, 100))
|
||||
|
||||
@@ -207,3 +207,101 @@
|
||||
if(!key_valid)
|
||||
GLOB.topic_status_cache = .
|
||||
|
||||
/datum/world_topic/jsonstatus
|
||||
keyword = "jsonstatus"
|
||||
|
||||
/datum/world_topic/jsonstatus/Run(list/input, addr)
|
||||
. = list()
|
||||
.["mode"] = "hidden" // GLOB.master_mode - woops we don't want people to know if there's secret/extended :)
|
||||
.["round_id"] = "[GLOB.round_id]"
|
||||
.["players"] = GLOB.clients.len
|
||||
var/list/adm = get_admin_counts()
|
||||
var/list/presentmins = adm["present"]
|
||||
var/list/afkmins = adm["afk"]
|
||||
.["admins"] = presentmins.len + afkmins.len //equivalent to the info gotten from adminwho
|
||||
.["security_level"] = "[NUM2SECLEVEL(GLOB.security_level)]"
|
||||
.["round_duration"] = WORLDTIME2TEXT("hh:mm:ss")
|
||||
.["map"] = SSmapping.config.map_name
|
||||
return json_encode(.)
|
||||
|
||||
/datum/world_topic/jsonplayers
|
||||
keyword = "jsonplayers"
|
||||
|
||||
/datum/world_topic/jsonplayers/Run(list/input, addr)
|
||||
. = list()
|
||||
for(var/client/C in GLOB.clients)
|
||||
if(C.holder?.fakekey)
|
||||
. += C.holder.fakekey
|
||||
continue
|
||||
. += C.key
|
||||
return json_encode(.)
|
||||
|
||||
/datum/world_topic/jsonmanifest
|
||||
keyword = "jsonmanifest"
|
||||
|
||||
/datum/world_topic/jsonmanifest/Run(list/input, addr)
|
||||
var/list/command = list()
|
||||
var/list/security = list()
|
||||
var/list/engineering = list()
|
||||
var/list/medical = list()
|
||||
var/list/science = list()
|
||||
var/list/cargo = list()
|
||||
var/list/civilian = list()
|
||||
var/list/misc = list()
|
||||
for(var/datum/data/record/R in GLOB.data_core.general)
|
||||
var/name = R.fields["name"]
|
||||
var/rank = R.fields["rank"]
|
||||
var/real_rank = rank // make_list_rank(R.fields["real_rank"])
|
||||
if(real_rank in GLOB.security_positions)
|
||||
security[name] = rank
|
||||
else if(real_rank in GLOB.engineering_positions)
|
||||
engineering[name] = rank
|
||||
else if(real_rank in GLOB.medical_positions)
|
||||
medical[name] = rank
|
||||
else if(real_rank in GLOB.science_positions)
|
||||
science[name] = rank
|
||||
else if(real_rank in GLOB.supply_positions)
|
||||
cargo[name] = rank
|
||||
else if(real_rank in GLOB.civilian_positions)
|
||||
civilian[name] = rank
|
||||
else
|
||||
misc[name] = rank
|
||||
// mixed departments, /datum/department when
|
||||
if(real_rank in GLOB.command_positions)
|
||||
command[name] = rank
|
||||
|
||||
. = list()
|
||||
.["Command"] = command
|
||||
.["Security"] = security
|
||||
.["Engineering"] = engineering
|
||||
.["Medical"] = medical
|
||||
.["Science"] = science
|
||||
.["Cargo"] = cargo
|
||||
.["Civilian"] = civilian
|
||||
.["Misc"] = misc
|
||||
return json_encode(.)
|
||||
|
||||
/datum/world_topic/jsonrevision
|
||||
keyword = "jsonrevision"
|
||||
|
||||
/datum/world_topic/jsonrevision/Run(list/input, addr)
|
||||
var/datum/getrev/revdata = GLOB.revdata
|
||||
var/list/data = list(
|
||||
"date" = copytext(revdata.date, 1, 11),
|
||||
"dd_version" = world.byond_version,
|
||||
"dd_build" = world.byond_build,
|
||||
"dm_version" = DM_VERSION,
|
||||
"dm_build" = DM_BUILD,
|
||||
"revision" = revdata.commit,
|
||||
"round_id" = "[GLOB.round_id]",
|
||||
"testmerge_base_url" = "[CONFIG_GET(string/githuburl)]/pull/"
|
||||
)
|
||||
if (revdata.testmerge.len)
|
||||
for (var/datum/tgs_revision_information/test_merge/TM in revdata.testmerge)
|
||||
data["testmerges"] += list(list(
|
||||
"id" = TM.number,
|
||||
"desc" = TM.title,
|
||||
"author" = TM.author
|
||||
))
|
||||
|
||||
return json_encode(data)
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
initial_flow = 1.5
|
||||
gauzed_clot_rate = 0.8
|
||||
internal_bleeding_chance = 30
|
||||
internal_bleeding_coefficient = 1.25
|
||||
internal_bleeding_coefficient = 1
|
||||
threshold_minimum = 40
|
||||
threshold_penalty = 15
|
||||
status_effect_type = /datum/status_effect/wound/pierce/moderate
|
||||
@@ -142,10 +142,10 @@
|
||||
occur_text = "looses a violent spray of blood, revealing a pierced wound"
|
||||
sound_effect = 'sound/effects/wounds/pierce2.ogg'
|
||||
severity = WOUND_SEVERITY_SEVERE
|
||||
initial_flow = 2.25
|
||||
initial_flow = 2
|
||||
gauzed_clot_rate = 0.6
|
||||
internal_bleeding_chance = 60
|
||||
internal_bleeding_coefficient = 1.5
|
||||
internal_bleeding_coefficient = 1.25
|
||||
threshold_minimum = 60
|
||||
threshold_penalty = 25
|
||||
status_effect_type = /datum/status_effect/wound/pierce/severe
|
||||
@@ -159,10 +159,10 @@
|
||||
occur_text = "blasts apart, sending chunks of viscera flying in all directions"
|
||||
sound_effect = 'sound/effects/wounds/pierce3.ogg'
|
||||
severity = WOUND_SEVERITY_CRITICAL
|
||||
initial_flow = 3
|
||||
initial_flow = 2.7
|
||||
gauzed_clot_rate = 0.4
|
||||
internal_bleeding_chance = 80
|
||||
internal_bleeding_coefficient = 1.75
|
||||
internal_bleeding_coefficient = 1.5
|
||||
threshold_minimum = 110
|
||||
threshold_penalty = 40
|
||||
status_effect_type = /datum/status_effect/wound/pierce/critical
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
occur_text = "is cut open, slowly leaking blood"
|
||||
sound_effect = 'sound/effects/wounds/blood1.ogg'
|
||||
severity = WOUND_SEVERITY_MODERATE
|
||||
initial_flow = 1.5
|
||||
initial_flow = 1.25
|
||||
minimum_flow = 0.375
|
||||
max_per_type = 3
|
||||
clot_rate = 0.12
|
||||
@@ -270,8 +270,8 @@
|
||||
occur_text = "is ripped open, veins spurting blood"
|
||||
sound_effect = 'sound/effects/wounds/blood2.ogg'
|
||||
severity = WOUND_SEVERITY_SEVERE
|
||||
initial_flow = 2.4375
|
||||
minimum_flow = 2.0625
|
||||
initial_flow = 2
|
||||
minimum_flow = 1.75
|
||||
clot_rate = 0.07
|
||||
max_per_type = 4
|
||||
threshold_minimum = 60
|
||||
@@ -288,8 +288,8 @@
|
||||
occur_text = "is torn open, spraying blood wildly"
|
||||
sound_effect = 'sound/effects/wounds/blood3.ogg'
|
||||
severity = WOUND_SEVERITY_CRITICAL
|
||||
initial_flow = 3.1875
|
||||
minimum_flow = 3
|
||||
initial_flow = 2.75
|
||||
minimum_flow = 2.5
|
||||
clot_rate = -0.05 // critical cuts actively get worse instead of better
|
||||
max_per_type = 5
|
||||
threshold_minimum = 90
|
||||
|
||||
Reference in New Issue
Block a user