[MIRROR] Harddel Fix Pack #42 + Better Live Reftracking Support [MDB IGNORE] (#10639)

* Harddel Fix Pack #42 + Better Live Reftracking Support

* awooga

Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com>
This commit is contained in:
SkyratBot
2022-01-12 22:57:49 +01:00
committed by GitHub
parent b0de5b283f
commit 65b8082678
110 changed files with 895 additions and 711 deletions

View File

@@ -1,4 +1,4 @@
//#define LOWMEMORYMODE //uncomment this to load centcom and runtime station and thats it.
#define LOWMEMORYMODE //uncomment this to load centcom and runtime station and thats it.
#include "map_files\generic\CentCom_skyrat.dmm" //SKYRAT EDIT ADDITION - SMMS

View File

@@ -7,6 +7,8 @@
#define isatom(A) (isloc(A))
#define isdatum(thing) (istype(thing, /datum))
#define isweakref(D) (istype(D, /datum/weakref))
//Turfs

View File

@@ -57,9 +57,16 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
SEND_TEXT(world.log, text)
#endif
#ifdef REFERENCE_TRACKING
#if defined(REFERENCE_DOING_IT_LIVE)
#define log_reftracker(msg) log_harddel("## REF SEARCH [msg]")
/proc/log_harddel(text)
WRITE_LOG(GLOB.harddel_log, text)
#elif defined(REFERENCE_TRACKING) // Doing it locally
#define log_reftracker(msg) log_world("## REF SEARCH [msg]")
#else
#else //Not tracking at all
#define log_reftracker(msg)
#endif

View File

@@ -16,7 +16,7 @@
* Adds a memory to a mob's mind if conditions are met, called wherever the memory takes place (memory for catching on fire in mob's fire code, for example)
* Argument:
* * memory_type: defined string in memory_defines.dm, shows the memories.json file which story parts to use (and generally what type it is)
* * extra_info: the contents of the story. You're gonna want at least the protagonist for who is the main character in the story
* * extra_info: the contents of the story. You're gonna want at least the protagonist for who is the main character in the story (Any non basic type will be converted to a string on insertion)
* * story_value: the quality of the memory, make easy or roundstart memories have a low value so they don't flood persistence
* * memory_flags: special specifications for skipping parts of the memory like moods for stories where showing moods doesn't make sense
* Returns the datum memory created, null otherwise.

View File

@@ -39,6 +39,16 @@
#define TRACK_MAX_SHARE //Allows max share tracking, for use in the atmos debugging ui
#endif //ifdef TESTING
/// If this is uncommented, we set up the ref tracker to be used in a live environment
/// And to log events to [log_dir]/harddels.log
//#define REFERENCE_DOING_IT_LIVE
#ifdef REFERENCE_DOING_IT_LIVE
// compile the backend
#define REFERENCE_TRACKING
// actually look for refs
#define GC_FAILURE_HARD_LOOKUP
#endif // REFERENCE_DOING_IT_LIVE
//#define UNIT_TESTS //If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between
#ifndef PRELOAD_RSC //set to:

View File

@@ -89,3 +89,7 @@ GLOBAL_PROTECT(picture_logging_id)
GLOBAL_VAR(picture_logging_prefix)
GLOBAL_PROTECT(picture_logging_prefix)
/////
#ifdef REFERENCE_DOING_IT_LIVE
GLOBAL_LIST_EMPTY(harddel_log)
GLOBAL_PROTECT(harddel_log)
#endif

View File

@@ -144,6 +144,14 @@
icon_state = "flash"
alpha = 80
/atom/movable/screen/fullscreen/bluespace_sparkle
icon = 'icons/effects/effects.dmi'
screen_loc = "WEST,SOUTH to EAST,NORTH"
icon_state = "shieldsparkles"
layer = FLASH_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
show_when_dead = TRUE
/atom/movable/screen/fullscreen/color_vision/green
color = "#00ff00"

View File

@@ -632,6 +632,9 @@ GLOBAL_LIST_EMPTY(colored_images)
/datum/controller/subsystem/air/proc/start_processing_machine(obj/machinery/machine)
if(machine.atmos_processing)
return
if(QDELETED(machine))
stack_trace("We tried to add a garbage collecting machine to SSair. Don't")
return
machine.atmos_processing = TRUE
atmos_machinery += machine

View File

@@ -372,7 +372,7 @@ SUBSYSTEM_DEF(garbage)
#ifdef REFERENCE_TRACKING
if (QDEL_HINT_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled, display all references to this object, then queue the object for deletion.
SSgarbage.Queue(D)
D.find_references()
D.find_references() //This breaks ci. Consider it insurance against somehow pring reftracking on accident
if (QDEL_HINT_IFFAIL_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object.
SSgarbage.Queue(D)
SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE

View File

@@ -40,7 +40,8 @@ SUBSYSTEM_DEF(parallax)
if(!istype(movable_eye))
continue
for (movable_eye; isloc(movable_eye.loc) && !isturf(movable_eye.loc); movable_eye = movable_eye.loc);
while(isloc(movable_eye.loc) && !isturf(movable_eye.loc))
movable_eye = movable_eye.loc
//get the last movable holding the mobs eye
if(movable_eye == processing_client.movingmob)

View File

@@ -33,7 +33,8 @@
/datum/action/proc/link_to(Target)
target = Target
RegisterSignal(Target, COMSIG_ATOM_UPDATED_ICON, .proc/OnUpdatedIcon)
RegisterSignal(target, COMSIG_ATOM_UPDATED_ICON, .proc/OnUpdatedIcon)
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/clear_ref, override = TRUE)
/datum/action/Destroy()
if(owner)
@@ -49,7 +50,7 @@
return
Remove(owner)
owner = M
RegisterSignal(owner, COMSIG_PARENT_QDELETING, .proc/owner_deleted)
RegisterSignal(owner, COMSIG_PARENT_QDELETING, .proc/clear_ref, override = TRUE)
//button id generation
var/counter = 0
@@ -75,9 +76,12 @@
else
Remove(owner)
/datum/action/proc/owner_deleted(datum/source)
/datum/action/proc/clear_ref(datum/ref)
SIGNAL_HANDLER
if(ref == owner)
Remove(owner)
if(ref == target)
qdel(src)
/datum/action/proc/Remove(mob/M)
for(var/datum/weakref/reference as anything in sharers)
@@ -93,7 +97,10 @@
M.update_action_buttons()
if(owner)
UnregisterSignal(owner, COMSIG_PARENT_QDELETING)
if(target == owner)
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/clear_ref)
owner = null
if(button)
button.moved = FALSE //so the button appears in its normal position when given to another owner.
button.locked = FALSE
button.id = null

View File

@@ -22,16 +22,16 @@
///Max attemps to make
var/max_attempts = 3
/datum/ai_behavior/item_move_close_and_attack/setup(datum/ai_controller/controller, target_key, throw_count_key)
. = ..()
controller.current_movement_target = controller.blackboard[target_key]
var/datum/weakref/target_ref = controller.blackboard[target_key]
controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/item_move_close_and_attack/perform(delta_time, datum/ai_controller/controller, target_key, throw_count_key)
. = ..()
var/obj/item/item_pawn = controller.pawn
var/atom/throw_target = controller.blackboard[target_key]
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/atom/throw_target = target_ref?.resolve()
item_pawn.visible_message(span_warning("[item_pawn] hurls towards [throw_target]!"))
item_pawn.throw_at(throw_target, rand(4,5), 9)
@@ -48,12 +48,15 @@
controller.blackboard -= target_key
controller.blackboard[throw_count_key] = 0
/datum/ai_behavior/item_move_close_and_attack/haunted
/datum/ai_behavior/item_move_close_and_attack/ghostly
attack_sound = 'sound/items/haunted/ghostitemattack.ogg'
max_attempts = 4
/datum/ai_behavior/item_move_close_and_attack/haunted/finish_action(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
var/atom/throw_target = controller.blackboard[target_key]
/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted
/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted/finish_action(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/atom/throw_target = target_ref?.resolve()
var/list/hauntee_list = controller.blackboard[BB_TO_HAUNT_LIST]
hauntee_list[throw_target]--
return ..()

View File

@@ -1,10 +1,8 @@
/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed
/datum/ai_behavior/item_move_close_and_attack/cursed
attack_sound = 'sound/items/haunted/ghostitemattack.ogg'
max_attempts = 4
/datum/ai_behavior/item_move_close_and_attack/cursed/reset_blackboard(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
var/atom/throw_target = controller.blackboard[target_key]
/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed/reset_blackboard(datum/ai_controller/controller, succeeded, target_key, throw_count_key)
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/atom/throw_target = target_ref?.resolve()
//dropping our target from the blackboard if they are no longer a valid target after the attack behavior
if(get_dist(throw_target, controller.pawn) > CURSED_VIEW_RANGE)
controller.blackboard[target_key] = null

View File

@@ -2,7 +2,8 @@
var/obj/item/item_pawn = controller.pawn
//make sure we have a target
var/mob/living/carbon/curse_target = controller.blackboard[BB_CURSE_TARGET]
var/datum/weakref/target_ref = controller.blackboard[BB_CURSE_TARGET]
var/mob/living/carbon/curse_target = target_ref?.resolve()
if(!curse_target)
controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_CURSE_TARGET, /mob/living/carbon, CURSED_VIEW_RANGE)
return
@@ -11,4 +12,4 @@
controller.blackboard[BB_CURSE_TARGET] = null
return
controller.current_movement_target = curse_target
controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/cursed)
controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly/cursed)

View File

@@ -79,20 +79,22 @@
pawn.activate_hand(pawn.get_active_hand())
finish_action(controller, TRUE)
/// Use the currently held item, or unarmed, on an object in the world
/// Use the currently held item, or unarmed, on a weakref to an object in the world
/datum/ai_behavior/use_on_object
required_distance = 1
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
/datum/ai_behavior/use_on_object/setup(datum/ai_controller/controller, target_key)
. = ..()
controller.current_movement_target = controller.blackboard[target_key]
var/datum/weakref/target_ref = controller.blackboard[target_key]
controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/use_on_object/perform(delta_time, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/pawn = controller.pawn
var/obj/item/held_item = pawn.get_item_by_slot(pawn.get_active_hand())
var/atom/target = controller.blackboard[BB_MONKEY_CURRENT_PRESS_TARGET]
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/atom/target = target_ref?.resolve()
if(!target || !pawn.CanReach(target))
finish_action(controller, FALSE)
@@ -113,13 +115,15 @@
/datum/ai_behavior/give/setup(datum/ai_controller/controller, target_key)
. = ..()
controller.current_movement_target = controller.blackboard[target_key]
var/datum/weakref/target_ref = controller.blackboard[target_key]
controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/give/perform(delta_time, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/pawn = controller.pawn
var/obj/item/held_item = pawn.get_item_by_slot(pawn.get_active_hand())
var/atom/target = controller.blackboard[target_key]
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/atom/target = target_ref?.resolve()
if(!target || !pawn.CanReach(target) || !isliving(target))
finish_action(controller, FALSE)
@@ -151,12 +155,14 @@
/datum/ai_behavior/consume/setup(datum/ai_controller/controller, target_key)
. = ..()
controller.current_movement_target = controller.blackboard[target_key]
var/datum/weakref/target_ref = controller.blackboard[target_key]
controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/consume/perform(delta_time, datum/ai_controller/controller, target_key, hunger_timer_key)
. = ..()
var/mob/living/living_pawn = controller.pawn
var/obj/item/target = controller.blackboard[target_key]
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/obj/item/target = target_ref.resolve()
if(!(target in living_pawn.held_items))
if(!living_pawn.put_in_hand_check(target))
@@ -187,7 +193,7 @@
. = ..()
var/find_this_thing = search_tactic(controller, locate_path, search_range)
if(find_this_thing)
controller.blackboard[set_key] = find_this_thing
controller.blackboard[set_key] = WEAKREF(find_this_thing)
finish_action(controller, TRUE)
else
finish_action(controller, FALSE)
@@ -341,7 +347,8 @@
/datum/ai_behavior/setup_instrument/perform(delta_time, datum/ai_controller/controller, song_instrument_key, song_lines_key)
. = ..()
var/obj/item/instrument/song_instrument = controller.blackboard[song_instrument_key]
var/datum/weakref/instrument_ref = controller.blackboard[song_instrument_key]
var/obj/item/instrument/song_instrument = instrument_ref.resolve()
var/datum/song/song = song_instrument.song
var/song_lines = controller.blackboard[song_lines_key]
@@ -357,7 +364,8 @@
/datum/ai_behavior/play_instrument/perform(delta_time, datum/ai_controller/controller, song_instrument_key)
. = ..()
var/obj/item/instrument/song_instrument = controller.blackboard[song_instrument_key]
var/datum/weakref/instrument_ref = controller.blackboard[song_instrument_key]
var/obj/item/instrument/song_instrument = instrument_ref.resolve()
var/datum/song/song = song_instrument.song
song.start_playing(controller.pawn)
@@ -377,5 +385,5 @@
possible_targets += thing
if(!possible_targets.len)
finish_action(controller, FALSE)
controller.blackboard[target_key] = pick(possible_targets)
controller.blackboard[target_key] = WEAKREF(pick(possible_targets))
finish_action(controller, TRUE)

View File

@@ -8,12 +8,13 @@
* * BB_SONG_LINES - not set by this subtree, is the song loaded into the song datum.
*/
/datum/ai_planning_subtree/generic_play_instrument/SelectBehaviors(datum/ai_controller/controller, delta_time)
if(!controller.blackboard[BB_SONG_INSTRUMENT])
var/datum/weakref/player_ref = controller.blackboard[BB_SONG_INSTRUMENT]
var/obj/item/instrument/song_player = player_ref?.resolve()
if(!song_player)
controller.queue_behavior(/datum/ai_behavior/find_and_set/in_hands, BB_SONG_INSTRUMENT, /obj/item/instrument)
return //we can't play a song since we do not have an instrument
var/obj/item/instrument/song_player = controller.blackboard[BB_SONG_INSTRUMENT]
var/list/parsed_song_lines = splittext(controller.blackboard[BB_SONG_LINES], "\n")
popleft(parsed_song_lines) //remove BPM as it is parsed out
if(!compare_list(song_player.song.lines, parsed_song_lines) || !song_player.song.repeat)
@@ -53,7 +54,8 @@
if(world.time < controller.blackboard[BB_NEXT_HUNGRY])
return
if(!controller.blackboard[BB_FOOD_TARGET])
var/datum/weakref/food_ref = controller.blackboard[BB_FOOD_TARGET]
if(!food_ref?.resolve())
controller.queue_behavior(/datum/ai_behavior/find_and_set/edible, BB_FOOD_TARGET, /obj/item, 2)
return

View File

@@ -13,11 +13,11 @@
var/list/to_haunt_list = controller.blackboard[BB_TO_HAUNT_LIST]
for(var/i in to_haunt_list)
if(to_haunt_list[i] <= 0)
for(var/mob/living/potential_target as anything in to_haunt_list)
if(to_haunt_list[potential_target] <= 0)
to_haunt_list -= potential_target
continue
var/mob/living/potential_target = i
if(get_dist(potential_target, item_pawn) <= 7)
controller.blackboard[BB_HAUNT_TARGET] = potential_target
controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/haunted, BB_HAUNT_TARGET, BB_HAUNTED_THROW_ATTEMPT_COUNT)
controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly/haunted, BB_HAUNT_TARGET, BB_HAUNTED_THROW_ATTEMPT_COUNT)
return SUBTREE_RETURN_FINISH_PLANNING

View File

