|
286 | 286 | . = ..()
|
287 | 287 | if(active && AM != monitor.hasprox_receiver && !(AM in monitor.nested_receiver_locs))
|
288 | 288 | monitor.hasprox_receiver.HasProximity(AM)
|
| 289 | + |
| 290 | +/// A custom proximity monitor used for tracking players around a table of cards. |
| 291 | + |
| 292 | +/datum/component/proximity_monitor/table |
| 293 | + /// How far away you can be (in terms of table squares). |
| 294 | + var/max_table_distance |
| 295 | + /// How far away you can be (euclidean distance). |
| 296 | + var/max_total_distance |
| 297 | + /// The UID of the deck |
| 298 | + var/deck_uid |
| 299 | + /// Whether the monitors created should be visible. Used for debugging. |
| 300 | + var/monitors_visible = FALSE |
| 301 | + |
| 302 | +/datum/component/proximity_monitor/table/Initialize(_radius = 1, _always_active = FALSE, _max_table_distance = 5) |
| 303 | + max_table_distance = _max_table_distance |
| 304 | + max_total_distance = _max_table_distance |
| 305 | + . = ..(_radius, _always_active) |
| 306 | + if(istype(parent, /obj/item/deck)) |
| 307 | + // this is important for tracking traits and attacking multiple cards. so it's not a true UID, sue me |
| 308 | + var/obj/item/deck/D = parent |
| 309 | + deck_uid = D.main_deck_id |
| 310 | + else |
| 311 | + deck_uid = parent.UID() |
| 312 | + addtimer(CALLBACK(src, PROC_REF(refresh)), 0.5 SECONDS) |
| 313 | + |
| 314 | +/datum/component/proximity_monitor/table/proc/refresh() |
| 315 | + var/list/tables = list() |
| 316 | + var/list/prox_mon_spots = list() |
| 317 | + crawl_along(get_turf(parent), tables, prox_mon_spots, 0) |
| 318 | + QDEL_LIST_CONTENTS(proximity_checkers) |
| 319 | + create_prox_checkers() |
| 320 | + |
| 321 | +/// Crawl along an extended table, and return a list of all turfs that we should start tracking. |
| 322 | +/datum/component/proximity_monitor/table/proc/crawl_along(turf/current_turf, list/visited_tables = list(), list/prox_mon_spots = list(), distance_from_start) |
| 323 | + var/obj/structure/current_table = locate(/obj/structure/table) in current_turf |
| 324 | + |
| 325 | + if(QDELETED(current_table)) |
| 326 | + // if there's no table here, we're still adjacent to a table, so this is a spot you could play from |
| 327 | + prox_mon_spots |= current_turf |
| 328 | + return |
| 329 | + |
| 330 | + if(current_table in visited_tables) |
| 331 | + return |
| 332 | + |
| 333 | + visited_tables |= current_table |
| 334 | + prox_mon_spots |= current_turf |
| 335 | + |
| 336 | + if(distance_from_start + 1 > max_table_distance) |
| 337 | + return |
| 338 | + |
| 339 | + for(var/direction in GLOB.alldirs) |
| 340 | + var/turf/next_turf = get_step(current_table, direction) |
| 341 | + if(!istype(next_turf)) |
| 342 | + stack_trace("Failed to proceed in direction [dir2text(direction)] when building card proximity monitors.") |
| 343 | + continue |
| 344 | + if(get_dist_euclidian(get_turf(parent), next_turf) > max_total_distance) |
| 345 | + continue |
| 346 | + .(next_turf, visited_tables, prox_mon_spots, distance_from_start + 1) |
| 347 | + |
| 348 | +/datum/component/proximity_monitor/table/create_prox_checkers() |
| 349 | + update_prox_checkers(FALSE) |
| 350 | + |
| 351 | +/** |
| 352 | + * Update the proximity monitors making up this component. |
| 353 | + * Arguments: |
| 354 | + * * clear_existing - If true, any existing proximity monitors attached to this will be deleted. |
| 355 | + */ |
| 356 | +/datum/component/proximity_monitor/table/proc/update_prox_checkers(clear_existing = TRUE) |
| 357 | + var/list/tables = list() |
| 358 | + var/list/prox_mon_spots = list() |
| 359 | + if(length(proximity_checkers)) |
| 360 | + QDEL_LIST_CONTENTS(proximity_checkers) |
| 361 | + |
| 362 | + var/atom/movable/atom_parent = parent |
| 363 | + |
| 364 | + // if we don't have a parent, just treat it normally |
| 365 | + if(!isturf(atom_parent.loc) || !locate(/obj/structure/table) in get_turf(parent)) |
| 366 | + return |
| 367 | + |
| 368 | + |
| 369 | + LAZYINITLIST(proximity_checkers) |
| 370 | + crawl_along(get_turf(parent), tables, prox_mon_spots, 0) |
| 371 | + |
| 372 | + // For whatever reason their turf is null. Create the checkers in nullspace for now. When the parent moves to a valid turf, they can be recenetered. |
| 373 | + for(var/T in prox_mon_spots) |
| 374 | + create_single_prox_checker(T, /obj/effect/abstract/proximity_checker/table) |
| 375 | + |
| 376 | + for(var/atom/table in tables) |
| 377 | + RegisterSignal(table, COMSIG_PARENT_QDELETING, PROC_REF(on_table_qdel), TRUE) |
| 378 | + |
| 379 | +/datum/component/proximity_monitor/table/on_receiver_move(datum/source, atom/old_loc, dir) |
| 380 | + update_prox_checkers() |
| 381 | + |
| 382 | +/datum/component/proximity_monitor/table/RegisterWithParent() |
| 383 | + if(ismovable(hasprox_receiver)) |
| 384 | + RegisterSignal(hasprox_receiver, COMSIG_MOVABLE_MOVED, PROC_REF(on_receiver_move)) |
| 385 | + |
| 386 | +/datum/component/proximity_monitor/table/proc/on_table_qdel() |
| 387 | + SIGNAL_HANDLER // COMSIG_PARENT_QDELETED |
| 388 | + update_prox_checkers() |
| 389 | + |
| 390 | +/obj/effect/abstract/proximity_checker/table |
| 391 | + /// The UID for the deck, used in the setting and removal of traits |
| 392 | + var/deck_uid |
| 393 | + |
| 394 | +/obj/effect/abstract/proximity_checker/table/Initialize(mapload, datum/component/proximity_monitor/table/P) |
| 395 | + . = ..() |
| 396 | + deck_uid = P.deck_uid |
| 397 | + // catch any mobs on our tile |
| 398 | + for(var/mob/living/L in get_turf(src)) |
| 399 | + register_on_mob(L) |
| 400 | + |
| 401 | + if(P.monitors_visible) |
| 402 | + icon = 'icons/obj/playing_cards.dmi' |
| 403 | + icon_state = "tarot_the_unknown" |
| 404 | + invisibility = INVISIBILITY_MINIMUM |
| 405 | + layer = MOB_LAYER |
| 406 | + |
| 407 | +/obj/effect/abstract/proximity_checker/table/Destroy() |
| 408 | + var/obj/effect/abstract/proximity_checker/table/same_monitor |
| 409 | + for(var/obj/effect/abstract/proximity_checker/table/mon in get_turf(src)) |
| 410 | + if(mon != src && mon.deck_uid == src) |
| 411 | + // if we have another monitor on our space that shares our deck, |
| 412 | + // transfer the signals to it. |
| 413 | + same_monitor = mon |
| 414 | + |
| 415 | + for(var/mob/living/L in get_turf(src)) |
| 416 | + remove_from_mob(L) |
| 417 | + if(!isnull(same_monitor)) |
| 418 | + same_monitor.register_on_mob(L) |
| 419 | + return ..() |
| 420 | + |
| 421 | +/obj/effect/abstract/proximity_checker/table/proc/register_on_mob(mob/living/L) |
| 422 | + ADD_TRAIT(L, TRAIT_PLAYING_CARDS, "deck_[deck_uid]") |
| 423 | + RegisterSignal(L, COMSIG_MOVABLE_MOVED, PROC_REF(on_move_from_monitor), TRUE) |
| 424 | + RegisterSignal(L, COMSIG_PARENT_QDELETING, PROC_REF(remove_from_mob), TRUE) |
| 425 | + |
| 426 | + |
| 427 | +/obj/effect/abstract/proximity_checker/table/proc/remove_from_mob(mob/living/L) |
| 428 | + if(QDELETED(L)) |
| 429 | + return |
| 430 | + // otherwise, clean up |
| 431 | + REMOVE_TRAIT(L, TRAIT_PLAYING_CARDS, "deck_[deck_uid]") |
| 432 | + UnregisterSignal(L, COMSIG_MOVABLE_MOVED) |
| 433 | + |
| 434 | +/obj/effect/abstract/proximity_checker/table/Crossed(atom/movable/AM, oldloc) |
| 435 | + if(!isliving(AM)) |
| 436 | + return |
| 437 | + |
| 438 | + var/mob/mover = AM |
| 439 | + |
| 440 | + // This should hopefully ensure that multiple decks around each other don't overlap |
| 441 | + register_on_mob(mover) |
| 442 | + |
| 443 | +/// Triggered when someone moves from a tile that contains our monitor. |
| 444 | +/obj/effect/abstract/proximity_checker/table/proc/on_move_from_monitor(atom/movable/tracked, atom/old_loc) |
| 445 | + SIGNAL_HANDLER // COMSIG_MOVABLE_MOVED |
| 446 | + for(var/obj/effect/abstract/proximity_checker/table/mon in get_turf(tracked)) |
| 447 | + // if we're moving onto a turf that shares our stuff, keep the signals and stuff registered |
| 448 | + if(mon.deck_uid == deck_uid) |
| 449 | + return |
| 450 | + |
| 451 | + // otherwise, clean up |
| 452 | + remove_from_mob(tracked) |
0 commit comments