@@ -127,7 +127,7 @@
// flee from anyone who attacked us and we didn't beat down
for(var/mob/living/L in view(living_pawn, MONKEY_FLEE_VISION))
if(controller.blackboard[BB_MONKEY_ENEMIES][L] && L.stat == CONSCIOUS)
if(controller.blackboard[BB_MONKEY_ENEMIES][WEAKREF(L)] && L.stat == CONSCIOUS)
target = L
break
@@ -141,12 +141,14 @@
/datum/ai_behavior/monkey_attack_mob/setup(datum/ai_controller/controller, target_key)
. = ..()
controller.current_movement_target = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
var/datum/weakref/target_ref = controller.blackboard[target_key]
controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/monkey_attack_mob/perform(delta_time, datum/ai_controller/controller, target_key)
. = ..()
var/mob/living/target = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
var/datum/weakref/target_ref = controller.blackboard[target_key]
var/mob/living/target = target_ref?.resolve()
var/mob/living/living_pawn = controller.pawn
if(!target || target.stat != CONSCIOUS)
@@ -167,11 +169,11 @@
monkey_attack(controller, target, delta_time, FALSE)
/datum/ai_behavior/monkey_attack_mob/finish_action(datum/ai_controller/controller, succeeded)
/datum/ai_behavior/monkey_attack_mob/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
var/mob/living/living_pawn = controller.pawn
walk(living_pawn, 0)
controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] = null
controller.blackboard[target_key] = null
/// attack using a held weapon otherwise bite the enemy, then if we are angry there is a chance we might calm down a little
/datum/ai_behavior/monkey_attack_mob/proc/monkey_attack(datum/ai_controller/controller, mob/living/target, delta_time, disarm)
@@ -219,14 +221,16 @@
if(controller.blackboard[BB_MONKEY_AGGRESSIVE])
return
/// mob refs are uids, so this is safe
var/datum/weakref/target_ref = WEAKREF(target)
if(DT_PROB(MONKEY_HATRED_REDUCTION_PROB, delta_time))
controller.blackboard[BB_MONKEY_ENEMIES][target]--
controller.blackboard[BB_MONKEY_ENEMIES][target_ref]--
// if we are not angry at our target, go back to idle
if(controller.blackboard[BB_MONKEY_ENEMIES][target] <= 0)
if(controller.blackboard[BB_MONKEY_ENEMIES][target_ref] <= 0)
var/list/enemies = controller.blackboard[BB_MONKEY_ENEMIES]
enemies.Remove(target)
if(controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] == target)
enemies.Remove(target_ref)
if(controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET] == WEAKREF(target))
finish_action(controller, TRUE)
/datum/ai_behavior/disposal_mob
@@ -234,8 +238,8 @@
/datum/ai_behavior/disposal_mob/setup(datum/ai_controller/controller, attack_target_key, disposal_target_key)
. = ..()
controller.current_movement_target = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
var/datum/weakref/target_ref = controller.blackboard[attack_target_key]
controller.current_movement_target = target_ref?.resolve()
/datum/ai_behavior/disposal_mob/finish_action(datum/ai_controller/controller, succeeded, attack_target_key, disposal_target_key)
. = ..()
@@ -249,7 +253,8 @@
if(controller.blackboard[BB_MONKEY_DISPOSING]) //We are disposing, don't do ANYTHING!!!!
return
var/mob/living/target = controller.blackboard[attack_target_key]
var/datum/weakref/target_ref = controller.blackboard[attack_target_key]
var/mob/living/target = target_ref?.resolve()
var/mob/living/living_pawn = controller.pawn
controller.current_movement_target = target
@@ -259,7 +264,8 @@
target.grabbedby(living_pawn)
return //Do the rest next turn
var/obj/machinery/disposal/disposal = controller.blackboard[disposal_target_key]
var/datum/weakref/disposal_ref = controller.blackboard[disposal_target_key]
var/obj/machinery/disposal/disposal = disposal_ref.resolve()
controller.current_movement_target = disposal
if(living_pawn.Adjacent(disposal))
@@ -269,7 +275,8 @@
/datum/ai_behavior/disposal_mob/proc/try_disposal_mob(datum/ai_controller/controller, attack_target_key, disposal_target_key)
var/mob/living/living_pawn = controller.pawn
var/mob/living/target = controller.blackboard[attack_target_key]
var/datum/weakref/target_ref = controller.blackboard[attack_target_key]
var/mob/living/target = target_ref?.resolve()
var/obj/machinery/disposal/disposal = controller.blackboard[disposal_target_key]
controller.blackboard[BB_MONKEY_DISPOSING] = TRUE
@@ -292,9 +299,9 @@
if(!DT_PROB(MONKEY_RECRUIT_PROB, delta_time))
continue
var/datum/ai_controller/monkey/monkey_ai = L.ai_controller
var/atom/your_enemy = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
var/datum/weakref/enemy_ref = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
var/list/enemies = L.ai_controller.blackboard[BB_MONKEY_ENEMIES]
enemies[your_enemy] = MONKEY_RECRUIT_HATED_AMOUNT
enemies[enemy_ref] = MONKEY_RECRUIT_HATED_AMOUNT
monkey_ai.blackboard[BB_MONKEY_RECRUIT_COOLDOWN] = world.time + MONKEY_RECRUIT_COOLDOWN
finish_action(controller, TRUE)
@@ -302,10 +309,11 @@
var/list/enemies = controller.blackboard[enemies_key]
var/list/valids = list()
for(var/mob/living/possible_enemy in view(MONKEY_ENEMY_VISION, controller.pawn))
if(possible_enemy == controller.pawn || (!enemies[possible_enemy] && (!controller.blackboard[BB_MONKEY_AGGRESSIVE] || HAS_AI_CONTROLLER_TYPE(possible_enemy, /datum/ai_controller/monkey)))) //Are they an enemy? (And do we even care?)
var/datum/weakref/enemy_ref = WEAKREF(possible_enemy)
if(possible_enemy == controller.pawn || (!enemies[enemy_ref] && (!controller.blackboard[BB_MONKEY_AGGRESSIVE] || HAS_AI_CONTROLLER_TYPE(possible_enemy, /datum/ai_controller/monkey)))) //Are they an enemy? (And do we even care?)
continue
// Weighted list, so the closer they are the more likely they are to be chosen as the enemy
valids[possible_enemy] = CEILING(100 / (get_dist(controller.pawn, possible_enemy) || 1), 1)
valids[enemy_ref] = CEILING(100 / (get_dist(controller.pawn, possible_enemy) || 1), 1)
if(!valids.len)
finish_action(controller, FALSE)

View File

@@ -125,7 +125,7 @@ have ways of interacting with a specific mob and control it.
///Reactive events to being hit
/datum/ai_controller/monkey/proc/retaliate(mob/living/L)
var/list/enemies = blackboard[BB_MONKEY_ENEMIES]
enemies[L] += MONKEY_HATRED_AMOUNT
enemies[WEAKREF(L)] += MONKEY_HATRED_AMOUNT
/datum/ai_controller/monkey/proc/on_attackby(datum/source, obj/item/I, mob/user)
SIGNAL_HANDLER

View File

@@ -36,7 +36,8 @@
controller.queue_behavior(/datum/ai_behavior/monkey_set_combat_target, BB_MONKEY_CURRENT_ATTACK_TARGET, BB_MONKEY_ENEMIES)
return SUBTREE_RETURN_FINISH_PLANNING
var/mob/living/selected_enemy = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
var/datum/weakref/target_ref = controller.blackboard[BB_MONKEY_CURRENT_ATTACK_TARGET]
var/mob/living/selected_enemy = target_ref?.resolve()
if(!selected_enemy.stat) //He's up, get him!
if(living_pawn.health < MONKEY_FLEE_HEALTH) //Time to skeddadle

View File

@@ -2,13 +2,14 @@
var/obj/item/item_pawn = controller.pawn
//make sure we have a target
var/mob/living/carbon/curse_target = controller.blackboard[BB_ITEM_TARGET]
var/datum/weakref/target_ref = controller.blackboard[BB_ITEM_TARGET]
var/mob/living/carbon/curse_target = target_ref?.resolve()
if(curse_target && get_dist(curse_target, item_pawn) > ITEM_AGGRO_VIEW_RANGE)
controller.blackboard[BB_ITEM_TARGET] = null
return
if(!controller.blackboard[BB_ITEM_TARGET])
if(!curse_target)
controller.queue_behavior(/datum/ai_behavior/find_and_set/item_target, BB_ITEM_TARGET, /mob/living/carbon, ITEM_AGGRO_VIEW_RANGE)
controller.queue_behavior(/datum/ai_behavior/item_move_close_and_attack/ghostly, BB_ITEM_TARGET, BB_ITEM_THROW_ATTEMPT_COUNT)

View File

@@ -11,45 +11,3 @@
item_pawn.visible_message(span_warning("[item_pawn] slips out of the hands of [item_holder]!"))
item_holder.dropItemToGround(item_pawn, TRUE)
finish_action(controller, TRUE)
///This behavior is for obj/items, it is used to move closer to a target and throw themselves towards them.
/datum/ai_behavior/item_move_close_and_attack
required_distance = 3
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
action_cooldown = 20
///Sound to use
var/attack_sound
///Max attempts to make
var/max_attempts = 3
/datum/ai_behavior/item_move_close_and_attack/setup(datum/ai_controller/controller, aggro_list_key, target_key, throw_count_key)
. = ..()
controller.current_movement_target = controller.blackboard[target_key]
/datum/ai_behavior/item_move_close_and_attack/perform(delta_time, datum/ai_controller/controller, aggro_list_key, target_key, throw_count_key)
. = ..()
var/obj/item/item_pawn = controller.pawn
var/atom/throw_target = controller.blackboard[target_key]
item_pawn.visible_message(span_warning("[item_pawn] hurls towards [throw_target]!"))
item_pawn.throw_at(throw_target, rand(4,5), 9)
playsound(item_pawn.loc, attack_sound, 100, TRUE)
controller.blackboard[throw_count_key]++
if(controller.blackboard[throw_count_key] >= max_attempts)
finish_action(controller, TRUE, target_key, throw_count_key)
/datum/ai_behavior/item_move_close_and_attack/finish_action(datum/ai_controller/controller, succeeded, aggro_list_key, target_key, throw_count_key)
. = ..()
var/atom/throw_target = controller.blackboard[target_key]
controller.blackboard -= target_key
controller.blackboard[throw_count_key] = 0
if(aggro_list_key)
var/list/aggro_list = controller.blackboard[aggro_list_key]
aggro_list[throw_target]--
/datum/ai_behavior/item_move_close_and_attack/ghostly
attack_sound = 'sound/items/haunted/ghostitemattack.ogg'
max_attempts = 4

View File

@@ -26,7 +26,7 @@
if(found_seat)
customer_pawn.say(pick(customer_data.found_seat_lines))
controller.blackboard[BB_CUSTOMER_MY_SEAT] = found_seat
controller.blackboard[BB_CUSTOMER_MY_SEAT] = WEAKREF(found_seat)
attending_venue.linked_seats[found_seat] = customer_pawn
finish_action(controller, TRUE)
return
@@ -47,7 +47,9 @@
var/mob/living/simple_animal/robot_customer/customer_pawn = controller.pawn
var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO]
if(get_turf(controller.blackboard[BB_CUSTOMER_MY_SEAT]) == get_turf(customer_pawn))
var/datum/weakref/seat_ref = controller.blackboard[BB_CUSTOMER_MY_SEAT]
var/obj/structure/holosign/robot_seat/seat_marker = seat_ref?.resolve()
if(get_turf(seat_marker) == get_turf(customer_pawn))
var/obj/structure/chair/my_seat = locate(/obj/structure/chair) in get_turf(customer_pawn)
if(my_seat)
controller.pawn.setDir(my_seat.dir) //Sit in your seat
@@ -79,7 +81,9 @@
var/datum/customer_data/customer_data = controller.blackboard[BB_CUSTOMER_CUSTOMERINFO]
customer_pawn.say(pick(customer_data.wait_for_food_lines))
if(get_turf(controller.blackboard[BB_CUSTOMER_MY_SEAT]) == get_turf(controller.pawn))
var/datum/weakref/seat_ref = controller.blackboard[BB_CUSTOMER_MY_SEAT]
var/obj/structure/holosign/robot_seat/seat_marker = seat_ref?.resolve()
if(get_turf(seat_marker) == get_turf(controller.pawn))
var/obj/structure/chair/my_seat = locate(/obj/structure/chair) in get_turf(controller.pawn)
if(my_seat)
controller.pawn.setDir(my_seat.dir) //Sit in your seat

View File

@@ -67,9 +67,10 @@
var/mob/living/simple_animal/robot_customer/customer = pawn
var/datum/venue/attending_venue = blackboard[BB_CUSTOMER_ATTENDING_VENUE]
var/datum/customer_data/customer_data = blackboard[BB_CUSTOMER_CUSTOMERINFO]
attending_venue.mob_blacklist[greytider] += 1
//Living mobs are tagged, so these will always be valid
attending_venue.mob_blacklist[REF(greytider)] += 1
switch(attending_venue.mob_blacklist[greytider])
switch(attending_venue.mob_blacklist[REF(greytider)])
if(1)
customer.say(customer_data.first_warning_line)
return

View File

@@ -8,13 +8,14 @@
controller.queue_behavior(/datum/ai_behavior/break_spine, BB_CUSTOMER_CURRENT_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
var/obj/my_seat = controller.blackboard[BB_CUSTOMER_MY_SEAT]
var/datum/weakref/seat_ref = controller.blackboard[BB_CUSTOMER_MY_SEAT]
var/obj/structure/holosign/robot_seat/seat_marker = seat_ref?.resolve()
if(!my_seat) //We havn't got a seat yet! find one!
if(!seat_marker) //We havn't got a seat yet! find one!
controller.queue_behavior(/datum/ai_behavior/find_seat)
return SUBTREE_RETURN_FINISH_PLANNING
controller.current_movement_target = my_seat
controller.current_movement_target = seat_marker
if(!controller.blackboard[BB_CUSTOMER_CURRENT_ORDER]) //We havn't ordered yet even ordered yet. go on! go over there and go do it!
controller.queue_behavior(/datum/ai_behavior/order_food)

View File

@@ -85,8 +85,11 @@
/datum/component/aura_healing/Destroy(force, silent)
STOP_PROCESSING(SSaura_healing, src)
var/alert_category = "aura_healing_[REF(src)]"
QDEL_LIST_ASSOC_VAL(current_alerts)
for(var/mob/living/alert_holder in current_alerts)
alert_holder.clear_alert(alert_category)
current_alerts.Cut()
return ..()
@@ -108,7 +111,7 @@
if (!(candidate in current_alerts))
var/atom/movable/screen/alert/aura_healing/alert = candidate.throw_alert(alert_category, /atom/movable/screen/alert/aura_healing, new_master = parent)
alert.desc = "You are being healed by [parent]."
current_alerts[candidate] = alert
current_alerts += candidate
if (should_show_effect && candidate.health < candidate.maxHealth)
new /obj/effect/temp_visual/heal(get_turf(candidate), healing_color)

View File

@@ -38,4 +38,4 @@
if(ishuman(L))
var/mob/living/carbon/human/H = L
H.physiology.damage_resistance += 100
..()
return ..()

View File

@@ -28,8 +28,8 @@
var/skill_mod
///Some gloves, generally ones that increase mobility, may have a minimum distance to fly. Rocket gloves are especially dangerous with this, be sure you'll hit your target or have a clear background if you miss, or else!
var/min_distance
///The throwdatum we're currently dealing with, if we need it
var/datum/thrownthing/tackle
///A wearkef to the throwdatum we're currently dealing with, if we need it
var/datum/weakref/tackle_ref
/datum/component/tackler/Initialize(stamina_cost = 25, base_knockdown = 1 SECONDS, range = 4, speed = 1, skill_mod = 0, min_distance = min_distance)
if(!iscarbon(parent))
@@ -61,10 +61,10 @@
UnregisterSignal(parent, list(COMSIG_MOB_CLICKON, COMSIG_MOVABLE_IMPACT, COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_POST_THROW))
///Store the thrownthing datum for later use
/datum/component/tackler/proc/registerTackle(mob/living/carbon/user, datum/thrownthing/TT)
/datum/component/tackler/proc/registerTackle(mob/living/carbon/user, datum/thrownthing/tackle)
SIGNAL_HANDLER
tackle = TT
tackle_ref = WEAKREF(tackle)
tackle.thrower = user
///See if we can tackle or not. If we can, leap!
@@ -143,7 +143,9 @@
/datum/component/tackler/proc/sack(mob/living/carbon/user, atom/hit)
SIGNAL_HANDLER
var/datum/thrownthing/tackle = tackle_ref?.resolve()
if(!tackling || !tackle)
tackle = null
return
user.toggle_throw_mode()
@@ -445,7 +447,7 @@
/datum/component/tackler/proc/resetTackle()
tackling = FALSE
QDEL_NULL(tackle)
QDEL_NULL(tackle_ref)
UnregisterSignal(parent, COMSIG_MOVABLE_MOVED)
///A special case for splatting for handling windows
@@ -526,7 +528,10 @@
I.throw_at(get_ranged_target_turf(I, pick(GLOB.alldirs), range = dist), range = dist, speed = sp)
I.visible_message(span_danger("[I] goes flying[sp > 3 ? " dangerously fast" : ""]!")) // standard embed speed
var/datum/thrownthing/tackle = tackle_ref?.resolve()
playsound(owner, 'sound/weapons/smash.ogg', 70, TRUE)
if(tackle)
tackle.finalize(hit=TRUE)
resetTackle()

View File

@@ -145,7 +145,10 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
new_dna.species = new species.type
new_dna.species.species_traits = species.species_traits
new_dna.real_name = real_name
new_dna.mutations = mutations.Copy()
// Mutations aren't gc managed, but they still aren't templates
// Let's do a proper copy
for(var/datum/mutation/human/mutation in mutations)
new_dna.add_mutation(mutation, mutation.class, mutation.timeout)
//See mutation.dm for what 'class' does. 'time' is time till it removes itself in decimals. 0 for no timer
/datum/dna/proc/add_mutation(mutation, class = MUT_OTHER, time)

View File

@@ -10,7 +10,7 @@
var/datum/mind/memorizer_mind
///the action done to the target, see memory.dm in _DEFINES
var/action
///extra information used in the memories to more accurately describe what happened. Assoc list of key -> string identifying what kind of info it is, value is an atom or string identifying the detail.
///extra information used in the memories to more accurately describe what happened. Assoc list of key -> string identifying what kind of info it is, value is a string identifying the detail.
var/list/extra_info
///mood of the person memorizing the event when it happend. can change the style.
var/memorizer_mood
@@ -24,6 +24,14 @@
src.memorizer_mind = memorizer_mind
src.memorizer = memorizer
src.action = action
//You can feed atoms in, but they're gonna be reduced to text
for(var/key in extra_info)
var/thing = extra_info[key]
if(!isdatum(thing))
continue
var/datum/reduce_to_string = thing
extra_info[key] = "[reduce_to_string]"
src.extra_info = extra_info
src.memorizer_mood = memorizer_mood
src.story_value = story_value

View File

@@ -716,13 +716,13 @@
* and gives them a fallback spell if no uplink was found
*/
/datum/mind/proc/try_give_equipment_fallback()
var/datum/component/uplink/uplink
var/uplink_exists
var/datum/antagonist/traitor/traitor_datum = has_antag_datum(/datum/antagonist/traitor)
if(traitor_datum)
uplink = traitor_datum.uplink
if(!uplink)
uplink = find_syndicate_uplink(check_unlocked = TRUE)
if(!uplink && !(locate(/obj/effect/proc_holder/spell/self/special_equipment_fallback) in spell_list))
uplink_exists = traitor_datum.uplink_ref
if(!uplink_exists)
uplink_exists = find_syndicate_uplink(check_unlocked = TRUE)
if(!uplink_exists && !(locate(/obj/effect/proc_holder/spell/self/special_equipment_fallback) in spell_list))
AddSpell(new /obj/effect/proc_holder/spell/self/special_equipment_fallback(null, src))
/datum/mind/proc/take_uplink()

View File

@@ -23,7 +23,7 @@
var/instability = 0 //instability the holder gets when the mutation is not native
var/blocks = 4 //Amount of those big blocks with gene sequences
var/difficulty = 8 //Amount of missing sequences. Sometimes it removes an entire pair for 2 points
var/timed = FALSE //Boolean to easily check if we're going to self-destruct
var/timeout //Time between mutation creation and removal. If this exists, we have a timer
var/alias //'Mutation #49', decided every round to get some form of distinction between undiscovered mutations
var/scrambled = FALSE //Wheter we can read it if it's active. To avoid cheesing with mutagen
var/class //Decides player accesibility, sorta
@@ -51,7 +51,7 @@
class = class_
if(timer)
addtimer(CALLBACK(src, .proc/remove), timer)
timed = TRUE
timeout = timer
if(copymut && istype(copymut, /datum/mutation/human))
copy_mutation(copymut)
update_valid_chromosome_list()

View File

@@ -141,8 +141,6 @@
LAZYCLEARLIST(client_mobs_in_contents)
LAZYCLEARLIST(important_recursive_contents)//has to be before moveToNullspace() so that we can exit our spatial_grid cell if we're in it
. = ..()
for(var/movable_content in contents)
@@ -150,6 +148,12 @@
moveToNullspace()
//This absolutely must be after moveToNullspace()
//We rely on Entered and Exited to manage this list, and the copy of this list that is on any /atom/movable "Containers"
//If we clear this before the nullspace move, a ref to this object will be hung in any of its movable containers
LAZYCLEARLIST(important_recursive_contents)
vis_locs = null //clears this atom out of all viscontents
vis_contents.Cut()
@@ -818,9 +822,9 @@
for(var/atom/movable/location as anything in get_nested_locs(src) + src)
LAZYREMOVEASSOC(location.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE, src)
///propogates new_client's mob through our nested contents, similar to other important_recursive_contents procs
///propogates ourselves through our nested contents, similar to other important_recursive_contents procs
///main difference is that client contents need to possibly duplicate recursive contents for the clients mob AND its eye
/atom/movable/proc/enable_client_mobs_in_contents(client/new_client)
/mob/proc/enable_client_mobs_in_contents()
var/turf/our_turf = get_turf(src)
if(our_turf && SSspatial_grid.initialized)
@@ -829,11 +833,10 @@
SSspatial_grid.enter_pre_init_queue(src, RECURSIVE_CONTENTS_CLIENT_MOBS)
for(var/atom/movable/movable_loc as anything in get_nested_locs(src) + src)
LAZYORASSOCLIST(movable_loc.important_recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS, new_client.mob)
///Clears the clients channel of this movables important_recursive_contents list and all nested locs
/atom/movable/proc/clear_important_client_contents(client/former_client)
LAZYORASSOCLIST(movable_loc.important_recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS, src)
///Clears the clients channel of this mob
/mob/proc/clear_important_client_contents()
var/turf/our_turf = get_turf(src)
if(our_turf && SSspatial_grid.initialized)
@@ -842,7 +845,7 @@
SSspatial_grid.remove_from_pre_init_queue(src, RECURSIVE_CONTENTS_CLIENT_MOBS)
for(var/atom/movable/movable_loc as anything in get_nested_locs(src) + src)
LAZYREMOVEASSOC(movable_loc.important_recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS, former_client.mob)
LAZYREMOVEASSOC(movable_loc.important_recursive_contents, RECURSIVE_CONTENTS_CLIENT_MOBS, src)
///Sets the anchored var and returns if it was sucessfully changed or not.
/atom/movable/proc/set_anchored(anchorvalue)

View File

@@ -19,10 +19,6 @@
var/obj/item/card/id/inserted_scan_id
circuit = /obj/item/circuitboard/computer/bountypad
/obj/machinery/computer/piratepad_control/civilian/Initialize(mapload)
. = ..()
pad = /obj/machinery/piratepad/civilian
/obj/machinery/computer/piratepad_control/civilian/attackby(obj/item/I, mob/living/user, params)
if(isidcard(I))
if(id_insert(user, I, inserted_scan_id))
@@ -33,7 +29,7 @@
/obj/machinery/computer/piratepad_control/multitool_act(mob/living/user, obj/item/multitool/I)
if(istype(I) && istype(I.buffer,/obj/machinery/piratepad/civilian))
to_chat(user, span_notice("You link [src] with [I.buffer] in [I] buffer."))
pad = I.buffer
pad_ref = WEAKREF(I.buffer)
return TRUE
/obj/machinery/computer/piratepad_control/civilian/LateInitialize()
@@ -41,10 +37,11 @@
if(cargo_hold_id)
for(var/obj/machinery/piratepad/civilian/C in GLOB.machines)
if(C.cargo_hold_id == cargo_hold_id)
pad = C
pad_ref = WEAKREF(C)
return
else
pad = locate() in range(4,src)
var/obj/machinery/piratepad/civilian/pad = locate() in range(4,src)
pad_ref = WEAKREF(pad)
/obj/machinery/computer/piratepad_control/civilian/recalc()
if(sending)
@@ -58,6 +55,7 @@
playsound(loc, 'sound/machines/synth_no.ogg', 30 , TRUE)
return FALSE
status_report = "Civilian Bounty: "
var/obj/machinery/piratepad/pad = pad_ref?.resolve()
for(var/atom/movable/AM in get_turf(pad))
if(AM == pad)
continue
@@ -83,6 +81,7 @@
return FALSE
var/datum/bounty/curr_bounty = inserted_scan_id.registered_account.civilian_bounty
var/active_stack = 0
var/obj/machinery/piratepad/pad = pad_ref?.resolve()
for(var/atom/movable/AM in get_turf(pad))
if(AM == pad)
continue
@@ -152,7 +151,7 @@
/obj/machinery/computer/piratepad_control/civilian/ui_data(mob/user)
var/list/data = list()
data["points"] = points
data["pad"] = pad ? TRUE : FALSE
data["pad"] = pad_ref?.resolve() ? TRUE : FALSE
data["sending"] = sending
data["status_report"] = status_report
data["id_inserted"] = inserted_scan_id
@@ -178,7 +177,7 @@
. = ..()
if(.)
return
if(!pad)
if(!pad_ref?.resolve())
return
if(!usr.canUseTopic(src, BE_CLOSE) || (machine_stat & (NOPOWER|BROKEN)))
return

View File

@@ -9,12 +9,17 @@
var/mob/camera/ai_eye/remote/eyeobj
var/mob/living/current_user = null
var/list/networks = list("ss13")
var/datum/action/innate/camera_off/off_action = new
var/datum/action/innate/camera_jump/jump_action = new
///Camera action button to move up a Z level
var/datum/action/innate/camera_multiz_up/move_up_action = new
///Camera action button to move down a Z level
var/datum/action/innate/camera_multiz_down/move_down_action = new
/// Typepath of the action button we use as "off"
/// It's a typepath so subtypes can give it fun new names
var/datum/action/innate/camera_off/off_action
/// Typepath for jumping
var/datum/action/innate/camera_jump/jump_action
/// Typepath of the move up action
var/datum/action/innate/camera_multiz_up/move_up_action
/// Typepath of the move down action
var/datum/action/innate/camera_multiz_down/move_down_action
/// List of all actions to give to a user when they're well, granted actions
var/list/actions = list()
///Should we supress any view changes?
var/should_supress_view_changes = TRUE
@@ -34,6 +39,17 @@
if(lock_override & CAMERA_LOCK_CENTCOM)
z_lock |= SSmapping.levels_by_trait(ZTRAIT_CENTCOM)
if(off_action)
actions += new off_action(src)
if(jump_action)
actions += new jump_action(src)
//Camera action button to move up a Z level
if(move_up_action)
actions += new move_up_action(src)
//Camera action button to move down a Z level
if(move_down_action)
actions += new move_down_action(src)
/obj/machinery/computer/camera_advanced/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
for(var/i in networks)
networks -= i
@@ -51,25 +67,8 @@
eyeobj.origin = src
/obj/machinery/computer/camera_advanced/proc/GrantActions(mob/living/user)
if(off_action)
off_action.target = user
off_action.Grant(user)
actions += off_action
if(jump_action)
jump_action.target = user
jump_action.Grant(user)
actions += jump_action
if(move_up_action)
move_up_action.target = user
move_up_action.Grant(user)
actions += move_up_action
if(move_down_action)
move_down_action.target = user
move_down_action.Grant(user)
actions += move_down_action
for(var/datum/action/to_grant as anything in actions)
to_grant.Grant(user)
/obj/machinery/proc/remove_eye_control(mob/living/user)
CRASH("[type] does not implement ai eye handling")
@@ -80,7 +79,6 @@
for(var/V in actions)
var/datum/action/A = V
A.Remove(user)
actions.Cut()
for(var/V in eyeobj.visibleCameraChunks)
var/datum/camerachunk/C = V
C.remove(eyeobj)
@@ -252,12 +250,11 @@
button_icon_state = "camera_off"
/datum/action/innate/camera_off/Activate()
if(!target || !isliving(target))
if(!owner || !isliving(owner))
return
var/mob/living/C = target
var/mob/camera/ai_eye/remote/remote_eye = C.remote_control
var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/console = remote_eye.origin
console.remove_eye_control(target)
console.remove_eye_control(owner)
/datum/action/innate/camera_jump
name = "Jump To Camera"
@@ -265,10 +262,9 @@
button_icon_state = "camera_jump"
/datum/action/innate/camera_jump/Activate()
if(!target || !isliving(target))
if(!owner || !isliving(owner))
return
var/mob/living/C = target
var/mob/camera/ai_eye/remote/remote_eye = C.remote_control
var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/origin = remote_eye.origin
var/list/L = list()
@@ -300,8 +296,8 @@
if(final)
playsound(origin, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE)
remote_eye.setLoc(get_turf(final))
C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
C.clear_fullscreen("flash", 3) //Shorter flash than normal since it's an ~~advanced~~ console!
owner.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
owner.clear_fullscreen("flash", 3) //Shorter flash than normal since it's an ~~advanced~~ console!
else
playsound(origin, 'sound/machines/terminal_prompt_deny.ogg', 25, FALSE)
@@ -311,14 +307,13 @@
button_icon_state = "move_up"
/datum/action/innate/camera_multiz_up/Activate()
if(!target || !isliving(target))
if(!owner || !isliving(owner))
return
var/mob/living/user_mob = target
var/mob/camera/ai_eye/remote/remote_eye = user_mob.remote_control
var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
if(remote_eye.zMove(UP))
to_chat(user_mob, span_notice("You move upwards."))
to_chat(owner, span_notice("You move upwards."))
else
to_chat(user_mob, span_notice("You couldn't move upwards!"))
to_chat(owner, span_notice("You couldn't move upwards!"))
/datum/action/innate/camera_multiz_down
name = "Move down a floor"
@@ -326,11 +321,10 @@
button_icon_state = "move_down"
/datum/action/innate/camera_multiz_down/Activate()
if(!target || !isliving(target))
if(!owner || !isliving(owner))
return
var/mob/living/user_mob = target
var/mob/camera/ai_eye/remote/remote_eye = user_mob.remote_control
var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
if(remote_eye.zMove(DOWN))
to_chat(user_mob, span_notice("You move downwards."))
to_chat(owner, span_notice("You move downwards."))
else
to_chat(user_mob, span_notice("You couldn't move downwards!"))
to_chat(owner, span_notice("You couldn't move downwards!"))

View File

@@ -181,11 +181,12 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
ui.open()
/datum/crewmonitor/proc/show(mob/M, source)
ui_sources[WEAKREF(M)] = source
ui_sources[WEAKREF(M)] = WEAKREF(source)
ui_interact(M)
/datum/crewmonitor/ui_host(mob/user)
return ui_sources[WEAKREF(user)]
var/datum/weakref/host_ref = ui_sources[WEAKREF(user)]
return host_ref?.resolve()
/datum/crewmonitor/ui_data(mob/user)
var/z = user.z

View File

@@ -36,14 +36,10 @@
/// Do we have someone paying to use this?
var/paying_customer = FALSE //Ticked yes if passing inuse()
/// Who's pockets are we sifting through when we're used?
var/datum/bank_account/account //payer's account.
/// Who's paying?
var/mob/living/carbon/human/H //The person using the console in each instance. Used for paying for the kiosk.
var/datum/weakref/paying_ref //The person using the console in each instance. Used for paying for the kiosk.
/// Who's getting scanned?
var/mob/living/carbon/human/altPatient //If scanning someone else, this will be the target.
/// Used to find the money.
var/obj/item/card/id/C //the account of the person using the console.
var/datum/weakref/patient_ref //If scanning someone else, this will be the target.
/obj/machinery/medical_kiosk/Initialize(mapload) //loaded subtype for mapping use
. = ..()
@@ -51,16 +47,20 @@
scanner_wand = new/obj/item/scanner_wand(src)
/obj/machinery/medical_kiosk/proc/inuse() //Verifies that the user can use the interface, followed by showing medical information.
if(C?.registered_account)
account = C.registered_account
if(account?.account_job?.paycheck_department == payment_department)
var/mob/living/carbon/human/paying = paying_ref?.resolve()
if(!paying)
paying_ref = null
return
var/obj/item/card/id/card = paying.get_idcard(TRUE)
if(card?.registered_account?.account_job?.paycheck_department == payment_department)
use_power(20)
paying_customer = TRUE
say("Hello, esteemed medical staff!")
RefreshParts()
return
var/bonus_fee = pandemonium ? rand(10,30) : 0
if(attempt_charge(src, H, bonus_fee) & COMPONENT_OBJ_CANCEL_CHARGE )
if(attempt_charge(src, paying, bonus_fee) & COMPONENT_OBJ_CANCEL_CHARGE )
return
use_power(20)
paying_customer = TRUE
@@ -113,9 +113,10 @@
span_notice("You press [O] into the side of [src], clicking into place."))
//This will be the scanner returning scanner_wand's selected_target variable and assigning it to the altPatient var
if(W.selected_target)
if(!(altPatient == W.return_patient()))
var/datum/weakref/target_ref = WEAKREF(W.return_patient())
if(patient_ref != target_ref)
clearScans()
altPatient = W.return_patient()
patient_ref = target_ref
user.visible_message(span_notice("[W.return_patient()] has been set as the current patient."))
W.selected_target = null
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
@@ -168,12 +169,13 @@
if (ui)
ui.close()
return
patient_distance = get_dist(src.loc,altPatient)
if(altPatient == null)
var/mob/living/carbon/human/patient = patient_ref?.resolve()
patient_distance = get_dist(src.loc, patient)
if(patient == null)
say("Scanner reset.")
altPatient = user
patient_ref = WEAKREF(user)
else if(patient_distance>5)
altPatient = null
patient_ref = null
say("Patient out of range. Resetting biometrics.")
clearScans()
return
@@ -183,19 +185,22 @@
ui.open()
icon_state = "[base_icon_state]_active"
RefreshParts()
H = user
C = H.get_idcard(TRUE)
var/mob/living/carbon/human/paying = user
paying_ref = WEAKREF(paying)
/obj/machinery/medical_kiosk/ui_data(mob/living/carbon/human/user)
var/mob/living/carbon/human/patient = patient_ref?.resolve()
var/list/data = list()
var/patient_name = altPatient.name
if(!patient)
return
var/patient_name = patient.name
var/patient_status = "Alive."
var/max_health = altPatient.maxHealth
var/total_health = altPatient.health
var/brute_loss = altPatient.getBruteLoss()
var/fire_loss = altPatient.getFireLoss()
var/tox_loss = altPatient.getToxLoss()
var/oxy_loss = altPatient.getOxyLoss()
var/max_health = patient.maxHealth
var/total_health = patient.health
var/brute_loss = patient.getBruteLoss()
var/fire_loss = patient.getFireLoss()
var/tox_loss = patient.getToxLoss()
var/oxy_loss = patient.getOxyLoss()
var/chaos_modifier = 0
var/sickness = "Patient does not show signs of disease."
@@ -203,18 +208,18 @@
var/bleed_status = "Patient is not currently bleeding."
var/blood_status = " Patient either has no blood, or does not require it to function."
var/blood_percent = round((altPatient.blood_volume / BLOOD_VOLUME_NORMAL)*100)
var/blood_type = altPatient.dna.blood_type
var/blood_percent = round((patient.blood_volume / BLOOD_VOLUME_NORMAL)*100)
var/blood_type = patient.dna.blood_type
var/blood_warning = " "
for(var/thing in altPatient.diseases) //Disease Information
for(var/thing in patient.diseases) //Disease Information
var/datum/disease/D = thing
if(!(D.visibility_flags & HIDDEN_SCANNER))
sickness = "Warning: Patient is harboring some form of viral disease. Seek further medical attention."
sickness_data = "\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]"
if(altPatient.has_dna()) //Blood levels Information
if(altPatient.is_bleeding())
if(patient.has_dna()) //Blood levels Information
if(patient.is_bleeding())
bleed_status = "Patient is currently bleeding!"
if(blood_percent <= 80)
blood_warning = " Patient has low blood levels. Seek a large meal, or iron supplements."
@@ -223,12 +228,12 @@
blood_status = "Patient blood levels are currently reading [blood_percent]%. Patient has [ blood_type] type blood. [blood_warning]"
var/trauma_status = "Patient is free of unique brain trauma."
var/clone_loss = altPatient.getCloneLoss()
var/brain_loss = altPatient.getOrganLoss(ORGAN_SLOT_BRAIN)
var/clone_loss = patient.getCloneLoss()
var/brain_loss = patient.getOrganLoss(ORGAN_SLOT_BRAIN)
var/brain_status = "Brain patterns normal."
if(LAZYLEN(altPatient.get_traumas()))
if(LAZYLEN(patient.get_traumas()))
var/list/trauma_text = list()
for(var/t in altPatient.get_traumas())
for(var/t in patient.get_traumas())
var/datum/brain_trauma/trauma = t
var/trauma_desc = ""
switch(trauma.resilience)
@@ -247,15 +252,15 @@
var/addict_list = list()
var/hallucination_status = "Patient is not hallucinating."
if(altPatient.reagents.reagent_list.len) //Chemical Analysis details.
for(var/r in altPatient.reagents.reagent_list)
if(patient.reagents.reagent_list.len) //Chemical Analysis details.
for(var/r in patient.reagents.reagent_list)
var/datum/reagent/reagent = r
if(reagent.chemical_flags & REAGENT_INVISIBLE) //Don't show hidden chems
continue
chemical_list += list(list("name" = reagent.name, "volume" = round(reagent.volume, 0.01)))
if(reagent.overdosed)
overdose_list += list(list("name" = reagent.name))
var/obj/item/organ/stomach/belly = altPatient.getorganslot(ORGAN_SLOT_STOMACH)
var/obj/item/organ/stomach/belly = patient.getorganslot(ORGAN_SLOT_STOMACH)
if(belly?.reagents.reagent_list.len) //include the stomach contents if it exists
for(var/bile in belly.reagents.reagent_list)
var/datum/reagent/bit = bile
@@ -267,13 +272,13 @@
var/bit_vol = bit.volume - belly.food_reagents[bit.type]
if(bit_vol > 0)
chemical_list += list(list("name" = bit.name, "volume" = round(bit_vol, 0.01)))
for(var/datum/addiction/addiction_type as anything in altPatient.mind.active_addictions)
for(var/datum/addiction/addiction_type as anything in patient.mind.active_addictions)
addict_list += list(list("name" = initial(addiction_type.name)))
if (altPatient.hallucinating())
if (patient.hallucinating())
hallucination_status = "Subject appears to be hallucinating. Suggested treatments: bedrest, mannitol or psicodine."
if(altPatient.stat == DEAD || HAS_TRAIT(altPatient, TRAIT_FAKEDEATH) || ((brute_loss+fire_loss+tox_loss+oxy_loss+clone_loss) >= 200)) //Patient status checks.
if(patient.stat == DEAD || HAS_TRAIT(patient, TRAIT_FAKEDEATH) || ((brute_loss+fire_loss+tox_loss+oxy_loss+clone_loss) >= 200)) //Patient status checks.
patient_status = "Dead."
if((brute_loss+fire_loss+tox_loss+oxy_loss+clone_loss) >= 80)
patient_status = "Gravely Injured"
@@ -357,6 +362,6 @@
scan_active |= KIOSK_SCANNING_REAGENTS
paying_customer = FALSE
if("clearTarget")
altPatient = null
patient_ref = null
clearScans()
. = TRUE

View File

@@ -254,7 +254,9 @@
/obj/effect/anomaly/bluespace/detonate()
var/turf/T = pick(get_area_turfs(impact_area))
if(T)
if(!T)
return
// Calculate new position (searches through beacons in world)
var/obj/item/beacon/chosen
var/list/possible = list()
@@ -264,9 +266,10 @@
if(possible.len > 0)
chosen = pick(possible)
if(chosen)
// Calculate previous position for transition
if(!chosen)
return
// Calculate previous position for transition
var/turf/FROM = T // the turf of origin we're travelling FROM
var/turf/TO = get_turf(chosen) // the turf of origin we're travelling TO
@@ -293,22 +296,13 @@
A.forceMove(newloc)
if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect
var/mob/M = A
if(M.client)
INVOKE_ASYNC(src, .proc/blue_effect, M)
var/mob/give_sparkles = A
if(give_sparkles.client)
blue_effect(give_sparkles)
/obj/effect/anomaly/bluespace/proc/blue_effect(mob/M)
var/obj/blueeffect = new /obj(src)
blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH"
blueeffect.icon = 'icons/effects/effects.dmi'
blueeffect.icon_state = "shieldsparkles"
blueeffect.layer = FLASH_LAYER
blueeffect.plane = FULLSCREEN_PLANE
blueeffect.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
M.client.screen += blueeffect
sleep(20)
M.client.screen -= blueeffect
qdel(blueeffect)
/obj/effect/anomaly/bluespace/proc/blue_effect(mob/make_sparkle)
make_sparkle.overlay_fullscreen("bluespace_flash", /atom/movable/screen/fullscreen/bluespace_sparkle, 1)
addtimer(CALLBACK(make_sparkle, /mob/.proc/clear_fullscreen, "bluespace_flash"), 2 SECONDS)
/////////////////////

View File

@@ -202,8 +202,10 @@
///////////////////////////////////////////////
//FOAM EFFECT DATUM
/datum/effect_system/foam_spread
var/amount = 10 // the size of the foam spread.
var/obj/chemholder
/// the size of the foam spread.
var/amount = 10
/// Stupid hack alertm exists to hold chems for us
var/atom/movable/chem_holder/chemholder
effect_type = /obj/effect/particle_effect/foam
var/metal = 0
@@ -221,14 +223,11 @@
/datum/effect_system/foam_spread/New()
..()
chemholder = new /obj()
var/datum/reagents/R = new/datum/reagents(1000, REAGENT_HOLDER_INSTANT_REACT) //same as above
chemholder.reagents = R
R.my_atom = chemholder
chemholder = new()
chemholder.create_reagents(1000, REAGENT_HOLDER_INSTANT_REACT)
/datum/effect_system/foam_spread/Destroy()
qdel(chemholder)
chemholder = null
QDEL_NULL(chemholder)
return ..()
/datum/effect_system/foam_spread/set_up(amt=5, loca, datum/reagents/carry = null, metaltype = 0)

View File

@@ -265,20 +265,18 @@
/datum/effect_system/smoke_spread/chem
var/obj/chemholder
/// Evil evil hack so we have something to "hold" our reagents
var/atom/movable/chem_holder/chemholder
effect_type = /obj/effect/particle_effect/smoke/chem
/datum/effect_system/smoke_spread/chem/New()
..()
chemholder = new /obj()
var/datum/reagents/R = new (500, REAGENT_HOLDER_INSTANT_REACT) //This is a safety for now to prevent smoke generating more smoke as the smoke reagents react in the smoke. This is prevented naturally from happening even if this is off, but I want to be sure that any edge cases are prevented before I get a chance to rework smoke reactions (specifically adding water or reacting away stabilizing agent in the middle of it).
chemholder.reagents = R
R.my_atom = chemholder
chemholder = new()
//This is a safety for now to prevent smoke generating more smoke as the smoke reagents react in the smoke. This is prevented naturally from happening even if this is off, but I want to be sure that any edge cases are prevented before I get a chance to rework smoke reactions (specifically adding water or reacting away stabilizing agent in the middle of it).
chemholder.create_reagents(500, REAGENT_HOLDER_INSTANT_REACT)
/datum/effect_system/smoke_spread/chem/Destroy()
qdel(chemholder)
chemholder = null
QDEL_NULL(chemholder)
return ..()
/datum/effect_system/smoke_spread/chem/set_up(datum/reagents/carry = null, radius = 1, loca, silent = FALSE)

View File

@@ -87,6 +87,8 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
var/slot_flags = 0
pass_flags = PASSTABLE
pressure_resistance = 4
/// This var exists as a weird proxy "owner" ref
/// It's used in a few places. Stop using it, and optimially replace all uses please
var/obj/item/master = null
///Price of an item in a vending machine, overriding the base vending machine price. Define in terms of paycheck defines as opposed to raw numbers.
@@ -243,7 +245,9 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
updateEmbedding()
/obj/item/Destroy()
item_flags &= ~DROPDEL //prevent reqdels
// This var exists as a weird proxy "owner" ref
// It's used in a few places. Stop using it, and optimially replace all uses please
master = null
if(ismob(loc))
var/mob/m = loc
m.temporarilyRemoveItemFromInventory(src, TRUE)
@@ -585,7 +589,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
for(var/X in actions)
var/datum/action/A = X
A.Remove(user)
if(item_flags & DROPDEL)
if(item_flags & DROPDEL && !QDELETED(src))
qdel(src)
item_flags &= ~IN_INVENTORY
SEND_SIGNAL(src, COMSIG_ITEM_DROPPED, user)
@@ -1054,7 +1058,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
return
/obj/item/proc/unembedded()
if(item_flags & DROPDEL)
if(item_flags & DROPDEL && !QDELETED(src))
qdel(src)
return TRUE
@@ -1080,7 +1084,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
///In case we want to do something special (like self delete) upon failing to embed in something.
/obj/item/proc/failedEmbed()
if(item_flags & DROPDEL)
if(item_flags & DROPDEL && !QDELETED(src))
qdel(src)
///Called by the carbon throw_item() proc. Returns null if the item negates the throw, or a reference to the thing to suffer the throw else.

View File

@@ -31,7 +31,8 @@
var/list/stored_data = list()
var/current_channel
var/mob/living/simple_animal/bot/active_bot
/// Weakref to the currently controlled bot
var/datum/weakref/active_bot_ref
var/list/botlist = list()
/obj/item/cartridge/engineering
@@ -680,15 +681,16 @@
var/parse = emoji_parse(":[href_list["emoji"]]:")
to_chat(usr, parse)
var/mob/living/simple_animal/bot/active_bot = active_bot_ref?.resolve()
//Bot control section! Viciously ripped from radios for being laggy and terrible.
if(href_list["op"])
switch(href_list["op"])
if("control")
active_bot = locate(href_list["bot"]) in GLOB.bots_list
active_bot_ref = WEAKREF(locate(href_list["bot"]) in GLOB.bots_list)
if("botlist")
active_bot = null
active_bot_ref = null
if("summon") //Args are in the correct order, they are stated here just as an easy reminder.
active_bot.bot_control("summon", usr, host_pda.GetAccess())
else //Forward all other bot commands to the bot itself!
@@ -705,7 +707,22 @@
/obj/item/cartridge/proc/bot_control()
if(active_bot)
var/mob/living/simple_animal/bot/active_bot = active_bot_ref?.resolve()
if(!active_bot)
menu += "<BR><A href='byond://?src=[REF(src)];op=botlist'>[PDAIMG(refresh)]Scan for active bots</A><BR><BR>"
var/turf/current_turf = get_turf(src)
var/zlevel = current_turf.z
var/botcount = 0
for(var/B in GLOB.bots_list) //Git da botz
var/mob/living/simple_animal/bot/Bot = B
if(!(Bot.bot_mode_flags & BOT_MODE_ON) || Bot.z != zlevel || !(Bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || !(Bot.bot_type in bot_access)) //Only non-emagged bots on the same Z-level are detected!
continue //Also, the PDA must have access to the bot type.
menu += "<A href='byond://?src=[REF(src)];op=control;bot=[REF(Bot)]'><b>[Bot.name]</b> ([Bot.get_mode()])<BR>"
botcount++
if(!botcount) //No bots at all? Lame.
menu += "No bots found.<BR>"
return menu
menu += "<B>[active_bot]</B><BR> Status: (<A href='byond://?src=[REF(src)];op=control;bot=[REF(active_bot)]'>[PDAIMG(refresh)]<i>refresh</i></A>)<BR>"
menu += "Model: [active_bot.bot_type]<BR>"
menu += "Location: [get_area(active_bot)]<BR>"
@@ -743,20 +760,6 @@
menu += "Keep an ID inserted to upload access codes upon summoning."
menu += "<HR><A href='byond://?src=[REF(src)];op=botlist'>[PDAIMG(back)]Return to bot list</A>"
else
menu += "<BR><A href='byond://?src=[REF(src)];op=botlist'>[PDAIMG(refresh)]Scan for active bots</A><BR><BR>"
var/turf/current_turf = get_turf(src)
var/zlevel = current_turf.z
var/botcount = 0
for(var/B in GLOB.bots_list) //Git da botz
var/mob/living/simple_animal/bot/Bot = B
if(!(Bot.bot_mode_flags & BOT_MODE_ON) || Bot.z != zlevel || !(Bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || !(Bot.bot_type in bot_access)) //Only non-emagged bots on the same Z-level are detected!
continue //Also, the PDA must have access to the bot type.
menu += "<A href='byond://?src=[REF(src)];op=control;bot=[REF(Bot)]'><b>[Bot.name]</b> ([Bot.get_mode()])<BR>"
botcount++
if(!botcount) //No bots at all? Lame.
menu += "No bots found.<BR>"
return
return menu

View File

@@ -37,11 +37,11 @@
/obj/item/flamethrower/Destroy()
if(weldtool)
qdel(weldtool)
QDEL_NULL(weldtool)
if(igniter)
qdel(igniter)
QDEL_NULL(igniter)
if(ptank)
qdel(ptank)
QDEL_NULL(ptank)
return ..()
/obj/item/flamethrower/process()

View File

@@ -12,7 +12,7 @@
desc = "An industrial computer integrated with a camera-assisted rapid construction drone."
networks = list("ss13")
circuit = /obj/item/circuitboard/computer/base_construction
off_action = new/datum/action/innate/camera_off/base_construction
off_action = /datum/action/innate/camera_off/base_construction
jump_action = null
icon_screen = "mining"
icon_keyboard = "rd_key"
@@ -23,8 +23,6 @@
var/list/structures = list()
///Internal RCD. Some construction actions rely on having this.
var/obj/item/construction/rcd/internal/internal_rcd
///Actions given to the console user to help with base building. Actions are generally carried out at the location of the eyeobj
var/list/datum/action/innate/construction_actions
/obj/machinery/computer/camera_advanced/base_construction/Initialize(mapload)
. = ..()
@@ -38,10 +36,10 @@
* Fill the construction_actios list with actions
*
* Instantiate each action object that we'll be giving to users of
* this console, and put it in the construction actions list.
* this console, and put it in the actions list
*/
/obj/machinery/computer/camera_advanced/base_construction/proc/populate_actions_list()
construction_actions = list()
return
/**
* Reload materials used by the console
@@ -81,11 +79,6 @@
///Go through every action object in the construction_action list (which should be fully initialized by now) and grant it to the user.
/obj/machinery/computer/camera_advanced/base_construction/GrantActions(mob/living/user)
..()
for (var/datum/action/innate/construction_action in construction_actions)
if(construction_action)
construction_action.target = src
construction_action.Grant(user)
actions += construction_action
//When the eye is in use, make it visible to players so they know when someone is building.
eyeobj.invisibility = 0

View File

@@ -15,13 +15,12 @@
structures["turrets"] = 4
/obj/machinery/computer/camera_advanced/base_construction/aux/populate_actions_list()
construction_actions = list()
construction_actions.Add(new /datum/action/innate/construction/switch_mode())//Action for switching the RCD's build modes
construction_actions.Add(new /datum/action/innate/construction/build()) //Action for using the RCD
construction_actions.Add(new /datum/action/innate/construction/airlock_type()) //Action for setting the airlock type
construction_actions.Add(new /datum/action/innate/construction/window_type()) //Action for setting the window type
construction_actions.Add(new /datum/action/innate/construction/place_structure/fan()) //Action for spawning fans
construction_actions.Add(new /datum/action/innate/construction/place_structure/turret()) //Action for spawning turrets
actions += new /datum/action/innate/construction/switch_mode(src) //Action for switching the RCD's build modes
actions += new /datum/action/innate/construction/build(src) //Action for using the RCD
actions += new /datum/action/innate/construction/airlock_type(src) //Action for setting the airlock type
actions += new /datum/action/innate/construction/window_type(src) //Action for setting the window type
actions += new /datum/action/innate/construction/place_structure/fan(src) //Action for spawning fans
actions += new /datum/action/innate/construction/place_structure/turret(src) //Action for spawning turrets
/obj/machinery/computer/camera_advanced/base_construction/aux/find_spawn_spot()
//Aux base controller. Where the eyeobj will spawn.

View File

@@ -11,6 +11,5 @@
internal_rcd.matter = internal_rcd.max_matter
/obj/machinery/computer/camera_advanced/base_construction/centcom/populate_actions_list()
construction_actions = list()
construction_actions.Add(new /datum/action/innate/construction/switch_mode())//Action for switching the RCD's build modes
construction_actions.Add(new /datum/action/innate/construction/build()) //Action for using the RCD
actions += new /datum/action/innate/construction/switch_mode(src) //Action for switching the RCD's build modes
actions += new /datum/action/innate/construction/build(src) //Action for using the RCD

View File

@@ -29,6 +29,10 @@
AddElement(/datum/element/climbable, climb_time = crate_climb_time, climb_stun = 0)
update_appearance()
/obj/structure/closet/crate/Destroy()
QDEL_NULL(manifest)
return ..()
/obj/structure/closet/crate/update_overlays()
. = ..()
if(broken)
@@ -80,7 +84,7 @@
/obj/structure/closet/crate/open(mob/living/user, force = FALSE)
. = ..()
if(. && manifest)
if(. && !QDELETED(manifest))
to_chat(user, span_notice("The manifest is torn off [src]."))
playsound(src, 'sound/items/poster_ripped.ogg', 75, TRUE)
manifest.forceMove(get_turf(src))

View File

@@ -35,6 +35,9 @@ GLOBAL_VAR(restart_counter)
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = GLOB.world_econ_log = GLOB.world_shuttle_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl
#ifdef REFERENCE_DOING_IT_LIVE
GLOB.harddel_log = GLOB.world_game_log
#endif
GLOB.revdata = new
@@ -147,6 +150,10 @@ GLOBAL_VAR(restart_counter)
#ifdef UNIT_TESTS
GLOB.test_log = "[GLOB.log_directory]/tests.log"
start_log(GLOB.test_log)
#endif
#ifdef REFERENCE_DOING_IT_LIVE
GLOB.harddel_log = "[GLOB.log_directory]/harddels.log"
start_log(GLOB.harddel_log)
#endif
start_log(GLOB.world_game_log)
start_log(GLOB.world_attack_log)

View File

@@ -378,7 +378,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
//print the key
if(islist(key))
recursive_list_print(output, key, datum_handler, atom_handler)
else if(is_proper_datum(key) && (datum_handler || (isatom(key) && atom_handler)))
else if(isdatum(key) && (datum_handler || (isatom(key) && atom_handler)))
if(isatom(key) && atom_handler)
output += atom_handler.Invoke(key)
else
@@ -392,7 +392,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
var/value = input[key]
if(islist(value))
recursive_list_print(output, value, datum_handler, atom_handler)
else if(is_proper_datum(value) && (datum_handler || (isatom(value) && atom_handler)))
else if(isdatum(value) && (datum_handler || (isatom(value) && atom_handler)))
if(isatom(value) && atom_handler)
output += atom_handler.Invoke(value)
else
@@ -684,7 +684,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
switch(query_tree[1])
if("call")
for(var/i in found)
if(!is_proper_datum(i))
if(!isdatum(i))
continue
world.SDQL_var(i, query_tree["call"][1], null, i, superuser, src)
obj_count_finished++
@@ -713,7 +713,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
if("set" in query_tree)
var/list/set_list = query_tree["set"]
for(var/d in found)
if(!is_proper_datum(d))
if(!isdatum(d))
continue
SDQL_internal_vv(d, set_list)
obj_count_finished++
@@ -724,7 +724,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
state = SDQL2_STATE_SWITCHING
/datum/sdql2_query/proc/SDQL_print(object, list/text_list, print_nulls = TRUE)
if(is_proper_datum(object))
if(isdatum(object))
text_list += "<A HREF='?_src_=vars;[HrefToken(TRUE)];Vars=[REF(object)]'>[REF(object)]</A> : [object]"
if(istype(object, /atom))
var/atom/A = object
@@ -1012,7 +1012,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
var/static/list/exclude = list("usr", "src", "marked", "global", "MC", "FS", "CFG")
var/long = start < expression.len
var/datum/D
if(is_proper_datum(object))
if(isdatum(object))
D = object
if (object == world && (!long || expression[start + 1] == ".") && !(expression[start] in exclude) && copytext(expression[start], 1, 3) != "SS") //3 == length("SS") + 1
@@ -1201,9 +1201,6 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/sdql2_vv_all, new(null
query_list += word
return query_list
/proc/is_proper_datum(thing)
return istype(thing, /datum) || istype(thing, /client)
/obj/effect/statclick/SDQL2_delete/Click()
if(!usr.client?.holder)
message_admins("[key_name_admin(usr)] non-holder clicked on a statclick! ([src])")

View File

@@ -48,9 +48,11 @@
log_reftracker("Finished searching datums")
//Warning, attempting to search clients like this will cause crashes if done on live. Watch yourself
#ifndef REFERENCE_DOING_IT_LIVE
for(var/client/thing) //clients
DoSearchVar(thing, "Clients -> [thing.type]", search_time = starting_time)
log_reftracker("Finished searching clients")
#endif
log_reftracker("Completed search for references to a [type].")

View File

@@ -55,11 +55,11 @@ GLOBAL_LIST_EMPTY(antagonists)
///name of the UI that will try to open, right now using a generic ui
var/ui_name = "AntagInfoGeneric"
///button to access antag interface
var/datum/action/antag_info/info_button
///weakref to button to access antag interface
var/datum/weakref/info_button_ref
/// The HUD shown to teammates, created by `add_team_hud`
var/datum/atom_hud/alternate_appearance/team_hud
/// A weakref to the HUD shown to teammates, created by `add_team_hud`
var/datum/weakref/team_hud_ref
/datum/antagonist/New()
GLOB.antagonists += src
@@ -69,7 +69,7 @@ GLOBAL_LIST_EMPTY(antagonists)
GLOB.antagonists -= src
if(owner)
LAZYREMOVE(owner.antag_datums, src)
QDEL_NULL(team_hud)
QDEL_NULL(team_hud_ref)
owner = null
return ..()
@@ -94,6 +94,8 @@ GLOBAL_LIST_EMPTY(antagonists)
remove_innate_effects(old_body)
if(!soft_antag && old_body && old_body.stat != DEAD && !LAZYLEN(old_body.mind?.antag_datums))
old_body.remove_from_current_living_antags()
var/datum/action/antag_info/info_button = info_button_ref?.resolve()
if(info_button)
info_button.Remove(old_body)
info_button.Grant(new_body)
apply_innate_effects(new_body)
@@ -138,6 +140,7 @@ GLOBAL_LIST_EMPTY(antagonists)
///Called by the add_antag_datum() mind proc after the instanced datum is added to the mind's antag_datums list.
/datum/antagonist/proc/on_gain()
SHOULD_CALL_PARENT(TRUE)
var/datum/action/antag_info/info_button
if(!owner)
CRASH("[src] ran on_gain() without a mind")
if(!owner.current)
@@ -145,6 +148,7 @@ GLOBAL_LIST_EMPTY(antagonists)
if(ui_name)//in the future, this should entirely replace greet.
info_button = new(src)
info_button.Grant(owner.current)
info_button_ref = WEAKREF(info_button)
if(!silent)
greet()
if(ui_name)
@@ -201,8 +205,8 @@ GLOBAL_LIST_EMPTY(antagonists)
LAZYREMOVE(owner.antag_datums, src)
if(!LAZYLEN(owner.antag_datums) && !soft_antag)
owner.current.remove_from_current_living_antags()
if(info_button)
QDEL_NULL(info_button)
if(info_button_ref)
QDEL_NULL(info_button_ref)
if(!silent && owner.current)
farewell()
UnregisterSignal(owner, COMSIG_PRE_MINDSHIELD_IMPLANT)
@@ -408,14 +412,14 @@ GLOBAL_LIST_EMPTY(antagonists)
/// Adds a HUD that will show you other members with the same antagonist.
/// If an antag typepath is passed to `antag_to_check`, will check that, otherwise will use the source type.
/datum/antagonist/proc/add_team_hud(mob/target, antag_to_check)
QDEL_NULL(team_hud)
QDEL_NULL(team_hud_ref)
team_hud = target.add_alt_appearance(
team_hud_ref = WEAKREF(target.add_alt_appearance(
/datum/atom_hud/alternate_appearance/basic/has_antagonist,
"antag_team_hud_[REF(src)]",
image(hud_icon, target, antag_hud_name),
antag_to_check || type,
)
))
// Add HUDs that they couldn't see before
for (var/datum/atom_hud/alternate_appearance/basic/has_antagonist/antag_hud as anything in GLOB.has_antagonist_huds)

View File

@@ -167,7 +167,7 @@
icon_state = "gizmo_scan"
inhand_icon_state = "silencer"
var/mode = GIZMO_SCAN
var/mob/living/marked = null
var/datum/weakref/marked_target
var/obj/machinery/abductor/console/console
/obj/item/abductor/gizmo/attack_self(mob/user)
@@ -221,6 +221,7 @@
to_chat(user, span_notice("You scan [target] and add [target.p_them()] to the database."))
/obj/item/abductor/gizmo/proc/mark(atom/target, mob/living/user)
var/mob/living/marked = marked_target?.resolve()
if(marked == target)
to_chat(user, span_warning("This specimen is already marked!"))
return
@@ -236,12 +237,13 @@
return
to_chat(user, span_notice("You begin preparing [target] for transport..."))
if(do_after(user, 100, target = target))
marked = target
marked_target = WEAKREF(target)
to_chat(user, span_notice("You finish preparing [target] for transport."))
/obj/item/abductor/gizmo/Destroy()
if(console)
console.gizmo = null
console = null
. = ..()

View File

@@ -2,13 +2,10 @@
name = "Human Observation Console"
var/team_number = 0
networks = list("ss13", "abductor")
var/datum/action/innate/teleport_in/tele_in_action = new
var/datum/action/innate/teleport_out/tele_out_action = new
var/datum/action/innate/teleport_self/tele_self_action = new
var/datum/action/innate/vest_mode_swap/vest_mode_action = new
var/datum/action/innate/vest_disguise_swap/vest_disguise_action = new
var/datum/action/innate/set_droppoint/set_droppoint_action = new
var/obj/machinery/abductor/console/console
/// We can't create our actions until after LateInitialize
/// So we instead do it on the first call to GrantActions
var/abduct_created = FALSE
lock_override = TRUE
icon = 'icons/obj/abductor.dmi'
@@ -16,6 +13,12 @@
icon_keyboard = null
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
/obj/machinery/computer/camera_advanced/abductor/Destroy()
if(console)
console.camera = null
console = null
return ..()
/obj/machinery/computer/camera_advanced/abductor/CreateEye()
..()
eyeobj.visible_icon = TRUE
@@ -24,38 +27,15 @@
eyeobj.invisibility = INVISIBILITY_OBSERVER
/obj/machinery/computer/camera_advanced/abductor/GrantActions(mob/living/carbon/user)
if(!abduct_created)
actions += new /datum/action/innate/teleport_in(console.pad)
actions += new /datum/action/innate/teleport_out(console)
actions += new /datum/action/innate/teleport_self(console.pad)
actions += new /datum/action/innate/vest_mode_swap(console)
actions += new /datum/action/innate/vest_disguise_swap(console)
actions += new /datum/action/innate/set_droppoint(console)
..()
if(tele_in_action)
tele_in_action.target = console.pad
tele_in_action.Grant(user)
actions += tele_in_action
if(tele_out_action)
tele_out_action.target = console
tele_out_action.Grant(user)
actions += tele_out_action
if(tele_self_action)
tele_self_action.target = console.pad
tele_self_action.Grant(user)
actions += tele_self_action
if(vest_mode_action)
vest_mode_action.target = console
vest_mode_action.Grant(user)
actions += vest_mode_action
if(vest_disguise_action)
vest_disguise_action.target = console
vest_disguise_action.Grant(user)
actions += vest_disguise_action
if(set_droppoint_action)
set_droppoint_action.target = console
set_droppoint_action.Grant(user)
actions += set_droppoint_action
/obj/machinery/computer/camera_advanced/abductor/proc/IsScientist(mob/living/carbon/human/H)
return HAS_TRAIT(H, TRAIT_ABDUCTOR_SCIENTIST_TRAINING)

View File

@@ -34,6 +34,21 @@
. = ..()
possible_gear = get_abductor_gear()
/obj/machinery/abductor/console/Destroy()
if(gizmo)
gizmo.console = null
gizmo = null
if(experiment)
experiment.console = null
experiment = null
if(pad)
pad.console = null
pad = null
if(camera)
camera.console = null
camera = null
return ..()
/**
* get_abductor_gear: Returns a list of a filtered abductor gear sorted by categories
*/
@@ -95,7 +110,7 @@
data["credits"] = experiment.credits
data["pad"] = pad ? TRUE : FALSE
if(pad)
data["gizmo"] = gizmo && gizmo.marked ? TRUE : FALSE
data["gizmo"] = gizmo && gizmo.marked_target?.resolve() ? TRUE : FALSE
data["vest"] = vest ? TRUE : FALSE
if(vest)
data["vest_mode"] = vest.mode
@@ -143,8 +158,9 @@
return TRUE
/obj/machinery/abductor/console/proc/TeleporterRetrieve()
if(pad && gizmo?.marked)
pad.Retrieve(gizmo.marked)
var/mob/living/marked = gizmo.marked_target?.resolve()
if(pad && marked)
pad.Retrieve(marked)
/obj/machinery/abductor/console/proc/TeleporterSend()
if(pad)
@@ -192,6 +208,7 @@
for(var/obj/machinery/abductor/pad/p in GLOB.machines)
if(p.team_number == team_number)
pad = p
pad.console = src
break
for(var/obj/machinery/abductor/experiment/e in GLOB.machines)

View File

@@ -15,6 +15,12 @@
var/message_cooldown = 0
var/breakout_time = 450
/obj/machinery/abductor/experiment/Destroy()
if(console)
console.experiment = null
console = null
return ..()
/obj/machinery/abductor/experiment/MouseDrop_T(mob/target, mob/user)
if(user.stat != CONSCIOUS || HAS_TRAIT(user, TRAIT_UI_BLOCKED) || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target))
return

View File

@@ -4,6 +4,13 @@
icon = 'icons/obj/abductor.dmi'
icon_state = "alien-pad-idle"
var/turf/teleport_target
var/obj/machinery/abductor/console/console
/obj/machinery/abductor/pad/Destroy()
if(console)
console.pad = null
console = null
return ..()
/obj/machinery/abductor/pad/proc/Warp(mob/living/target)
if(!target.buckled)

View File

@@ -263,7 +263,7 @@
for(var/obj/I in all_items) //Check for wanted items
if(istype(I, /obj/item/photo))
var/obj/item/photo/P = I
if(P.picture && (target.current in P.picture.mobs_seen) && !(target.current in P.picture.dead_seen)) //Does the picture exist and is the target in it and is the target not dead
if(P.picture && (WEAKREF(target.current) in P.picture.mobs_seen) && !(WEAKREF(target.current) in P.picture.dead_seen)) //Does the picture exist and is the target in it and is the target not dead
return TRUE
return FALSE

View File

@@ -12,7 +12,8 @@
return
if(ishuman(A))
if(A in drained_mobs)
//Humans are tagged, so this is fine
if(REF(A) in drained_mobs)
to_chat(src, span_revenwarning("[A]'s soul is dead and empty.") )
else if(in_range(src, A))
Harvest(A)
@@ -97,7 +98,7 @@
to_chat(src, span_revennotice("[target]'s soul has been considerably weakened and will yield no more essence for the time being."))
target.visible_message(span_warning("[target] slumps onto the ground."), \
span_revenwarning("Violets lights, dancing in your vision, getting clo--"))
drained_mobs.Add(target)
drained_mobs += REF(target)
target.death(0)
else
to_chat(src, span_revenwarning("[target ? "[target] has":"[target.p_theyve(TRUE)]"] been drawn out of your grasp. The link has been broken."))

View File

@@ -23,7 +23,7 @@
var/list/traitor_flavor
///reference to the uplink this traitor was given, if they were.
var/datum/component/uplink/uplink
var/datum/weakref/uplink_ref
/// The uplink handler that this traitor belongs to.
var/datum/uplink_handler/uplink_handler
@@ -40,7 +40,8 @@
if(give_uplink)
owner.give_uplink(silent = TRUE, antag_datum = src)
uplink = owner.find_syndicate_uplink()
var/datum/component/uplink/uplink = owner.find_syndicate_uplink()
uplink_ref = WEAKREF(uplink)
if(uplink)
if(uplink_handler)
uplink.uplink_handler = uplink_handler
@@ -63,8 +64,6 @@
uplink_items += item
uplink_handler.extra_purchasable += create_uplink_sales(uplink_sale_count, /datum/uplink_category/discounts, -1, uplink_items)
RegisterSignal(uplink, COMSIG_PARENT_QDELETING, .proc/on_uplink_lost)
if(give_objectives)
forge_traitor_objectives()
@@ -78,10 +77,6 @@
return ..()
/datum/antagonist/traitor/proc/on_uplink_lost(datum/source)
SIGNAL_HANDLER
uplink = null
/datum/antagonist/traitor/on_removal()
owner.special_role = null
return ..()
@@ -181,6 +176,7 @@
component.delete_if_from_source(src)
/datum/antagonist/traitor/ui_static_data(mob/user)
var/datum/component/uplink/uplink = uplink_ref?.resolve()
var/list/data = list()
data["has_codewords"] = should_give_codewords
if(should_give_codewords)

View File

@@ -13,6 +13,11 @@
var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device
var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank
/obj/item/onetankbomb/Destroy()
bombassembly = null
bombtank = null
return ..()
/obj/item/onetankbomb/IsSpecialAssembly()
return TRUE

View File

@@ -19,6 +19,18 @@
var/static/rotation_flags = ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_FLIP | ROTATION_VERBS
AddComponent(/datum/component/simple_rotation, rotation_flags)
/obj/item/assembly_holder/Destroy()
QDEL_NULL(a_left)
QDEL_NULL(a_right)
return ..()
/obj/item/assembly_holder/Exited(atom/movable/gone, direction)
. = ..()
if(gone == a_left)
a_left = null
else if(gone == a_right)
a_right = null
/obj/item/assembly_holder/IsAssemblyHolder()
return TRUE

View File

@@ -89,6 +89,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor, 30)
icon_state = "[base_icon]_open"
return ..()
/obj/machinery/bluespace_vendor/Exited(atom/movable/gone, direction)
if(gone == internal_tank)
internal_tank = null
return ..()
/obj/machinery/bluespace_vendor/process()
if(mode == BS_MODE_OPEN)
return

View File

@@ -496,7 +496,7 @@ GLOBAL_LIST_INIT(gas_id_to_canister, init_gas_id_to_canister())
/obj/machinery/portable_atmospherics/canister/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0)
. = ..()
if(!.)
if(!. || QDELETED(src))
return
SSair.start_processing_machine(src)

View File

@@ -3,8 +3,8 @@
var/order_id = 0
var/errors = 0
/obj/item/paper/fluff/jobs/cargo/manifest/New(atom/A, id, cost)
..()
/obj/item/paper/fluff/jobs/cargo/manifest/Initialize(mapload, id, cost)
. = ..()
order_id = id
order_cost = cost
@@ -65,7 +65,7 @@
return P
/datum/supply_order/proc/generateManifest(obj/container, owner, packname, cost) //generates-the-manifests.
var/obj/item/paper/fluff/jobs/cargo/manifest/P = new(container, id, cost)
var/obj/item/paper/fluff/jobs/cargo/manifest/P = new(null, id, cost)
var/station_name = (P.errors & MANIFEST_ERROR_NAME) ? new_station_name() : station_name()

View File

@@ -509,6 +509,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
var/announce_join = mob.client?.prefs?.read_preference(/datum/preference/toggle/broadcast_login_logout)
if (!stealth_admin)
deadchat_broadcast(" has disconnected.", "<b>[mob][mob.get_realname_string()]</b>", follow_target = mob, turf_target = get_turf(mob), message_type = DEADCHAT_LOGIN_LOGOUT, admin_only=!announce_join)
mob.become_uncliented()
GLOB.clients -= src
GLOB.directory -= ckey
@@ -542,10 +543,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
send2adminchat("Server", "[cheesy_message] (No admins online)")
QDEL_LIST_ASSOC_VAL(char_render_holders)
if(movingmob != null)
LAZYREMOVE(movingmob.client_mobs_in_contents, mob)
movingmob = null
active_mousedown_item = null
SSambience.remove_ambience_client(src)
SSmouse_entered.hovers -= src

View File

@@ -16,8 +16,7 @@
/obj/item/clothing/suit/hooded/Destroy()
. = ..()
qdel(hood)
hood = null
QDEL_NULL(hood)
/obj/item/clothing/suit/hooded/proc/MakeHood()
if(!hood)

View File

@@ -312,7 +312,7 @@
/obj/machinery/computer/piratepad_control
name = "cargo hold control terminal"
var/status_report = "Ready for delivery."
var/obj/machinery/piratepad/pad
var/datum/weakref/pad_ref
var/warmup_time = 100
var/sending = FALSE
var/points = 0
@@ -328,7 +328,7 @@
. = ..()
if (istype(I) && istype(I.buffer,/obj/machinery/piratepad))
to_chat(user, span_notice("You link [src] with [I.buffer] in [I] buffer."))
pad = I.buffer
pad_ref = WEAKREF(I.buffer)
return TRUE
/obj/machinery/computer/piratepad_control/LateInitialize()
@@ -336,10 +336,11 @@
if(cargo_hold_id)
for(var/obj/machinery/piratepad/P in GLOB.machines)
if(P.cargo_hold_id == cargo_hold_id)
pad = P
pad_ref = WEAKREF(P)
return
else
pad = locate() in range(4,src)
var/obj/machinery/piratepad/pad = locate() in range(4, src)
pad_ref = WEAKREF(pad)
/obj/machinery/computer/piratepad_control/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -350,7 +351,7 @@
/obj/machinery/computer/piratepad_control/ui_data(mob/user)
var/list/data = list()
data["points"] = points
data["pad"] = pad ? TRUE : FALSE
data["pad"] = pad_ref?.resolve() ? TRUE : FALSE
data["sending"] = sending
data["status_report"] = status_report
return data
@@ -359,7 +360,7 @@
. = ..()
if(.)
return
if(!pad)
if(!pad_ref?.resolve())
return
switch(action)
@@ -380,6 +381,7 @@
status_report = "Predicted value: "
var/value = 0
var/datum/export_report/ex = new
var/obj/machinery/piratepad/pad = pad_ref?.resolve()
for(var/atom/movable/AM in get_turf(pad))
if(AM == pad)
continue
@@ -402,6 +404,7 @@
return
var/datum/export_report/ex = new
var/obj/machinery/piratepad/pad = pad_ref?.resolve()
for(var/atom/movable/AM in get_turf(pad))
if(AM == pad)
@@ -447,6 +450,7 @@
return
sending = TRUE
status_report = "Sending... "
var/obj/machinery/piratepad/pad = pad_ref?.resolve()
pad.visible_message(span_notice("[pad] starts charging up."))
pad.icon_state = pad.warmup_state
sending_timer = addtimer(CALLBACK(src,.proc/send),warmup_time, TIMER_STOPPABLE)
@@ -458,6 +462,7 @@
status_report = "Ready for delivery."
if(custom_report)
status_report = custom_report
var/obj/machinery/piratepad/pad = pad_ref?.resolve()
pad.icon_state = pad.idle_state
deltimer(sending_timer)

View File

@@ -422,8 +422,12 @@
eat(user)
/datum/spacevine_controller
///Canonical list of all the vines we "own"
var/list/obj/structure/spacevine/vines
///Queue of vines to process
var/list/growth_queue
//List of currently processed vines, on this level to prevent runtime tomfoolery
var/list/obj/structure/spacevine/queue_end
var/spread_multiplier = 5
var/spread_cap = 30
var/list/vine_mutations_list
@@ -432,9 +436,16 @@
/datum/spacevine_controller/New(turf/location, list/muts, potency, production, datum/round_event/event = null)
vines = list()
growth_queue = list()
<<<<<<< HEAD
var/obj/structure/spacevine/SV = spawn_spacevine_piece(location, null, muts)
if (event)
event.announce_to_ghosts(SV)
=======
queue_end = list()
var/obj/structure/spacevine/vine = spawn_spacevine_piece(location, null, muts)
if(event)
event.announce_to_ghosts(vine)
>>>>>>> f8aad14ae87 (Harddel Fix Pack #42 + Better Live Reftracking Support (#63877))
START_PROCESSING(SSobj, src)
vine_mutations_list = list()
init_subtypes(/datum/spacevine_mutation/, vine_mutations_list)
@@ -459,9 +470,13 @@
/datum/spacevine_controller/Destroy()
STOP_PROCESSING(SSobj, src)
vines.Cut()
growth_queue.Cut()
queue_end.Cut()
return ..()
/datum/spacevine_controller/proc/spawn_spacevine_piece(turf/location, obj/structure/spacevine/parent, list/muts)
<<<<<<< HEAD
var/obj/structure/spacevine/SV = new(location)
growth_queue += SV
vines += SV
@@ -469,6 +484,14 @@
if(muts && muts.len)
for(var/datum/spacevine_mutation/M in muts)
M.add_mutation_to_vinepiece(SV)
=======
var/obj/structure/spacevine/vine = new(location)
growth_queue += vine
vines += vine
vine.master = src
for(var/datum/spacevine_mutation/mutation in muts)
mutation.add_mutation_to_vinepiece(vine)
>>>>>>> f8aad14ae87 (Harddel Fix Pack #42 + Better Live Reftracking Support (#63877))
if(parent)
SV.mutations |= parent.mutations
var/parentcolor = parent.atom_colours[FIXED_COLOUR_PRIORITY]
@@ -482,6 +505,7 @@
location.Entered(SV, null)
return SV
<<<<<<< HEAD
/datum/spacevine_controller/proc/VineDestroyed(obj/structure/spacevine/S)
S.master = null
vines -= S
@@ -492,15 +516,28 @@
KZ.set_potency(mutativeness * 10)
KZ.set_production(11 - (spread_cap / initial(spread_cap)) * 5) //Reverts spread_cap formula so resulting seed gets original production stat or equivalent back.
qdel(src)
=======
/datum/spacevine_controller/proc/VineDestroyed(obj/structure/spacevine/vine)
vine.master = null
vines -= vine
growth_queue -= vine
queue_end -= vine
if(length(vines))
return
var/obj/item/seeds/kudzu/seed = new(vine.loc)
seed.mutations |= vine.mutations
seed.set_potency(mutativeness * 10)
seed.set_production(11 - (spread_cap / initial(spread_cap)) * 5) //Reverts spread_cap formula so resulting seed gets original production stat or equivalent back.
qdel(src)
>>>>>>> f8aad14ae87 (Harddel Fix Pack #42 + Better Live Reftracking Support (#63877))
/datum/spacevine_controller/process(delta_time)
if(!LAZYLEN(vines))
var/vine_count = length(vines)
if(!vine_count)
qdel(src) //space vines exterminated. Remove the controller
return
if(!growth_queue)
qdel(src) //Sanity check
return
<<<<<<< HEAD
var/length = round(clamp(delta_time * 0.5 * vines.len / spread_multiplier, 1, spread_cap))
var/i = 0
var/list/obj/structure/spacevine/queue_end = list()
@@ -521,9 +558,30 @@
SV.spread()
if(i >= length)
=======
var/spread_max = round(clamp(delta_time * 0.5 * vine_count / spread_multiplier, 1, spread_cap))
var/amount_processed = 0
for(var/obj/structure/spacevine/vine as anything in growth_queue)
growth_queue -= vine
queue_end += vine
for(var/datum/spacevine_mutation/mutation in vine.mutations)
mutation.process_mutation(vine)
if(vine.energy >= 2) //If tile is fully grown
vine.entangle_mob()
else if(DT_PROB(10, delta_time)) //If tile isn't fully grown
vine.grow()
vine.spread()
amount_processed++
if(amount_processed >= spread_max)
>>>>>>> f8aad14ae87 (Harddel Fix Pack #42 + Better Live Reftracking Support (#63877))
break
growth_queue = growth_queue + queue_end
//We can only do so much work per process, but we still want to process everything at some point
//So we shift the queue a bit
growth_queue += queue_end
/obj/structure/spacevine/proc/grow()
if(!energy)

View File

@@ -40,9 +40,17 @@
seed_modifier = round(seed.potency / 25)
var/obj/item/stack/cotton = new cotton_type(user.loc, 1 + seed_modifier)
var/old_cotton_amount = cotton.amount
for(var/obj/item/stack/ST in user.loc)
if(ST != cotton && istype(ST, cotton_type) && ST.amount < ST.max_amount)
ST.attackby(cotton, user)
for(var/obj/item/stack/potential_stack in user.loc)
if(QDELETED(potential_stack))
continue
if(potential_stack == cotton)
continue
if(!istype(potential_stack, cotton_type))
continue
if(potential_stack.amount >= potential_stack.max_amount)
continue
potential_stack.attackby(cotton, user)
if(cotton.amount > old_cotton_amount)
to_chat(user, span_notice("You add the newly-formed [cotton_name] to the stack. It now contains [cotton.amount] [cotton_name]."))
qdel(src)

View File

@@ -211,7 +211,7 @@
mutated_seed = new mutated_seed
for(var/datum/plant_gene/trait/trait in parent.myseed.genes)
if((trait.mutability_flags & PLANT_GENE_MUTATABLE) && trait.can_add(mutated_seed))
mutated_seed.genes += trait
mutated_seed.genes += trait.Copy()
t_prod = new t_prod(output_loc, mutated_seed)
t_prod.transform = initial(t_prod.transform)
t_prod.transform *= TRANSFORM_USING_VARIABLE(t_prod.seed.potency, 100) + 0.5

View File

@@ -43,11 +43,10 @@
charges--
if(!charges)
var/turf/T = get_turf(src)
T.visible_message(span_warning("The cover and contents of [src] start shifting and changing!"))
T.visible_message(span_warning("The cover and contents of [src] start shifting and changing! It slips out of your hands!"))
new /obj/item/book/manual/random(T)
qdel(src)
var/obj/item/book/manual/random/book = new(T)
user.put_in_active_hand(book)
/obj/item/language_manual/codespeak_manual
name = "codespeak manual"

View File

@@ -874,10 +874,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
observetarget = null
client?.perspective = initial(client.perspective)
sight = initial(sight)
if(target)
UnregisterSignal(target, COMSIG_MOVABLE_Z_CHANGED)
if(target.observers)
target.observers -= src
UNSETEMPTY(target.observers)
LAZYREMOVE(target.observers, src)
/mob/dead/observer/verb/observe()
set name = "Observe"
@@ -920,8 +919,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
RegisterSignal(mob_eye, COMSIG_MOVABLE_Z_CHANGED, .proc/on_observing_z_changed)
if(mob_eye.hud_used)
client.screen = list()
LAZYINITLIST(mob_eye.observers)
mob_eye.observers |= src
LAZYOR(mob_eye.observers, src)
mob_eye.hud_used.show_hud(mob_eye.hud_used.hud_version, src)
observetarget = mob_eye

View File

@@ -434,7 +434,7 @@
dna.temporary_mutations.Remove(mut)
continue
for(var/datum/mutation/human/HM in dna.mutations)
if(HM?.timed)
if(HM?.timeout)
dna.remove_mutation(HM.type)
/*

View File

@@ -40,6 +40,7 @@
remove_from_all_data_huds()
GLOB.mob_living_list -= src
QDEL_LAZYLIST(diseases)
QDEL_LIST(surgeries)
return ..()
/mob/living/onZImpact(turf/T, levels, message = TRUE)

View File

@@ -44,7 +44,8 @@
radiomod = ";" //AIs will, by default, state their laws on the internal radio.
///Used as a fake multitoool in tcomms machinery
var/obj/item/multitool/aiMulti
var/mob/living/simple_animal/bot/Bot
///Weakref to the bot the ai's commanding right now
var/datum/weakref/bot_ref
var/tracking = FALSE //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed.
var/datum/effect_system/spark_spread/spark_system //So they can initialize sparks whenever
@@ -222,7 +223,7 @@
QDEL_NULL(alert_control)
malfhack = null
current = null
Bot = null
bot_ref = null
controlled_equipment = null
linked_core = null
apc_override = null
@@ -503,16 +504,16 @@
to_chat(src, span_danger("Selected location is not visible."))
/mob/living/silicon/ai/proc/call_bot(turf/waypoint)
if(!Bot)
var/mob/living/simple_animal/bot/bot = bot_ref?.resolve()
if(!bot)
return
if(Bot.calling_ai && Bot.calling_ai != src) //Prevents an override if another AI is controlling this bot.
if(bot.calling_ai && bot.calling_ai != src) //Prevents an override if another AI is controlling this bot.
to_chat(src, span_danger("Interface error. Unit is already in use."))
return
to_chat(src, span_notice("Sending command to bot..."))
call_bot_cooldown = world.time + CALL_BOT_COOLDOWN
Bot.call_bot(src, waypoint)
bot.call_bot(src, waypoint)
call_bot_cooldown = 0
/mob/living/silicon/ai/proc/alarm_triggered(datum/source, alarm_type, area/source_area)

View File

@@ -58,20 +58,23 @@
if(!is_interactable(usr))
return
var/mob/living/simple_animal/bot/bot
switch(action)
if("callbot") //Command a bot to move to a selected location.
if(owner.call_bot_cooldown > world.time)
to_chat(usr, span_danger("Error: Your last call bot command is still processing, please wait for the bot to finish calculating a route."))
return
owner.Bot = locate(params["ref"]) in GLOB.bots_list
if(!owner.Bot || !(owner.Bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || owner.control_disabled)
bot = locate(params["ref"]) in GLOB.bots_list
owner.bot_ref = WEAKREF(bot)
if(!bot || !(bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || owner.control_disabled)
return
owner.waypoint_mode = TRUE
to_chat(usr, span_notice("Set your waypoint by clicking on a valid location free of obstructions."))
. = TRUE
if("interface") //Remotely connect to a bot!
owner.Bot = locate(params["ref"]) in GLOB.bots_list
if(!owner.Bot || !(owner.Bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || owner.control_disabled)
bot = locate(params["ref"]) in GLOB.bots_list
owner.bot_ref = WEAKREF(bot)
if(!bot || !(bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || owner.control_disabled)
return
owner.Bot.attack_ai(usr)
bot.attack_ai(usr)
. = TRUE

View File

@@ -148,6 +148,7 @@
QDEL_NULL(inv1)
QDEL_NULL(inv2)
QDEL_NULL(inv3)
QDEL_NULL(hands)
QDEL_NULL(spark_system)
QDEL_NULL(alert_control)
cell = null

View File

@@ -43,7 +43,10 @@
/mob/living/simple_animal/robot_customer/Destroy()
var/datum/venue/attending_venue = ai_controller.blackboard[BB_CUSTOMER_ATTENDING_VENUE]
attending_venue.current_visitors -= src
attending_venue.linked_seats[ai_controller.blackboard[BB_CUSTOMER_MY_SEAT]] = null
var/datum/weakref/seat_ref = ai_controller.blackboard[BB_CUSTOMER_MY_SEAT]
var/obj/structure/holosign/robot_seat/our_seat = seat_ref?.resolve()
if(attending_venue.linked_seats[our_seat])
attending_venue.linked_seats[our_seat] = null
QDEL_NULL(hud_to_show_on_hover)
return ..()

View File

@@ -25,6 +25,9 @@
footstep_type = FOOTSTEP_MOB_SHOE
/mob/living/simple_animal/hostile/boss/paper_wizard/Destroy()
QDEL_LIST(copies)
return ..()
//Summon Ability
//Lets the wizard summon his art to fight for him
@@ -126,13 +129,18 @@
loot = list()
var/mob/living/simple_animal/hostile/boss/paper_wizard/original
/mob/living/simple_animal/hostile/boss/paper_wizard/copy/Destroy()
if(original)
original.copies -= src
original = null
return ..()
//Hit a fake? eat pain!
/mob/living/simple_animal/hostile/boss/paper_wizard/copy/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
if(amount > 0) //damage
if(original)
original.minimum_distance = 3
original.retreat_distance = 3
original.copies -= src
for(var/c in original.copies)
qdel(c)
for(var/mob/living/L in range(5,src))

View File

@@ -27,6 +27,7 @@
/mob/Login()
if(!client)
return FALSE
canon_client = client
add_to_player_list()
lastKnownIP = client.address
computer_id = client.computer_id
@@ -62,8 +63,6 @@
if(!client)
return FALSE
//We do this here to prevent hanging refs from ghostize or whatever, since if we were in another mob before this'll take care of it
clear_important_client_contents(client)
enable_client_mobs_in_contents(client)
SEND_SIGNAL(src, COMSIG_MOB_LOGIN)

View File

@@ -4,17 +4,11 @@
SStgui.on_logout(src)
unset_machine()
remove_from_player_list()
clear_client_in_contents()
..()
if(loc)
loc.on_log(FALSE)
if(client)
for(var/foo in client.player_details.post_logout_callbacks)
var/datum/callback/CB = foo
CB.Invoke()
clear_important_client_contents(client)
become_uncliented()
return TRUE

View File

@@ -1054,6 +1054,9 @@
if(oldname == newname)
log_message("[src] failed name change as the new name was the same as the old one: [oldname]", LOG_OWNERSHIP)
return FALSE
if(!istext(newname) && !isnull(newname))
stack_trace("[src] attempted to change its name from [oldname] to the non string value [newname]")
return FALSE
log_message("[src] name changed from [oldname] to [newname]", LOG_OWNERSHIP)
@@ -1339,11 +1342,22 @@
SIGNAL_HANDLER
set_active_storage(null)
///clears the client mob in our client_mobs_in_contents list
/mob/proc/clear_client_in_contents()
if(client?.movingmob)
LAZYREMOVE(client.movingmob.client_mobs_in_contents, src)
client.movingmob = null
/// Cleanup proc that's called when a mob loses a client, either through client destroy or logout
/// Logout happens post client del, so we can't just copypaste this there. This keeps things clean and consistent
/mob/proc/become_uncliented()
if(!canon_client)
return
for(var/foo in canon_client.player_details.post_logout_callbacks)
var/datum/callback/CB = foo
CB.Invoke()
if(canon_client?.movingmob)
LAZYREMOVE(canon_client.movingmob.client_mobs_in_contents, src)
canon_client.movingmob = null
clear_important_client_contents()
canon_client = null
///Shows a tgui window with memories
/mob/verb/memory()

View File

@@ -17,6 +17,13 @@
throwforce = 10
blocks_emissive = EMISSIVE_BLOCK_GENERIC
pass_flags_self = PASSMOB
/// The current client inhabiting this mob. Managed by login/logout
/// This exists so we can do cleanup in logout for occasions where a client was transfere rather then destroyed
/// We need to do this because the mob on logout never actually has a reference to client
/// We also need to clear this var/do other cleanup in client/Destroy, since that happens before logout
/// HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
var/client/canon_client
var/shift_to_open_context_menu = TRUE
///when this be added to vis_contents of something it inherit something.plane, important for visualisation of mob in openspace.

View File

@@ -65,6 +65,9 @@
/obj/item/modular_computer/Destroy()
wipe_program(forced = TRUE)
for(var/datum/computer_file/program/idle as anything in idle_threads)
idle.kill_program(TRUE)
idle_threads.Cut()
STOP_PROCESSING(SSobj, src)
for(var/port in all_components)
var/obj/item/computer_hardware/component = all_components[port]

View File

@@ -15,25 +15,19 @@
tgui_id = "NtosConfiguration"
program_icon = "cog"
var/obj/item/modular_computer/movable = null
/datum/computer_file/program/computerconfig/ui_data(mob/user)
movable = computer
var/obj/item/computer_hardware/hard_drive/hard_drive = movable.all_components[MC_HDD]
var/obj/item/computer_hardware/battery/battery_module = movable.all_components[MC_CELL]
if(!istype(movable))
movable = null
// No computer connection, we can't get data from that.
if(!movable)
if(!computer)
return 0
var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD]
var/obj/item/computer_hardware/battery/battery_module = computer.all_components[MC_CELL]
var/list/data = get_header_data()
data["disk_size"] = hard_drive.max_capacity
data["disk_used"] = hard_drive.used_capacity
data["power_usage"] = movable.last_power_usage
data["power_usage"] = computer.last_power_usage
data["battery_exists"] = battery_module ? 1 : 0
if(battery_module?.battery)
data["battery_rating"] = battery_module.battery.maxcharge
@@ -43,8 +37,8 @@
data["battery"] = list("max" = battery_module.battery.maxcharge, "charge" = round(battery_module.battery.charge))
var/list/all_entries[0]
for(var/I in movable.all_components)
var/obj/item/computer_hardware/H = movable.all_components[I]
for(var/I in computer.all_components)
var/obj/item/computer_hardware/H = computer.all_components[I]
all_entries.Add(list(list(
"name" = H.name,
"desc" = H.desc,
@@ -63,7 +57,7 @@
return
switch(action)
if("PC_toggle_component")
var/obj/item/computer_hardware/H = movable.find_hardware_by_name(params["name"])
var/obj/item/computer_hardware/H = computer.find_hardware_by_name(params["name"])
if(H && istype(H))
H.enabled = !H.enabled
. = TRUE

View File

@@ -18,7 +18,6 @@
var/download_completion = FALSE //GQ of downloaded data.
var/download_netspeed = 0
var/downloaderror = ""
var/obj/item/modular_computer/my_computer = null
var/emagged = FALSE
var/list/main_repo
var/list/antag_repo
@@ -130,9 +129,7 @@
return FALSE
/datum/computer_file/program/ntnetdownload/ui_data(mob/user)
my_computer = computer
if(!istype(my_computer))
if(!istype(computer))
return
var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
var/list/access = card_slot?.GetAccess()
@@ -150,7 +147,7 @@
data["downloadspeed"] = download_netspeed
data["downloadcompletion"] = round(download_completion, 0.1)
var/obj/item/computer_hardware/hard_drive/hard_drive = my_computer.all_components[MC_HDD]
var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD]
data["disk_size"] = hard_drive.max_capacity
data["disk_used"] = hard_drive.used_capacity
data["emagged"] = emagged

View File

@@ -15,8 +15,8 @@
program_icon = "plug"
var/has_alert = 0
var/obj/structure/cable/attached_wire
var/obj/machinery/power/apc/local_apc
var/datum/weakref/attached_wire_ref
var/datum/weakref/local_apc_ref
var/list/history = list()
var/record_size = 60
var/record_interval = 50
@@ -38,19 +38,22 @@
/datum/computer_file/program/power_monitor/proc/search() //keep in sync with /obj/machinery/computer/monitor's version
var/turf/T = get_turf(computer)
attached_wire = locate(/obj/structure/cable) in T
if(attached_wire)
attached_wire_ref = WEAKREF(locate(/obj/structure/cable) in T)
if(attached_wire_ref)
return
var/area/A = get_area(computer) //if the computer isn't directly connected to a wire, attempt to find the APC powering it to pull it's powernet instead
if(!A)
return
local_apc = A.apc
var/obj/machinery/power/apc/local_apc = WEAKREF(A.apc)
if(!local_apc)
return
if(!local_apc.terminal) //this really shouldn't happen without badminnery.
local_apc = null
local_apc_ref = WEAKREF(local_apc)
/datum/computer_file/program/power_monitor/proc/get_powernet() //keep in sync with /obj/machinery/computer/monitor's version
var/obj/structure/cable/attached_wire = attached_wire_ref?.resolve()
var/obj/machinery/power/apc/local_apc = local_apc_ref?.resolve()
if(attached_wire || (local_apc?.terminal))
return attached_wire ? attached_wire.powernet : local_apc.terminal.powernet
return FALSE

View File

@@ -12,8 +12,6 @@
program_icon = "robot"
///Number of simple robots on-station.
var/botcount = 0
///Used to find the location of the user for the purposes of summoning robots.
var/mob/current_user
///Access granted by the used to summon robots.
var/list/current_access = list()
@@ -33,12 +31,11 @@
data["access_on_card"] = id_card ? id_card.access : null
botcount = 0
current_user = user
for(var/mob/living/simple_animal/bot/simple_bot as anything in GLOB.bots_list)
if(simple_bot.z != zlevel || !(simple_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) //Only non-emagged bots on the same Z-level are detected!
continue
if(computer && !simple_bot.check_access(current_user)) // Only check Bots we can access)
if(computer && !simple_bot.check_access(user)) // Only check Bots we can access)
continue
var/list/newbot = list(
"name" = simple_bot.name,
@@ -71,10 +68,11 @@
return data
/datum/computer_file/program/robocontrol/ui_act(action, list/params)
/datum/computer_file/program/robocontrol/ui_act(action, list/params, datum/tgui/ui)
. = ..()
if(.)
return
var/mob/current_user = ui.user
var/obj/item/computer_hardware/card_slot/card_slot
var/obj/item/card/id/id_card
if(computer)

View File

@@ -14,12 +14,6 @@
size = 5
tgui_id = "NtosRobotact"
program_icon = "terminal"
///A typed reference to the computer, specifying the borg tablet type
var/obj/item/modular_computer/tablet/integrated/tablet
/datum/computer_file/program/robotact/Destroy()
tablet = null
return ..()
/datum/computer_file/program/robotact/run_program(mob/living/user)
if(!istype(computer, /obj/item/modular_computer/tablet/integrated))
@@ -27,7 +21,7 @@
return FALSE
. = ..()
if(.)
tablet = computer
var/obj/item/modular_computer/tablet/integrated/tablet = computer
if(tablet.device_theme == "syndicate")
program_icon_state = "command-syndicate"
return TRUE
@@ -37,6 +31,10 @@
var/list/data = get_header_data()
if(!iscyborg(user))
return data
//Implied, since we can't run on non tablets
var/obj/item/modular_computer/tablet/integrated/tablet = computer
var/mob/living/silicon/robot/borgo = tablet.borgo
data["name"] = borgo.name
@@ -80,6 +78,8 @@
if(!iscyborg(user))
return data
var/mob/living/silicon/robot/borgo = user
//Implied
var/obj/item/modular_computer/tablet/integrated/tablet = computer
data["Laws"] = borgo.laws.get_law_list(TRUE, TRUE, FALSE)
data["borgLog"] = tablet.borglog
@@ -90,7 +90,8 @@
. = ..()
if(.)
return
//Implied type, memes
var/obj/item/modular_computer/tablet/integrated/tablet = computer
var/mob/living/silicon/robot/borgo = tablet.borgo
switch(action)
@@ -149,7 +150,9 @@
* law changes and borg log additions.
*/
/datum/computer_file/program/robotact/proc/force_full_update()
if(tablet)
if(!istype(computer, /obj/item/modular_computer/tablet/integrated))
return
var/obj/item/modular_computer/tablet/integrated/tablet = computer
var/datum/tgui/active_ui = SStgui.get_open_ui(tablet.borgo, src)
if(active_ui)
active_ui.send_full_update()

View File

@@ -15,7 +15,8 @@
program_icon = "eye"
var/list/network = list("ss13")
var/obj/machinery/camera/active_camera
/// Weakref to the active camera
var/datum/weakref/camera_ref
/// The turf where the camera was last updated.
var/turf/last_camera_turf
var/list/concurrent_users = list()
@@ -92,6 +93,7 @@
var/list/data = get_header_data()
data["network"] = network
data["activeCamera"] = null
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
if(active_camera)
data["activeCamera"] = list(
name = active_camera.c_tag,
@@ -121,7 +123,7 @@
var/c_tag = params["name"]
var/list/cameras = get_available_cameras()
var/obj/machinery/camera/selected_camera = cameras[c_tag]
active_camera = selected_camera
camera_ref = WEAKREF(selected_camera)
playsound(src, get_sfx("terminal_type"), 25, FALSE)
if(!selected_camera)
@@ -141,10 +143,11 @@
user.client.clear_map(map_name)
// Turn off the console
if(length(concurrent_users) == 0 && is_living)
active_camera = null
camera_ref = null
playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE)
/datum/computer_file/program/secureye/proc/update_active_camera_screen()
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
// Show static if can't use the camera
if(!active_camera?.can_use())
show_camera_static()

View File

@@ -12,6 +12,8 @@
/obj/item/computer_hardware/hard_drive/on_remove(obj/item/modular_computer/remove_from, mob/user)
remove_from.shutdown_computer()
for(var/datum/computer_file/program/program in stored_files)
program.computer = null
/obj/item/computer_hardware/hard_drive/proc/install_default_programs()
store_file(new/datum/computer_file/program/computerconfig(src)) // Computer configuration utility, allows hardware control and displays more info than status bar

View File

@@ -22,6 +22,16 @@
var/list/ticket_holders = list()
var/list/obj/item/ticket_machine_ticket/tickets = list()
/obj/machinery/ticket_machine/Initialize(mapload)
. = ..()
update_appearance()
/obj/machinery/ticket_machine/Destroy()
for(var/obj/item/ticket_machine_ticket/ticket in tickets)
ticket.source = null
tickets.Cut()
return ..()
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
/obj/machinery/ticket_machine/multitool_act(mob/living/user, obj/item/I)
@@ -46,10 +56,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
tickets.Cut()
update_appearance()
/obj/machinery/ticket_machine/Initialize(mapload)
. = ..()
update_appearance()
/obj/machinery/ticket_machine/proc/increment()
if(current_number > ticket_number)
return
@@ -178,21 +184,21 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
if(ticket_number >= max_number)
to_chat(user,span_warning("Ticket supply depleted, please refill this unit with a hand labeller refill cartridge!"))
return
if((user in ticket_holders) && !(obj_flags & EMAGGED))
var/user_ref = REF(user)
if((user_ref in ticket_holders) && !(obj_flags & EMAGGED))
to_chat(user, span_warning("You already have a ticket!"))
return
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 100, FALSE)
ticket_number ++
ticket_number++
to_chat(user, span_notice("You take a ticket from [src], looks like you're ticket number #[ticket_number]..."))
var/obj/item/ticket_machine_ticket/theirticket = new /obj/item/ticket_machine_ticket(get_turf(src))
theirticket.name = "Ticket #[ticket_number]"
theirticket.maptext = MAPTEXT(ticket_number)
theirticket.saved_maptext = MAPTEXT(ticket_number)
theirticket.ticket_number = ticket_number
theirticket.source = src
theirticket.owner = user
theirticket.owner_ref = user_ref
user.put_in_hands(theirticket)
ticket_holders += user
ticket_holders += user_ref
tickets += theirticket
if(obj_flags & EMAGGED) //Emag the machine to destroy the HOP's life.
ready = FALSE
@@ -214,9 +220,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
resistance_flags = FLAMMABLE
max_integrity = 50
var/saved_maptext = null
var/mob/living/carbon/owner
var/owner_ref // A ref to our owner. Doesn't need to be weak because mobs have unique refs
var/obj/machinery/ticket_machine/source
var/ticket_number
/obj/item/ticket_machine_ticket/attack_hand(mob/user, list/modifiers)
. = ..()
@@ -233,9 +238,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/ticket_machine, 32)
update_appearance()
/obj/item/ticket_machine_ticket/Destroy()
if(owner && source)
source.ticket_holders -= owner
source.tickets[ticket_number] = null
owner = null
if(source)
source.ticket_holders -= owner_ref
source.tickets -= src
source = null
return ..()

View File

@@ -1,7 +1,9 @@
/datum/picture
var/picture_name = "picture"
var/picture_desc = "This is a picture."
/// List of weakrefs pointing at mobs that appear in this photo
var/list/mobs_seen = list()
/// List of weakrefs pointing at dead mobs that appear in this photo
var/list/dead_seen = list()
var/caption
var/icon/picture_image
@@ -20,9 +22,11 @@
if(!isnull(desc))
picture_desc = desc
if(!isnull(mobs_spotted))
mobs_seen = mobs_spotted
for(var/mob/seen as anything in mobs_spotted)
mobs_seen += WEAKREF(seen)
if(!isnull(dead_spotted))
dead_seen = dead_spotted
for(var/mob/seen as anything in dead_spotted)
dead_seen += WEAKREF(seen)
if(!isnull(image))
picture_image = image
if(!isnull(icon))

View File

@@ -35,8 +35,13 @@
if(!P.see_ghosts) ///Dont bother with this last bit if we can't see ghosts
return
for(var/i in P.mobs_seen) //Any ghosts in the pic? its a haunted photo ooooo~
if(isobserver(i))
for(var/datum/weakref/seen_ref in P.mobs_seen) //Any ghosts in the pic? its a haunted photo ooooo~
var/mob/seen = seen_ref.resolve()
if(!seen)
P.mobs_seen -= seen_ref
continue
if(!isobserver(seen))
continue
set_custom_materials(list(/datum/material/hauntium = 2000))
break

View File

@@ -142,7 +142,7 @@
new_light = new /obj/machinery/light/small/built(loc)
new_light.setDir(dir)
transfer_fingerprints_to(new_light)
if(cell)
if(!QDELETED(cell))
new_light.cell = cell
cell.forceMove(new_light)
cell = null

View File

@@ -38,8 +38,7 @@
*/
/obj/item/gun/medbeam/proc/LoseTarget()
if(active)
qdel(current_beam)
current_beam = null
QDEL_NULL(current_beam)
active = FALSE
on_beam_release(current_target)
current_target = null
@@ -51,6 +50,7 @@
*/
/obj/item/gun/medbeam/proc/beam_died()
SIGNAL_HANDLER
current_beam = null
active = FALSE //skip qdelling the beam again if we're doing this proc, because
if(isliving(loc))
to_chat(loc, span_warning("You lose control of the beam!"))
@@ -87,7 +87,7 @@
last_check = world.time
if(!los_check(loc, current_target))
qdel(current_beam)//this will give the target lost message
QDEL_NULL(current_beam)//this will give the target lost message
return
if(current_target)

View File

@@ -1992,6 +1992,10 @@
reagents = new /datum/reagents(max_vol, flags)
reagents.my_atom = src
/atom/movable/chem_holder
name = "This atom exists to hold chems. If you can see this, make an issue report"
desc = "God this is stupid"
#undef REAGENT_TRANSFER_AMOUNT
#undef REAGENT_PURITY

View File

@@ -27,8 +27,10 @@
use_power = IDLE_POWER_USE
circuit = /obj/item/circuitboard/machine/experimentor
var/recentlyExperimented = 0
var/mob/trackedIan
var/mob/trackedRuntime
/// Weakref to the first ian we can find at init
var/datum/weakref/tracked_ian_ref
/// Weakref to the first runtime we can find at init
var/datum/weakref/tracked_runtime_ref
var/badThingCoeff = 0
var/resetTime = 15
var/cloneMode = FALSE
@@ -72,8 +74,8 @@
/obj/machinery/rnd/experimentor/Initialize(mapload)
. = ..()
trackedIan = locate(/mob/living/simple_animal/pet/dog/corgi/ian) in GLOB.mob_living_list
trackedRuntime = locate(/mob/living/simple_animal/pet/cat/runtime) in GLOB.mob_living_list
tracked_ian_ref = WEAKREF(locate(/mob/living/simple_animal/pet/dog/corgi/ian) in GLOB.mob_living_list)
tracked_runtime_ref = WEAKREF(locate(/mob/living/simple_animal/pet/cat/runtime) in GLOB.mob_living_list)
SetTypeReactions()
critical_items_typecache = typecacheof(list(
@@ -477,9 +479,10 @@
if(globalMalf > 16 && globalMalf < 35)
visible_message(span_warning("[src] melts [exp_on], ian-izing the air around it!"))
throwSmoke(loc)
if(trackedIan)
throwSmoke(trackedIan.loc)
trackedIan.forceMove(loc)
var/mob/living/tracked_ian = tracked_ian_ref?.resolve()
if(tracked_ian)
throwSmoke(tracked_ian.loc)
tracked_ian.forceMove(loc)
investigate_log("Experimentor has stolen Ian!", INVESTIGATE_EXPERIMENTOR) //...if anyone ever fixes it...
else
new /mob/living/simple_animal/pet/dog/corgi(loc)
@@ -494,9 +497,10 @@
if(globalMalf > 51 && globalMalf < 75)
visible_message(span_warning("[src] encounters a run-time error!"))
throwSmoke(loc)
if(trackedRuntime)
throwSmoke(trackedRuntime.loc)
trackedRuntime.forceMove(drop_location())
var/mob/living/tracked_runtime = tracked_runtime_ref?.resolve()
if(tracked_runtime)
throwSmoke(tracked_runtime.loc)
tracked_runtime.forceMove(drop_location())
investigate_log("Experimentor has stolen Runtime!", INVESTIGATE_EXPERIMENTOR)
else
new /mob/living/simple_animal/pet/cat(loc)

View File

@@ -30,13 +30,6 @@
desc = "A computer used for remotely handling slimes."
networks = list("ss13")
circuit = /obj/item/circuitboard/computer/xenobiology
var/datum/action/innate/slime_place/slime_place_action
var/datum/action/innate/slime_pick_up/slime_up_action
var/datum/action/innate/feed_slime/feed_slime_action
var/datum/action/innate/monkey_recycle/monkey_recycle_action
var/datum/action/innate/slime_scan/scan_action
var/datum/action/innate/feed_potion/potion_action
var/datum/action/innate/hotkey_help/hotkey_help
var/obj/machinery/monkey_recycler/connected_recycler
var/list/stored_slimes
@@ -51,13 +44,14 @@
/obj/machinery/computer/camera_advanced/xenobio/Initialize(mapload)
. = ..()
slime_place_action = new
slime_up_action = new
feed_slime_action = new
monkey_recycle_action = new
scan_action = new
potion_action = new
hotkey_help = new
actions += new /datum/action/innate/slime_place(src)
actions += new /datum/action/innate/slime_pick_up(src)
actions += new /datum/action/innate/feed_slime(src)
actions += new /datum/action/innate/monkey_recycle(src)
actions += new /datum/action/innate/slime_scan(src)
actions += new /datum/action/innate/feed_potion(src)
actions += new /datum/action/innate/hotkey_help(src)
stored_slimes = list()
for(var/obj/machinery/monkey_recycler/recycler in GLOB.monkey_recyclers)
if(get_area(recycler.loc) == get_area(loc))
@@ -91,42 +85,6 @@
/obj/machinery/computer/camera_advanced/xenobio/GrantActions(mob/living/user)
..()
if(slime_up_action)
slime_up_action.target = src
slime_up_action.Grant(user)
actions += slime_up_action
if(slime_place_action)
slime_place_action.target = src
slime_place_action.Grant(user)
actions += slime_place_action
if(feed_slime_action)
feed_slime_action.target = src
feed_slime_action.Grant(user)
actions += feed_slime_action
if(monkey_recycle_action)
monkey_recycle_action.target = src
monkey_recycle_action.Grant(user)
actions += monkey_recycle_action
if(scan_action)
scan_action.target = src
scan_action.Grant(user)
actions += scan_action
if(potion_action)
potion_action.target = src
potion_action.Grant(user)
actions += potion_action
if(hotkey_help)
hotkey_help.target = src
hotkey_help.Grant(user)
actions += hotkey_help
RegisterSignal(user, COMSIG_XENO_SLIME_CLICK_CTRL, .proc/XenoSlimeClickCtrl)
RegisterSignal(user, COMSIG_XENO_TURF_CLICK_CTRL, .proc/XenoTurfClickCtrl)
RegisterSignal(user, COMSIG_XENO_MONKEY_CLICK_CTRL, .proc/XenoMonkeyClickCtrl)

View File

@@ -8,8 +8,6 @@
move_up_action = null
move_down_action = null
var/datum/action/innate/shuttledocker_rotate/rotate_action = new
var/datum/action/innate/shuttledocker_place/place_action = new
var/shuttleId = ""
var/shuttlePortId = ""
var/shuttlePortName = "custom location"
@@ -29,6 +27,8 @@
/obj/machinery/computer/camera_advanced/shuttle_docker/Initialize(mapload)
. = ..()
GLOB.navigation_computers += src
actions += new /datum/action/innate/shuttledocker_rotate(src)
actions += new /datum/action/innate/shuttledocker_place(src)
if(!mapload)
connect_to_shuttle(SSshuttle.get_containing_shuttle(src))
@@ -68,19 +68,9 @@
/obj/machinery/computer/camera_advanced/shuttle_docker/GrantActions(mob/living/user)
if(jumpto_ports.len)
jump_action = new /datum/action/innate/camera_jump/shuttle_docker
actions += new /datum/action/innate/camera_jump/shuttle_docker(src)
..()
if(rotate_action)
rotate_action.target = user
rotate_action.Grant(user)
actions += rotate_action
if(place_action)
place_action.target = user
place_action.Grant(user)
actions += place_action
/obj/machinery/computer/camera_advanced/shuttle_docker/CreateEye()
shuttle_port = SSshuttle.getShuttle(shuttleId)
if(QDELETED(shuttle_port))
@@ -321,10 +311,9 @@
button_icon_state = "mech_cycle_equip_off"
/datum/action/innate/shuttledocker_rotate/Activate()
if(QDELETED(target) || !isliving(target))
if(QDELETED(owner) || !isliving(owner))
return
var/mob/living/C = target
var/mob/camera/ai_eye/remote/remote_eye = C.remote_control
var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin
origin.rotateLandingSpot()
@@ -334,22 +323,20 @@
button_icon_state = "mech_zoom_off"
/datum/action/innate/shuttledocker_place/Activate()
if(QDELETED(target) || !isliving(target))
if(QDELETED(owner) || !isliving(owner))
return
var/mob/living/C = target
var/mob/camera/ai_eye/remote/remote_eye = C.remote_control
var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/shuttle_docker/origin = remote_eye.origin
origin.placeLandingSpot(target)
origin.placeLandingSpot(owner)
/datum/action/innate/camera_jump/shuttle_docker
name = "Jump to Location"
button_icon_state = "camera_jump"
/datum/action/innate/camera_jump/shuttle_docker/Activate()
if(QDELETED(target) || !isliving(target))
if(QDELETED(owner) || !isliving(owner))
return
var/mob/living/C = target
var/mob/camera/ai_eye/remote/remote_eye = C.remote_control
var/mob/camera/ai_eye/remote/remote_eye = owner.remote_control
var/obj/machinery/computer/camera_advanced/shuttle_docker/console = remote_eye.origin
playsound(console, 'sound/machines/terminal_prompt_deny.ogg', 25, FALSE)
@@ -382,7 +369,7 @@
if(isnull(selected))
playsound(console, 'sound/machines/terminal_prompt_deny.ogg', 25, FALSE)
return
if(QDELETED(src) || QDELETED(target) || !isliving(target))
if(QDELETED(src) || QDELETED(owner) || !isliving(owner))
return
playsound(src, "terminal_type", 25, FALSE)
var/turf/T = get_turf(L[selected])
@@ -390,6 +377,6 @@
return
playsound(console, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE)
remote_eye.setLoc(T)
to_chat(target, span_notice("Jumped to [selected]."))
C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
C.clear_fullscreen("flash", 3)
to_chat(owner, span_notice("Jumped to [selected]."))
owner.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
owner.clear_fullscreen("flash", 3)

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